AWS CDKに入門したいなーとぼんやり思いつつ時間が経ってしまったけど触る理由が出来たのでハンズオンを試しつつ、学んだことをメモしておこうと思う。
筆者の背景としては、AWSのIaCといえばTerraformで生きてきた身なのでコードでIaCをしていく感覚がわからず、正直食わず嫌いなところがあった。
それでも触ってみようと思った理由はAWS CDKを触るちゃんとした機会が出てきたことと「CDKはいいぞ!」という声も聞くようになったので触ってみようと思った。
AWS CDKとは?
CFn(CloudFormation)を抽象化しプログラミング言語でよしなに書けるツール
抽象化することでコード量を少なくできるメリットがある
言語はTypescriptやGolang, Pythonなどが選択可能
CDKをデプロイすると裏ではCFnが実行されるという感じ
環境について
訳があってv1を利用しているが2022/06/01以降からv1はメンテナンスモードになっているのでいまから触る方はv2を使っていきましょう。
FYI: AWS CDKのv1はいつまでサポートされるのか調べてみた
yarn cdk --version yarn run v1.22.19 $ cdk --version 1.153.1 (build 9a7e599)
ハンズオンの資料はAWS公式のものを利用
AWS CDK の使用を開始する
cdk init
をすると以下のようなディレクトリ、ファイルが生成される。
重要なファイルのいくつかとそれらの用途は次のとおりです。 bin/cdk-project.ts - これは CDK アプリケーションへのエントリポイントです。これにより、lib/* で定義したすべてのスタックがロード/作成されます。 lib/cdk-project-stack.ts - ここでメインの CDK アプリケーションスタックが定義されます。リソースとそのプロパティはこちらに含めることができます。 package.json - ここで、プロジェクトの依存関係、およびいくつかの追加情報とビルドスクリプト (npm build、npm test、npm watch) を定義します。 cdk.json - このファイルは、アプリケーションの実行方法、および CDK とプロジェクトに関連する追加設定とパラメータをツールキットに指示します。
ここでcdk.json
の内容を確認
一部手を加えているところもあるが、"app": "npx ts-node --prefer-ts-exts bin/sample-ts.ts"
とあり、これがcdk
のコマンドを実行したときに呼び出されるコマンドの実体という理解。
bin/sample-ts.ts
がエントリーポイントとなり、これを基点としてlib/*
以下のコードが実行されるという流れと理解
{ "app": "npx ts-node --prefer-ts-exts bin/sample-ts.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:enableStackNameDuplicates": true, "aws-cdk:enableDiffNoFail": true, "@aws-cdk/core:stackRelativeExports": true, "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, "@aws-cdk/aws-kms:defaultKeyPolicies": true, "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, "@aws-cdk/aws-efs:defaultEncryptionAtRest": true, "@aws-cdk/aws-lambda:recognizeVersionProps": true, "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, "@aws-cdk/core:checkSecretUsage": true, "@aws-cdk/aws-iam:minimizePolicies": true, "@aws-cdk/core:target-partitions": ["aws", "aws-cn"], "staging": { "systemName": "staging", "envType": "stg", "instanceType": "t3.micro" }, "production": { "systemName": "production", "envType": "prd", "instanceType": "m5.large" } } }
cdkのコマンドについて
ckd ls
ckdに含まれるスタック一覧を確認するコマンド
cdk synth
cdkのコードをCFnテンプレートとして吐き出す(cdk.out
ディレクトリ)
cdk diff
ckdアプリのバージョンとすでにデプロイされているバージョンとを比較し、変更点を一覧で出力するコマンド
cdk deploy
cdkをデプロイするコマンド
すべてのスタックをデプロイしたい場合は"*"
or "--all"
オプションをつける
cdk destroy
デプロイしたスタックを削除するコマンド 特定のスタックのみを削除することも可能
cdk bootstrap
初回だけ実行するコマンド
CFnのテンプレートを置くためのS3を作成してくれる
よく使いそうなコマンドとしてはcdk diff
,cdk synth
,cdk deploy
,cdk destroy
なのかな
サンプルコードとContextについて
以下はハンズオンで生成したvpcを作成するコード(vpcとpublic subnetを2つ) 一部手を加えているので説明する
import * as cdk from "@aws-cdk/core"; import { Vpc, SubnetType } from "@aws-cdk/aws-ec2"; export class SampleTsStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const stage = this.node.tryGetContext("stage"); const contextParam = this.node.tryGetContext(stage); const vpc = new Vpc(this, "MainVpc", { maxAzs: 2, subnetConfiguration: [ { cidrMask: 24, name: "public-subnet", subnetType: SubnetType.PUBLIC, }, ], vpcName: `${contextParam.systemName}-${contextParam.envType}-vpc`, }); } }
以下のコードでContextから受け取った値を取得する処理を実行している
const stage = this.node.tryGetContext("stage"); const contextParam = this.node.tryGetContext(stage);
Contextとは「CFnのパラメーターのようにCDKのコードに外部から値を注入することができる」機能のこと
Contextについては以下の記事が参考になりました
FYI:
実践!AWS CDK #4 Context
このサンプルコードではcdk.json
に定義されている以下の値を取得して、vpcの名前をContextオプションで渡された引数によって切り替えるというコードになっている
"staging": { "systemName": "staging", "envType": "stg", "instanceType": "t3.micro" }, "production": { "systemName": "production", "envType": "prd", "instanceType": "m5.large" }
実行コマンド
yarn cdk synth -c stage=staging
Resources: MainVpc919A5E7E: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: staging-stg-vpc Metadata: aws:cdk:path: SampleTsStack/MainVpc/Resource
yarn cdk synth -c stage=production
Resources: MainVpc919A5E7E: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: production-prd-vpc Metadata: aws:cdk:path: SampleTsStack/MainVpc/Resource
vpcのNameタグがContext(-c
)の引数によって切り替わったことがわかる
Contextを利用することで外部から値を注入できるのでリソースの名前やその他振る舞いをコードで制御することが出来る
普段個人のアカウントでAWS環境をセットアップするときに使っているTerraformのコードを貼っておく
素で書くともうちょっと長くなると思うが楽をしたくてvpc moduleを利用している
HCLが苦手な方もいると思うがぱっと見た感じなにをしているか想像は付きやすいとは思う
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "3.11.0" name = "develop" cidr = "10.0.0.0/16" azs = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] enable_nat_gateway = true enable_vpn_gateway = false single_nat_gateway = true one_nat_gateway_per_az = false reuse_nat_ips = false tags = { Terraform = "true" Environment = "dev" } }
所感
CDKで柔軟にIaCできるのは楽しいなというのが一番の感想
またテストコードも書けるのでSnapshot Testを用意してあげることで保守も頑張れそうな気がした
TerraformにはWorkspacesがあるのでContextはそれとちょっと似ているかなーと思うが、Terraformで環境毎の制御をするには記述方法に限界がある。そのため、CDKの方がより柔軟に制御ができそう(ただし、あまり複雑なことをするとコードの視認性が悪くなりそうなのでバランスが大事になると思う)
ファイル分割やContextを他のスタックでも使い回す方法など試してみたいことが山程あるのでしばらくはcdkにどっぷり浸かって楽しんでいこうと思う
あとCDKの情報をキャッチアップするにあたってTwitterのコミュニティに参加してみた
v1を使っているけどv2でちゃんとキャッチアップしようと思っている(migration方法も調べておく)
FYI
- もし知識ゼロのバックエンドエンジニアがインフラ構築するとしたら? 僕がAWS CDKを使って挑んだ環境づくりのTips
- AWS CDK Conference Japan 2022
- 5分で理解するAWS CDK
See you next time:)