
コンテナイメージのビルド&プッシュと、AWS Serverless Application Model (SAM)のビルド&デプロイを、GitHub Actionsで設定してみた
コンテナとCI/CDをGitHub Actionsで設定したい
おのやんです。
みなさん、CI/CD設定してますか?(挨拶
今回、同一のGitHub リポジトリで管理している複数システムのCI/CDを、GitHub Actionsで設定する機会がありました。GitHub Actionsのワークフローファイルはそこまで複雑なロジックもなく、設計図通りに書けるイメージでしたが、意外と動作確認などに手こずりました。また、似たような実装をする際のテンプレートも欲しいな、という気持ちにもなりました。
ということで、「最低限これを設定すれば動くようになるだろう」というワークフローファイルを、本ブログにて残しておこうと思います。
なお、今回はGitHub Actionsの記法などには言及せず、全体的な視点でお話ししようと思います。実際に筆者がGitHub Actionsをキャッチアップする際にお世話になった記事を載せておきますので、よければご確認ください。
IAMロールは既に作ってあるものとします
コードブロック中に出てくる${{ inputs.env-name }}-role-github-actions-oicd
というIAMロールは、こちらのブログを参考に作成したOICD用IAMロールになりますので、ご留意いただければと思います。
GitHub Actionsワークフロー構造
GitHub Actionsのワークフローファイルは、今回次のように配置しました。
GitHub Actionsの再利用可能なワークフロー(actions
ディレクトリ配下)を用いて、メインのワークフロー(workflows
ディレクトリ配下)を実行する際に、メインのワークフローから再利用可能なワークフローをそれぞれ呼び出してしています。
├── actions
│ ├── container
│ │ └── action.yaml
│ ├── sam
│ │ └── action.yaml
│ └── update-ecs-service
│ └── action.yaml
└── workflows
├── deploy-system1-to-dev.yaml
同様の仕組みは次のブログも参考にしています。
各ワークフローファイルの紹介
それぞれのワークフローは、再利用可能なワークフローを順番に呼び出すことで構成されています。
今回は、system1
というプロジェクトの中に、system1-component1
(Amazon ECS(以下、ECS)上で動作しているシステム)と、system1-component2
(AWS Lambda(以下、Lambda)上で動作しているシステム)の、2つのサブシステムに分けています。2つのサブシステムのAWS基盤は、ともに1つのSAMテンプレートにて管理しています。
メインで実行されるワークフロー
メインで実行されるワークフローファイルは、次のようになります。
name: Deploy system1 to dev environment
on:
push:
branches:
- feature/**
paths:
- system1/**
pull_request:
branches:
- develop
types:
- closed
paths:
- sysutem1/**
env:
AWS_DEV_ACCOUNT_ID: xxxxxxxxxxxx
AWS_ENV_NAME: dev
permissions:
id-token: write
actions: write
contents: read
pull-requests: read
jobs:
#===================================================#
# system1-component1のコンテナイメージを開発環境にプッシュ
#===================================================#
push_system1-component1_image_to_dev:
needs:
- cfn_lint_system1
- pytest_system1-component1
- pytest_system1-component2
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Push system1 component1 container image
uses: ./.github/actions/container
with:
aws-account-id: ${{ env.AWS_DEV_ACCOUNT_ID }}
env-name: ${{ env.AWS_ENV_NAME }}
working-directory: system1
image-name: system1-component1
dockerfile-path: ./system1-component1
image-tag: 'latest'
#===================================================#
# System1を開発環境にデプロイ
#===================================================#
deploy_system1_to_dev:
needs:
- push_system1-component1_image_to_dev
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy System1
uses: ./.github/actions/sam
with:
aws-account-id: ${{ env.AWS_DEV_ACCOUNT_ID }}
env-name: ${{ env.AWS_ENV_NAME }}
working-directory: system1
#===================================================#
# ECSサービスを更新
#===================================================#
update_system1_ecs_service:
needs:
- deploy_system1_to_dev
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Update ECS Service
uses: ./.github/actions/update-ecs-service
with:
aws-account-id: ${{ env.AWS_DEV_ACCOUNT_ID }}
env-name: ${{ env.AWS_ENV_NAME }}
system-name: system1-component1
コンテナのワークフロー
続いて、それぞれのワークフローを見てみます。container/action.yaml
では、実行対象のディレクトリのDockerfile
を参考にして、コンテナイメージをビルドし、Amazon ECR(以下、ECR)リポジトリにpushしています。
Dockerコンテナをいい感じにビルド&プッシュしてくれる有志のワークフローもあったのですが、もともと手動で運用していたコンテナ関係のコマンドが3行のみでしたので、実直にコマンドを直書きしても運用工数は変わらないだろう、ということで、run:
配下にコマンドを記述しています。
name: Build container image and push it to ECR
inputs:
aws-account-id:
type: string
description: AWS account id
env-name:
type: choice
description: The environment name to deploy
options:
- dev
- prd
working-directory:
type: choice
description: The working directory to run the commands
dockerfile-path:
type: string
description: Path to Dockerfile relative to working directory
image-tag:
type: string
description: Image tag to push
default: 001
image-name:
type: string
description: Image name to push
runs:
using: composite
steps:
- name: Get AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.env-name }}-role-github-actions-oicd
aws-region: ap-northeast-1
- name: Login to ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Push image to ECR
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ inputs.env-name }}-${{ inputs.image-name }}-ecr
IMAGE_TAG: ${{ inputs.image-tag }}
run: |
# Build Docker image
docker build --platform linux/amd64 -t $ECR_REPOSITORY ${{ inputs.dockerfile-path }}
# Tag Docker image for ECR
docker tag $ECR_REPOSITORY:latest $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
# Push Docker image to ECR
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
SAMのワークフロー
SAMのワークフローも同じく、SAMビルドとSAMデプロイのコマンドを素直に実行するワークフローとして作成しました
name: Build and deploy SAM application
inputs:
aws-account-id:
type: string
description: AWS account id
env-name:
type: choice
description: The environment name to deploy
options:
- dev
- prd
working-directory:
type: choice
description: The working directory to run the commands
runs:
using: composite
steps:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.12
- name: Setup SAM
uses: aws-actions/setup-sam@v2
- name: Get credential
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.env-name }}-role-github-actions-oicd
aws-region: ap-northeast-1
- name: SAM build
run: sam build
shell: bash
working-directory: ${{ inputs.working-directory }}
- name: SAM deploy
run: sam deploy --config-env ${{ inputs.env-name }} --no-confirm-changeset --no-fail-on-empty-changeset
shell: bash
working-directory: ${{ inputs.working-directory }}
Amazon ECSサービスの更新ワークフロー
今回SAMで管理しているAWSサービスの中に、Amazon ECS(以下、ECS)があります。上記のコンテナpush、SAMアプリケーションデプロイの後に、Amazon ECR(以下、ECR)上のコンテナイメージをもとにECSのタスクを再作成する必要がありましたので、ここもGitHub Actionsの処理に加えています。
name: Update ECS Service
inputs:
aws-account-id:
type: string
description: AWS account id
required: true
env-name:
type: choice
description: The environment name to deploy
required: true
options:
- dev
- prd
system-name:
type: string
description: System name to update
required: true
runs:
using: composite
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.env-name }}-role-github-actions-oicd
aws-region: ap-northeast-1
- name: Update ECS Service
run: |
aws ecs update-service \
--cluster ${{ inputs.env-name }}-${{ inputs.system-name }}-ecs-cluster \
--service ${{ inputs.env-name }}-${{ inputs.system-name }}-ecs-service \
--force-new-deployment
shell: bash
こちらの実装は、次の記事を参考にしています
実装に移す際のテンプレート的な例が欲しかった
今回、GitHub Actionsで複数の処理を各Jobで順番に実行する例を作ってみました。冒頭でも紹介しましたが、この記事は、「細かい文法は理解したので素早く実装に移れるサンプルなどないかな」と思って探したが結局見つからずに自前で実装した経緯があって作成しました。
この記事がどなたかの参考になれば幸いです。では!