こんにちは、ちゃりおです。
CDKで複数環境を構築する際に、環境差異をどう表現するか悩むことがよくあります。
(例えば、PRDとSTGを同一のCDKコードから作成しようとして、STGは料金節約のためにスペック下げたいときとか)
色々方法あると思うのですが、おすすめの方法と辛くなる方法について紹介しようと思います。
やりすぎると辛くなる方法
適量やるのは問題ないですが、やりすぎるとコードが読みづらくなり構成を把握しづらくなる方法です。
if文・三項演算子を多用しすぎる
例えば、環境ごとにECSのタスク数を変えたいとします。
タスク数を本番は2、STGは1にしたい場合、以下のように表現できると思います。
// 変数envに環境の名前が入っている
const loadBalancedFargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
cluster,
memoryLimitMiB: 1024,
cpu: 512,
// 本番はタスクを2つにする
desiredCount: env === "PRD" ? 2 : 1,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
},
});
上記くらいであれば、そこまで読みづらくはないと思います。
ただ、増えてくると辛くなってきます。
メモリとCPUも環境ごとに変えたりとすると、以下のようになります。
2つ増えただけですが、結構読みづらくなったのではないでしょうか。
const loadBalancedFargateService = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
cluster,
memoryLimitMiB: env === "PRD" ? 1024 : 512,
cpu: env === "PRD" ? 512 : 256,
desiredCount: env === "PRD" ? 2 : 1,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
},
});
IaCツールを導入する目的の一つに、「コードから設定値を確認できる状態にする」があると思います。
しかし、上記のように条件分岐が増えていくと、認知負荷が上がり設定値をコードから読み取りづらくなります。
構成が大きく違うなら、ファイルを分けることも検討しましょう。
cdk.jsonに書きすぎる
コード内に条件分岐を入れすぎると、読むのが難しくなることがわかりました。
環境差異を別ファイルにしたほうが良さそうです。
コード上でtryGetContextを使うことで、「cdk.json」に定義している変数を取得することができます。
Get a value from a context variable
以下のように設定値を別ファイルにすることができます。
const context = this.node.tryGetContext(env);
new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
cluster,
memoryLimitMiB: context.fargate.memomemoryLimitMiBry,
cpu: context.fargate.cpu,
desiredCount: context.fargate.desiredCount,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
},
});
cdk.json
{
"app": "npx ts-node --prefer-ts-exts bin/ckd-nag.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
"@aws-cdk/core:stackRelativeExports": true,
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true
},
"prd": {
"fargate": {
"memoryLimitMiB": 1024,
"cpu": 512,
"desiredCount": 2
}
}
"stg": {
"fargate": {
"memoryLimitMiB": 512,
"cpu": 256,
"desiredCount": 1
}
}
}
ifを多用するよりは、cdk.jsonとcdkのコードを見れば設定値がわかるので認知負荷は低くなりそうです。
ただ、この方法はやりすぎると「cdk.jsonが肥大化する」という問題があります。
上記ではECSだけですが、VPC・S3・RDSなどの各AWSリソースも入ってくると結構なボリュームになりそうです。
また、cdk.jsonにはcdk自体の設定値が入っています。(機能フラグやwatchの設定など)
cdk自体の設定と作成されるリソースの設定が同じところにあるのは、なんとなく気持ちわるい気もします。
cdk.jsonはAWSのアカウントIDやリージョンを記載するくらいに留めておく方針が、個人的にはおすすめです。
個人的におすすめの方法
cdk.jsonとは別に、configファイルを作って分ける方法がおすすめです。
「The CDK Book」の「7. Configureation Management」で紹介されていた方法です。
この方法は環境ごとにファイルを分けれたり、型を使えるメリットがあります。
まとめ
今回はCDKで環境差異を分ける方法についてでした。
- ifを使いすぎない
- 構成が違うならファイルを分けることも検討 - cdk.jsonに書きすぎない
- cdk.jsonの肥大化に気をつける
- アカウントIDとリージョンくらいにしておくのがおすすめ
短く書くことに固執せずに、認知負荷の低いコードにしてメンテナンスや引き継ぎの際に困らないようにしていきたいです。
環境差異を表現するおすすめの方法については、サンプルコード付きで後ほどブログ書きます。
参考
https://thecdkbook.com/
https://qiita.com/yktko/items/da8d5ae9a540d5427b01
https://cdk-dev.slack.com/archives/C017RG58LM8/p1638533921290600