sky’s 雑記

主にAndroidとサーバーサイドの技術について記事を書きます

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

[2] デバッグ用にAmazon ECS Exec を使用 - Amazon ECS

[3] run one-off task (e.g. migrations) · Issue #54 · aws-actions/amazon-ecs-deploy-task-definition · GitHub