どーも。shihopowerです。今回はCDKの存在意義をまとめました。CDKを学び始めたとき、「CloudFormationはスタックを一つ一つしか作れないけれど、CDKは複数スタックを一気に作れるもの」という風に理解していたのですが、先輩から「半分正解で、半分間違っているよ」という風に言われ、本質的理解ができていないことに気づきました。
そこで、CloudFormationとの比較を通じてCDKがなぜ存在するのかを整理することにしました。
目次
- 私が誤解していたこと:複数スタックはCloudFormationでも作れる
- CDKの存在意義①:プログラミング言語でインフラを書ける
- CDKの存在意義②:高レベルな抽象化(Constructs)
- CDKの存在意義③:スタック間の依存関係管理が楽
- CDKの存在意義④:テスト・再利用ができる
- まとめ
1.私が誤解していたこと:複数スタックはCloudFormationでも作れる
まず誤解を解くと、複数スタックの作成はCloudFormationでも可能です。
- **ネストされたスタック(Nested Stacks)**を使う方法
- 複数のテンプレートファイルを別々にデプロイする方法
ただし、スタック間の連携や管理が煩雑になるという問題があります。この点については後ほど詳しく説明します。
2.CDKの存在意義①:プログラミング言語でインフラを書ける
CloudFormationはYAML/JSONで記述しますが、CDKはTypeScript・Python・Javaなどのプログラミング言語で書けます。
コピペ地獄からの解放
たとえば同じ構成のS3バケットを5つ作りたい場合、CloudFormationではほぼ同じYAMLを5回書く必要があります。
# CloudFormation:ほぼ同じブロックを5回コピペ
MyBucket1:
Type: AWS::S3::Bucket
Properties:
BucketName: my-app-bucket-1
MyBucket2:
Type: AWS::S3::Bucket
Properties:
BucketName: my-app-bucket-2
# ... 以下繰り返し
CDKではループが使えます。
// CDK:ループで一気に書ける
for (let i = 1; i <= 5; i++) {
new Bucket(this, `MyBucket${i}`, {
bucketName: `my-app-bucket-${i}`,
});
}
タイポをデプロイ前に防げる
タイポ(typo)とは
"typographical error"の略で、入力ミス・打ち間違いのことです。
例:SubnetId → SubnetID(末尾の大文字小文字が違う)
CloudFormationはYAMLの文字列で値を参照するため、タイポしてもエディタ上では一切エラーが出ません。実際にAWSにデプロイして初めてエラーに気づきます。
CDKはプログラミング言語なので、IDEの型チェック・補完が効き、タイポをデプロイ前に検出できます。
3.CDKの存在意義②:高レベルな抽象化(Constructs)
CloudFormationはすべてのリソースを細かく書く必要がありますが、CDKにはL2・L3と呼ばれる高レベルな抽象(Construct)が用意されています。
たとえばALB + ECSの構成を作る場合、CloudFormationでは関連リソース(ALB・ターゲットグループ・ECSサービス・セキュリティグループ等)をすべて記述する必要があり、数百行になります。
CDKのL3 Constructを使えば、これだけです。
new ApplicationLoadBalancedFargateService(this, 'Service', {
cluster,
image: ContainerImage.fromRegistry('nginx'),
memoryLimitMiB: 512,
desiredCount: 2,
});
ベストプラクティスに沿ったセキュリティグループやIAMロールの設定もデフォルトで入るため、書く量が大幅に減ります。
4.CDKの存在意義③:スタック間の依存関係管理が楽
ここがCDKの最も強力な特徴のひとつです。「VPCスタック」と「EC2スタック」を分けて管理する例で比較します。
CloudFormationの場合(手動管理で辛い)
VPCスタック側:Outputsで値をExportする
# vpc-stack.yaml
Outputs:
VpcId:
Value: !Ref MyVPC
Export:
Name: "MyApp-VpcId" # Export名を手動で命名・管理
SubnetId:
Value: !Ref MySubnet
Export:
Name: "MyApp-SubnetId"
EC2スタック側:Export名を文字列でImportする
# ec2-stack.yaml
Resources:
MyEC2:
Type: AWS::EC2::Instance
Properties:
SubnetId: !ImportValue "MyApp-SubnetId" # 文字列参照 → タイポしてもデプロイ時まで気づかない
VpcId: !ImportValue "MyApp-VpcId"
CloudFormationの辛いポイント:
| 課題 | 内容 |
|---|---|
| Export名の管理 | グローバルでユニークな名前を自分で命名・管理する必要がある |
| 参照ミス | 文字列参照のためタイポしてもデプロイ時まで気づけない |
| デプロイ順序 | VPCスタック → EC2スタックの順を自分で把握して手動実行する必要がある |
| スタックの制約 | Exportされている値を参照先が使っている間は、スタックを変更・削除できない |
CDKの場合(オブジェクト参照で自動解決)
// vpc-stack.ts
export class VpcStack extends Stack {
public readonly vpc: Vpc; // 外から参照できるようにpublicで公開
public readonly subnet: Subnet;
constructor(scope: App, id: string) {
super(scope, id);
this.vpc = new Vpc(this, 'MyVPC');
this.subnet = new Subnet(this, 'MySubnet', { ... });
}
}
// ec2-stack.ts
export class Ec2Stack extends Stack {
constructor(scope: App, id: string, vpc: Vpc, subnet: Subnet) {
super(scope, id);
new Instance(this, 'MyEC2', {
vpc: vpc, // オブジェクトをそのまま渡すだけ
subnet: subnet, // 文字列じゃないのでタイポしない
});
}
}
// app.ts(エントリーポイント)
const app = new App();
const vpcStack = new VpcStack(app, 'VpcStack');
const ec2Stack = new Ec2Stack(app, 'Ec2Stack', vpcStack.vpc, vpcStack.subnet);
cdk deploy --all を実行すると、CDKが裏で以下をすべて自動でやってくれます。
- EC2StackがVpcStackに依存していることを自動検出
- VpcStackを先にデプロイ
- VpcStackのOutputsに自動でExportを追加
- EC2StackのテンプレートにImportValueを自動挿入
- EC2Stackをデプロイ
生成されるCloudFormationテンプレートにはちゃんとExport/ImportValueが書かれていますが、それを自分で書く必要がないわけです。
比較まとめ
| 課題 | CloudFormation | CDK |
|---|---|---|
| Export名の管理 | 自分で命名・管理 | 自動生成 |
| 参照ミス | 実行時エラー(文字列) | コンパイル時エラー(型チェック) |
| デプロイ順序 | 自分で把握して手動実行 |
--allで自動的に正しい順序で実行 |
| スタックが増えたとき | 依存関係を頭で管理 | コードを見れば依存関係が明確 |
特にスタックが5個・10個と増えてきたときに、CloudFormationでは依存関係の把握とデプロイ順序の管理が急速に辛くなります。CDKではコードの構造がそのまま依存関係を表すので、複雑になっても管理しやすいという点が大きなメリットです。
5.CDKの存在意義④:テスト・再利用ができる
インフラにユニットテストが書ける
コードなので、インフラ構成に対してテストが書けます。
test('EC2インスタンスタイプの確認', () => {
const app = new App();
const stack = new Ec2Stack(app, 'TestStack');
const template = Template.fromStack(stack);
template.hasResourceProperties('AWS::EC2::Instance', {
InstanceType: 't3.micro',
});
});
「本番環境に意図しないインスタンスタイプが使われていないか」「セキュリティグループが意図した設定になっているか」などをCI/CDで自動確認できます。
Constructとして再利用・配布できる
自社のベストプラクティスをConstructとしてパッケージ化し、npmやPyPIで配布できます。たとえば「セキュリティ要件を満たしたS3バケット」「標準的なVPC構成」などをConstructとして作っておき、社内の別プロジェクトでも再利用するといった使い方が可能です。
6.まとめ
CDKはCloudFormationの上位レイヤーです(cdk synthコマンドで最終的にはCFnテンプレートに変換されます)。
| 比較項目 | CloudFormation | CDK |
|---|---|---|
| 記述形式 | YAML / JSON | TypeScript・Python等 |
| 複数スタック | できる(管理は煩雑) | できる(依存関係も自動解決) |
| 抽象化 | 低(全部書く) | 高(L2/L3 Constructで省力化) |
| ループ・条件 | 限定的(Mappings等) | フル活用できる |
| タイポ検出 | デプロイ時 | コンパイル時(IDE上) |
| テスト | 難しい | 書ける |
| 再利用 | 難しい | Constructとしてパッケージ化可能 |
「複数スタックを一気に作れる」という認識は正しいですが、それよりもプログラミングの力でインフラ管理を楽にするという点がCDKの本質的な存在意義です。
特にスタックが増えてくると、CDKのメリットが大きく実感できるようになります。まずは小さなプロジェクトでCDKを試してみることをおすすめします。
本記事がどなたかの参考になれば幸いです。