Github Actionsでワンショットタスクを実行する
最近はサーバーサイドの仕事が増えているので、 記事もサーバーサイドよりの技術が増えるかもしれません。
ECS Fargateにアプリケーションをデプロイする際、 あわせてDBスキーマの更新マイグレーションを行いたいユースケースについてです。
結論
steps: - uses: actions/checkout@v2 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }} aws-region: ap-northeast-1 role-session-name: GithubActionDeployment - name: Install AWS CLI shell: bash run: | if ! [ -x "$(command -v aws)" ]; then curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install --update aws --version fi - name: ECS run migration task env: task_definition: # task definition arn cluster: # cluster vpc_configuration: # vpn conf container_overrides: '{"containerOverrides": [{"name": "web", "command": ["php", "artisan", "migrate"] }]}' run: | task_arn=$(aws ecs run-task \ --region ap-northeast-1 \ --launch-type FARGATE \ --cluster '${{ env.cluster }}' \ --network-configuration 'awsvpcConfiguration=${{ env.vpc_configuration }}' \ --task-definition '${{ env.task_definition}}' \ --overrides '${{ env.container_overrides }}' \ --query tasks[0].taskArn --output text)
背景
ワンショットタスク
DBマイグレーションのようなコマンド実行後に正常終了するECSタスクのことを指しています。ECSタスクを構成するessentialなコンテナが全て終了するとECSタスクも終了するため、起動後正常終了するDockerコンテナと考えて良いです。
通常のECSタスクを構成するコンテナはCMD ["nginx", "-g", "daemon off;"]
のようにフォアグラウンドで起動し続けるため意識することがありませんでしたが、バックグラウンドで起動するプロセスや起動後終了するプロセスの場合CMD実行後コンテナは正常終了します。
よってGithub ActionsでDBマイグレーションのようなワンショットタスクを実行したい場合コンテナのCMDにマイグレーションコマンドを指定したECSタスクを作成すれば良いことになります。
方法
run-task
aws cliのrun-taskコマンド[1]のoverridesオプションでCMDなどコンテナの定義を上書きすることができるのでこれを採用します。
{ "containerOverrides": [ { "name": "string", "command": ["string", ...], "environment": [ { "name": "string", "value": "string" } ... ], "environmentFiles": [ { "value": "string", "type": "s3" } ... ], "cpu": integer, "memory": integer, "memoryReservation": integer, "resourceRequirements": [ { "value": "string", "type": "GPU"|"InferenceAccelerator" } ... ] } ... ], "cpu": "string", "inferenceAcceleratorOverrides": [ { "deviceName": "string", "deviceType": "string" } ... ], "executionRoleArn": "string", "memory": "string", "taskRoleArn": "string", "ephemeralStorage": { "sizeInGiB": integer } }
例としてLaravelのmigrationをワンショットで実行することを考えるとコマンドは以下のようになります。
aws ecs run-task \ --region ap-northeast-1 \ --launch-type FARGATE \ --cluster '${{ env.cluster }}' \ --network-configuration 'awsvpcConfiguration=${{ env.vpc_configuration }}' \ --task-definition '${{ env.task_definition}}' \ --overrides '{"containerOverrides": [{"name": "web", "command": ["php", "artisan", "migrate"] }]}' \ --query tasks[0].taskArn --output text)
以上の内容をGithub Actionsに落とすと冒頭のようなworkflowになります。
その他検討した方法
専用のtaskを定義する
CMDのみが異なるサーバーを起動しているタスクとほぼ同一のタスクを定義する必要があり冗長なので却下しました。
ENTRYPOINT
CMDではなくENTRYPOINTであればコンテナ立ち上げ時に複数命令を実行可能。ただフォアグラウンド命令の前にDBマイグレーションの実行を挟むことになるので、ワンショットにはならず柔軟性に欠けるため却下しました。
execute-command
execute-command[2]でecsタスクに対してコマンドを実行できるようになったのでそれを利用する方法です。やっていることはCMDをoverrideしてrun-taskすることと変わらないはずなので今のところ要件は満たせると思っています。
aws ecs execute-command \ --cluster '${{ env.cluster }}' \ --task '${{ env.task_definition}}' \ --container web \ --interactive \ --command "php artisan migrate"
amazon-ecs-deploy-task-definition
通常のタスクのデプロイに使っているaws公式のworkflowを利用する方法。現在はone-offタスクの実行は未サポートらしい[3]、 上述の他の方法で対応可能なので今後もサポートはされない気がしています。
ref
[1] run-task — AWS CLI 1.25.58 Command Reference