Goで書いたCDKをGitHub Actionsする
はじめに
こんにちは、@thousan_da です。
IaCをCI/CDしたかったので、AWS CDKをGitHub Actionsで自動化しました。
仕事ではTerraformとCircleCIを使っているため、これは趣味です。
使ったサービス/技術
- AWS CDK (Cloud Development Kit)
- GitHub Actions
- Go
背景
クラウドを使っていて、リソースを画面からぽちぽち作るのは管理が大変になるので、IaC (Infrastructure as Code) を使っている人は多いと思います。しかしながら、IaCの運用を手作業で行なっているとこれもまたつらいです。
ということで、CDKのCI/CD環境をGitHub Actionsで作ります。
検索してみると、やっぱりCDKはTypeScriptで書いている人が多いですね。でも僕はGoで書きたいので、GoでセットアップしたCDKを使う方法を紹介します (2023年6月現在、GoのCDKはまだDeveloper Previewバージョンです) 。
mainブランチに対してPull Requestを作ったらdiffを表示し、それがマージされたらdeployされるようにしました。
ワークフローの構成
今回作ったワークフローの構成です。
- ジョブ1: "cdk-diff"
- インフラの変更差分を可視化するジョブ
-
cdk diff
の実行結果を見ることができる - Pull Requestの作成/更新をトリガーに実行される
- diffの出力結果をPull Requestにコメントする
- ジョブ2: "cdk-deploy"
- インフラの変更を適用するジョブ
-
cdk deploy
を実行する
Actionsの設定ファイル
CDK Diff
name: CDK Workflow
on:
pull_request:
branches: [ main ]
jobs:
cdk-diff: # Pull Requestのときのみ実行する
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code # ブランチを切り替える
uses: actions/checkout@v3
- name: Setup Node.js # Node.jsをセットアップする
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup Go # Goをセットアップする
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Install dependencies # 依存ライブラリをインストールする
run: |
npm install -g aws-cdk
go mod tidy
- name: Configure AWS credentials # GitHubに保存したAWSのSecretsを取り出して設定する
uses: aws-actions/configure-aws-credentials@v2
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: CDK Diff # 存在するリソースと設定の差分を取得する -> 得られた文字列をoutputを使って次のsrepに渡す (複数行に渡るのでヒアドキュメントで書く)
id: diff
run: |
diff=$(cdk diff) || true
echo "$diff"
echo "cdk_diff<<EOF" >> $GITHUB_OUTPUT
echo "$diff" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Comment PR # Pull Requestのコメントに上記の差分を書き込む (変更が無い場合は "No changes" とコメントする)
uses: actions/github-script@v3
with:
script: |
const diff = `${{ steps.diff.outputs.cdk_diff }}`;
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: diff ? diff : "No changes"
});
CDK Deploy
name: CDK Workflow
on:
push:
branches: [ main ]
jobs:
cdk-deploy:
if: ${{ github.event_name == 'push' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Install dependencies
run: |
npm install -g aws-cdk
go mod tidy
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
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: Deploy with CDK # 設定の変更をデプロイする
run: |
cdk synth
cdk deploy --require-approval never
作業手順
- CDKプロジェクトを作成する
- Actionsを設定する
CDKプロジェクトを作成する
ローカル環境にNode.jsをインストールしていない場合はインストールします。僕はanyenvでインストールしたnodenvでインストールしたNode.jsを使っています。
次に、CDKをインストールしてCDKプロジェクトを作成します。(npmに明るい方は、グローバルインストールを避けたり、npxを使ったりしてもokです)
# CDKをインストールする
$ npm install -g aws-cdk
# CDKプロジェクトを作る
$ mkdir my-cdk
$ cd my-cdk
$ cdk init app --language go
# CDKブートストラップ (そのregionで今まで一度も実行したことが無い場合のみ)
# 参考: https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/bootstrapping.html
$ cdk bootstrap
できたらGitHubのリポジトリを作成してpushします。
Actionsを設定する
まずはGitHubリポジトリにSecretを登録します。AWSのアカウントでaws-access-key-idとaws-secret-access-keyを用意してから、ブラウザで先ほどpushしたリポジトリを開き、この二つを登録します。
Settingsタブ > 左メニューの"Secrets and variables" > "Actions"をクリック
New repository secret
をクリックし、上記2つをそれぞれ、AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYという名前で保存します。
次に、リポジトリに設定ファイルを追加します。mainから作業用ブランチを切り、my-cdk/.github/workflows/main.yaml
というファイルを以下の内容で作成し、commitします。(cdk-diffとcdk-deployを一つのファイルにまとめたもの)
このブランチをpushした後、mainに向けてPull Requestを作成するとワークフローが実行されるはずです!
name: CDK Workflow
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
cdk-diff:
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Install dependencies
run: |
npm install -g aws-cdk
go mod tidy
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
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: CDK Diff
id: diff
run: |
diff=$(cdk diff) || true
echo "$diff"
echo "cdk_diff<<EOF" >> $GITHUB_OUTPUT
echo "$diff" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Comment PR
uses: actions/github-script@v3
with:
script: |
const diff = `${{ steps.diff.outputs.cdk_diff }}`;
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: diff ? diff : "No changes"
});
cdk-deploy:
if: ${{ github.event_name == 'push' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Install dependencies
run: |
npm install -g aws-cdk
go mod tidy
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
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: Deploy with CDK
run: |
cdk synth
cdk deploy --require-approval never
続いて、my-cdk/my-cdk.goを編集して何かリソースを定義してみて、Pull Requestのコメントに差分が表示されることと、そのPull Requestをマージされたら定義したリソースが作成されることを確認してみてください。
プロジェクトを作成した時点で、リソース定義の例が書いてあるかもしれません。僕が作成した際には、以下のようにSQSキューの例が書かれていました。
func NewMyCdkStack(scope constructs.Construct, id string, props *MyCdkStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
// The code that defines your stack goes here
// example resource
awssqs.NewQueue(stack, jsii.String("MyCdkQueue"), &awssqs.QueueProps{
VisibilityTimeout: awscdk.Duration_Seconds(jsii.Number(300)),
})
return stack
}
Discussion