とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

PleasanterをDockerで運用する際のパラメータ問題について

アプリケーションのバージョンアップって面倒ですよね。そこで便利なのがコンテナです。実行環境一式をまるごとイメージとしてカプセル化できるため、ポータビリティの向上はもちろん、副作用を抑えたバージョンアップやロールバックも簡単になり、運用コストを大幅に下げることが期待できます。

Pleasanterは1.5.0.0で.NET 10に対応しました。そして1.5.0.0の公式Dockerイメージは、含まれている.NETが10に変更されています。コンテナを使えばこうした環境側のバージョンアップを考えなくてもいいため、とても楽ですよね。

Pleasanterのバージョンアップにおける問題点

さて、アプリケーションを動かすためには、様々な設定をコンテナ内に持ち込む必要があります。たとえばDBの接続パラメータであったり、などです。とはいえ個別の設定情報を埋め込んだ、自分専用コンテナイメージをビルドするのは悪手です。ましてや機密情報などは、絶対に埋め込んではいけません。というわけで設定パラメータは、実行時に環境変数などを介して渡すのがセオリーです。ですがPleasanterが環境変数経由で渡せるのは、DB接続情報とSMTPサーバーの情報程度のみで、それ以外の設定やサーバースクリプトなどは、Parametersディレクトリ以下にファイルとして持たせなくてはなりません。ここがコンテナと非常に相性が悪い部分です。

またバージョンアップ時には、最新版のパラメータJSONと現状のバージョンを比較し、正しくマージする必要があります。新バージョンでは新しいパラメータが増えていることがあり、古いバージョンのJSONをそのまま持ち込むと、Pleasanterが起動しないこともよくあるためです。このバージョンアップ作業の面倒さは、Pleasanterをオンプレで運用している人なら、常々感じているのではないでしょうか。

変更点のみを管理し、最新版にマージする運用

そこで筆者は、Parametersディレクトリをまるごと別ボリュームとして用意し、コンテナにマウントする方法を考えました。

Pleasanterの公式Dockerイメージには、デフォルトの「/app/App_Data/Parameters」ディレクトリが含まれています。これをコピーした上で変更したいパラメータをマージし、その成果物をアプリケーションコンテナにマウントするわけです。この方法であれば、自分が変更したいパラメータだけを管理しておけばよく、また常に動かしたいバージョンのコンテナに対して変更点をマージするため、設定ファイルのバージョン違いによるパラメータの不足も防げます。

それではDocker Composeを使って、パラメータの用意とPleasanterの起動を行ってみましょう。

パッチしたボリュームの用意

まず.envに、以下のようにAPP_VERSIONを定義しておきます。これはコンテナのタグと、ボリュームの名前に使用します。同じボリュームを使い回すよりも、バージョンごとに異なるボリュームを用意した方が、切り戻しが楽だと考えたためです。

APP_VERSION=1.5.0.0

パラメータの変更内容は、patchesディレクトリ以下に用意します。変更対象のJSONと同じ名前のファイルに、「変更したい内容のみ」を記述してください。例えばMail.jsonへのパッチであれば、以下のようになります。

{
    "SmtpHost": "smtp.example.com",
    "SmtpPort": 587,
    "SmtpUserName": "XXXXXX",
    "SmtpPassword": "XXXXXXXX",
    "SmtpEnableSsl": true,
    (...略...)
}

同様に、「ExtendedHtmls」「ExtendedScripts」「ExtendedServerScripts」あたりが必要な場合も、ディレクトリごとpatchesディレクトリの中に置いておきます。

続いてパッチ用のシェルスクリプトを用意します。scriptsディレクトリを作り、その中にsetup-parameters.shというスクリプトを以下の内容で作成してください。

#!/bin/sh

OFFICIAL_DIR="/app/App_Data/Parameters"
PATCH_DIR="/patches"
DEST_DIR="/app_data_params"

echo "Starting parameter merge..."

apt-get update
apt-get install -y jq

cp -r "${OFFICIAL_DIR}/"* "${DEST_DIR}/"

cp -r "${PATCH_DIR}/ExtendedHtmls" "${DEST_DIR}/"
cp -r "${PATCH_DIR}/ExtendedScripts" "${DEST_DIR}/"
cp -r "${PATCH_DIR}/ExtendedServerScripts" "${DEST_DIR}/"

for patch_file in $(ls ${PATCH_DIR}/*.json)
do
    filename=$(basename "$patch_file")
    official_file="${OFFICIAL_DIR}/${filename}"

    if [ -f "$official_file" ]; then
        echo "Merging ${filename}..."
        jq -s add "$official_file" "$patch_file" > "${DEST_DIR}/${filename}"
    else
        echo "Warning: ${filename} not found in official parameters. Copying as is."
        cp "$patch_file" "${DEST_DIR}/${filename}"
    fi
done

echo "Parameter setup complete."

内容は見ての通りです。/app/App_Data/Parametersの中身を/app_data_paramsに一旦コピーした上で、patches以下にある「ExtendedHtmls」「ExtendedScripts」「ExtendedServerScripts」を追加しています。その後、patches以下にあるJSONをjqコマンドを使ってマージし、コピー先に上書きしています。

compose.yamlは以下の通りです。指定したバージョンのPleasanter公式イメージを使い、上記のスクリプトを実行しています。実行するスクリプト、パッチ内容、マージ後のディレクトリを取り出すためのボリュームを、スクリプト内で使用するディレクトリへマウントしています。

services:
  parameter-setup:
    image: mirror.gcr.io/implem/pleasanter:${APP_VERSION}
    user: root
    entrypoint: ["/bin/sh", "/scripts/setup-parameters.sh"]
    volumes:
      - ./patches:/patches:ro
      - ./scripts:/scripts:ro
      - parameters-vol:/app_data_params

volumes:
  parameters-vol:
    name: "parameters-vol-${APP_VERSION}"

docker compose upすれば、マージ後のParametersディレクトリを含む「parameters-vol-${APP_VERSION}」というDockerボリュームが手に入ります。

Pleasanter本体の起動

あとは作成したボリュームを使い、Pleasanterを起動すればよいわけです。pleasanter本体のcompose.yamlの一部を抜粋すると、以下のようになります。

services:
  pleasanter:
    container_name: pleasanter
    restart: unless-stopped
    image: docker.io/implem/pleasanter:${APP_VERSION}
    volumes:
      - parameters-vol:/app/App_Data/Parameters
    environment:
      Implem.Pleasanter_Rds_PostgreSQL_SaConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_SaConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_OwnerConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_OwnerConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_UserConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_UserConnectionString}
    ports:
      - "8080:8080"

volumes:
  parameters-vol:
    name: "parameters-vol-${APP_VERSION}"

この方式を採用してから、バージョンアップが格段に楽になりました。APP_VERSIONを書き換えて新しいボリュームを作り、(CodeDefinerを必要ならば実行して)アプリを立ち上げ直すだけです。ちなみにCodeDefinerもCompose化しているため、pull & runのみでOKです。

今回は事前にボリュームを用意する方法を採用しましたが、アプリの起動時に実行してもいいかもしれません。あるいはもっと楽な方法があったら、ぜひ教えてください。