CDK Pipelineを導入するときの手順メモ
0. 前提や事前準備
- マルチアカウントでのデプロイの話
- CI/CD用のAWSアカウントから、アプリケーション用のAWSアカウントにデプロイ
- AWSアカウントは事前作っておいて、Admin権限を持っておく
- 自社の場合、ControlTowerを使って管理している。
-
aws-sso-utils
で設定までやっているとよい。
1. aws-cdkをインストール
$ npm install -g aws-cdk
2. cdk init
アプリケーションと同じリポジトリにcdk
というディレクトリを作成し、その中にCDKのコードを置くことを想定。
$ mkdir cdk
$ cd cdk
$ cdk init app --language typescript
@aws-cdk/pipelines
のインストール
3. cdk
ディレクトリで@aws-cdk/pipelines
をインストール。
$ npm install @aws-cdk/pipelines
cdk.json
に"@aws-cdk/core:newStyleStackSynthesis": true
を追加
4. CDK Pipelinesを利用する場合に必要。
{
"app": "…",
"context": {
…,
"@aws-cdk/core:newStyleStackSynthesis": true
}
}
5. 空のパイプラインをデプロイする
5.1 CodePipelineで接続を作る
レポジトリにプッシュしたコードをCodePipelineがプルするために、CodePipelineに接続を作る。
- CI/CD用のAWSアカウントでAWSコンソールに入る
- AWSコンソールでCodePipelineを開く
- 左メニュー → 設定 → 接続をクリック
- 「接続を作成」ボタンを押し、使うリポジトリサービス(ex: Github, BitBucket...)を選択し、認証する
- 接続のARNが発行されるので、それをコピーしておく
5.2 パイプラインのStackを書く
cdk/lib/pipeline-stack.ts
ファイル名やクラス名のベストプラクティス、わからん。
// cdk/lib/pipeline-stack.ts
import { Construct, Stack, StackProps } from "@aws-cdk/core";
import {
CodePipeline,
CodePipelineSource,
ShellStep,
} from "@aws-cdk/pipelines";
export class PipelineStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new CodePipeline(this, "Pipeline", {
synth: new ShellStep("Synth", {
input: CodePipelineSource.connection(
"OWNER/REPO", // リポジトリがgithub.com/hoge/fugaなら、"hoge/fuga"
"main", // ブランチ名
{
connectionArn: "接続のARN",
}
),
commands: [
// アプリケーション側でビルドがあれば
"npm ci",
"npm run build",
// CDKのビルド
"cd cdk",
"npm ci",
"npm run build",
"npx cdk synth",
],
// 今回cdkディレクトリ以下にCDKのコードがあるので指定
primaryOutputDirectory: "cdk/cdk.out",
}),
// マルチアカウントでデプロイする場合に必要
crossAccountKeys: true,
});
}
}
cdk/bin/cdk.ts
cdk init
後の状態だと、cdk/lib/cdk-stack.ts
のStackをインスタンス化しているコードになっているはず。
それを↑で書いたPipelineStackのインスタンス化に変える。env
にはCI/CD用のAWSアカウントのIDやリージョンを指定する。
最後にapp.synth()
も書く。
import "source-map-support/register";
import * as cdk from "@aws-cdk/core";
import { PipelineStack } from "../lib/pipeline-stack";
const app = new cdk.App();
new PipelineStack(app, "HogeHogeAppPipelineStack", {
env: { account: "CI/CDのAWSアカウントのID(数字のやつ)", region: "ap-northeast-1" },
});
app.synth();
この辺のコードは正直雰囲気でしか理解できておらず、いつか完全理解したい。
5.3 cdk bootstrap
マルチアカウントでデプロイする場合、CI/CD用のAWSアカウントとデプロイ先AWSアカウントで2回cdk bootstrap
が必要。
デプロイ先は--trust CI/CD用AWSアカウントのID
を付けて実行する。
CloudFormationの実行権限をAdministratorAccessにしてしまっているが、いい方法がわからん!
$ cd cdk
# CI/CD用AWSアカウントのcdk bootstrap
$ npx cdk bootstrap \
--profile CI/CD用AWSアカウントのAdmin権限を持ったprofile \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
aws://CI/CD用AWSアカウントのID/ap-northeast-1
# デプロイ先AWSアカウントのcdk bootstrap
$ npx cdk bootstrap \
--profile デプロイ先AWSアカウントのAdmin権限を持ったprofile \
--trust CI/CD用AWSアカウントのID \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \
aws://デプロイ先AWSアカウントのID/ap-northeast-1
5.4 cdk deploy
最初の1回は手元でcdk deploy
が必要。その後はリポジトリにプッシュすれば、デプロイできるようになる。引数にcdk/bin/cdk.ts
でインスタンス化したパイプラインのスタックのIDを渡す(上記例だとHogeHogeAppPipelineStack
)。
$ npx cdk deploy \
--profile CI/CD用AWSアカウントの管理者権限のprofile \
HogeHogeAppPipelineStack
cdk deploy
が必要なケース。
再度、CodePipeline
のオプションで渡している部分を変更するときは再度cdk deploy
が必要な場合があるっぽい。例えば、synth
に渡しているShellStep
のcommands
を変更する場合は、コミットしてプッシュしても反映されなかった。この辺のルールはよくわかってないが、コードを変更してプッシュしても反映されない場合は試してみるとよさそう。
6. アプリケーションのStackを書く
import * as cdk from "@aws-cdk/core";
export class HogeHogeAppStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
…好きなように書いて…
}
}
7. Stageを追加する
まず、Stage
のサブクラスを作る。そのコンストラクタ内で↑で作ったアプリケーションのStackをインスタンス化する。この時に渡すIDのベストプラクティスがわからないので、とりあえずStackと同じ名前にしている。
import * as cdk from "@aws-cdk/core";
import { HogeHogeAppStack } from "./hoge-hoge-app-stack";
export class DeployAppStage extends cdk.Stage {
constructor(scope: cdk.Construct, id: string, props?: cdk.StageProps) {
super(scope, id, props);
new HogeHogeAppStack(this, "HogeHogeApp");
}
}
CodePipelie
のaddStage
メソッドで↑のStageのサブクラスを追加する。
import * as cdk from "@aws-cdk/core";
import {
CodePipeline,
CodePipelineSource,
ShellStep,
} from "@aws-cdk/pipelines";
import { DeployAppStage } from "./deploy-app-stage";
export class PipelineStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const pipeline = new CodePipeline(this, "Pipeline", { …割愛… });
// 追記
pipeline.addStage(
new DeployAppStage(this, "Prod", {
env: { account: "デプロイ先AWSアカウントのID", region: "ap-northeast-1" },
})
);
}
}
コードをコミットしてプッシュすれば、CodePipelieがデプロイを実行する。
上記の例では本番環境のみを追加しているが、テスト環境などある場合はその分addStage
を増やせばよい。また、本番環境前に手動での承認を挟む、なども可能。