🚀

CDKで管理しているECSをGitHubActionsでデプロイする方法

2023/12/11に公開

概要

CDKで管理しているリソースを自動リリースする仕組み。実際に本番運用している構成となります。
まずは、実際の完成形のGitHubActionsのコードです。

構成
/
├── batch
│    └── Dockerfile
└── cdk

name: dev-batch-deploy

on:
  push:
    branches:
      - main
    paths:
      - 'batch/**'
  workflow_dispatch:

env:
  CDK_APPENV: dev
  AWS_REGION: ap-northeast-1
  AWS_ACCOUNT_ID: <ACCOUNT ID>
  AWS_ACCTIONS_ROLE: <dev-github-role>
  AWS_ECR_REPOSITORY: <dev-batch-ecr>
  AWS_TAG_PATAMETEER_STORE: <docker-batch-tag>

permissions:
  id-token: write
  contents: read

jobs:
  dev-deploy-app-ecs:
    name: dev-deploy-app-ecs
    runs-on: ubuntu-latest
    steps:
      # https://github.com/actions/checkout
      - name: Checkout
        uses: actions/checkout@v4

      # https://github.com/aws-actions/configure-aws-credentials
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.AWS_ACCTIONS_ROLE }}
          aws-region: ${{ env.AWS_REGION }}

      # https://github.com/aws-actions/amazon-ecr-login
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
          cd batch
          docker build -t $ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - name: Update ImageTag Parameter Store
        id: update-tag
        run: |
          aws ssm put-parameter --name $AWS_TAG_PATAMETEER_STORE --value ${{ github.sha }} --type String --overwrite

      - name: Set Up Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install CDK
        id: install-cdk
        run: |
          npm install -g aws-cdk
          cdk --version

      - name: Run CDK
        id: run-cdk
        run: |
          cd cdk
          npm install
          cdk deploy batch-stack --context appEnv=$CDK_APPENV --require-approval never

解説

トリガー

batch配下にアプリが存在し、それらがmainブランチに入った事をきっかけに動作させます。CDKなどの修正などで動かしたい場合も考慮し、手動実行workflow_dispatchも設定しています。これはdev環境用なのでpushを使っていますが、prdはworkflow_dispatchのみで運用を行っています。

on:
  push:
    branches:
      - main
    paths:
      - 'batch/**'
  workflow_dispatch:

環境変数

devとprdでは意図的にyaml自体を分けています。共通化を考えましたが、規模が小さいことから安全に変更していけるよう分けています。envに設定している内容はGitHubのシークレットを使って運用しても良いでしょう。

env:
  CDK_APPENV: dev
  AWS_REGION: ap-northeast-1
  AWS_ACCOUNT_ID: <ACCOUNT ID>
  AWS_ACCTIONS_ROLE: <dev-github-role>
  AWS_ECR_REPOSITORY: <dev-batch-ecr>
  AWS_TAG_PATAMETEER_STORE: <docker-batch-tag>

Jobs Step 1 Checkout

Ubuntuにコードをチェックアウトしています。説明すべき点は少ないです。

jobs:
  dev-deploy-app-ecs:
    name: dev-deploy-app-ecs
    runs-on: ubuntu-latest
    steps:
      # https://github.com/actions/checkout
      - name: Checkout
        uses: actions/checkout@v4

Jobs Step 2 OIDC認証

GitHub用にAWSのIAM管理したくないためOIDCを利用します。
GitHub ActionsでAWSにOIDC認証してみたという記事で詳しくAWS側の設定方法を説明していますので参照ください。

      # https://github.com/aws-actions/configure-aws-credentials
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.AWS_ACCTIONS_ROLE }}
          aws-region: ${{ env.AWS_REGION }}

Jobs Step 3 ECR Login

ECRにログインします。
aws-actions/amazon-ecr-loginを利用すればたったこれだけ。とてもシンプル

      # https://github.com/aws-actions/amazon-ecr-login
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

Jobs Step 4 Build,Tag,Push

コンテナをビルドし、GitHubハッシュ値でタグをつけてECRへプッシュします。GitHubハッシュ値をタグに使う理由は、いつ時点で作成したコンテナかをタグからプログラムを探せるようにするためです。1つ前のタグを切り戻しをどの箇所にすべきかも簡単に探せることを意識しています。

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
          cd batch
          docker build -t $ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/$AWS_ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

Jobs Step 6 Update Parameter Store -> Tag

Dockerにつけたタグをパラメーターストアに更新します。これは、後続のCDKデプロイ時にコンテナのデグレが起きないよう同期を取るためです。ECSのデプロイ運用でLatest運用せずCDKと実際のECRバージョンを乖離させない方法で詳しく説明していますのでご参照ください。

      - name: Update ImageTag Parameter Store
        id: update-tag
        run: |
          aws ssm put-parameter --name $AWS_TAG_PATAMETEER_STORE --value ${{ github.sha }} --type String --overwrite

Jobs Step 7,8,9 Node & CDK Install -> deploy

cdk deployコマンドを愚直に実行することが最大のポイントです。GitHubActionsに存在しているECSデプロイは使ってはいけません。
最初、私はECSのタスク定義を更新するActionsを使っていましたが、CDK Deployと共存できない以下の問題が発生しました。

  • CDKが裏で作り出す(.cdkout配下にできるファイル)Cloudformationは機械的な生成のため、cdk diffをするだけではActionsを利用したデプロイ後に差分なしと言われる(ここまでは良い)
  • しかし、実際にcdk deployを動かすとActionsで動かしたあとでは存在していないはずの差分が生まれているようでデプロイが動いてしまう。
      - name: Set Up Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install CDK
        id: install-cdk
        run: |
          npm install -g aws-cdk
          cdk --version

      - name: Run CDK
        id: run-cdk
        run: |
          cd cdk
          npm install
          cdk deploy batch-stack --context appEnv=$CDK_APPENV --require-approval never

さいごに

私のサンプルは少人数で小規模なプロジェクトでした。大規模では上のサンプルに自動テストSlack通知を追加すると良いと思います。読んでくださった皆様の参考になりましたら幸いです。

Discussion