GitHub Actions + ecspressoでCI/CDパイプラインを構築する
目次
こんにちは、クラウドビルダーズのきむです。
本記事は、クラウドビルダーズ株式会社のアドベントカレンダー2024の8日目の記事となります!
今回は下記の投稿の続きです。
上記の投稿ではecspressoを使用してECSをデプロイする方法をご紹介しました。
実際の運用ではCI/CDパイプラインを構築してデプロイの自動化をすることが多いと思います。
今回はGitHub Actionsを使用してCI/CDパイプラインを構築します。
ソースコード
今回使用するソースコードは下記に置いています。
IAMロールの作成
ECRへのイメージプッシュやAWS CDKの利用をする場合はAWS認証情報が必要になります。
GitHub Actionsでは下記を使用してIAMロールによる一時的な認証情報を使用することができます。
GitHub Actionのワークフローを定義する前にIAMロールを作成します。
下記のファイルでIAMロールを定義しています。
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Props } from '@bin/main';
export class Iam extends Construct {
constructor(scope: Construct, id: string, props: Props) {
super(scope, id);
const repoName = process.env.GIT_REPO;
if (repoName) {
const oidcProvider = new iam.OpenIdConnectProvider(
this,
'GitHubOIDCProvider',
{
url: 'https://token.actions.githubusercontent.com',
clientIds: ['sts.amazonaws.com'],
},
);
new iam.Role(this, 'GitHubOIDCRole', {
roleName: `${props.prefix}-github-oidc-role`,
assumedBy: new iam.FederatedPrincipal(
oidcProvider.openIdConnectProviderArn,
{
StringEquals: {
'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com',
},
StringLike: {
'token.actions.githubusercontent.com:sub': [
`repo:${repoName}:ref:refs/heads/*`,
`repo:${repoName}:pull_request`,
],
},
},
'sts:AssumeRoleWithWebIdentity',
),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'),
],
});
}
}
}
検証のためにAdministratorAccessをロールにアタッチしていますが、実際の運用では適切なポリシーをアタッチしましょう。
環境変数GIT_REPOにはGitHub Actionsを実行するリポジトリを設定しています。
cdk.shでIAMロールをデプロイしましょう。
./cdk.sh deploy:n
デプロイが完了したら作成されたIAMロールのARNが必要になるので控えておきます。
シークレットの設定
上記で控えたIAMロールのARNやイメージを保存するECRのURIなどをActionsのシークレットに設定し、Actionsから参照します。
まずはリポジトリのSettingsを開きます。
続いてActionsのシークレット設定画面を開きます。
Repository secretsにシークレットを追加します。
下記の通り2つのシークレットを追加しました。
ワークフロー定義の作成
今回はmainブランチへのpushをトリガーとして実行されるActionsを作成します。
ワークフロー定義はAWS CDK用とecspresso用の2つを用意しました。
いずれのワークフローもpath-filterを使用して関連するファイルが変更された場合にのみ適切なデプロイが実行されるようにしています。
cdk workflow
name: cdk workflow
permissions:
id-token: write
contents: read
on:
push:
branches: [main]
workflow_dispatch:
env:
# Github Actionsを実行しているリポジトリ名を取得
GIT_REPO: ${{ github.repository }}
jobs:
initialize:
runs-on: ubuntu-latest
permissions: read-all
outputs:
infra_changed: ${{ steps.filter.outputs.infra }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# infraディレクトリ以下のファイル変更を検知
- name: Detect File Changes
uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.event.before }}
filters: |
infra:
- 'infra/bin/**'
- 'infra/lib/**'
deploy:
runs-on: ubuntu-latest
needs: [initialize]
# infraディレクトリ以下のファイル変更があった場合のみデプロイを実行
if: |
github.event_name == 'push' &&
needs.initialize.outputs.infra_changed == 'true'
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: npm install
shell: bash
run: |
npm install
working-directory: infra
- name: Set Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
aws-region: ap-northeast-1
- name: Run CDK Deploy
run: |
npm run cdk:diff -- -c isFirst=n
working-directory: infra
ecsoreso workflow
name: ecsoreso workflow
permissions:
id-token: write
contents: read
on:
push:
branches: [main]
workflow_dispatch:
env:
SYS_NAME: ecspresso-demo
CLUSTER: cluster
SERVICE: service
TAG: ${{ github.sha }} # コミットハッシュを取得
APPLICATION_NAME: ecs-app
DEPLOYMENT_GROUP_NAME: ecs-deploy-group
ECR_URI: ${{ secrets.ECR_URI }} # シークレットからECR URIを取得
jobs:
initialize:
runs-on: ubuntu-latest
permissions: read-all
outputs:
app_changed: ${{ steps.filter.outputs.app }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# appディレクトリ以下のファイル変更を検知
- name: Detect File Changes
uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.event.before }}
filters: |
app:
- 'app/main.go'
- 'app/ecspresso/**'
deploy:
runs-on: ubuntu-latest
needs: [initialize]
# appディレクトリ以下のファイル変更があった場合のみデプロイを実行
if: |
github.event_name == 'push' &&
needs.initialize.outputs.app_changed == 'true'
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
aws-region: ap-northeast-1
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.22.5
- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Build and Push Image
uses: docker/build-push-action@v5
with:
context: ./app
platforms: linux/arm64
push: true
tags: ${{ env.ECR_URI }}:${{ github.sha }}
- name: Set up ecspresso
uses: kayac/ecspresso@v2
with:
version: latest
- run: |
ecspresso deploy --config ecspresso/ecspresso.jsonnet
working-directory: app
作成されるECSはARMアーキテクチャとなっているので、QEMUとBuildxのセットアップを行いARM用のイメージをビルドできるようにしています。イメージのタグにはGitHubのコミットハッシュ値を使用します。
GitHub Actionsを実行する
実際にリポジトリにpushを行いActionsを実行してみます。
Actions実行前にALBにアクセスすると’Hello, World!’が返却されるようになっています。
curl -i http://ecspresso-demo-alb-xxxxxxxxxx.ap-northeast-1.elb.amazonaws.com
HTTP/1.1 200 OK
Date: Sat, 07 Dec 2024 12:19:59 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 13
Connection: close
Hello, World!%
main.goを修正して返却される文字列を変更してリポジトリにpushします。
package main
import (
"net/http"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
// return c.String(http.StatusOK, "Hello, World!") コメントアウト
return c.String(http.StatusOK, "Welcome to ecspresso-demo!") // コメントアウト解除
})
e.Logger.Fatal(e.Start(":8080"))
}
git add .
git commit -m 'ecspresso demo'
git push origin HEAD
Actionsの画面を確認すると2つのワークフローが実行されています。
AWS CDK用のワークフローを確認すると、deployが実行されていないことがわかります。
変更したファイルは’app/main.go’のみとなっているので、path-filterによりAWS CDKの関連ファイルの変更が検知されなかったためです。
ecspresso用のワークフローではファイルの変更を検知しdeployまで実行されています。
デプロイ完了後にALBへアクセスすると、返却される文字列が修正後の’Welcome to ecspresso-demo’に変更されています。
curl -i http://ecspresso-demo-alb-xxxxxxxxxx.ap-northeast-1.elb.amazonaws.com
HTTP/1.1 200 OK
Date: Sat, 07 Dec 2024 13:26:00 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 26
Connection: close
Welcome to ecspresso-demo!%
さいごに
専用のActionsが用意されているのでGitHub Actionsでもecspressoを簡単に利用することができます。
今回はpushをトリガーにデプロイするのみでしたが、pull requestをトリガーにテスト行うなどの処理を組み込めばより実用的なワークフローを構築することができると思います。ぜひ活用してみてください。