🛠️

GitHub Actions + AWS CodeBuildでPRごとの検証環境を作ってみた

2021/10/17に公開

はじめに

必要に応じて検証環境の追加・削除などの管理をするのが面倒くさいので、PR作成時に検証環境を構築、PRマージ・クローズ時に検証環境を削除ができないか考えてみました。
今回の作成したGitHub Actions ワークフロー、Terraformなどはこちらのリポジトリにあります。

概要図

概要図

どのように実現したか

実現あたり、コンテナイメージのプッシュ、ECS サービスのデプロイはGitHub Actions、Terraformの実行はAWS CodeBuildで行うことにしました。

なぜTerraformの実行はCodeBuildを利用するようにしたかというと、CodeBuildはVPC内のリソース(今回の場合はAurora Serverless)にアクセスできるからです。
これによってアプリケーション、DBマイグレーション時に使用するMySQL ユーザーをTerraformで作成することができます。(検証環境とはいえrootユーザーを使用したくなかったため)

まずはVPC、ALBなどの共通で使用するリソースを作成してきます。
Route53でALBのaliasの値を検証環境で使用するドメインのワイドルカードを指定し、すべての名前解決がALBに向くようにします。

共通リソース

その後、PRのオープン時にGitHub ActionsとAWS CodeBuildを使用してECS、Aurora Serverless、SQSなどの個別リソースをPRごとに作成します。
ECS サービスのTarget Groupは共通リソースとして作成されたALBのListener Ruleに追加されます。

PRのマージ・クローズ時にはGitHub ActionsとAWS CodeBuildを使用してオープン時に作成した個別リソースを削除します。

個別リソース

ワークフロー

PR作成時

PR作成時のワークフローは下記のとおりです。

PR作成時

GitHub ActionsからのCodeBuild実行は以下のとおりです。
step Import source credentialsではCodeBuildがリポジトリに接続するための認証情報を更新しています。

env:
  ENV: pr${{ github.event.pull_request.number }}
  TFSTATE_BUCKET: ${{ secrets.TFSTATE_BUCKET }}
  IMAGE_TAG: ${{ github.sha }}

jobs:
  CreateEnv:
    runs-on: ubuntu-20.04
    timeout-minutes: 10

    steps:
    - name: Checkout
      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: ap-northeast-1

    - name: Import source credentials
      run: aws codebuild import-source-credentials --token ${{ secrets.PERSONAL_ACCESS_TOKEN }} --server-type GITHUB --auth-type PERSONAL_ACCESS_TOKEN

    - name: Run CodeBuild
      uses: aws-actions/aws-codebuild-run-build@v1
      with:
        project-name: qa-apply
        env-vars-for-codebuild: |
          ENV,
          TFSTATE_BUCKET

PR作成時に実行するCodeBuildの内容は下記のとおりです。
こちらでは、tfenvのインストールとworkspace作成、terraform applyを行っています。

version: 0.2

phases:
  install:
    runtime-versions:
      golang: 1.16
    commands:
      - git clone https://github.com/tfutils/tfenv.git ~/.tfenv
      - ln -s ~/.tfenv/bin/* /usr/local/bin
      - tfenv install 1.0.7
  build:
    commands:
      - cd terraform/qa
      - terraform init -backend-config="bucket=$TFSTATE_BUCKET"
      - terraform workspace new $ENV
      - terraform apply -var qa_common_tfstate_bucket=$TFSTATE_BUCKET -auto-approve

検証環境の個別リソース作成に成功すると下記のように検証環境のURLをコメントします。

PR作成時のコメント

URLをクリックすると、アプリケーションがデプロイされています。(今回はデプロイ完了まで待機していないので、反映までタイムラグがあります)

Web

PRへの追加コミット時

追加コミット時のワークフローは下記のとおりです。

追加コミット時

今回はDBマイグレーション時のタスク実行、ECS サービスのデプロイはaws-actionsを使わずに面白法人カヤック様が提供しているecspressoを使用しています。
ecspressoを使用した理由としては、タスク定義、サービス定義でtfstateの値をできるからです。
Terraformで構築しているときは本当にこれが便利です。

  DbMigrate:
    runs-on: ubuntu-20.04
    timeout-minutes: 10
    needs: [BuildWeb, BuildWorker, BuildMigrate]

    steps:
    - name: Checkout
      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: ap-northeast-1

    - name: Setup ecspresso
      uses: kayac/ecspresso@v1
      with:
        version: v1.6.2

    - name: Run DB Migration
      working-directory: ./ecspresso/migrate
      run: ecspresso run --config config.yaml

  DeployApp:
    runs-on: ubuntu-20.04
    timeout-minutes: 10
    needs: DbMigrate

    steps:
    - name: Checkout
      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: ap-northeast-1

    - name: Setup ecspresso
      uses: kayac/ecspresso@v1
      with:
        version: v1.6.2

    - name: Deploy ECS service
      working-directory: ./ecspresso/app
      run: ecspresso deploy --config config.yaml --no-wait

PRマージ・クローズ時

PRマージ・クローズ時のワークフローは下記のとおりです。

PRマージ・クローズ時

PRマージ・クローズ時に実行するCodeBuildの内容は下記のとおりです。
こちらでは、tfenvのインストールとworkspace削除、terraform destroyを行っています。

version: 0.2

phases:
  install:
    runtime-versions:
      golang: 1.16
    commands:
      - git clone https://github.com/tfutils/tfenv.git ~/.tfenv
      - ln -s ~/.tfenv/bin/* /usr/local/bin
      - tfenv install 1.0.7
  build:
    commands:
      - cd terraform/qa
      - terraform init -backend-config="bucket=$TFSTATE_BUCKET"
      - terraform workspace select $ENV
      - terraform destroy -var qa_common_tfstate_bucket=$TFSTATE_BUCKET -auto-approve
      - terraform workspace select default
      - terraform workspace delete $ENV

検証環境の個別リソース削除に成功すると下記のようにコメントします。

PRマージ・クローズ時のコメント

さいごに

今回は構成から省きましたが、CodeDeployによるBlue/Greenデプロイへの変更、ECS サービスのオートスケーリング追加、コスト削減のため各AWSサービスエンドポイントの追加をしてもよさそうですね。

Discussion