この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
開発の規模が大きくなると、CI/CDに時間がかかるようになります。特にクラウド環境を用いた開発で、インフラ構成までコードで管理している場合、差分の確認やインフラサービスの更新で処理の待ち時間が発生します。
各機能やサービスに依存関係がないのであれば、処理を並列に実行することで、デプロイ等にかかる時間を短縮することが出来ます。デプロイ以外にもビルドやテストで時間がかかっているのであれば、機能単位などに分割して並列に実行させるのも良いと思います。
本記事ではAWS環境へのデプロイをGitHub Actionsで並列に実行させてみます。
ワークフローを実装
AWS環境にデプロイするワークフローを実装します。.github/workflows
にYAMLファイルを作成すると、プッシュ時にGitHub Actionsがワークフローを実行します。
以下のワークフローでは、指定したブランチにプッシュされた際、パッケージのインストールやビルドとテスト、ブランチに対応したAWS環境への並列デプロイを実行します。
.github/workflows/deploy-aws.yml
name: Deploy AWS
env:
PROJECT_NAME: sample
on:
push:
branches:
- develop
- release
- main
jobs:
setup:
name: Setup
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
AWS_ACCOUNT_ID: ${{ steps.setenv.outputs.AWS_ACCOUNT_ID }}
STAGE_NAME: ${{ steps.setenv.outputs.STAGE_NAME }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node
uses: actions/setup-node@v1
with:
node-version: "14.x"
- name: Set environment
id: setenv
env:
ITG_AWS_ACCOUNT_ID: 111111111111
STG_AWS_ACCOUNT_ID: 222222222222
PRD_AWS_ACCOUNT_ID: 333333333333
run: |
if ${{ github.ref == 'refs/heads/develop' }}; then
echo "::set-output name=AWS_ACCOUNT_ID::$ITG_AWS_ACCOUNT_ID"
echo "::set-output name=STAGE_NAME::itg"
elif ${{ github.ref == 'refs/heads/release' }}; then
echo "::set-output name=AWS_ACCOUNT_ID::$STG_AWS_ACCOUNT_ID"
echo "::set-output name=STAGE_NAME::stg"
elif ${{ github.ref == 'refs/heads/main' }}; then
echo "::set-output name=AWS_ACCOUNT_ID::$PRD_AWS_ACCOUNT_ID"
echo "::set-output name=STAGE_NAME::prd"
else
echo "Invalid branch name."
exit 1
fi
- 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: Cache build files
uses: actions/cache@v2
env:
cache-name: cache-build-files
with:
path: "**/build"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.sha }}
- name: Install
run: make install
- name: Build
run: make build
test:
name: Test
needs:
- setup
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node
uses: actions/setup-node@v1
with:
node-version: "14.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: Lint
run: make lint
- name: Unit test
run: make test-unit
deploy-1:
name: Deploy 1
needs:
- setup
- test
runs-on: ubuntu-latest
timeout-minutes: 10
env:
AWS_DEFAULT_REGION: ap-northeast-1
AWS_ACCOUNT_ID: ${{ needs.setup.outputs.AWS_ACCOUNT_ID }}
STAGE_NAME: ${{ needs.setup.outputs.STAGE_NAME }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node
uses: actions/setup-node@v1
with:
node-version: "14.x"
- 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_DEFAULT_REGION }}
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.STAGE_NAME }}-${{ env.PROJECT_NAME }}-assume-role
role-duration-seconds: 3600
- 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: Restore build files
uses: actions/cache@v2
env:
cache-name: cache-build-files
with:
path: "**/build"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.sha }}
- name: Deploy
run: |
make deploy TARGET=stack-name-1 STAGE=$STAGE_NAME
deploy-2:
name: Deploy 2
needs:
- setup
- test
runs-on: ubuntu-latest
timeout-minutes: 10
env:
AWS_DEFAULT_REGION: ap-northeast-1
AWS_ACCOUNT_ID: ${{ needs.setup.outputs.AWS_ACCOUNT_ID }}
STAGE_NAME: ${{ needs.setup.outputs.STAGE_NAME }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node
uses: actions/setup-node@v1
with:
node-version: "14.x"
- 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_DEFAULT_REGION }}
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.STAGE_NAME }}-${{ env.PROJECT_NAME }}-assume-role
role-duration-seconds: 3600
- 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: Restore build files
uses: actions/cache@v2
env:
cache-name: cache-build-files
with:
path: "**/build"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.sha }}
- name: Deploy
run: |
make deploy TARGET=stack-name-2 STAGE=$STAGE_NAME
解説
ワークフローの実装について、要点を解説していきます。
name: Deploy AWS
env:
PROJECT_NAME: sample
on:
push:
branches:
- develop
- release
- main
ワークフロー名とワークフローの環境変数を定義します。on
push
branches
で指定したブランチにプッシュした際にワークフローが実行されるように設定しています。
setup:
name: Setup
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
AWS_ACCOUNT_ID: ${{ steps.setenv.outputs.AWS_ACCOUNT_ID }}
STAGE_NAME: ${{ steps.setenv.outputs.STAGE_NAME }}
Setupジョブの設定を定義しています。コードの記述ミスなどで長時間実行されないように、必ずtimeout-minutes
を設定しておきます。他のジョブで値を取り込めるようにoutputs
を定義します。(後ほど登場します。)
- name: Set environment
id: setenv
env:
ITG_AWS_ACCOUNT_ID: 111111111111
STG_AWS_ACCOUNT_ID: 222222222222
PRD_AWS_ACCOUNT_ID: 333333333333
run: |
if ${{ github.ref == 'refs/heads/develop' }}; then
echo "::set-output name=AWS_ACCOUNT_ID::$ITG_AWS_ACCOUNT_ID"
echo "::set-output name=STAGE_NAME::itg"
elif ${{ github.ref == 'refs/heads/release' }}; then
echo "::set-output name=AWS_ACCOUNT_ID::$STG_AWS_ACCOUNT_ID"
echo "::set-output name=STAGE_NAME::stg"
elif ${{ github.ref == 'refs/heads/main' }}; then
echo "::set-output name=AWS_ACCOUNT_ID::$PRD_AWS_ACCOUNT_ID"
echo "::set-output name=STAGE_NAME::prd"
else
echo "Invalid branch name."
exit 1
fi
プッシュされたブランチに対してデプロイ先のAWSアカウントを設定しています。他のジョブでも設定した値を利用できるように、set-output
を使用します。
- 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: Cache build files
uses: actions/cache@v2
env:
cache-name: cache-build-files
with:
path: "**/build"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.sha }}
- name: Install
run: make install
- name: Build
run: make build
パッケージとビルドしたファイルを他のジョブで利用できるようにキャッシュしています。
test:
name: Test
needs:
- setup
runs-on: ubuntu-latest
timeout-minutes: 5
Testジョブの設定を定義しています。needs
でsetup
を指定しているので、Setupジョブが完了後に実行されます。
- 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: Lint
run: make lint
- name: Unit test
run: make test-unit
Setupジョブでキャッシュしたnode_modules
を取得して、テストを実行しています。
deploy-1:
name: Deploy 1
needs:
- setup
- test
runs-on: ubuntu-latest
timeout-minutes: 10
Deployジョブの設定を定義しています。needs
でsetup
とtest
を指定しているので、2つのジョブが完了後に実行されます。他のDeployジョブでも同様にneeds
を設定することで、ジョブを並列に実行することができます。
env:
AWS_DEFAULT_REGION: ap-northeast-1
AWS_ACCOUNT_ID: ${{ needs.setup.outputs.AWS_ACCOUNT_ID }}
STAGE_NAME: ${{ needs.setup.outputs.STAGE_NAME }}
ジョブの環境変数を定義しています。Setupジョブで出力したAWS_ACCOUNT_ID
とSTAGE_NAME
を環境変数にセットします。
- 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_DEFAULT_REGION }}
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.STAGE_NAME }}-${{ env.PROJECT_NAME }}-assume-role
role-duration-seconds: 3600
指定したAWSアカウントのIAMロールにAssume Roleしています。AWS_ACCESS_KEY_ID
とAWS_SECRET_ACCESS_KEY
はあらかじめOrganizationもしくはリポジトリのSecretsに登録しておきます。また、Assume Role先のIAM Roleも作成しておく必要があります。これにより、AWS CLIやCDKでAWS環境へデプロイを実行できるようになります。
- 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: Restore build files
uses: actions/cache@v2
env:
cache-name: cache-build-files
with:
path: "**/build"
key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.sha }}
- name: Deploy
run: |
make deploy TARGET=stack-name-1 STAGE=$STAGE_NAME
最後にSetupジョブでキャッシュしたパッケージとビルドファイルを取得して、デプロイを実行しています。デプロイのジョブを並列で実行するために、CloudFormationのスタックはある程度分けています。なお、make deploy
コマンドではAWS CDKのdeployコマンドなどを実行しています。
ジョブ名はスタック名などにしておくと分かりやすいかもしれません。
まとめ
GitHub Actionsのワークフローではneeds
によってジョブの依存関係を定義して、並列実行を分かりやすく実装することが出来ました。また、キャッシュやAWSの資格情報設定などのActionが提供されていて、ワークフローの定義がとても簡単でした。
GitHub Actionsのワークフロー構文やコンテキストについてはドキュメントに分かりやすく記載されているので、一度目を通してから実装に着手するのが良いと思います。