🔥

GitHubActions 複数のCodeDeployのデプロイを新コンテナが立ち上がるまで待機するためのShell芸

に公開

GitHubActionsでCodeDeployのデプロイを行い、新コンテナが立ち上がるまで待機したい...しかも複数環境...なんてことはないでしょうか?
私はあります、そんなときに使えるShell芸を紹介します。

コード例

name: example workflow

on:
  workflow_call:
    inputs:
      ENVIRONMENT:
        required: true
        type: string
      AWS_REGION:
        required: false
        type: string
        default: ap-northeast-1
    secrets:
      AWS_ROLE_ARN:
        required: true

permissions:
  contents: read
  id-token: write

jobs:
  call-build-image:
    # 省略、buildをするジョブ
  call-deploy-app1:
    needs: call-build-image
    # 省略、CodeDeployでデプロイを実行するジョブその1
    # CodeDeployのデプロイメントIDを返すようにしています、app2とapp3も同様
  call-deploy-app2:
    needs: call-build-image
    # 省略、CodeDeployでデプロイを実行するジョブその2
  call-deploy-app3:
    needs: call-build-image
    # 省略、CodeDeployでデプロイを実行するジョブその3
  save-deployment-metadata:
    needs:
      [
        call-build-image,
        call-deploy-app1,
        call-deploy-app2,
        call-deploy-app3,
      ]
    runs-on: ubuntu-latest
    steps:
      - name: Create deployment_metadata.json
        run: |
          # 一時的な配列を作成
          DEPLOYMENT_IDS=()

          # app1のデプロイメントIDが存在する場合、配列に追加
          if [[ -n "${{ needs.call-deploy-app1.outputs.codedeploy-deployment-id }}" ]]; then
            DEPLOYMENT_IDS+=("\"${{ needs.call-deploy-app1.outputs.codedeploy-deployment-id }}\"")
          fi

          # app2のデプロイメントIDが存在する場合、配列に追加
          if [[ -n "${{ needs.call-deploy-app2.outputs.codedeploy-deployment-id }}" ]]; then
            DEPLOYMENT_IDS+=("\"${{ needs.call-deploy-app2.outputs.codedeploy-deployment-id }}\"")
          fi

          # app3のデプロイメントIDが存在する場合、配列に追加
          if [[ -n "${{ needs.call-deploy-app3.outputs.codedeploy-deployment-id }}" ]]; then
            DEPLOYMENT_IDS+=("\"${{ needs.call-deploy-app3.outputs.codedeploy-deployment-id }}\"")
          fi

          # 配列の要素をカンマで結合
          IDS_STRING=$(IFS=,; echo "${DEPLOYMENT_IDS[*]}")

          # JSONファイルを作成
          cat << EOF > deployment_metadata.json
          {
            "codedeploy_deployment_ids": [
              ${IDS_STRING}
            ]
          }
          EOF

      - name: Upload deployment_metadata.json
        uses: actions/upload-artifact@v4
        with:
          name: deployment-metadata
          path: deployment_metadata.json
          retention-days: 1

  wait-for-codedeploy-ready:
    needs: save-deployment-metadata
    runs-on: ubuntu-latest
    environment:
      name: ${{ inputs.ENVIRONMENT }}
    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ inputs.AWS_REGION }}

      - name: Download deployment_metadata.json
        uses: actions/download-artifact@v4
        with:
          name: deployment-metadata

      - name: Wait for CodeDeploy deployments to be ready
        run: |
          DEPLOYMENT_IDS=$(jq -r '.codedeploy_deployment_ids[]' deployment_metadata.json)

          for DEPLOYMENT_ID in $DEPLOYMENT_IDS; do
            DEPLOYMENT_ID=$(echo $DEPLOYMENT_ID | tr -d '"')
            echo "Waiting for deployment: $DEPLOYMENT_ID"
            SECONDS=0
            TIMEOUT=600  # 10分(600秒)

            while true; do
              STATUS=$(aws deploy get-deployment --deployment-id $DEPLOYMENT_ID --query 'deploymentInfo.status' --output text)
              echo "Current status: $STATUS"
              if [[ "$STATUS" == "Ready" ]]; then
                echo "Deployment $DEPLOYMENT_ID is ready."
                break
              elif [[ "$STATUS" == "Failed" ]]; then
                echo "Deployment $DEPLOYMENT_ID failed or stopped."
                exit 1
              fi
              if [[ $SECONDS -ge $TIMEOUT ]]; then
                echo "Timeout: Deployment $DEPLOYMENT_ID did not become Ready within 10 minutes."
                exit 1
              fi
              sleep 10
            done
          done
          echo "All deployments are ready."

ざっくり解説

  • コンテナのビルド、デプロイの処理は省略しています
    • デプロイ時にこちらのActionを使い、CodeDeployのデプロイメントIDを戻り値としています
  • 戻り値から取得したデプロイメントIDをjsonでアーティファクトとして保存します
  • アーティファクトから取り出したデプロイメントIDを使って、ループしながらaws deploy get-deploymentを実行しています
    • ステータスがReadyとなったデプロイはbreakしてループを抜けます
    • 全てがReadyとなるか、10分間のタイムアウトとなった場合にワークフローを終了します

余談

AIが発達して本当に気軽にShell芸ができるようになりました、万歳。

参考

https://docs.aws.amazon.com/cli/latest/reference/deploy/get-deployment.html
https://github.com/aws-actions/amazon-ecs-deploy-task-definition

SMARTCAMP Engineer Blog

Discussion