Zenn
📝

GO言語アプリケーションのCI/CD環境構築

2025/03/27に公開

初めに

エンジニアとして働き始めて2年目であり、極力気をつけましたが誤った情報を発信しているかもしれません。
その際はぜひコメントにてご指摘いただければ幸いです。

CI/CDとは

CI/CDとは「Continuos Integration(CI)」と「Continuous Delivery/Deployment(CD)」の略称となります。

CIについて

複数の開発者がコード変更を頻繁にリポジトリに統合(インテグレーション)し、各統合で自動化されたビルドやテストで検証します。

  • エラーの早期発見
    • 新しいコードが既存のコードと統合される度に、自動的にビルドとテストが実行されるため、失敗した場合バグやエラーの早期発見につながる
  • 共同開発の促進
    • 様々な開発者が各々ローカルでIDEを使用し、コードを変更を加えるとマージした際、多くの競合(コンフリクト)が発生し得る。コード変更がリポジトリに頻繁にプッシュされることにより、コードの競合を減らす事ができます。
  • 自動化されたテストとビルドによるフィードバック
    • 自動化されたテストやビルドにより、開発者自身が変更の影響をすぐに知る事ができます。また必要な対応も迅速に行う事ができます。

CDについて

CDは「継続的デリバリー」又は「継続的デプロイメント」を指します。
CIで自動化されたテスト、及びビルドされた検証済みコードを自動的にリリースする事です。

  • 継続的デリバリー

    • 新しい機能や修正を開発、検証、本番などの環境にデプロイする準備を整える役割を持ちます。
    • 開発者によるアプリケーションへの変更に対して、バグがないかを自動的にテストし、リポジトリにアップデートします。
  • 継続的デプロイメント

    • リポジトリから開発、検証、本番などの環境への開発者による変更のリリースを自動化し、クライアントが利用できるようするというものです。

CI/CDに利用できるツール一覧

1. Jenkins

  • 概要: オープンソースの自動化サーバーで、多くのプラグインを利用して自分のニーズに合わせてカスタマイズ可能です。
  • 公式サイト: https://www.jenkins.io

2. GitLab CI/CD

3. CircleCI

  • 概要: 高性能かつスケーラブルなCI/CDツールで、Dockerサポートや多様な環境へのデプロイが可能です。
  • 公式サイト: https://circleci.com

4. GitHub Actions

  • 概要: GitHubリポジトリと直接統合可能なCI/CDツールで、コードのすべての段階を自動化するためのワークフローを作成できます。
  • 公式サイト: https://github.com/features/actions

5. Travis CI

  • 概要: GitHubリポジトリと連携して自動ビルドとテストを行うクラウドベースのCIツールです。オープンソースプロジェクトに特に人気があります。
  • 公式サイト: https://travis-ci.org

今回はネットや弊社のナレッジが多く、ランキングサイトにも上位にランク付けされていたGithub Actionsを使用して、CI/CD環境構築していきます。

GitHub Actionsの流れ

1.開発者が VSCode でコードを変更し、GitHub に push する。
2.GitHub Actions が push をトリガーにビルド・テストを実行し、Docker を使ってコンテナイメージを作成する。
3.GitHub Actions が作成したイメージを AWS ECR にプッシュ(アップロード)する。
4.AWS ECS が ECR に保存されたイメージをプル(取得)してコンテナを起動しデプロイが完了する。

CI/CDの必要性

こちら自分なりに書いてたら長くなったので別記事にまとめてみました。
https://zenn.dev/goat_spike/articles/010547de1b1b41

実装コード

CI/CDファイル

name: ci-cd

on:
  push:
    branches:
      - '**'
      # 現在は全てのブランチのpushで作動。buildステップはdevelopのみ作動

