先に結論
Elastic Beanstalk のオートスケーリング設定が有効になっている環境で複数の EC2 インスタンスに対してデプロイする際に、どれか一つの EC2 インスタンス上で DB マイグレーションのコマンドを実行したいとします。
その場合、以下のような設定ファイルを追加してデプロイすると実現できます。
container_commands:
01_migrate: # リーダー判定用の一時ファイルを作成
command: "touch /tmp/leader_only"
leader_only: true
files:
"/opt/elasticbeanstalk/hooks/appdeploy/post/10_post_migrate.sh":
mode: "000755"
owner: root
group: root
content: |
#!/bin/bash -eu
if [ -f /tmp/leader_only ] # リーダーインスタンスでのみ実行
then
# リーダー判定用の一時ファイルを削除する
rm /tmp/leader_only
# 最後に起動した Docker コンテナ上で migrate を実行する (CakePHP3の場合)
docker exec `docker ps -l -q` bin/cake migrations migrate
fi
上記の説明
上記の方法は container_commands:
で直接 migrate せずに files:
でシェルスクリプトを作成し、そのスクリプトの中で migrate するという回りくどい手順になっています。その辺りの事情をデプロイの流れと共に説明します。
Elastic Beanstalk (Docker) のデプロイ時の流れ
Elastic Beanstalk には デプロイ処理の各タイミングで任意スクリプトをフック実行する為の仕組み があります。予め決められたディレクトリにシェルスクリプトを置いておくと、ファイル名の辞書順に実行してくれる仕組みです。
このフック実行用のディレクトリには、インスタンスを作成した段階で Elastic Beanstalk を構成するいくつかのスクリプト(以下を参照)が入っていますが、ユーザーが任意のスクリプトを追加することもできます。
1. pre hook スクリプトの実行:
- 1-1. /opt/elasticbeanstalk/hooks/appdeploy/pre/00clean_dir.sh
- デプロイの準備。/var/app/current の初期化を行う。
- 1-2. /opt/elasticbeanstalk/hooks/appdeploy/pre/01unzip.sh
- アプリケーションを unzip して /var/app/current へ展開する。
- 1-3. /opt/elasticbeanstalk/hooks/appdeploy/pre/02loopback-check.sh
- 1-4. /opt/elasticbeanstalk/hooks/appdeploy/pre/03build.sh
- /var/app/current にある
Dockerfile
ORDockerrun.aws.json
を元に Docker Image を作成 (docker build
ORdocker pull
) する。
- /var/app/current にある
2. .ebextensions の container_commands
の実行:
- 2-1. .ebextensions/*.config に container_commands がある場合、順にそのコマンドを実行する。
- 既に新バージョンのアプリは既に unzip 済みのため、それらに触れることができる。
- このとき、新バージョン docker コンテナは まだ起動していない。
参考: .ebextensions の container_commands の挙動メモ - Qiita
3. enact hook スクリプトの実行:
- 3-1. /opt/elasticbeanstalk/hooks/appdeploy/enact/00run.sh
- 03build.sh で作成したイメージを
docker run -d
する。
- 03build.sh で作成したイメージを
- 3-2. /opt/elasticbeanstalk/hooks/appdeploy/enact/01flip.sh
-
staging-app
をcurrent-app
にする。
-
4. post hook スクリプトの実行:
- 4-1. /opt/elasticbeanstalk/hooks/appdeploy/post/00_clean_imgs.sh
- 未使用のイメージやコンテナを削除する。
- 4-2. /opt/elasticbeanstalk/hooks/appdeploy/post/01_monitor_pids.sh
上記のような流れのため、デプロイ時に docker exec
したい場合は 00run.sh
以降で行う必要があります。container_commands のタイミングでは実現できません。
その為 .ebextensions で post hook 用のシェルスクリプトを作成して、その中で docker exec
するという方法を取っています。
また、オートスケーリングの設定をしている場合、作成したフックスクリプトはデプロイされる全てのコンテナ上で実行されるため、そのままだとマイグレーションが何度も実行されることになります。
それでは困るのでリーダーインスタンスでのみ事前に /tmp/leader_only
のファイルを作成しておき、そのファイルが存在する場合にのみ post hook スクリプトを実行するようにしています。
container_commands の leader_only オプションについて引用:
Elastic Beanstalk によって選択された単一のインスタンスでコマンドを実行するのみです。リーダー専用コンテナコマンドは、他のコンテナコマンドより前に実行されます。