🏗️

GitHub ActionsでECSデプロイワークフロー

に公開

はじめに

現在、社内ではインフラの標準化が進んでおり、TerraformでAWS構築をしています。
そこで、CodeBuildを使った標準化が存在していますが、あえてActionsで作成を行いました。

できるだけ簡潔に。ほぼコピペで動くことをポリシーとして。

実装した内容

  • Stg, Prd環境別デプロイ
  • ECRへプッシュ
  • task-definition.jsonの取得
  • task-definition.jsonの書き換え
  • task-definition.jsonの反映

デプロイするコンテナ

  • Nginx
  • Wordpress

今回はWordpressのアプリということで、上記二つのみですがコンテナ定義を変更するだけなので、モダンな環境にしても動くと思われます。

実装

tl;dr
今回は二つのコンテナを使用するため、再利用性を考慮してビルド処理を別ファイルに切り出しています。

.github/actions/container-build/action.yml

name: Container Build
inputs:
    ecr-repository-uri:
        required: true
        description: ECRリポジトリのURI
    dockerfile-path:
        required: true
        description: Dockerfileのパス
    build-context:
        required: true
        description: Docker build の context パス
outputs:
    container-image:
        value: ${{ steps.meta.outputs.tags }}
        description: ビルドしたコンテナイメージ
runs:
    using: composite
    steps:
        -   uses: aws-actions/amazon-ecr-login@v2
        -   uses: docker/metadata-action@v5
            id: meta
            with:
                images: ${{ inputs.ecr-repository-uri }}
                tags: |
                    type=raw,value=latest
        -   uses: docker/build-push-action@v5
            with:
                push: true
                file: ${{ inputs.dockerfile-path }}
                context: ${{ inputs.build-context }}
                tags: ${{ steps.meta.outputs.tags }}
                labels: ${{ steps.meta.outputs.labels }}

.github/workflows/deploy.yml

name: Deploy
on:
  push:
    branches:
      - staging
      - main
  workflow_dispatch:
env:
  ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ID }}:role/${{ secrets.ROLE_NAME }}
  SESSION_NAME: gh-oidc-${{ github.run_id }}-${{ github.run_attempt }}
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      
      - name: Set environment variables for staging
        if: github.ref == 'refs/heads/staging'
        run: |
          echo "TASK_DEFINITION_NAME=stg-app" >> $GITHUB_ENV
          echo "TASK_DEFINITION_FAMILY=stg-app" >> $GITHUB_ENV
          echo "CLUSTER_NAME=stg" >> $GITHUB_ENV
          echo "SERVICE_NAME=app" >> $GITHUB_ENV
          echo "NGINX_ECR_REPOSITORY_URI=${{ vars.NGINX_ECR_REPOSITORY_URI_STAGING }}" >> $GITHUB_ENV
          echo "WORDPRESS_ECR_REPOSITORY_URI=${{ vars.WORDPRESS_ECR_REPOSITORY_URI_STAGING }}" >> $GITHUB_ENV

      - name: Set environment variables for main
        if: github.ref == 'refs/heads/main'
        run: |
          echo "TASK_DEFINITION_NAME=prd-app" >> $GITHUB_ENV
          echo "TASK_DEFINITION_FAMILY=prd-app" >> $GITHUB_ENV
          echo "CLUSTER_NAME=prd" >> $GITHUB_ENV
          echo "SERVICE_NAME=app" >> $GITHUB_ENV
          echo "NGINX_ECR_REPOSITORY_URI=${{ vars.NGINX_ECR_REPOSITORY_URI_MAIN }}" >> $GITHUB_ENV
          echo "WORDPRESS_ECR_REPOSITORY_URI=${{ vars.WORDPRESS_ECR_REPOSITORY_URI_MAIN }}" >> $GITHUB_ENV

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ env.ROLE_ARN }}
          role-session-name: ${{ env.SESSION_NAME }}
          aws-region: ap-northeast-1
      - uses: ./.github/actions/container-build/
        id: build-nginx
        with:
          ecr-repository-uri: ${{ env.NGINX_ECR_REPOSITORY_URI }}
          build-context: .
          dockerfile-path: docker/nginx/Dockerfile
      - uses: ./.github/actions/container-build
        id: build-wordpress
        with:
          ecr-repository-uri: ${{ env.WORDPRESS_ECR_REPOSITORY_URI }}
          build-context: .
          dockerfile-path: docker/wordpress/Dockerfile

      - name: Get current ECS task definition
        id: get-task-definition
        run: |
          aws ecs describe-task-definition \
          --task-definition ${{ env.TASK_DEFINITION_NAME }} \
          --query taskDefinition \
          --output json > task-definition.json

      - name: Render new ECS task definition (Nginx)
        id: render-nginx
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ steps.get-task-definition.outputs.task-definition }}
          container-name: nginx
          task-definition-family: ${{ env.TASK_DEFINITION_FAMILY }}
          image: ${{ steps.build-nginx.outputs.container-image }}

      - name: Render new ECS task definition (WordPress)
        id: render-wordpress
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
            task-definition: ${{ steps.render-nginx.outputs.task-definition }}
            container-name: wordpress
            image: ${{ steps.build-wordpress.outputs.container-image }}

      - name: Deploy ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          cluster: ${{ env.CLUSTER_NAME }}
          service: ${{ env.SERVICE_NAME }}
          task-definition: ${{ steps.render-wordpress.outputs.task-definition }}
          wait-for-service-stability: false

解説

AWS認証

まずAWS認証の権限についてはOIDCを使用しています。下記記事を参考にしました。
https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws
${{ secrets.AWS_ID}}, ${{secrets.ROLE_NAME}}についてはここで作成したIAM情報をGitHubのシークレットに保存してください。

大まかな処理の流れ

  1. GitHub Actionsが発火したブランチ別に環境変数を取得
  2. AWS認証
  3. Dockerfileをビルドし、ECRにプッシュ
  4. task-definition.jsonの取得
  5. task-definition.jsonの書き換え
  6. task-definition.jsonの反映

AWSへの操作については下記Actionを使用しています。
https://github.com/aws-actions/amazon-ecs-deploy-task-definition

https://github.com/aws-actions/amazon-ecs-render-task-definition

注意点

wait-for-service-stability: falseとしていますが、trueとした場合、ECSが安定状態になるまで待機をします。GitHub Actionsは実行時間の従量課金のため、デプロイを実行した時点で処理を終了させています。
ちなみに、現在の実行時間は1デプロイあたり2m弱くらいです。

株式会社ソニックムーブ

Discussion