AWS CDK(Cloud Development Kit)は、TypeScriptやPythonなどのプログラミング言語を使ってAWSインフラをコードとして定義できるフレームワークです。従来のCloudFormationテンプレート(JSON/YAML)と比較して、型安全性、コードの再利用性、IDEサポートなど多くの利点があります。
筆者はこれまでTerraformやCloudFormationを使ってきたが、CDKに移行してからインフラ構築の生産性が大きく向上した。特にTypeScriptとの組み合わせは、フロントエンドやバックエンドのコードベースとの統一感があり、チーム全体の学習コストを抑えられます。
CDKには3つの重要な概念があります。
Constructには3つのレベルがあり、L1(CloudFormationリソースそのもの)、L2(ベストプラクティスが適用された高水準API)、L3(複数リソースをまとめたパターン)があります。実務ではL2を中心に使い、必要に応じてL1にフォールバックするのが一般的です。
CDKプロジェクトの初期化は非常に簡単です。
npm install -g aws-cdk
mkdir my-infrastructure && cd my-infrastructure
cdk init app --language typescript
生成されるディレクトリ構造は以下のとおりです。
my-infrastructure/
├── bin/
│ └── my-infrastructure.ts # エントリーポイント
├── lib/
│ └── my-infrastructure-stack.ts # スタック定義
├── test/
│ └── my-infrastructure.test.ts # テスト
├── cdk.json
├── package.json
└── tsconfig.json
ここでは、VPC、ECSクラスタ、ALBを含む典型的なWebアプリケーションのインフラを構築してみましょう。
import * as cdk from "aws-cdk-lib";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecs_patterns from "aws-cdk-lib/aws-ecs-patterns";
import { Construct } from "constructs";
export class WebAppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPCの作成(2AZ、NATゲートウェイ1台)
const vpc = new ec2.Vpc(this, "AppVpc", {
maxAzs: 2,
natGateways: 1,
subnetConfiguration: [
{
name: "Public",
subnetType: ec2.SubnetType.PUBLIC,
cidrMask: 24,
},
{
name: "Private",
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
cidrMask: 24,
},
],
});
// ECSクラスタの作成
const cluster = new ecs.Cluster(this, "AppCluster", {
vpc,
containerInsights: true,
});
// Fargateサービス + ALBのパターン
const service = new ecs_patterns
.ApplicationLoadBalancedFargateService(
this, "AppService", {
cluster,
cpu: 256,
memoryLimitMiB: 512,
desiredCount: 2,
taskImageOptions: {
image: ecs.ContainerImage.fromAsset("./app"),
containerPort: 3000,
environment: {
NODE_ENV: "production",
},
},
publicLoadBalancer: true,
}
);
// オートスケーリングの設定
const scaling = service.service.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 10,
});
scaling.scaleOnCpuUtilization("CpuScaling", {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});
}
}
わずか60行程度のコードで、VPC、サブネット、NATゲートウェイ、ALB、ECS Fargateサービス、オートスケーリングまで含むインフラが定義できます。同じ構成をCloudFormationのYAMLで書くと、軽く数百行になるでしょう。
本番・ステージング・開発環境で異なる設定を管理するのは、IaCにおける重要なテーマです。CDKではTypeScriptのインターフェースを活用して型安全に管理できます。
interface EnvironmentConfig {
envName: string;
account: string;
region: string;
cpu: number;
memory: number;
desiredCount: number;
maxCapacity: number;
}
const environments: Record = {
dev: {
envName: "dev",
account: "111111111111",
region: "ap-northeast-1",
cpu: 256,
memory: 512,
desiredCount: 1,
maxCapacity: 2,
},
prod: {
envName: "prod",
account: "222222222222",
region: "ap-northeast-1",
cpu: 1024,
memory: 2048,
desiredCount: 3,
maxCapacity: 20,
},
};
CDKのもう一つの大きな利点は、インフラのテストが書けることです。assertionsモジュールを使えば、生成されるCloudFormationテンプレートの内容を検証できます。
import * as cdk from "aws-cdk-lib";
import { Template } from "aws-cdk-lib/assertions";
import { WebAppStack } from "../lib/web-app-stack";
test("ECS Service is created with correct config", () => {
const app = new cdk.App();
const stack = new WebAppStack(app, "TestStack");
const template = Template.fromStack(stack);
template.hasResourceProperties("AWS::ECS::Service", {
DesiredCount: 2,
LaunchType: "FARGATE",
});
template.resourceCountIs("AWS::EC2::VPC", 1);
});
インフラのテストを書く文化がチームに根付けば、本番環境での予期しない変更を事前に防げます。cdk diffコマンドと組み合わせることで、デプロイ前のレビュープロセスも格段に改善されます。
CDKを実運用で使う際のポイントをいくつか挙げます。
CDKはインフラとアプリケーションの境界を曖昧にし、エンジニアがよりビジネスロジックに集中できる環境を提供してくれます。まだ導入していないチームには、ぜひ小さなプロジェクトから試してみることをお勧めします。