LoginSignup
1
0

More than 3 years have passed since last update.

ECS ServiceのCD PipelineをGitHub Actionsで構築

Last updated at Posted at 2020-11-04

DockerイメージのビルドからECS Serviceの更新までのCD PipelineをGitHub Actionsで構築したいと思います。

※ イメージタグの管理方法、それからECS Task DefinitionとServiceの更新はIaCのツールによって変わるので具体的なコマンドは省略します。

フロー

PlantUMLでCD Pipelineのフローを可視化したのが以下です。

plantuml (13).png

BuildとDeploy Stageに分けている理由はいくつかあります。
GitHub Actionsではneedsに含まれているジョブがスキップされたら、そのジョブもスキップされてしまいます。
なのでECS更新ジョブでDockerイメージのビルド・プッシュジョブがneedsに含めてしまうと、ECRにイメージが存在していたらECS更新ジョブもスキップされてしまいます。
だとするならば、ECRにイメージが存在しているかチェックするのを止めて毎回新イメージをビルド・プッシュすればいいですが、イメージ以外のパラメータを更新をしたい場合に不便です。
このような理由から2つのStageに分けます。

CD Pipeline on GitHub Actions

前提

  • Dockerイメージ名(タグを含む)をファイルで管理している

Workflow

Build Stage

name: Build Stage

on:
  push:
    tags:
      - 'v*'

# 複数のイメージをBulid・Pushする場合のために、共通パラメータはworkflowレベルで環境変数化
# secretsを環境変数化してもログ上ではマスキングされるので問題なし
env:
  DOCKER_BUILDKIT: 1
  SLACK_CHANNEL: '#random'
  AWS_DEFAULT_REGION: ap-northeast-1
  AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

jobs:
  precheck:
    name: Check image existence
    runs-on: ubuntu-20.04
    outputs:
      # ECRに存在していればTrue、存在していなければFalse
      sample: ${{ steps.sample.outputs.existence }}
    steps:
      - uses: actions/checkout@v2

      - name: Check sample image
        id: sample
        run: # ファイルで管理しているイメージがECRに存在しているかチェックする処理

  sample:
    name: Push sample image
    runs-on: ubuntu-20.04
    needs: [precheck]
    # ECRに存在していなければ実行
    if: ${{ ! needs.precheck.outputs.sample }}
    steps:
      - uses: actions/checkout@v2

      - name: Get image name
        id: getter
        # $(...)でファイルからイメージ名(タグを含む)を取得
        # 具体的な処理の記述は省略
        run: echo "::set-output name=image::$(...)"

      - name: Build image
        env:
          DOCKER_CONTENT_TRUST: 1
        run: docker build -t ${{ steps.getter.outputs.image }} .

      # Trivyによってイメージをスキャン
      - uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ steps.getter.outputs.image }}
          severity: 'HIGH,CRITICAL'
          exit-code: '1'
          ignore-unfixed: true

      - uses: hands-lab/push-ecr-action@v1.1
        with:
          image: ${{ steps.getter.outputs.image }}
          aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_DEFAULT_REGION }}
          aws-account-id: ${{ env.AWS_ACCOUNT_ID }}

      # 成否をSlackへ通知
      - uses: lazy-actions/slatify@master
        if: ${{ always() }}
        with:
          type: ${{ job.status }}
          job_name: ':ecr: *Push sample image to ECR*'
          channel: ${{ env.SLACK_CHANNEL }}
          url: ${{ secrets.CRM_SLACK_WEBHOOK }}

precheckジョブでECRに同じイメージ名(タグを含む)が存在しているかチェックします。
そのサンプルスクリプトを一応書いておきます。
\$repoにECRのリポジトリ名、\$tagにイメージタグを代入すれば :ok:
GitHub Actionsは空文字であればFalse、それ以外はTrueになります。以下のスクリプトの場合、ECRに該当するイメージがなければ空文字になるので上記のworkflowでもそのまま使えます。

aws ecr batch-get-image \
    --repository-name "$repo" \
    --image-ids "imageTag=$tag" \
    --query 'images[].imageId.imageTag' \
    --output text

sampleジョブはprecheckジョブの出力次第でスキップされます。
ECRに存在していないタグであれば実行します。
あとはイメージをビルドしてプッシュしているだけです。
しいて言えば、trivyの公式GitHub Actionでイメージスキャンをしています。exit-code: '1'をセットして脆弱性があればプッシュしない = workflow失敗というようにしています。

Deploy Stage

name: Deploy Stage

on:
  workflow_run:
    # Build Stage完了後にDeploy Stageを実行
    workflows:
      - Build Stage
    types:
      - completed

env:
  DOCKER_BUILDKIT: 1
  SLACK_CHANNEL: '#random'
  AWS_DEFAULT_REGION: ap-northeast-1
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

jobs:
  deploy:
    name: Deployment
    runs-on: ubuntu-20.04
    # Build Stageが成功している場合のみ実行
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - uses: actions/checkout@v2
        with:
          # Build Stageと同じコミットSHAにチェックアウト
          # refの指定がなければデフォルトブランチにチェックアウトします
          ref: ${{ github.event.workflow_run.head_sha }}

      - name: Update ECS Task Definition and Service
        run: # 具体的な処理は省略

      # 成否をSlackへ通知
      - uses: lazy-actions/slatify@master
        if: ${{ always() }}
        with:
          type: ${{ job.status }}
          job_name: ':ecs: *Deploy*'
          channel: ${{ env.SLACK_CHANNEL }}
          url: ${{ secrets.CRM_SLACK_WEBHOOK }}

Build Stage完了後にDeploy Stageを呼び出すために、workflow_runというイベントトリガーを使用します。
workflow_runは依存元のworkflowの成否によらず発火するのでDeploy StageでBuild Stageの成否を明示的にチェックする必要があります。それが if: ${{ github.event.workflow_run.conclusion == 'success' }} になります。

Reference

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0