Github Actions で aws copilot の複数サービスを並列でデプロイ(処理)する

15519 ワード

動機

aws copilotは デプロイ 時に、ひとつひとつのサービスをデプロイ処理し、サービスが複数あるときはすごい時間がかかります。
ひとつのサービスで 5分弱× 30回(デプロイ回数)です。
サービスがひとつや2つなら、ローカルでデプロイコマンドを叩きますが、サービスが30個ぐらい運用しているので、ターミナルのタブを増やしてコマンドを叩くのもやぶさかではありません。

Github Actions の matrix を使用

Github Actions の matrix は 並列で job を実行できる仕組みです。
最大256個の job が実行できるようですね!

流れ

  1. copilot svc ls --json で サービスを抽出
  2. matrixの引数に渡して、各デプロイに必要なjobを実行させる

設定yamlファイル

.github/workflows/deploy-all-svcs.yml
name: 🚀 Copilot Deploy All Services

on:
  push:
    branches: [deploy] # deploy で push されたとき のみ 実行
  workflow_dispatch:
    branches: [ master ]

jobs:
  set-matrix:
    name: Set All Services
    runs-on: ubuntu-latest # { "services": [{ "app": "copilot", "name": "copilot-svc-1", "type": "Load Balanced Web Service" },... ] } => ["copilot-svc-1", "copilot-svc-2" ...]
    outputs:
      svcs: ${{ steps.set-matrix.outputs.value }}

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: AWS Configure And Setup Copilot
        uses: ./.github/actions/setup-copilot
        with:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Set Outputs
        id: set-matrix
        run: |
          allsvcs=$(copilot svc ls --json | jq -c '[.services[].name]')
          echo "::set-output name=value::${allsvcs}"

  deploy-all:
    needs: set-matrix
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        svc: ${{ fromJSON(needs.set-matrix.outputs.svcs) }}
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: AWS Configure And Setup Copilot
        uses: ./.github/actions/setup-copilot
        with:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: 🎡 Deploy All Services
        shell: bash
        run: |
          copilot svc deploy --name ${{ matrix.svc }}
.github/actions/setup-copilot/action.yml
name: Setup Copilot

description: Login AWS For Setup Copilot

inputs:
  AWS_ACCESS_KEY_ID:
    description: AWS Access Key ID
    default: ''
    required: true
  AWS_SECRET_ACCESS_KEY:
    description: AWS Secret Access Key
    default: ''
    required: true
  AWS_REGION:
    description: AWS Region
    default: ap-northeast-1
    required: false

runs:
  using: 'composite'
  steps:
    - name: Checkout
      uses: actions/checkout@v2
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ inputs.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ inputs.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ inputs.AWS_REGION }}
    - name: Install Copilot CLI
      shell: bash
      run: |
        mkdir -p $GITHUB_WORKSPACE/bin
        # download copilot
        curl -Lo copilot-linux https://github.com/aws/copilot-cli/releases/download/v1.8.3/copilot-linux && \
        # make copilot bin executable
        chmod +x copilot-linux && \
        # move to path
        mv copilot-linux $GITHUB_WORKSPACE/bin/copilot && \
        # add to PATH
        echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH

ポイント

  1. トリガーは deploy ブランチ に push されたとき または、Github Actionsのコンソールから手動実行にしていますが、任意でOKです。
  2. set-matrix で、copilot svc ls を実行するための copilot をインストール
  3. 2の結果を 次のjobで並列実行するため、取得したサービス名の配列を outputs で渡す
  4. deploy-allの job で 再度 copilot を インストール(job間で環境は引き継がれない)
  5. copilot svc deploy で サービス名を指定した copilot コマンドを実行
  6. action.yml は 2と4で copilot のインストールと aws のセットアップが重複した処理を共通化した内容です。using: 'composite'を使うことで、処理を共通化しています。

結果

デプロイに 30サービス × 5分 = 150分 が 10分弱ぐらいで完了します。

おまけ:slackに通知

すべて job が 完了したら結果をまとめてslackに流したい場合は、slack-workflow-statusというactionsが公開されているのでそれを使ってみても良さそうです。
自分が実装したときは、下記のymlファイルになりました。

jobs:

  # deploy-all までの job は省略

  slack-workflow-status:
      if: always()
      name: Post Workflow Status To Slack
      needs: deploy-all
      runs-on: ubuntu-latest
      steps:
        - name: 💁 Copilot Deploy All Services Result
          uses: Gamesight/slack-workflow-status@master
          with:
            repo_token: ${{ github.token }}
            slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
            channel: "#your-channel-name"
            include_jobs: true