インフラ

GithubActionsでECSデプロイ コンテナビルドを並行処理する

こんにちは、ちゃりおです。

ECSデプロイにGithubActionsを使用しています。
コンテナビルドに時間がかかるコンテナが複数ある際に、ECSデプロイへのデプロイまでに時間がかかることがありました。

そこで複数コンテナのビルドを並行化を試してみました。

GithubActionsでコンテナビルドを並行処理する

最終的にこうなりました。
apacheとnodeのコンテナビルドを並列で実行して、その後ECSにデプロイしています。

name: 'deploy'

on:
  workflow_dispatch:
  push:

env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY_HTTPD: githubactions-parallel-build-1
  ECR_REPOSITORY_NODE: githubactions-parallel-build-2
  ECS_SERVICE: githubactions-parallel
  ECS_CLUSTER: test
  ECS_TASK_DEFINITION: githubactions-parallel
  CONTAINER_NAME_HTTPD: httpd
  CONTAINER_NAME_NODE: node

jobs:
  Build1:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.httpd-image.outputs.image}}
    steps:
      - uses: actions/checkout@v2
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: httpd Build, tag, and push image to Amazon ECR
        id: httpd-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ env.ECR_REPOSITORY_HTTPD}}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f docker/httpd/Dockerfile .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          echo "::set-output name=image::$ECR_REPOSITORY:$IMAGE_TAG"
  Build2:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.node-image.outputs.image}}
    steps:
      - uses: actions/checkout@v2
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: node Build, tag, and push image to Amazon ECR
        id: node-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ env.ECR_REPOSITORY_NODE}}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f docker/node/Dockerfile docker/node
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG 
          echo "::set-output name=image::$ECR_REPOSITORY:$IMAGE_TAG"

  Deploy:
    runs-on: ubuntu-latest
    needs: [Build1, Build2]
    steps:
      - uses: actions/checkout@v2
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: Download task definition
        run: |
          aws ecs describe-task-definition --task-definition $ECS_TASK_DEFINITION --query taskDefinition > task-definition.json
      - name: Render Amazon ECS task definition for first container
        id: render-container-httpd
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.CONTAINER_NAME_HTTPD }}
          image: ${{ steps.login-ecr.outputs.registry }}/${{ needs.Build1.outputs.image }}
      - name: Modify Amazon ECS task definition with second container
        id: render-container-node
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ steps.render-container-httpd.outputs.task-definition }}
          container-name: ${{ env.CONTAINER_NAME_NODE }}
          image: ${{ steps.login-ecr.outputs.registry }}/${{ needs.Build2.outputs.image }}

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.render-container-node.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

はまったところ

最初コンテナビルド時のOutputを公式ドキュメント通り、以下のようにしていました。

echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"

しかし、Deployのジョブ側でOutputを取得しようとすると何も取得できません。
原因は、シークレットを含む値のためGithubActionsに送られていないことでした。

jobs..outputs
シークレットを含む出力はランナー上で編集され、GitHub Actionsには送られません。

GitHub Actionsのワークフロー構文

$ECR_REGISTRYの部分を見てみると「***dkr...」のようになっていてシークレットが含まれているのがわかります。

回避策として、Build側では$ECR_REGISTRYを含まない形に変更し、Deploy側でもECRログインの処理を実行しました。

# Build Output
echo "::set-output name=image::$ECR_REPOSITORY:$IMAGE_TAG"

# Deploy Image取得
image: ${{ steps.login-ecr.outputs.registry }}/${{ needs.Build1.outputs.image }}

改善したいところ

AWSのキーセットとECRへのログインが毎回必要で書き方が冗長になっています。
書き方直していい感じにしたいです。

Amzonで「AWS」の本を見てみる

楽天で「AWS」の本をみてみる!!

cdk
GithubActions + CodeBuildでCDKを自動デプロイするこんにちは、ちゃりおです。 GithubActonsとCodeBuildでCDKを自動デプロイする方法についてです。 GibhuAc...
Github Actionsを使ってCDKを自動デプロイするこんにちは、ちゃりおです。 CDK手動反映だと反映漏れが発生することもあり、今回はGithub Actionsを使ってCDKを自動デプ...
cdk
「cdk diff」に必要なIAMポリシーこんにちは、ちゃりおです。 CI時に「cdk diff」を行いたいことがありました。 ローカルから実行するときは、「cdk deplo...