この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
デプロイ時間を短縮するために、GitHub Actions上でCDKを用いてスタックを並列にデプロイすることを検討したことはありませんか。 CircleCIではコマンド定義が可能ですが、GitHub Actions(以降GHAとする)の場合、スタック分ジョブを定義するか、独自のAction定義をする方法があります。今回は、簡単に(=共通化しつつ、記述量の少ない形)スタックを並列にデプロイする方法を紹介します。
※ 注意
CDK自体で並列にスタックをデプロイするオプションについては、いくつか進行中のPRが存在します。本体に取り込まれた場合この方法は必要なくなることが予想されます。
GitHub Actionsの設定
方針
設定の肝は、cdk list
とGHAのstrategyのmatrix
を活用します。実行結果のイメージは以下のようになります。
設定内容
例えばLambdaとAPIGatewayを使ったサーバーレスAPIをデプロイする際に、Lambdaだけ並列にデプロイしたい場合は、以下のようにします。設定のポイントは後述します。
name: Deploy
env:
PROJECT_NAME: projectName
on:
push:
branches:
- develop
- master
jobs:
setup:
name: Setup
runs-on: ubuntu-latest
outputs:
stageName: ${{ steps.setenv.outputs.stageName }}
stacks: ${{ steps.setenv.outputs.stacks }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node
uses: actions/setup-node@v1
with:
node-version: "16.x"
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: "**/node_modules"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
- name: Install
run: yarn install --frozen-lockfile
- name: Set environment
id: setenv
run: |
stageName=""
if ${{ github.ref == 'refs/heads/develop' }}; then
stageName=dev
elif ${{ github.ref == 'refs/heads/master' }}; then
stageName=prd
else
exit 1
fi
echo "::set-output name=stageName::$stageName"
api_lambda_stacks=$(yarn --silent \
cdk list --context stageName=$stageName *-api-lambda-* \
--long --json| \
jq -c 'map(.id)')
echo "::set-output name=stacks::$(echo $api_lambda_stacks | jq -c)"
deploy-api-lambda:
name: deploy-api-lambda
needs:
- setup
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
stack: ${{fromJson(needs.setup.outputs.stacks)}}
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node
uses: actions/setup-node@v2
with:
node-version: '16.x'
- name: Restore node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: "**/node_modules"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ロール名
aws-region: リージョン名
- name: Deploy
shell: bash
run: |
yarn deploy -c stageName=${{ needs.setup.outputs.stageName }} \
${{ matrix.stack }} \
--require-approval never
設定のポイント
setup
というジョブで、デプロイするスタック名のリストを動的に生成し、outputの変数に設定します。後述しますが、本変数をstrategyのmatrixに指定することになります。
# zsh
yarn --silent cdk list --context stageName=dev \*-api-lambda-\* --long --json| jq -c 'map(.id)'
["dev-a-api-lambda-article","dev-b-api-lambda-auth","dev-c-api-lambda-doc","dev-d-api-lambda-git-hub-app","dev-e-api-lambda-user"]
コマンドオプションの補足
- yarnの
--silent
オプションは標準出力にyarnの出力が含まれないようにするため - cdkの
--long
--json
オプションはlist結果をjqでパースしやすくするため
前述のoutputの変数をstrategyのmatrixに指定して並列にデプロイすることが実現できます。
deploy-api-lambda:
name: deploy-api-lambda
...
strategy:
fail-fast: false
matrix:
stack: ${{fromJson(needs.setup.outputs.stacks)}}
...
- name: Deploy
shell: bash
run: |
yarn deploy -c stageName=${{ needs.setup.outputs.STAGE_NAME }} \
${{ matrix.stack }} \
--require-approval never
Lambdaがデプロイされたあと、APIGatewayをデプロイしたい場合は、以下のコードで実行順の制御が可能です。
deploy-api:
name: deploy-api
needs:
- setup
- deploy-api-lambda:
steps:
# APIスタックのデプロイロジックを書く
最後に
CircleCIのコマンド定義が欲しくなったら基本的にはstrategyのmatrixで代用可能か考えてみると良さそうです。 またNodejsFunctionだと、各スタックデプロイ時に全てソースに対してビルドが走ってしまうため、クレジット消費が無駄になってしまう問題があります。事前にビルド、キャッシュし、NodejsFunctionは利用しないことで防ぐことが可能です。esbuildは高速なので、気にならない程度だとは思いますがご注意ください。