jobs:
  ########## CI ##########
  ######### Lint #########
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.24

      - name: Install golang ci-lint
        run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5

      - name: Run golang ci-lint
        run: golangci-lint run --timeout=10m
  ######### Test #########
  test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set environment variables from secrets
        run: |
          echo "APP_TIMEZONE=${{ secrets.APP_TIMEZONE }}" >> $GITHUB_ENV
          echo "DB_HOST=${{ secrets.DB_HOST }}" >> $GITHUB_ENV
          echo "DB_PORT=${{ secrets.DB_PORT }}" >> $GITHUB_ENV
          echo "DB_USER=${{ secrets.DB_USER }}" >> $GITHUB_ENV
          echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> $GITHUB_ENV
          echo "DB_NAME=${{ secrets.DB_NAME }}" >> $GITHUB_ENV
          echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}" >> $GITHUB_ENV
          echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> $GITHUB_ENV
          echo "REDMINE_APP_ACCESS_KEY=${{ secrets.REDMINE_APP_ACCESS_KEY }}" >> $GITHUB_ENV
          # etc...

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.24

      - name: Cache Go modules
        uses: actions/cache@v3
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: ${{ runner.os }}-go-

      - name: Install dependencies
        run: go mod tidy

      - name: Run unit tests
        run: go test -v ./...
  ########## Build ##########
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [lint, test] # lint, testジョブが成功した場合のみ作動
    if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' # developとmainブランチのみ作動
    permissions:
      id-token: write
      contents: read
    outputs:
      image-app: ${{ steps.build-app-image.outputs.image }}
      image-app2: ${{ steps.build-app2-image.outputs.image }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

        # github.event.before(前のコミット)とgithub.sha(現在のコミット)の間で変更されたファイルのリストを取得し変更の有無を変数に格納
      - name: Detect changes in codebase
        id: detect-changes
        run: |
          app2_changed="false"
          app_changed="false"
          if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^app2/'; then
            app2_changed="true"
          else if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^/'; then
            app_changed="true"
          fi
          echo "app2_changed=$app2_changed" >> $GITHUB_ENV
          echo "app_changed=$app_changed" >> $GITHUB_ENV

        # マルチプラットフォームのビルドを可能にするための設定
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

        # マルチプラットフォームのDockerイメージビルドをサポート、及びビルドプロセスの最適化
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

        # AWS credentialsの設定
      - name: Configure AWS credentials from IAM Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

        # AWS CLIを使用してECRに対して認証トークンを取得
        # 認証トークンを使用してDockerクライアントがECRにログイン      
      - name: Login to Amazon ECR for app2
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2
        with:
          registry: "123456789011,123456789012"  # ECRのレジストリを指定
    
        # Dockerイメージをビルド
      - name: Build, tag, and push docker image to Amazon ECR for app
        if: env.app_changed == true
        id: build-app-image
        env:
          ECR_REGISTRY: 123456789011.dkr.ecr.ap-northeast-1.amazonaws.com
          REPOSITORY: exapmle_repository
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG -f ./docker/app/Dockerfile .
          docker push $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG
          echo "image-app=$ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"

      - name: Build, tag, and push docker image to Amazon ECR for app2
        if: env.app2_changed == true
        id: build-app2-image
        env:
          ECR_REGISTRY: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com
          REPOSITORY: exapmle_repository2
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG -f ./docker/app2/Dockerfile .
          docker push $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG
          echo "image-app2=$ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"
  ########## CD ##########
  ##### Deploy for staging environment #####
  deploy-staging:
    name: Deploy to Amazon ECS for staging environment
    runs-on: ubuntu-latest
    environment:
      name: STG
    needs: [build] # buildジョブが成功した場合のみ作動
    if: github.ref == 'refs/heads/develop' # developブランチのみ作動
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Debug environment variables
        run: |
          echo "image-app: ${{ needs.build.outputs.image-app }}"
          echo "image-app2: ${{ needs.build.outputs.image-app2 }}"
    
      - name: Checkout code
        uses: actions/checkout@v4

        # AWS credentialsの設定
      - name: Configure AWS credentials from IAM Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

        # Amazon ECSタスク定義の新しいイメージIDを埋め込む
      - name: Fill in the new image ID in the Amazon ECS task definition for app
        id: task-def-app
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ secrets.TASK_DEFINITION_ARN }}
          container-name: app
          image: ${{ env.image-app}}

      - name: Fill in the new image ID in the Amazon ECS task definition for app2
        id: task-def-app2
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ steps.task-def-api.outputs.task-definition }}
          container-name: app2
          image: ${{ env.image-app2 }}

        # Amazon ECSタスク定義をデプロイ
      - name: Deploy Amazon ECS task definition for app
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def-app.outputs.task-definition }}
          service: ${{ secrets.CLUSTER_SERVICE_NAME }}
          cluster: ${{ secrets.CLUSTER_NAME }}
          wait-for-service-stability: true

      - name: Deploy Amazon ECS task definition for app2
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def-app2.outputs.task-definition }}
          service: ${{ secrets.CLUSTER_SERVICE_NAME }}
          cluster: ${{ secrets.CLUSTER_NAME }}
          wait-for-service-stability: true

  ##### Deploy for production environment #####
  deploy-production:
    name: Deploy to Amazon ECS for production environment
    runs-on: ubuntu-latest
    environment:
      name: PRD
    needs: [ build ] # buildジョブが成功した場合のみ作動
    if: github.ref == 'refs/heads/main' # mainブランチのみ作動
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Debug environment variables
        run: |
          echo "image-app: ${{ needs.build.outputs.image-app }}"
          echo "image-app2: ${{ needs.build.outputs.image-app2 }}"

      - name: Checkout code
        uses: actions/checkout@v4

        # AWS credentialsの設定
      - name: Configure AWS credentials from IAM Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

        # Amazon ECSタスク定義の新しいイメージIDを埋め込む
      - name: Fill in the new image ID in the Amazon ECS task definition for app
        id: task-def-app
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ secrets.TASK_DEFINITION_ARN }}
          container-name: app
          image: ${{ env.image-app}}

      - name: Fill in the new image ID in the Amazon ECS task definition for app2
        id: task-def-app2
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ steps.task-def-api.outputs.task-definition }}
          container-name: app2
          image: ${{ env.image-app2 }}

        # Amazon ECSタスク定義をデプロイ
      - name: Deploy Amazon ECS task definition for app
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def-app.outputs.task-definition }}
          service: ${{ secrets.CLUSTER_SERVICE_NAME }}
          cluster: ${{ secrets.CLUSTER_NAME }}
          wait-for-service-stability: true

      - name: Deploy Amazon ECS task definition for app2
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def-app2.outputs.task-definition }}
          service: ${{ secrets.CLUSTER_SERVICE_NAME }}
          cluster: ${{ secrets.CLUSTER_NAME }}
          wait-for-service-stability: true

