LayerX エンジニアブログ

LayerX の エンジニアブログです。

GitHub Actions から、ECS でのワンショットタスクを実行する

この記事は、6月から始まっている #LXベッテク月間 6日目の記事です。
昨日は talos さんの 「そろそろあの課題着手しなきゃ!」チームに日々蓄積されるIssueを、Notionで簡単にプロジェクト化〜TODO管理する方法 でした 🐬


バクラク請求書でリードエンジニアをしている @yyoshiki41(中川佳希)です!
最近たどり着いた git で差分があるか確認するコマンドは、git diff --exit-code --quiet です。

バクラクシリーズでは先月(2022年5月)、経費精算をリリースしました! 今後も更にコーポレート業務全体のデジタル化をサポートしていきます。

この記事では GitHub Actions から ECS でのワンショットタスクを実行する仕組みを紹介します。

処理の流れ

このあたりは先駆者となるツールも複数ありますが、Actions 上で実現したいことは大まかに以下です。

  1. コンテナの Build, レジストリーへのPush
  2. Task Definitions の作成
  3. ecs run task の実行
  4. タスクが完了するまで待つ
  5. 完了後のログ(CloudWatch Logs)をひろう

※ ECS スタックの作成や、Actions 側で設定必要なIAM(task 実行や CloudWatch Logs のログ取得)は割愛 🙏

そして最後に、一連をまとめた Composite Action を紹介します。

1. コンテナの Build, レジストリーへのPush

docker 公式でサポートされている GitHub Actions があるため、それを利用するだけです。

ビルド時間の短縮に欠かせない cache についても buildx 側で、GitHub Actions cache をサポート(experimental)しており、レジストリでキャッシュする場合同様に複雑な箇所は特にありません。

buildx で、GitHub Actions cache を使う場合の例

      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      -
        name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: user/app:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

2. Task Definitions の定義

コンテナの設定とワンショットで実行するタスク定義を行います。

aws-actions/amazon-ecs-render-task-definition で、ローカルの task-definition.json ファイルを上書きします。

    - name: Render Amazon ECS task definition
      id: render-web-container
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: web
        image: amazon/amazon-ecs-sample:latest
        environment-variables: "LOG_LEVEL=info"

3. ecs run task の実行

普段ならここで aws-actions/amazon-ecs-deploy-task-definition を利用するところですが、今回実現したいのは「コンテナのサービスイン => タスク終了 => ログをひろう」という一連のサイクルであるため、AWS が用意する API 叩いて実行する必要があります。

$ aws ecs run-task \
    --region {{ region }} \
    --launch-type {{ launch-type }} \
    --cluster {{ cluster }} \
    --network-configuration 'awsvpcConfiguration={{ vpc_configuration }}' \
    --task-definition {{ family:revision }} \
    --overrides '{"containerOverrides": [{"name": {{ container-name}}, "command": {{ command }} }]}'

最後の --overrideds の部分では、実行時に動的に変えたいコマンドオプションをわたしています。

4. タスクが完了するまで待つ

AWS CLI では、wait オプションが用意されています。(6秒ごとにヘルスチェックをおこない、完了までポーリング)

$ aws ecs wait tasks-stopped \
    --cluster {{ cluster }} \
    --tasks {{ task_arn }}

5. 完了後のログ(CloudWatch Logs)をひろう

AWS CLI で実行したタスクIDのログを取得する方法でこなれたものがないため、ecs-cli を用います。

$ ecs-cli logs --timestamps \
    --cluster {{ cluster }} \
    --task-id {{ task_id }} \
    --task-def {{ family }}:{{ revision }}

ちなみに、AWS CLI で該当のロググループから取得する場合はこちら。(--since でタイムスタンプでのフィルタ)

$ aws logs tail /ecs/your-log-group-name --since=10m

Composite Action にまとめる

github.com

今回、複数レポジトリから使用したいこともあり、一連処理をまとめた Composite Action にしました。(内部の動きは上で紹介したとおりです)

タスク実行後に、PR に対してコメントを残す例

      - name: Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ./infra/migrations/task-definition.json
          container-name: payer-stg-db-migration
          image: image-uri

      - name: Run ECS task
        id: run-task
        uses: yyoshiki41/ecs-run-task-action@v0.0.5
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          task-definition-family: dev-migration
          cluster: dev
          subnets: '["subnet-**"]'
          security-groups: '["sg-***"]'
          container-name: migration
          command: '["up"]'

      - name: Create PR Comment
        run: |
          gh pr comment ${{ github.event.pull_request.number }} --body "<details><summary>Logs</summary>

          \`\`\`
          ${{ steps.run-task.outputs.logs }}
          \`\`\`
          </details>"

GitHub Actions で出来ることが増えいていて嬉しい限りです。今後も CI/CD やデイリージョブを GitHub Actions に載せていきたいと思います!

LayerX ではエンジニア採用をオープンしています。カジュアルに話をする機会などもありますのでお待ちしております!

open.talentio.com