AWS CDK とは?初心者でもわかる基礎知識
Infra Structure as Code を革新する AWS CDK
AWS Cloud Development Kit(AWS CDK)は、プログラミング言語を使用してAWSインフラストラクチャをコードとして定義できる革新的なフレームワークです。従来のYAMLやJSONベースのテンプレート記述から脱却し、TypeScript、Python、Java、C#などの使い慣れたプログラミング言語でインフラを定義できることが最大の特徴です。
AWS CDKの主な特徴:
- 型安全性: コンパイル時にエラーを検出可能
- 自動補完: IDEのインテリジェンス機能が活用可能
- モジュール化: コードの再利用が容易
- 抽象化: 複雑なインフラ構成をシンプルに記述可能
CloudFormation との決定的な違い3つ
- 開発体験の向上
- CloudFormation:
yaml Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: my-unique-bucket-name VersioningConfiguration: Status: Enabled
- AWS CDK(TypeScript):
import * as s3 from 'aws-cdk-lib/aws-s3'; const bucket = new s3.Bucket(this, 'MyBucket', { bucketName: 'my-unique-bucket-name', versioned: true });
- ロジックの組み込み
- 条件分岐やループが自然に記述可能
- ユーティリティ関数の作成と再利用が容易
- 環境変数やパラメータの取り扱いが柔軟
- カスタムコンストラクト
- 複数のAWSリソースを1つのコンポーネントとして定義可能
- ベストプラクティスを組織内で共有可能
- サードパーティ製のコンストラクトライブラリが利用可能
TypeScript で始める AWS CDK
TypeScriptは、AWS CDKで最も人気のある言語です。その理由と基本的な使い方を見ていきましょう。
TypeScriptを選ぶ理由:
- 強力な型システム
- リソースのプロパティ名や値の型チェック
- コンパイル時のエラー検出
- IDEによる優れたコード補完
- 豊富なエコシステム
- npm パッケージの活用
- 多数のサードパーティライブラリ
- コミュニティサポート
基本的な構文例:
import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as s3 from 'aws-cdk-lib/aws-s3'; export class MyInfraStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // VPCの作成 const vpc = new ec2.Vpc(this, 'MyVPC', { maxAzs: 2, natGateways: 1 }); // S3バケットの作成 const bucket = new s3.Bucket(this, 'MyBucket', { versioned: true, encryption: s3.BucketEncryption.S3_MANAGED }); } }
このコードは以下の要素で構成されています:
- import文: 必要なCDKモジュールのインポート
- Stackクラス: インフラ定義の基本単位
- コンストラクト: VPCやS3バケットなどのAWSリソース
- プロパティ: リソースの設定値
AWS CDKは、このTypeScriptコードを自動的にCloudFormationテンプレートに変換し、AWSリソースをデプロイします。このプロセスにより、インフラストラクチャのバージョン管理、テスト、再利用が容易になります。
AWS CDK 開発環境構築の完全ガイド
Node.js と AWS CDK のインストール手順
AWS CDKを使用するための環境構築を、順を追って説明します。
- Node.jsのインストール
- Node.js公式サイトから最新のLTS版をダウンロード
- インストール後、ターミナルで動作確認
bash node --version npm --version
- AWS CDKのインストール
# グローバルにAWS CDKをインストール npm install -g aws-cdk # バージョン確認 cdk --version
- AWS認証情報の設定
# AWS CLIのインストール(まだの場合) pip install awscli # 認証情報の設定 aws configure
必要な情報:
- AWS Access Key ID
- AWS Secret Access Key
- Default region name
- Default output format
VSCodeによる効率的な開発環境セットアップ
VSCodeでAWS CDKを快適に開発するための設定を紹介します。
- 必須拡張機能のインストール
- AWS Toolkit
- TypeScript and JavaScript Language Features
- ESLint
- Prettier
- 推奨するVSCode設定
{ "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "typescript.updateImportsOnFileMove.enabled": "always", "javascript.updateImportsOnFileMove.enabled": "always" }
- デバッグ設定
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug CDK App", "program": "${workspaceRoot}/bin/your-app.ts", "preLaunchTask": "tsc: build - tsconfig.json", "outFiles": ["${workspaceRoot}/cdk.out/**/*.js"], "console": "integratedTerminal" } ] }
初めてのCDKプロジェクト作成方法
新規プロジェクトの作成から初期設定までを解説します。
- プロジェクトの初期化
# プロジェクトディレクトリの作成 mkdir my-cdk-project cd my-cdk-project # CDKプロジェクトの初期化(TypeScript使用) cdk init app --language typescript
- プロジェクト構造の理解
my-cdk-project/ ├── bin/ │ └── my-cdk-project.ts # エントリーポイント ├── lib/ │ └── my-cdk-project-stack.ts # メインのスタック定義 ├── test/ │ └── my-cdk-project.test.ts # テストファイル ├── cdk.json # CDK設定ファイル ├── package.json └── tsconfig.json
- 依存パッケージのインストール
# 必要なパッケージのインストール npm install @aws-cdk/assert @types/jest @types/node typescript
- 初期設定の確認と修正
cdk.json
の確認json { "app": "npx ts-node --prefer-ts-exts bin/my-cdk-project.ts", "context": { "@aws-cdk/core:enableDiffNoFail": "true", "@aws-cdk/core:newStyleStackSynthesis": "true" } }
- 動作確認
# コードのビルド npm run build # スタックの一覧表示 cdk ls # デプロイ前の変更確認 cdk diff # デプロイ(初回は bootstrap が必要) cdk bootstrap cdk deploy
このセットアップにより、AWS CDKを使用した開発を即座に開始できる環境が整います。次のステップでは、この環境を使用して実際のインフラストラクチャコードを書いていきます。
実践!AWS CDKによるインフラ構築の5つのテクニック
VPCとサブネットの効率的な定義方法
VPCの構築は多くのAWSインフラストラクチャの基盤となります。AWS CDKを使用することで、複雑なVPC構成も簡単に定義できます。
import * as ec2 from 'aws-cdk-lib/aws-ec2'; // 基本的なVPC構成 const vpc = new ec2.Vpc(this, 'MainVPC', { maxAzs: 2, // 使用するAZの数 natGateways: 1, // コスト最適化のため1つのNATゲートウェイを使用 subnetConfiguration: [ { name: 'Public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24, }, { name: 'Private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24, }, { name: 'Isolated', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 28, } ] }); // VPCエンドポイントの追加 vpc.addInterfaceEndpoint('SecretsEndpoint', { service: ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER });
EC2インスタンスの柔軟なデプロイ戦略
EC2インスタンスのデプロイでは、セキュリティグループやIAMロールなども含めて統合的に管理します。
import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as iam from 'aws-cdk-lib/aws-iam'; // セキュリティグループの定義 const securityGroup = new ec2.SecurityGroup(this, 'WebServerSG', { vpc, description: 'Allow web traffic', allowAllOutbound: true }); securityGroup.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP traffic' ); // IAMロールの定義 const role = new iam.Role(this, 'WebServerRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore') ] }); // EC2インスタンスの作成 const instance = new ec2.Instance(this, 'WebServer', { vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, role, securityGroup, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO), machineImage: new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 }), userData: ec2.UserData.forLinux() }); // User Dataスクリプトの追加 instance.userData.addCommands( 'yum update -y', 'yum install -y httpd', 'systemctl start httpd', 'systemctl enable httpd' );
S3バケットのセキュアな構築方法
S3バケットでは、セキュリティとコンプライアンスを考慮した設定が重要です。
import * as s3 from 'aws-cdk-lib/aws-s3'; import * as kms from 'aws-cdk-lib/aws-kms'; // KMSキーの作成 const key = new kms.Key(this, 'BucketKey', { enableKeyRotation: true, description: 'Key for S3 bucket encryption' }); // セキュアなS3バケットの作成 const bucket = new s3.Bucket(this, 'SecureBucket', { encryption: s3.BucketEncryption.KMS, encryptionKey: key, versioned: true, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, removalPolicy: cdk.RemovalPolicy.RETAIN, lifecycleRules: [ { transitions: [ { storageClass: s3.StorageClass.INTELLIGENT_TIERING, transitionAfter: Duration.days(90) } ], noncurrentVersionExpiration: Duration.days(90) } ] }); // バケットポリシーの追加 bucket.addToResourcePolicy(new iam.PolicyStatement({ effect: iam.Effect.DENY, actions: ['s3:*'], resources: [bucket.arnForObjects('*')], principals: [new iam.AnyPrincipal()], conditions: { 'Bool': { 'aws:SecureTransport': false } } }));
Lambda関数のスマートなデプロイ
Lambda関数のデプロイでは、依存関係の管理やIAM権限の設定が重要です。
import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as path from 'path'; // Lambda関数の作成 const handler = new lambda.Function(this, 'MyHandler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'lambda')), environment: { BUCKET_NAME: bucket.bucketName, }, tracing: lambda.Tracing.ACTIVE, // X-Ray トレーシングの有効化 timeout: Duration.seconds(30), memorySize: 256, architecture: lambda.Architecture.ARM_64, // Gravitonプロセッサの使用 }); // バケットアクセス権限の付与 bucket.grantRead(handler); // カスタムメトリクスの追加 handler.addFunctionUrl({ authType: lambda.FunctionUrlAuthType.IAM, cors: { allowedOrigins: ['*'], allowedMethods: [lambda.HttpMethod.ALL], allowedHeaders: ['*'] } });
IAMポリシーの堅実な管理方法
最小権限の原則に基づいたIAMポリシーの管理方法を示します。
import * as iam from 'aws-cdk-lib/aws-iam'; // カスタマー管理ポリシーの作成 const customPolicy = new iam.ManagedPolicy(this, 'CustomPolicy', { statements: [ new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: [ 's3:GetObject', 's3:PutObject' ], resources: [ bucket.arnForObjects('*') ], conditions: { 'StringEquals': { 'aws:PrincipalTag/Environment': ['prod', 'dev'] } } }) ] }); // IAMロールの作成と関連付け const role = new iam.Role(this, 'CustomRole', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), managedPolicies: [ customPolicy, iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole') ] }); // インラインポリシーの追加 role.addToPolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['kms:Decrypt'], resources: [key.keyArn] }));
これらのテクニックを組み合わせることで、セキュアで管理しやすいインフラストラクチャを構築できます。各コンポーネントは再利用可能なモジュールとして設計されており、異なるプロジェクトや環境での再利用も容易です。
AWS CDKのベストプラクティス2024
スタック分割によるメンテナンス性の向上
大規模なインフラストラクチャをCDKで管理する場合、適切なスタック分割が重要です。
- 責務に基づくスタック分割
// networking-stack.ts export class NetworkingStack extends cdk.Stack { public readonly vpc: ec2.Vpc; constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); this.vpc = new ec2.Vpc(this, 'MainVPC', { maxAzs: 2, natGateways: 1 }); } } // database-stack.ts interface DatabaseStackProps extends cdk.StackProps { vpc: ec2.Vpc; } export class DatabaseStack extends cdk.Stack { public readonly cluster: rds.DatabaseCluster; constructor(scope: cdk.App, id: string, props: DatabaseStackProps) { super(scope, id, props); this.cluster = new rds.DatabaseCluster(this, 'Database', { engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_13_4 }), vpc: props.vpc, instanceType: ec2.InstanceType.of( ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM ) }); } } // app.ts const app = new cdk.App(); const networkingStack = new NetworkingStack(app, 'NetworkingStack'); const databaseStack = new DatabaseStack(app, 'DatabaseStack', { vpc: networkingStack.vpc });
- 環境別のスタック管理
// config/environment-config.ts export interface EnvironmentConfig { readonly environment: string; readonly instanceType: ec2.InstanceType; readonly minCapacity: number; readonly maxCapacity: number; } export const environmentConfigs: { [key: string]: EnvironmentConfig } = { dev: { environment: 'dev', instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL), minCapacity: 1, maxCapacity: 2 }, prod: { environment: 'prod', instanceType: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE), minCapacity: 2, maxCapacity: 10 } };
カスタムコンストラクトを活用した再利用性の実現
カスタムコンストラクトを作成することで、共通のインフラパターンを再利用可能にします。
- 基本的なカスタムコンストラクト
// constructs/secure-bucket.ts export interface SecureBucketProps { readonly bucketName?: string; readonly lifecycleDays?: number; readonly logRetention?: number; } export class SecureBucket extends Construct { public readonly bucket: s3.Bucket; constructor(scope: Construct, id: string, props?: SecureBucketProps) { super(scope, id); // KMSキーの作成 const key = new kms.Key(this, 'BucketKey', { enableKeyRotation: true, description: `Key for ${id} bucket encryption` }); // セキュアなバケットの作成 this.bucket = new s3.Bucket(this, 'Bucket', { bucketName: props?.bucketName, encryption: s3.BucketEncryption.KMS, encryptionKey: key, versioned: true, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, lifecycleRules: props?.lifecycleDays ? [ { expiration: Duration.days(props.lifecycleDays) } ] : undefined }); // アクセスログの設定 if (props?.logRetention) { new logs.LogGroup(this, 'AccessLogs', { retention: props.logRetention }); } } }
- L3コンストラクトの作成
// constructs/web-application.ts export interface WebApplicationProps { readonly vpc: ec2.Vpc; readonly domain: string; readonly env: string; } export class WebApplication extends Construct { constructor(scope: Construct, id: string, props: WebApplicationProps) { super(scope, id); // ALBの作成 const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', { vpc: props.vpc, internetFacing: true }); // ECSクラスターの作成 const cluster = new ecs.Cluster(this, 'Cluster', { vpc: props.vpc, containerInsights: true }); // Fargeサービスの作成 const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'Service', { cluster, loadBalancer: alb, desiredCount: 2, taskImageOptions: { image: ecs.ContainerImage.fromAsset('./docker'), environment: { NODE_ENV: props.env } } }); // Auto Scalingの設定 const scaling = fargateService.service.autoScaleTaskCount({ maxCapacity: 4, minCapacity: 1 }); scaling.scaleOnCpuUtilization('CpuScaling', { targetUtilizationPercent: 70 }); } }
効果的なテスト戦略の実現方法
CDKのテストは、ユニットテスト、スナップショットテスト、アサーションテストの3つのレベルで実施します。
- ユニットテスト
// test/secure-bucket.test.ts import { Template } from 'aws-cdk-lib/assertions'; import * as cdk from 'aws-cdk-lib'; import { SecureBucket } from '../lib/constructs/secure-bucket'; describe('SecureBucket', () => { test('creates encrypted bucket', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'TestStack'); new SecureBucket(stack, 'TestBucket', { bucketName: 'test-bucket' }); const template = Template.fromStack(stack); template.hasResourceProperties('AWS::S3::Bucket', { BucketName: 'test-bucket', VersioningConfiguration: { Status: 'Enabled' }, PublicAccessBlockConfiguration: { BlockPublicAcls: true, BlockPublicPolicy: true, IgnorePublicAcls: true, RestrictPublicBuckets: true } }); }); });
- スナップショットテスト
// test/stack.test.ts import * as cdk from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import * as MyStack from '../lib/my-stack'; test('Stack creates expected resources', () => { const app = new cdk.App(); const stack = new MyStack.MyStack(app, 'MyTestStack'); const template = Template.fromStack(stack); expect(template.toJSON()).toMatchSnapshot(); });
- カスタムアサーション
// test/assertions.ts export function hasSecureS3Bucket(template: Template, bucketName: string) { template.hasResourceProperties('AWS::S3::Bucket', { BucketName: bucketName, BucketEncryption: { ServerSideEncryptionConfiguration: [ { ServerSideEncryptionByDefault: { SSEAlgorithm: 'aws:kms' } } ] }, PublicAccessBlockConfiguration: { BlockPublicAcls: true, BlockPublicPolicy: true, IgnorePublicAcls: true, RestrictPublicBuckets: true }, VersioningConfiguration: { Status: 'Enabled' } }); }
これらのベストプラクティスを適用することで、メンテナンス性が高く、再利用可能で、品質の担保されたCDKプロジェクトを実現できます。
チーム開発でのAWS CDK活用術
GitHubを使用したバージョン管理の実践
チーム開発においてGitHubを効果的に活用し、AWS CDKプロジェクトを管理する方法を解説します。
- 効果的なリポジトリ構成
project-root/ ├── bin/ │ └── app.ts # エントリーポイント ├── lib/ │ ├── constructs/ # 共有コンストラクト │ │ ├── secure-bucket.ts │ │ └── web-service.ts │ ├── stacks/ # 環境別スタック │ │ ├── network-stack.ts │ │ └── app-stack.ts │ └── configs/ # 環境設定 │ ├── dev.ts │ └── prod.ts ├── test/ # テストコード ├── .github/ # GitHub関連設定 └── cdk.json # CDK設定
- ブランチ戦略の実装
// lib/configs/base-config.ts export interface EnvironmentConfig { readonly environment: string; readonly tags: { [key: string]: string }; readonly vpc: { readonly maxAzs: number; readonly natGateways: number; }; } // lib/configs/dev.ts import { EnvironmentConfig } from './base-config'; export const devConfig: EnvironmentConfig = { environment: 'development', tags: { Environment: 'dev', Team: 'infrastructure' }, vpc: { maxAzs: 2, natGateways: 1 } };
CI/CDパイプラインの構築方法
AWS CDKプロジェクトの継続的インテグレーション/デリバリーを実現します。
- デプロイメントパイプライン
import * as cdk from 'aws-cdk-lib'; import * as pipelines from 'aws-cdk-lib/pipelines'; export class CdkPipelineStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { synth: new pipelines.ShellStep('Synth', { input: pipelines.CodePipelineSource.gitHub('owner/repo', 'main'), commands: [ 'npm ci', 'npm run build', 'npx cdk synth' ] }) }); // 開発環境のデプロイステージ const devStage = new ApplicationStage(this, 'Dev', { env: { account: '111111111111', region: 'ap-northeast-1' } }); pipeline.addStage(devStage, { pre: [ new pipelines.ShellStep('UnitTest', { commands: ['npm test'] }) ] }); } }
- 環境別デプロイメント設定
// lib/stage.ts export class ApplicationStage extends cdk.Stage { constructor(scope: Construct, id: string, props?: cdk.StageProps) { super(scope, id, props); const config = loadConfig(props?.env?.account); const networkStack = new NetworkStack(this, 'Network', { env: props?.env, config: config.network }); new ApplicationStack(this, 'Application', { env: props?.env, vpc: networkStack.vpc, config: config.application }); } }
コードレビューのポイントと注意点
効果的なコードレビューのために、以下の点に注意を払います:
- アーキテクチャレベルのレビュー
// 推奨パターン export class WebServiceStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // VPCの作成は別スタックで行い、参照として受け取る const vpc = ec2.Vpc.fromLookup(this, 'VPC', { vpcId: props.vpcId }); // セキュリティグループの定義 const securityGroup = new ec2.SecurityGroup(this, 'WebSG', { vpc, description: 'Security group for web servers', allowAllOutbound: false // 明示的なアウトバウンドルールの定義 }); // 必要最小限の権限を持つIAMロール const role = new iam.Role(this, 'WebServerRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName( 'AmazonSSMManagedInstanceCore' ) ] }); } }
- コスト最適化のレビュー
// コスト最適化の例 export class OptimizedStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // Auto Scalingの適切な設定 const asg = new autoscaling.AutoScalingGroup(this, 'ASG', { vpc, minCapacity: 1, maxCapacity: 3, instanceType: ec2.InstanceType.of( ec2.InstanceClass.T3A, // ARM版インスタンスの使用 ec2.InstanceSize.SMALL ) }); // スケーリングポリシーの設定 asg.scaleOnCpuUtilization('CpuScaling', { targetUtilizationPercent: 70, cooldown: cdk.Duration.seconds(300) }); } }
- セキュリティレビューのポイント
// セキュリティベストプラクティス export class SecureStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // 暗号化の有効化 const bucket = new s3.Bucket(this, 'DataBucket', { encryption: s3.BucketEncryption.KMS_MANAGED, enforceSSL: true, versioned: true, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL }); // セキュアなバケットポリシー bucket.addToResourcePolicy(new iam.PolicyStatement({ effect: iam.Effect.DENY, principals: [new iam.AnyPrincipal()], actions: ['s3:*'], resources: [bucket.arnForObjects('*')], conditions: { 'Bool': { 'aws:SecureTransport': false } } })); } }
これらの実践により、チームでのAWS CDK開発を効率的かつ安全に進めることができます。定期的なコードレビューとベストプラクティスの共有を通じて、プロジェクトの品質を継続的に向上させることが重要です。