CI/CDワークフローの可視化

Screenshot 2025-03-21 at 16.27.54.png

順を追って解説できればと思います。

CI/CD トリガー

on:
  push:
    branches:
      - '**'

現在は全てのブランチのpushで作動。buildステップはdevelopまたはmainブランチへのpushで作動するようにしています

job1 ~ lint ~

lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.24

      - name: Install golang ci-lint
        run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5

      - name: Run golang ci-lint
        run: golangci-lint run --timeout=10m

ここはGo言語の代表的なlinterである、golangci-lintを使用しています
Go言語のソースコードを解析して、コーディングスタイルや潜在的なエラーを検出してくれます

job2 ~ test ~

test:
    name: Test
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set environment variables from secrets
        run: |
          echo "APP_TIMEZONE=${{ secrets.APP_TIMEZONE }}" >> $GITHUB_ENV
          echo "DB_HOST=${{ secrets.DB_HOST }}" >> $GITHUB_ENV
          echo "DB_PORT=${{ secrets.DB_PORT }}" >> $GITHUB_ENV
          echo "DB_USER=${{ secrets.DB_USER }}" >> $GITHUB_ENV
          echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> $GITHUB_ENV
          echo "DB_NAME=${{ secrets.DB_NAME }}" >> $GITHUB_ENV
          echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}" >> $GITHUB_ENV
          echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> $GITHUB_ENV
          echo "REDMINE_APP_ACCESS_KEY=${{ secrets.REDMINE_APP_ACCESS_KEY }}" >> $GITHUB_ENV
          echo "COGNITO_USER_POOL_ID=${{ secrets.COGNITO_USER_POOL_ID }}" >> $GITHUB_ENV
          echo "AWS_S3_REGION=${{ secrets.AWS_S3_REGION }}" >> $GITHUB_ENV
          echo "AWS_S3_ENDPOINT=${{ secrets.AWS_S3_ENDPOINT }}" >> $GITHUB_ENV
          echo "AWS_S3_ACCESS_KEY_ID=${{ secrets.AWS_S3_ACCESS_KEY_ID }}" >> $GITHUB_ENV

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: 1.24

      - name: Cache Go modules
        uses: actions/cache@v3
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: ${{ runner.os }}-go-

      - name: Install dependencies
        run: go mod tidy

      - name: Run unit tests
        run: go test -v ./...

