アプリケーションのバージョンアップって面倒ですよね。そこで便利なのがコンテナです。実行環境一式をまるごとイメージとしてカプセル化できるため、ポータビリティの向上はもちろん、副作用を抑えたバージョンアップやロールバックも簡単になり、運用コストを大幅に下げることが期待できます。
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です。
今回は事前にボリュームを用意する方法を採用しましたが、アプリの起動時に実行してもいいかもしれません。あるいはもっと楽な方法があったら、ぜひ教えてください。