ここではソースコードに対して単体テストを行なっています
.envに記述してある情報はリポジトリー上では参照できないため、単体テストで使用する環境変数はGithub Secretsから参照しています
Github Secretsについて
様々なymlファイルから参照できるだけでなくセキュリティー的にも良さそうです

job3 ~ build ~

~ build section part1 ~

########## Build ##########
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [lint, test] # lint, testジョブが成功した場合のみ作動
    if: github.ref == 'refs/heads/develop' # developブランチのみ作動
    permissions:
      id-token: write
      contents: read
    outputs:
      image-app: ${{ steps.build-app-image.outputs.image }}
      image-app2: ${{ steps.build-app2-image.outputs.image }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

        # github.event.before(前のコミット)とgithub.sha(現在のコミット)の間で変更されたファイルのリストを取得し変更の有無を変数に格納
      - name: Detect changes in codebase
        id: detect-changes
        run: |
          app_changed="false"
          app2_changed="false"
          if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^app2/'; then
            app2_changed="true"
          else if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^/'; then
            app_changed="true"
          fi
          echo "app2_changed=$app2_changed" >> $GITHUB_ENV
          echo "app_changed=$app_changed" >> $GITHUB_ENV

こちらはneedslint, testのジョブを設定し、この二つが成功した場合のみbuildジョブが動くように設定しています
今回ECRにあるリポジトリーはappapp2とします。
前のコミットと現在のコミットの差分を比較し、ECRのリポジトリのどちらかまたは両方のビルドを行います

~ build section part2 ~

        # マルチプラットフォームのビルドを可能にするための設定
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

        # マルチプラットフォームのDockerイメージビルドをサポート、及びビルドプロセスの最適化
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

        # AWS credentialsの設定
      - name: Configure AWS credentials from IAM Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

        # AWS CLIを使用してECRに対して認証トークンを取得
        # 認証トークンを使用してDockerクライアントがECRにログイン      
      - name: Login to Amazon ECR for app2
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2
        with:
          registry: "123456789011,123456789012"  # ECRのレジストリを指定
Docker Buildsについて

Marketplaceに良さげなコードがあったので今回はDocker buildxを使用してイメージをビルドしていきたいと思います

AIに聞いたらdocker との主な違いは下記のようです

特長:

  • ​マルチプラットフォームビルド​: docker buildx buildコマンドを使用すると、複数のプラットフォーム(例: linux/amd64, linux/arm64)のためのイメージを同時にビルドできます
  • ​効率的なビルドキャッシュ​: BuildKitエンジンを使用することで、効率的なキャッシュの利用とビルドスピードの向上が可能です
  • ​複数のビルドノード​: 分散ビルドがサポートされており、クラスタリングや複数のビルドノードでビルドを行うことができます
  • ​高度な機能​: Buildxは、カスタムコンテントトラスト、ビルドの依存関係管理など、より高度なビルド機能を提供します

利点:

  • ​マルチプラットフォーム​: 複数のプラットフォーム向けにコンテナを同時にビルドでき、マルチアーキテクチャのサポートが容易です
  • ​パフォーマンス​: BuildKitの効率的なキャッシュ管理と並列ビルドにより、ビルドパフォーマンスが向上します
  • ​柔軟性​: 複雑なビルド要求に対応でき、多くのカスタマイゼーションが可能です

https://github.com/marketplace/actions/build-and-push-docker-images

IAM Roleについて

初めはAWSから張り出されたcredentialを参照していたのですが、ベテランエンジニアによるとAWSでIAMロールを作成し使用するのが一般的だそうです

  1. ID プロパイダを作成
  2. IAM ロールを作成
  3. GitHubActionsでOIDCを使用しAWS認証を行う
- name: Configure AWS credentials from IAM Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
-         aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
-         aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+         role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

IAMロール自体は下記のような構成で作成しております。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<AWS account ID>:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
                    "token.actions.githubusercontent.com:sub": "repo:<GitHubユーザー名>/<GitHubリポジトリ名>:*"
                }
            }
        }
    ]
}

こちらの記事を参考にしました

~ build section part3 ~

        # Dockerイメージをビルド
      - name: Build, tag, and push docker image to Amazon ECR for app
        if: env.app_changed == true
        id: build-app-image
        env:
          ECR_REGISTRY: 123456789011.dkr.ecr.ap-northeast-1.amazonaws.com
          REPOSITORY: exapmle_repository
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG -f ./docker/app/Dockerfile .
          docker push $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG
          echo "image-app=$ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"

      - name: Build, tag, and push docker image to Amazon ECR for app2
        if: env.app2_changed == true
        id: build-app2-image
        env:
          ECR_REGISTRY: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com
          REPOSITORY: exapmle_repository2
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG -f ./docker/app2/Dockerfile .
          docker push $ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG
          echo "image-app2=$ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"

build section part1にてapp_changedapp2_changedに対し値を入れていると思います
こちらの変数を利用して差分があるファイルのみイメージをビルドしています

echo "image-app=$ECR_REGISTRY/$REPOSITORY:$IMAGE_TAG" >> "$GITHUB_OUTPUT"

このコマンドはdeployジョブで各ビルド結果のイメージIDをを使用する為変数に格納しています
それぞれappのデプロイにはimage-appを、app2のデプロイにはimage-app2を使用します
このように変数に格納することでそれぞれのジョブごとに値の受け渡しをすることが可能です

job4 ~ deploy ~

~ deploy section part1 ~

##### Deploy for staging environment #####
  deploy-staging:
    name: Deploy to Amazon ECS for staging environment
    runs-on: ubuntu-latest
    environment:
      name: STG
    needs: [build] # buildジョブが成功した場合のみ作動
    if: github.ref == 'refs/heads/develop' # developブランチのみ作動
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

        # AWS credentialsの設定
      - name: Configure AWS credentials from IAM Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
name: STG

これはGithubのSettingsにおいてEnvironmentsという項目があり、そこにステージング環境と本番環境用でそれぞれの変数を定義しています。
ECRのリポジトリはそれぞれ異なるクラスター名やサービス名を持っているためGithubのEnvironmentsを使用して管理しています。

~ deploy section part2 ~

        # Amazon ECSタスク定義の新しいイメージIDを埋め込む
      - name: Fill in the new image ID in the Amazon ECS task definition for app
        id: task-def-app
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ secrets.TASK_DEFINITION_ARN }}
          container-name: app
          image: ${{ env.image-app}}

      - name: Fill in the new image ID in the Amazon ECS task definition for app2
        id: task-def-app2
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ steps.task-def-api.outputs.task-definition }}
          container-name: app2
          image: ${{ env.image-app2 }}

ここでbuildセクションで定義されたそれぞれのイメージIDが格納してある変数を参照しています
このような手法を取ることでジョブ間の値を受け渡すことができるだけでなく、ビルドとデプロイのステップをまとめなくてもいいので読みやすいコードになったかなと思います

~ deploy section part3 ~

# Amazon ECSタスク定義をデプロイ
      - name: Deploy Amazon ECS task definition for app
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def-app.outputs.task-definition }}
          service: ${{ secrets.CLUSTER_SERVICE_NAME }}
          cluster: ${{ secrets.CLUSTER_NAME }}
          wait-for-service-stability: true

      - name: Deploy Amazon ECS task definition for app2
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def-app2.outputs.task-definition }}
          service: ${{ secrets.CLUSTER_SERVICE_NAME }}
          cluster: ${{ secrets.CLUSTER_NAME }}
          wait-for-service-stability: true

最後はEnvironmentsに定義してあるクラスターとサービス名を参照し、デプロイ完了です

[引用]

CI/CDについて

GitHub Actionsについて

環境変数とシークレット

IAM ロール

GitHub Actionsのセキュリティについて

CI

CD

Discussion

ログインするとコメントできます