ChatGPTを活用して作ったAWS CDK (Python)のハンズオンです。誤りを含む可能性があります。
第1章 イントロダクション
テーマ:なぜ IaC?/CDKとは?/Pythonで学ぶ意義/ハンズオン準備
🏠 導入:IaCの世界へようこそ
霊夢「ねぇ魔理沙、AWSの設定ってコンソールでポチポチやるのめんどくさくない?」
魔理沙「めんどくさいし、同じ構成を再現できないのが地獄だぜ。」
霊夢「じゃあ“コードでインフラを書く”ってやつをやってみようよ!」
魔理沙「それが“Infrastructure as Code”、略して IaC だな!」
💡 1. なぜ Infrastructure as Code(IaC)か
霊夢「IaCって、要は“インフラもプログラムで管理する”ってこと?」
魔理沙「そうそう。例えばこんな感じだぜ。」
# CloudFormation(YAML)の例 Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: my-sample-bucket
霊夢「なるほど、設定がコードとして残るのね。」
魔理沙「そう。コードにすることで、」
- ✅ 再現性(同じ環境を何度でも構築可能)
- ✅ レビュー性(Gitで差分を見られる)
- ✅ 自動化(CI/CDに組み込みやすい)
魔理沙「この3つがめっちゃ強い!」
霊夢「でもYAML書くの面倒そう…」
魔理沙「そこで登場するのがAWS CDKだぜ!」
🚀 2. AWS CDK の概要と v2 への移行ポイント
魔理沙「AWS CDK(Cloud Development Kit)は、AWSのリソースをPythonやTypeScriptなどのプログラミング言語で書けるIaCツールだぜ。」
from aws_cdk import ( App, Stack, aws_s3 as s3, ) from constructs import Construct class MyStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyBucket", versioned=True) app = App() MyStack(app, "MyStack") app.synth()
霊夢「おお、これPythonのクラスで書けるんだ!」
魔理沙「そう。CloudFormationを“自動生成”してくれる。
手でYAMLを書かなくても、CDKが裏で作ってくれるのさ。」
🆕 CDK v2の特徴まとめ
| 比較項目 | v1 | v2 |
|---|---|---|
| パッケージ構成 | サービスごとに分割(例: aws-cdk.aws-s3) | 1つに統合(aws-cdk-lib) |
| constructモジュール | constructs v3系未対応 |
constructs v10対応 |
| CLI互換 | v1と基本互換 | 同様(cdk synth, cdk deploy) |
| 対応言語 | Python, TS, Java等 | 同様+型改善 |
魔理沙「つまりv2はシンプルで依存地獄から解放された。」
霊夢「Pythonの環境構築も楽になったってことね。」
🐍 3. Python で扱う CDK のメリット・適用シーン
霊夢「Python版のCDKって、TypeScript版とどう違うの?」
魔理沙「Pythonのメリットは“読みやすさ”と“データ操作の柔軟さ”だぜ。」
魔理沙「具体的には――」
- 🧩 可読性が高い(学習コストが低い)
- 📊 Pythonの標準ライブラリで設定を柔軟に生成可能
- 🔬 AI/自動生成との親和性が高い(ChatGPT等で補完しやすい)
- 🧠 機械学習や運用スクリプトとも統合しやすい
霊夢「つまりPythonエンジニアならすぐ始められるってことね!」
魔理沙「そう、インフラエンジニアじゃなくてもデプロイできるんだぜ。」
🧑💻 4. 本書の読み進め方・演習環境の構成
霊夢「この本ではどう進めるの?」
魔理沙「だいたいこんな流れだぜ。」
第1章: CDKの概要と環境構築 第2章: S3, EC2, Lambdaなど基本リソースをコード化 第3章: ネットワーク/サーバレス構成の実践 第4章: CI/CDとマルチ環境運用 第5章: 大規模設計とベストプラクティス
霊夢「演習環境はどうするの?」
魔理沙「ローカルのPython環境でOKだぜ。」
🧱 環境構築の流れ(ハンズオン準備)
# 1. 仮想環境を作成 python3 -m venv .venv source .venv/bin/activate # 2. CDK CLIをインストール npm install -g aws-cdk # 3. Pythonプロジェクトを初期化 mkdir my_cdk_project && cd my_cdk_project cdk init app --language python # 4. 依存関係をインストール pip install -r requirements.txt # 5. 動作確認 cdk synth
霊夢「なるほど、これで準備完了ね。」
魔理沙「次章では、実際に“Hello CDK”でS3バケットを立ち上げるぜ!」
📘 まとめ
| 項目 | 内容 |
|---|---|
| IaCとは | インフラをコードで管理する考え方 |
| AWS CDKとは | AWSリソースをPython/TS等で構築するIaCツール |
| v2の特徴 | aws-cdk-libで統一、依存が簡潔に |
| Pythonの利点 | 可読性・柔軟性・AI親和性 |
| 次のステップ | cdk init → S3バケット構築へGo! |
霊夢「CDK、なんか魔法陣を描くみたいでワクワクするね!」
魔理沙「そうだろ? コード一発でインフラが立ち上がるんだぜ。まるで呪文みたいにな!」
第2章 開発環境の構築とプロジェクト初期化
🌱 1. 前提条件:AWS アカウント、AWS CLI、Node.js、Python/venv 等
霊夢「さて魔理沙、CDKの準備って何が必要なの?」
魔理沙「大事なのは“AWSアカウントと開発環境”だぜ。IaCの舞台を整えよう!」
🧩 必要なものチェックリスト
| 項目 | 内容 | 確認コマンド |
|---|---|---|
| AWS アカウント | IAMユーザー or ルートアカウント | https://console.aws.amazon.com |
| AWS CLI | AWS操作用コマンドライン | aws --version |
| Node.js | CDK CLI動作に必要 | node -v |
| npm | Node.jsに付属 | npm -v |
| Python 3.9〜3.12 | CDK本体を書く言語 | python3 --version |
| pip | Pythonパッケージ管理 | pip --version |
| venv | 仮想環境ツール | 標準で付属 |
| Git | コード管理 | git --version |
霊夢「つまり“Node.jsはCDK CLI用”、Pythonは“CDKの中身を書く用”ってことね!」
魔理沙「その通り。PythonだけじゃCDK動かないから注意だぜ。」
🧙 AWS CLIの設定
aws configure
霊夢「あっ、これでアクセスキー設定ね!」
魔理沙「そう。リージョンも聞かれるけど、最初は ap-northeast-1(東京)でいいぜ。」
🪄 2. CDK v2 プロジェクトの初期化
魔理沙「さぁ、魔法陣を描こう(プロジェクトを作ろう)!」
🏗️ CDK CLIのインストール
npm install -g aws-cdk
霊夢「aws-cdk って npm で入れるのね。」
魔理沙「CLIはNode.jsで動くからな。Pythonのコードはあとで書くぜ。」
🧱 プロジェクトを作成
mkdir my_cdk_app cd my_cdk_app cdk init app --language python
出力例:
Applying project template app for python Initializing a new git repository... Executing Creating virtualenv... Executing Installing dependencies... ✅ All done!
霊夢「あっ、自動でvenvと依存関係入ってる!」
魔理沙「そう。ディレクトリ構成を見てみよう。」
my_cdk_app/ ├── app.py ├── my_cdk_app/ │ ├── __init__.py │ └── my_cdk_app_stack.py ├── requirements.txt ├── cdk.json └── README.md
🧩 3. 仮想環境・requirements.txt・aws-cdk-lib モジュール管理
霊夢「venvが自動でできるけど、手動で作る方法も知っておきたいわね。」
🧙 仮想環境の作成と有効化
python3 -m venv .venv source .venv/bin/activate # macOS/Linux # .venv\Scripts\activate # Windows
🔧 requirements.txt の中身
aws-cdk-lib==2.149.0 constructs>=10.0.0,<11.0.0
霊夢「aws-cdk-lib がCDKの本体ね!」
魔理沙「そうだぜ。v2からはこれ1本でOK。
昔みたいに aws-cdk.aws-s3 とかは不要になった。」
📦 依存関係のインストール
pip install -r requirements.txt
🔍 インストール確認
pip list | grep cdk
出力例:
aws-cdk-lib 2.149.0 constructs 10.3.0
🧠 4. スタック・アプリ・コンストラクトの役割整理
霊夢「app.py と my_cdk_app_stack.py の違いがまだわからない…」
魔理沙「CDKの構成は“3層構造”で覚えようぜ。」
App → Stack → Construct
| 階層 | 役割 | 例 |
|---|---|---|
| App | 全体をまとめる親 | app.py |
| Stack | デプロイ単位(CloudFormationスタック) | MyCdkAppStack |
| Construct | 個々のAWSリソースを表す部品 | s3.Bucket() |
📄 app.py の例
#!/usr/bin/env python3 from aws_cdk import App from my_cdk_app.my_cdk_app_stack import MyCdkAppStack app = App() MyCdkAppStack(app, "MyCdkAppStack") app.synth()
📄 my_cdk_app_stack.py の例
from aws_cdk import ( Stack, aws_s3 as s3, ) from constructs import Construct class MyCdkAppStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs): super().__init__(scope, construct_id, **kwargs) # S3バケットを作成 s3.Bucket( self, "MySampleBucket", versioned=True, removal_policy=None )
霊夢「おお、CDKの階層構造が見えてきた!」
魔理沙「StackはCloudFormationテンプレートに対応してるんだぜ。
synth コマンドで裏側のYAMLも見れる。」
⚙️ 5. デプロイの基本(bootstrap/synth/deploy/destroy)
🧱 デプロイ前の初期化(bootstrap)
cdk bootstrap
魔理沙「CDKが使うS3バケットやIAMロールを自動で作るコマンドだぜ。」
🧬 テンプレートの確認
cdk synth
出力例(抜粋)
Resources: MySampleBucket8D7B05F3: Type: AWS::S3::Bucket Properties: VersioningConfiguration: Status: Enabled
霊夢「これが自動生成されたCloudFormationテンプレートか!」
魔理沙「そう。Pythonコードがインフラ定義に変わった瞬間だぜ。」
🚀 デプロイ実行
cdk deploy
出力例:
✨ Synthesis time: 3.9s MyCdkAppStack: deploying... ✅ MyCdkAppStack (no changes)
霊夢「おぉ…AWSに本当にS3バケットができてる!」
魔理沙「これがIaCの魔法だぜ。」
💣 削除(destroy)
cdk destroy
霊夢「消すのも一瞬なのね。」
魔理沙「CI/CDで自動破棄もできるし、テストにも便利だぜ。」
📘 まとめ
| コマンド | 説明 |
|---|---|
cdk init app --language python |
新しいCDKプロジェクト作成 |
cdk synth |
CloudFormationテンプレート生成 |
cdk deploy |
AWSへデプロイ |
cdk destroy |
スタック削除 |
cdk bootstrap |
初回デプロイ用リソース作成 |
霊夢「これでPythonでAWSを操る準備が整ったね!」
魔理沙「ああ。次章ではいよいよ“S3・EC2・Lambda”の実戦だぜ。」
霊夢「コード多めのハンズオン、腕が鳴るわね!」
第3章 CDK の基本概念と構文
テーマ:CDKの中核概念を理解し、最初のS3バケットをコードで立ち上げよう! (この章から本格的にコードブロック多めで進行します)
🧩 1. Stack/App/Construct/Scope の用語整理
霊夢「ねぇ魔理沙、CDKのコードって App とか Stack とか Construct とか、似た単語が多くて混乱するんだけど…」
魔理沙「それはCDKの“3階建て構造”を覚えるのが早いぜ。」
🏗️ CDKの構造イメージ
App
└── Stack
└── Construct
└── AWSリソース(S3, Lambda, EC2など)
用語まとめ
| 用語 | 意味 | 対応イメージ |
|---|---|---|
| App | CDK全体のエントリーポイント(アプリケーション) | 「家の設計図全体」 |
| Stack | CloudFormationのスタック単位(デプロイ単位) | 「1つの建物」 |
| Construct | AWSリソースや論理構成を表す部品 | 「部屋や家具」 |
| Scope | Constructを配置する“親要素” | 「親子関係を示すもの」 |
霊夢「Scopeってつまり“どこに置くか”ってこと?」
魔理沙「そうだぜ。self, scope, id の3つは常にワンセットだ。」
class MyStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs)
📦 2. aws-cdk-lib パッケージとサービスモジュールの扱い
霊夢「CDKのv2になって、importがめちゃくちゃシンプルになったって聞いたわ!」
魔理沙「そう。v1では分割されてたAWSモジュールが、v2では全部 aws_cdk_lib に統合されたんだぜ。」
v1(旧)とv2(新)の違い
# v1(旧) from aws_cdk import ( core, aws_s3 as s3, aws_ec2 as ec2, ) # v2(新) from aws_cdk import ( App, Stack, aws_s3 as s3, aws_ec2 as ec2, )
v2での基本構造(例)
from aws_cdk import ( App, Stack, aws_s3 as s3, ) from constructs import Construct class MyStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyBucket", versioned=True) app = App() MyStack(app, "MyStack") app.synth()
霊夢「PythonコードからAWSリソースが呼び出せるって新鮮ね!」
魔理沙「しかも、すべてCloudFormationテンプレートに変換されるんだぜ。」
🐍 3. Python ならではの注意点(snake_case/予約語対応など)
霊夢「Pythonって命名ルール厳しいけど、CDKでも関係あるの?」
魔理沙「めっちゃある。AWSのプロパティはcamelCaseなのに、Pythonはsnake_case。
CDKでは自動で変換されるから安心だぜ。」
🐍 例:プロパティ変換
| CloudFormationのキー | Pythonでの指定方法 |
|---|---|
bucketName |
bucket_name |
removalPolicy |
removal_policy |
versioned |
versioned(そのまま) |
⚠️ Python予約語との衝突例
霊夢「もしプロパティ名がPythonの予約語とかぶったら?」
魔理沙「末尾にアンダースコア _ をつけるんだぜ。」
# 例:lambda は予約語なので aws_lambda.Function(self, "Fn", function_name="my-func", handler="app.handler", runtime=aws_lambda.Runtime.PYTHON_3_11, code=aws_lambda.Code.from_asset("lambda"), )
(lambda というキーワードが使えないため、aws_lambda モジュール名で回避)
💬 蛇足Tips:Python構文での注意
True/False/NoneをAWSテンプレートに変換可能- 文字列は基本
str型、リストや辞書も自動でCFN形式に変換される **kwargsで可変引数を渡す設計が多い
🧱 4. 初めてのスタック:S3バケット1つを作るハンズオン
霊夢「よし、そろそろ作ってみようか!」
魔理沙「いよいよだな。S3バケットを1行で作れる魔法を見せてやるぜ。」
📄 app.py
#!/usr/bin/env python3 from aws_cdk import App from my_cdk_app.my_cdk_app_stack import MyCdkAppStack app = App() MyCdkAppStack(app, "MyCdkAppStack") app.synth()
📄 my_cdk_app_stack.py
from aws_cdk import ( Stack, aws_s3 as s3, ) from constructs import Construct class MyCdkAppStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- 🌟 S3バケットを1つ作成する --- bucket = s3.Bucket( self, "MyFirstBucket", bucket_name="my-first-cdk-bucket-2025", versioned=True, block_public_access=s3.BlockPublicAccess.BLOCK_ALL, )
💻 デプロイ手順
cdk synth cdk deploy
✅ 出力例(デプロイ時)
✨ Synthesis time: 3.9s MyCdkAppStack: deploying... MyCdkAppStack: creating CloudFormation changeset... ✅ MyCdkAppStack Outputs: MyCdkAppStack.MyFirstBucketName = my-first-cdk-bucket-2025
霊夢「S3バケットできた!ほんとにコード1行で作れたのね。」
魔理沙「しかも、裏でCloudFormationが自動生成されてるんだぜ。」
🧬 5. CDK を用いた CloudFormation テンプレートの生成イメージ
霊夢「cdk synth の裏側ってどうなってるの?」
魔理沙「PythonコードがJSON/YAMLのCloudFormationテンプレートに変換されてるんだ。」
📄 出力例(cdk.out/MyCdkAppStack.template.json)
{ "Resources": { "MyFirstBucket8D7B05F3": { "Type": "AWS::S3::Bucket", "Properties": { "BucketName": "my-first-cdk-bucket-2025", "VersioningConfiguration": { "Status": "Enabled" }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, "IgnorePublicAcls": true, "RestrictPublicBuckets": true } } } } }
霊夢「つまり、Pythonのクラスが最終的にはこのテンプレートに変わるってこと?」
魔理沙「そうだぜ。これがCDK最大の魅力、“コードでCloudFormationを書く”仕組みさ。」
📘 まとめ
| 概念 | ポイント |
|---|---|
| App/Stack/Construct | CDKの3階層構造。Appが親でStackがデプロイ単位 |
| aws-cdk-lib | v2で統合されたメインパッケージ |
| Pythonの注意点 | snake_case・予約語・自動型変換に注意 |
| 初めてのスタック | s3.Bucket() でS3を簡単作成 |
| CloudFormation連携 | cdk synth → JSON/YAMLテンプレート生成 |
霊夢「PythonのクラスがそのままAWSリソースになるの、感動的ね。」
魔理沙「次章では、VPCやセキュリティグループをコードで構築して、
本格的なネットワーク構成に踏み込むぜ!」
第4章 ネットワーキング基盤 – VPC/サブネット/セキュリティグループ
🌐 1. VPC をコードで定義する(パブリック/プライベート)
霊夢「ねぇ魔理沙、AWSってVPCがないと何も始まらないんでしょ?」
魔理沙「その通り。VPCはクラウドの“ネットワークの土台”だぜ。まずはVPCをコードで作ってみよう。」
📄 network_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, ) from constructs import Construct class NetworkStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- 🌍 VPC作成 --- self.vpc = ec2.Vpc( self, "MyVpc", ip_addresses=ec2.IpAddresses.cidr("10.0.0.0/16"), max_azs=2, # 2つのAZを利用 nat_gateways=1, subnet_configuration=[ ec2.SubnetConfiguration( name="public", subnet_type=ec2.SubnetType.PUBLIC, cidr_mask=24 ), ec2.SubnetConfiguration( name="private", subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS, cidr_mask=24 ), ] )
霊夢「すご、これだけでVPC+サブネット+ルート構成が自動でできるの?」
魔理沙「ああ。Vpc コンストラクトが全部まとめてくれる。
しかも max_azs=2 で可用性も確保だ。」
💡 出力イメージ
10.0.0.0/16 ├── PublicSubnet1 (10.0.0.0/24) ├── PublicSubnet2 (10.0.1.0/24) ├── PrivateSubnet1 (10.0.2.0/24) └── PrivateSubnet2 (10.0.3.0/24)
🛣️ 2. サブネット、ルートテーブル、IGW、NAT ゲートウェイ
霊夢「自動生成でもいいけど、自分で細かく定義したいときは?」
魔理沙「もちろんできるぜ。CDKはCloudFormationリソースを直接定義できる。」
📄 custom_vpc_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, ) from constructs import Construct class CustomVpcStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- VPC作成 --- vpc = ec2.Vpc(self, "ManualVpc", ip_addresses=ec2.IpAddresses.cidr("10.1.0.0/16"), max_azs=2, nat_gateways=0, # 後で追加 subnet_configuration=[] ) # --- IGW作成 --- igw = ec2.CfnInternetGateway(self, "InternetGateway") ec2.CfnVPCGatewayAttachment( self, "VpcGatewayAttachment", vpc_id=vpc.vpc_id, internet_gateway_id=igw.ref ) # --- パブリックサブネット --- public_subnet = ec2.CfnSubnet( self, "PublicSubnetA", vpc_id=vpc.vpc_id, cidr_block="10.1.0.0/24", availability_zone="ap-northeast-1a", map_public_ip_on_launch=True ) # --- ルートテーブル --- route_table = ec2.CfnRouteTable(self, "PublicRouteTable", vpc_id=vpc.vpc_id) ec2.CfnRoute(self, "PublicRoute", route_table_id=route_table.ref, destination_cidr_block="0.0.0.0/0", gateway_id=igw.ref ) ec2.CfnSubnetRouteTableAssociation(self, "PublicAssoc", subnet_id=public_subnet.ref, route_table_id=route_table.ref ) # --- NATゲートウェイ(オプション) --- eip = ec2.CfnEIP(self, "Eip") nat_gw = ec2.CfnNatGateway( self, "NatGateway", subnet_id=public_subnet.ref, allocation_id=eip.attr_allocation_id )
霊夢「すごい、全部コードで制御できるんだね。」
魔理沙「CDKは“L2 Construct”のラッパー(ec2.Vpc)も、“L1リソース”(CfnVpc)も両方使える。
柔軟性が高いんだぜ。」
🛡️ 3. セキュリティグループ/ネットワークACL/構成ポリシー
霊夢「次はファイアウォール的なやつね!」
魔理沙「そうだ。まずはセキュリティグループ(SG)を作って、HTTP/SSHだけ許可しよう。」
📄 security_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, ) from constructs import Construct class SecurityStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- Webサーバ用SG --- self.web_sg = ec2.SecurityGroup( self, "WebSg", vpc=vpc, description="Allow HTTP and SSH", allow_all_outbound=True ) # --- ルール設定 --- self.web_sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(22), "SSH from anywhere") self.web_sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(80), "HTTP from anywhere")
霊夢「めっちゃシンプルね!」
魔理沙「Network ACLも同じように定義できるけど、実務ではSGの方が柔軟だぜ。」
📘 Network ACL(例:オプション)
nacl = ec2.NetworkAcl(self, "MyAcl", vpc=vpc) nacl.add_entry("InboundHTTP", cidr=ec2.AclCidr.any_ipv4(), rule_number=100, traffic=ec2.AclTraffic.tcp_port(80), direction=ec2.TrafficDirection.INGRESS, rule_action=ec2.Action.ALLOW, )
🧰 4. 実践演習:Webアプリ用ネットワーク構成の作成
霊夢「ここまでのVPCとSGを組み合わせて、Webアプリの基盤を作ろう!」
魔理沙「おう、VPC+EC2+SGで“Hello from Web Server”を出す環境を構築するぜ。」
📄 web_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, ) from constructs import Construct class WebStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- セキュリティグループ --- web_sg = ec2.SecurityGroup( self, "WebSecurityGroup", vpc=vpc, description="Allow SSH and HTTP", allow_all_outbound=True ) web_sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(22), "SSH access") web_sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(80), "HTTP access") # --- EC2インスタンス --- instance = ec2.Instance( self, "WebServer", instance_type=ec2.InstanceType("t3.micro"), machine_image=ec2.MachineImage.latest_amazon_linux2023(), vpc=vpc, security_group=web_sg, vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC), ) # --- ユーザーデータでApache起動 --- instance.user_data.add_commands( "yum install -y httpd", "systemctl enable httpd", "systemctl start httpd", "echo 'Hello from CDK Web Server' > /var/www/html/index.html" )
💻 デプロイコマンド
cdk synth cdk deploy
🌐 出力例
Outputs: WebStack.WebServerInstancePublicIp = 13.115.xxx.xxx
ブラウザでアクセスすると:
http://13.115.xxx.xxx/ → Hello from CDK Web Server
📘 まとめ
| 項目 | 内容 |
|---|---|
| VPC定義 | ec2.Vpc でサブネット・NAT・IGWを一括生成可能 |
| サブネット構成 | SubnetConfiguration で自動分割 or Cfnリソースで手動制御 |
| セキュリティ | SGで柔軟制御、NACLで低レベル制御 |
| 実践演習 | VPC+EC2+SG+Apache起動まで自動構築 |
霊夢「コードだけでネットワーク全部作れるなんて、もうAWSマスターだね!」
魔理沙「まだ序章だぜ。次章では、この基盤にアプリを載せる“コンピュート基盤(EC2/ECS/Fargate)”を構築するぞ!」
第5章 コンピュート基盤 – EC2、AutoScaling、ECS/Fargate
☁️ 1. EC2 インスタンス定義とキーペア/IAMロールの設定
霊夢「VPCは作れたけど、やっぱりサーバー(EC2)を立ててみたい!」
魔理沙「よっしゃ、まずは最小構成のEC2をCDKで書こうぜ。」
📄 ec2_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, aws_iam as iam, ) from constructs import Construct class Ec2Stack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- EC2用IAMロール --- role = iam.Role( self, "Ec2Role", assumed_by=iam.ServicePrincipal("ec2.amazonaws.com") ) role.add_managed_policy( iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") ) # --- セキュリティグループ --- sg = ec2.SecurityGroup( self, "Ec2SecurityGroup", vpc=vpc, description="Allow SSH and HTTP", allow_all_outbound=True ) sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(22), "SSH") sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(80), "HTTP") # --- EC2インスタンス --- instance = ec2.Instance( self, "MyEc2Instance", instance_type=ec2.InstanceType("t3.micro"), machine_image=ec2.MachineImage.latest_amazon_linux2023(), vpc=vpc, security_group=sg, role=role, key_name="my-keypair" # AWSコンソールで作成済みキー ) # --- ユーザーデータ --- instance.user_data.add_commands( "yum install -y httpd", "systemctl enable httpd", "systemctl start httpd", "echo '<h1>Hello from EC2 via CDK</h1>' > /var/www/html/index.html" )
霊夢「これでEC2+Apacheが自動で起動するんだ!」
魔理沙「しかもIAMロールも設定済み。SSMで入れるようになるぜ。」
💻 デプロイ
cdk synth cdk deploy
📈 2. オートスケーリンググループの導入
霊夢「アクセス増えても耐えられるようにしたいわ!」
魔理沙「それならAutoScalingGroup(ASG)を使うぜ。」
📄 asg_stack.py
from aws_cdk import ( Stack, aws_autoscaling as autoscaling, aws_ec2 as ec2, ) from constructs import Construct class AutoScalingStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- セキュリティグループ --- sg = ec2.SecurityGroup( self, "AsgSg", vpc=vpc, allow_all_outbound=True ) sg.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(80)) # --- AutoScalingグループ --- asg = autoscaling.AutoScalingGroup( self, "WebAsg", vpc=vpc, instance_type=ec2.InstanceType("t3.micro"), machine_image=ec2.MachineImage.latest_amazon_linux2023(), desired_capacity=2, min_capacity=1, max_capacity=3, security_group=sg ) asg.user_data.add_commands( "yum install -y httpd", "systemctl enable httpd", "systemctl start httpd", "echo 'Hello from AutoScalingGroup!' > /var/www/html/index.html" )
📈 スケーリングポリシーを追加
asg.scale_on_cpu_utilization(
"CpuScaling",
target_utilization_percent=60
)
霊夢「CPU60%超えたら自動で増えるのね!」
魔理沙「そう。クラウドらしいスケーラビリティだぜ。」
🐳 3. コンテナ運用:ECS or Fargate 利用のための CDK 構築
霊夢「最近のアプリはコンテナで動かすことが多いよね?」
魔理沙「おう! AWSならECSを使う。CDKなら簡単に構築できるぞ。」
📄 ecs_stack.py
from aws_cdk import ( Stack, aws_ecs as ecs, aws_ecs_patterns as ecs_patterns, aws_ec2 as ec2, ) from constructs import Construct class EcsStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- ECSクラスタ --- cluster = ecs.Cluster( self, "MyCluster", vpc=vpc ) # --- Fargateサービス (Hello Worldコンテナ) --- ecs_patterns.ApplicationLoadBalancedFargateService( self, "MyFargateService", cluster=cluster, cpu=256, memory_limit_mib=512, desired_count=2, public_load_balancer=True, task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_registry("nginx:latest"), container_port=80, ), )
霊夢「nginx のコンテナが2台、ALB付きで自動展開されるってこと!?」
魔理沙「そうだぜ。これがわずか数十行でできるのがCDKの強みだ。」
💡 CDKの力技:ECS + ALB 一括構築
- ALB(Application Load Balancer)
- ターゲットグループ
- ECSクラスター
- Fargateサービス → すべて自動作成
⚙️ 4. 演習:Webサービスを ECS+ALB で展開
霊夢「じゃあ、ECS+ALBで実際のWebアプリを動かしてみよう!」
魔理沙「Python FlaskをDocker化して動かす構成にしよう。」
📁 ディレクトリ構成
myapp/ ├── app.py ├── Dockerfile └── requirements.txt
📄 myapp/app.py
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello from Flask on Fargate via CDK!"
📄 myapp/requirements.txt
flask
📄 myapp/Dockerfile
FROM public.ecr.aws/lambda/python:3.11 COPY . . RUN pip install -r requirements.txt CMD ["app.app"]
📄 ecs_flask_stack.py
from aws_cdk import ( Stack, aws_ecs as ecs, aws_ecs_patterns as ecs_patterns, aws_ecr_assets as ecr_assets, aws_ec2 as ec2, ) from constructs import Construct class EcsFlaskStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) cluster = ecs.Cluster(self, "FlaskCluster", vpc=vpc) # --- DockerイメージをCDKでビルドしてECRにアップ --- image_asset = ecr_assets.DockerImageAsset( self, "FlaskImage", directory="myapp" ) ecs_patterns.ApplicationLoadBalancedFargateService( self, "FlaskService", cluster=cluster, cpu=256, memory_limit_mib=512, desired_count=2, public_load_balancer=True, task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_docker_image_asset(image_asset), container_port=8080, ) )
💻 デプロイ
cdk synth cdk deploy
🌐 出力例
Outputs: EcsFlaskStack.FlaskServiceLoadBalancerDNS = flask-alb-123456.ap-northeast-1.elb.amazonaws.com
ブラウザでアクセス:
http://flask-alb-123456.ap-northeast-1.elb.amazonaws.com → Hello from Flask on Fargate via CDK!
📘 まとめ
| 項目 | 内容 |
|---|---|
| EC2構築 | IAMロール・セキュリティグループ・ユーザーデータ設定 |
| AutoScaling | CPU利用率などでインスタンス数を自動調整 |
| ECS/Fargate | コンテナをマネージド実行環境で展開 |
| 演習 | FlaskアプリをECS+ALBで自動デプロイ |
霊夢「ついにCDKだけでコンテナWebアプリを立ち上げた!」
魔理沙「IaCとコンテナ運用を一緒にできるのがCDKの真骨頂だぜ。」
霊夢「次はデータベースを追加して、本格的なWebアプリにしたいわね!」
魔理沙「よし、第6章は“RDS/DynamoDB/ElastiCache”のデータ基盤だ!」
第6章 データ基盤 – RDS/DynamoDB/ElastiCache
🏗️ 1. RDS(MySQL/PostgreSQL)インスタンス定義とセキュリティ設定
霊夢「アプリ作ったけど、データを保存する場所が欲しいわ!」
魔理沙「任せろ。RDSをCDKで構築するぜ!」
📄 rds_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, aws_rds as rds, aws_secretsmanager as secretsmanager, ) from constructs import Construct class RdsStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- Secrets ManagerにDBパスワードを自動生成 --- db_secret = secretsmanager.Secret( self, "DBSecret", generate_secret_string=secretsmanager.SecretStringGenerator( secret_string_template='{"username": "admin"}', generate_string_key="password", exclude_punctuation=True, password_length=16 ) ) # --- セキュリティグループ(DBアクセス用) --- db_sg = ec2.SecurityGroup( self, "DbSecurityGroup", vpc=vpc, description="Allow access to DB", allow_all_outbound=True ) # --- RDSインスタンス作成 --- self.db_instance = rds.DatabaseInstance( self, "MyRdsInstance", engine=rds.DatabaseInstanceEngine.mysql( version=rds.MysqlEngineVersion.VER_8_0_35 ), credentials=rds.Credentials.from_secret(db_secret), instance_type=ec2.InstanceType("t3.micro"), vpc=vpc, vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS), multi_az=False, allocated_storage=20, max_allocated_storage=100, storage_encrypted=True, security_groups=[db_sg], deletion_protection=False, publicly_accessible=False, database_name="app_db", )
霊夢「すご!これだけでMySQLサーバーが立ち上がるの?」
魔理沙「そうだぜ。パスワードはSecrets Managerに自動保存されるし、セキュリティも安全設計だ。」
💡 もしPostgreSQLを使いたい場合
engine=rds.DatabaseInstanceEngine.postgres(
version=rds.PostgresEngineVersion.VER_15
)
🔒 アプリからのアクセス用設定(例)
db_sg.add_ingress_rule(
peer=ec2.Peer.any_ipv4(),
connection=ec2.Port.tcp(3306),
description="Allow MySQL access from app"
)
🧩 2. DynamoDB テーブル+グローバルセカンダリインデックス
霊夢「NoSQLならDynamoDBでしょ?」
魔理沙「もちろん。スキーマレスでスケーラブルなテーブルをCDKで構築できるぜ。」
📄 dynamodb_stack.py
from aws_cdk import ( Stack, aws_dynamodb as dynamodb, ) from constructs import Construct class DynamoDbStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- DynamoDBテーブル定義 --- table = dynamodb.Table( self, "UserTable", partition_key=dynamodb.Attribute( name="user_id", type=dynamodb.AttributeType.STRING ), billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST, # サーバレス課金 removal_policy=None ) # --- GSI(グローバルセカンダリインデックス) --- table.add_global_secondary_index( index_name="email-index", partition_key=dynamodb.Attribute( name="email", type=dynamodb.AttributeType.STRING ) )
霊夢「SQLいらずでテーブル作れるのね!」
魔理沙「そう。読み取り専用のGSIで検索高速化も簡単だ。」
💬 DynamoDBの特徴メモ
| 項目 | 内容 |
|---|---|
| 課金体系 | オンデマンド(リクエスト単価制)またはプロビジョニング |
| スキーマ | 無し(属性は動的) |
| インデックス | GSI(グローバル)、LSI(ローカル) |
| 利用例 | ユーザー情報、セッション管理、キャッシュ的用途 |
⚡ 3. ElastiCache(Redis)導入例とキャッシュ戦略
霊夢「DBだけだと遅くなる時あるよね?」
魔理沙「そういう時はElastiCache(Redis)でキャッシュを挟むのが定番だぜ。」
📄 redis_stack.py
from aws_cdk import ( Stack, aws_ec2 as ec2, aws_elasticache as elasticache, ) from constructs import Construct class RedisStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, **kwargs): super().__init__(scope, id, **kwargs) # --- Redis用SG --- redis_sg = ec2.SecurityGroup( self, "RedisSg", vpc=vpc, description="Allow Redis access", allow_all_outbound=True ) # --- サブネットグループ --- subnet_group = elasticache.CfnSubnetGroup( self, "RedisSubnetGroup", description="Subnet group for Redis", subnet_ids=[subnet.subnet_id for subnet in vpc.private_subnets] ) # --- Redisクラスタ作成 --- redis_cluster = elasticache.CfnCacheCluster( self, "RedisCluster", engine="redis", cache_node_type="cache.t3.micro", num_cache_nodes=1, cluster_name="MyRedisCluster", vpc_security_group_ids=[redis_sg.security_group_id], cache_subnet_group_name=subnet_group.ref )
霊夢「RedisもCDKで書けるんだ!」
魔理沙「ああ。これでRDSやDynamoDBの読み取りをキャッシュできるぜ。」
💡 キャッシュ戦略まとめ
| ケース | 戦略 |
|---|---|
| RDS+Redis | クエリ結果をキャッシュ(読み取り負荷分散) |
| DynamoDB+Redis | 頻出データを短期間保持(TTL付き) |
| API Gateway+Lambda+Redis | サーバレス構成で低レイテンシ化 |
🧠 4. 演習:API サービス+RDS or DynamoDB 関係構築
霊夢「最後にアプリからDBへ実際につないでみよう!」
魔理沙「Flask+RDS or DynamoDBでAPIサーバーを作るぜ。」
📁 構成例
api_app/ ├── app.py ├── Dockerfile ├── requirements.txt
📄 api_app/app.py(RDS利用)
from flask import Flask, jsonify import pymysql, os app = Flask(__name__) DB_HOST = os.getenv("DB_HOST") DB_USER = os.getenv("DB_USER") DB_PASS = os.getenv("DB_PASS") DB_NAME = os.getenv("DB_NAME", "app_db") @app.route("/") def hello(): return "Hello from Flask + RDS!" @app.route("/users") def users(): conn = pymysql.connect(host=DB_HOST, user=DB_USER, password=DB_PASS, database=DB_NAME) with conn.cursor() as cur: cur.execute("SELECT NOW() as current_time") result = cur.fetchone() conn.close() return jsonify(result)
📄 api_app/Dockerfile
FROM public.ecr.aws/lambda/python:3.11 COPY . . RUN pip install flask pymysql CMD ["app.app"]
📄 api_app/requirements.txt
flask pymysql
📄 ecs_api_stack.py
from aws_cdk import ( Stack, aws_ecs as ecs, aws_ecs_patterns as ecs_patterns, aws_ecr_assets as ecr_assets, aws_ec2 as ec2, ) from constructs import Construct class EcsApiStack(Stack): def __init__(self, scope: Construct, id: str, vpc: ec2.IVpc, db_host: str, **kwargs): super().__init__(scope, id, **kwargs) cluster = ecs.Cluster(self, "ApiCluster", vpc=vpc) image_asset = ecr_assets.DockerImageAsset( self, "ApiImage", directory="api_app" ) ecs_patterns.ApplicationLoadBalancedFargateService( self, "ApiService", cluster=cluster, cpu=256, memory_limit_mib=512, desired_count=2, public_load_balancer=True, task_image_options=ecs_patterns.ApplicationLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_docker_image_asset(image_asset), environment={ "DB_HOST": db_host, "DB_USER": "admin", "DB_PASS": "(Secrets Managerから取得する設定も可能)" }, container_port=8080, ) )
💻 デプロイ後にアクセス
curl http://api-alb-12345.ap-northeast-1.elb.amazonaws.com/users
→ {"current_time": "2025-10-29 11:20:33"}
📘 まとめ
| 項目 | 内容 |
|---|---|
| RDS構築 | MySQL/PostgreSQLをCDKで生成、Secrets Manager連携 |
| DynamoDB構築 | スキーマレス+GSI対応、オンデマンド課金 |
| ElastiCache構築 | Redisキャッシュクラスタでレスポンス高速化 |
| 演習 | Flask+RDS/DynamoDB接続APIをECS上に構築 |
霊夢「これでデータ基盤もバッチリね!」
魔理沙「次はこの基盤を“複数環境(dev/stg/prod)”で自動デプロイできるようにするぜ!」
第7章 サーバーレス基盤 – Lambda/API Gateway/EventBridge
🧠 1. Lambda 関数定義(Python)と IAM ポリシー設定
霊夢「サーバーを立てずに関数だけ動かすって、どういう仕組み?」
魔理沙「それが AWS Lambda だ。Pythonで関数を書くだけでコードが実行できるんだぜ。」
📁 ディレクトリ構成
lambda_app/ ├── handler.py └── requirements.txt
📄 lambda_app/handler.py
import json def handler(event, context): print("Received event:", json.dumps(event)) return { "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"message": "Hello from Lambda!"}) }
📄 lambda_stack.py
from aws_cdk import ( Stack, aws_lambda as _lambda, aws_iam as iam, ) from constructs import Construct class LambdaStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- Lambda 関数定義 --- fn = _lambda.Function( self, "HelloLambda", runtime=_lambda.Runtime.PYTHON_3_11, handler="handler.handler", code=_lambda.Code.from_asset("lambda_app"), timeout=None ) # --- IAM ポリシーを付与(CloudWatch Logs など) --- fn.add_to_role_policy(iam.PolicyStatement( actions=["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], resources=["*"] ))
霊夢「これでコードを置くだけで動くの?サーバーいらないの?」
魔理沙「ああ。AWSが自動でコンテナ化して実行してくれる。デプロイも cdk deploy 一発だ。」
💻 デプロイ確認
cdk synth cdk deploy
出力例:
Outputs: LambdaStack.HelloLambdaFunctionName = HelloLambda
💡 簡易テスト(CLI)
aws lambda invoke --function-name HelloLambda response.json cat response.json
出力例:
{"message": "Hello from Lambda!"}
🌐 2. API Gateway(REST or HTTP)を使った API構築
霊夢「Lambda単体だと呼び出しづらいね。」
魔理沙「API Gatewayを組み合わせると、HTTPで呼べるREST APIになるんだぜ。」
📄 api_gateway_stack.py
from aws_cdk import ( Stack, aws_lambda as _lambda, aws_apigateway as apigateway, ) from constructs import Construct class ApiGatewayStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- Lambda関数 --- fn = _lambda.Function( self, "ApiLambda", runtime=_lambda.Runtime.PYTHON_3_11, handler="handler.handler", code=_lambda.Code.from_asset("lambda_app"), ) # --- REST API --- api = apigateway.LambdaRestApi( self, "ApiGateway", handler=fn, rest_api_name="HelloApi", proxy=False ) # --- /hello エンドポイント追加 --- hello = api.root.add_resource("hello") hello.add_method("GET")
霊夢「URL叩くだけでLambdaが呼ばれるんだね!」
魔理沙「そう。しかもCDKが自動でIAMとログも設定してくれるぜ。」
💻 デプロイ後に表示されるURL
Outputs: ApiGatewayStack.ApiGatewayEndpoint = https://abc123.execute-api.ap-northeast-1.amazonaws.com/prod/hello
ブラウザでアクセス:
→ {"message": "Hello from Lambda!"}
💡 HTTP API版(軽量)
from aws_cdk import aws_apigatewayv2_alpha as apigw2 from aws_cdk import aws_apigatewayv2_integrations_alpha as integrations api = apigw2.HttpApi( self, "HttpApi", default_integration=integrations.HttpLambdaIntegration("LambdaIntegration", fn) )
🔔 3. イベント駆動:EventBridge/SNS/SQS の利用
霊夢「Lambdaが自動で起動する仕組みって?」
魔理沙「それがイベント駆動。EventBridgeやSNSでトリガーを設定できるぜ。」
📄 eventbridge_stack.py
from aws_cdk import ( Stack, aws_events as events, aws_events_targets as targets, aws_lambda as _lambda, ) from constructs import Construct class EventBridgeStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- Lambda関数 --- fn = _lambda.Function( self, "EventLambda", runtime=_lambda.Runtime.PYTHON_3_11, handler="handler.handler", code=_lambda.Code.from_asset("lambda_app"), ) # --- 5分ごとにLambda実行するスケジュールイベント --- rule = events.Rule( self, "ScheduleRule", schedule=events.Schedule.rate(cdk.Duration.minutes(5)) ) rule.add_target(targets.LambdaFunction(fn))
📄 SNS 連携例(メッセージ受信トリガー)
from aws_cdk import aws_sns as sns, aws_sns_subscriptions as subs topic = sns.Topic(self, "MyTopic") topic.add_subscription(subs.LambdaSubscription(fn))
📄 SQS キュー連携例(非同期処理)
from aws_cdk import aws_sqs as sqs queue = sqs.Queue(self, "MyQueue") fn.add_event_source_mapping( "QueueEventSource", event_source_arn=queue.queue_arn )
霊夢「つまり“イベントが来たら自動実行”って仕組みが簡単にできるのね!」
魔理沙「その通り。CDKならイベントドリブン構成もPython数行で定義できるんだぜ。」
⚙️ 4. 演習:フルサーバーレス構成で API+バックエンドを構築
霊夢「じゃあ今までのを全部組み合わせて、API→Lambda→DynamoDBにデータ保存する構成を作ろう!」
魔理沙「いいな。これがサーバーレスアプリの基本形だぜ。」
📁 構成
serverless_app/ ├── handler.py ├── requirements.txt
📄 serverless_app/handler.py
import json, boto3, os table_name = os.getenv("TABLE_NAME") dynamodb = boto3.resource("dynamodb") table = dynamodb.Table(table_name) def handler(event, context): if event["httpMethod"] == "POST": body = json.loads(event["body"]) item = {"id": body["id"], "message": body["message"]} table.put_item(Item=item) return {"statusCode": 201, "body": json.dumps({"status": "saved"})} elif event["httpMethod"] == "GET": result = table.scan() return {"statusCode": 200, "body": json.dumps(result["Items"])} else: return {"statusCode": 405, "body": "Method not allowed"}
📄 serverless_api_stack.py
from aws_cdk import ( Stack, aws_lambda as _lambda, aws_dynamodb as dynamodb, aws_apigateway as apigateway, ) from constructs import Construct class ServerlessApiStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- DynamoDBテーブル --- table = dynamodb.Table( self, "MessagesTable", partition_key=dynamodb.Attribute(name="id", type=dynamodb.AttributeType.STRING), billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST ) # --- Lambda関数 --- fn = _lambda.Function( self, "ServerlessHandler", runtime=_lambda.Runtime.PYTHON_3_11, handler="handler.handler", code=_lambda.Code.from_asset("serverless_app"), environment={"TABLE_NAME": table.table_name}, ) table.grant_read_write_data(fn) # --- API Gateway --- api = apigateway.LambdaRestApi( self, "ServerlessApi", handler=fn, proxy=False ) # --- ルーティング --- messages = api.root.add_resource("messages") messages.add_method("GET") # 一覧取得 messages.add_method("POST") # 登録
💻 デプロイ
cdk synth cdk deploy
🌐 実行例
POST
curl -X POST https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/messages \
-d '{"id": "1", "message": "Hello Serverless!"}'
GET
curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/messages
レスポンス:
[{"id": "1", "message": "Hello Serverless!"}]
霊夢「すごい!インフラもサーバーも書いてないのに動く!」
魔理沙「そう、これが“サーバーレス・アーキテクチャ”の真骨頂だぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| Lambda | Python関数を自動実行、サーバーレス処理単位 |
| API Gateway | HTTP経由でLambdaを公開できるエントリポイント |
| EventBridge/SNS/SQS | イベント駆動・非同期処理を実現 |
| 演習構成 | Lambda + API Gateway + DynamoDB の完全サーバーレスAPI |
霊夢「これでもう、インスタンスもネットワーク設定もいらないね!」
魔理沙「次章ではこのLambdaアプリを本番運用するための“環境分離(dev/stg/prod)”とCI/CD構成を学ぶぜ!」
第8章 ストレージ・配信基盤 – S3/CloudFront/CDN構成
🪣 1. S3 バケット定義、バケットポリシー、ライフサイクル管理
霊夢「Webサイトの静的ファイルを置く場所といえば?」
魔理沙「もちろんS3バケットだな。CDKなら数行で定義できるぜ!」
📄 s3_stack.py
from aws_cdk import ( Stack, aws_s3 as s3, Duration, ) from constructs import Construct class S3Stack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- S3バケット作成 --- self.bucket = s3.Bucket( self, "StaticSiteBucket", bucket_name="my-static-site-bucket-2025", versioned=True, # バージョニング有効 block_public_access=s3.BlockPublicAccess.BLOCK_ALL, # 後でCloudFront経由のみアクセス encryption=s3.BucketEncryption.S3_MANAGED, removal_policy=None, lifecycle_rules=[ s3.LifecycleRule( id="ExpireOldVersions", noncurrent_version_expiration=Duration.days(30) ) ] )
霊夢「block_public_access で直アクセスを防いでるのね!」
魔理沙「そう。CloudFront経由でしかアクセスできないようにするのがベストプラクティスだぜ。」
💡 ライフサイクル管理ルール
s3.LifecycleRule(
id="ExpireTempFiles",
prefix="tmp/",
expiration=Duration.days(7)
)
霊夢「tmp/ フォルダ配下のファイルを7日で削除できるのね。」
魔理沙「運用コスト削減にはこういうルールも大事だぜ。」
🧱 静的ホスティングを有効化(単独利用時)
self.bucket = s3.Bucket(
self, "StaticSite",
website_index_document="index.html",
website_error_document="error.html",
public_read_access=True
)
🌐 2. CloudFront/OAI(オリジンアクセスアイデンティティ)で静的サイト配信
霊夢「S3はできたけど、直接アクセスさせるのは不安よね?」
魔理沙「そこでCloudFront登場! OAIを使ってS3への安全な経路を作るんだ。」
📄 cdn_stack.py
from aws_cdk import ( Stack, aws_s3 as s3, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, ) from constructs import Construct class CdnStack(Stack): def __init__(self, scope: Construct, id: str, bucket: s3.IBucket, **kwargs): super().__init__(scope, id, **kwargs) # --- OAI(オリジンアクセスアイデンティティ) --- oai = cloudfront.OriginAccessIdentity(self, "OAI") # --- バケットポリシーにOAIを許可 --- bucket.add_to_resource_policy( statement=cloudfront.CloudFrontWebDistributionOriginAccessIdentity( self, "AllowOaiAccess" ) ) # --- CloudFront ディストリビューション --- self.distribution = cloudfront.Distribution( self, "SiteDistribution", default_behavior=cloudfront.BehaviorOptions( origin=origins.S3Origin(bucket, origin_access_identity=oai), viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS ), default_root_object="index.html", comment="Static website distribution with OAI" )
霊夢「OAIって、CloudFrontからS3への“認証付きパイプ”みたいなもの?」
魔理沙「その通り! CloudFront以外からのアクセスをブロックできるぜ。」
💡 キャッシュ設定の例
cloudfront.BehaviorOptions(
origin=origins.S3Origin(bucket, origin_access_identity=oai),
cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED,
viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS
)
霊夢「CACHING_OPTIMIZED で静的サイトをCDNキャッシュするのね!」
魔理沙「うむ。HTML・CSS・JSをCloudFrontが世界中に配る感じだな。」
💡 エラーページ指定
error_response = cloudfront.ErrorResponse(
http_status=404,
response_http_status=200,
response_page_path="/index.html"
)
霊夢「SPA構成のルーティングにも使えそう!」
魔理沙「React/VueのフロントをS3で配信するときに便利だぜ。」
🚀 3. 演習:静的サイトホスティング+CloudFront を CDK で構築
霊夢「じゃあ、S3+CloudFrontでWebサイトを配信してみよう!」
魔理沙「おう! CDKで静的サイトをアップロードしてみようぜ。」
📁 構成例
static_site/
├── index.html
├── about.html
└── images/
└── logo.png
📄 static_site_stack.py
from aws_cdk import ( Stack, aws_s3 as s3, aws_s3_deployment as s3_deploy, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, ) from constructs import Construct class StaticSiteStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- S3バケット(公開はCloudFront経由のみ) --- site_bucket = s3.Bucket( self, "StaticSiteBucket", block_public_access=s3.BlockPublicAccess.BLOCK_ALL, encryption=s3.BucketEncryption.S3_MANAGED ) # --- CloudFront OAI --- oai = cloudfront.OriginAccessIdentity(self, "OAI") site_bucket.grant_read(oai) # --- CloudFront Distribution --- distribution = cloudfront.Distribution( self, "StaticSiteDistribution", default_root_object="index.html", default_behavior=cloudfront.BehaviorOptions( origin=origins.S3Origin(site_bucket, origin_access_identity=oai), viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, ), ) # --- S3へファイルを自動デプロイ --- s3_deploy.BucketDeployment( self, "DeployStaticSite", sources=[s3_deploy.Source.asset("./static_site")], destination_bucket=site_bucket, distribution=distribution, distribution_paths=["/*"] )
💻 デプロイ
cdk synth cdk deploy
出力例:
Outputs: StaticSiteStack.SiteDistributionDomainName = d123abc456.cloudfront.net
🌍 ブラウザでアクセス
https://d123abc456.cloudfront.net
表示:
Hello from my Static Website via CloudFront!
💡 追加:WAF・キャッシュ制御などの発展設定
cloudfront.Distribution(
self, "AdvancedDistribution",
default_behavior=cloudfront.BehaviorOptions(
origin=origins.S3Origin(site_bucket, origin_access_identity=oai),
viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED,
),
price_class=cloudfront.PriceClass.PRICE_CLASS_100, # アジア中心
enable_logging=True,
)
霊夢「キャッシュポリシーや価格クラスまでコードで設定できるのね!」
魔理沙「そうだ。CDKなら“CDNの構成管理”もIaCの範囲内だぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| S3 | 静的ファイル格納・バージョニング・ライフサイクル管理 |
| CloudFront | グローバルCDN配信、HTTPSリダイレクト |
| OAI | S3への直接アクセスを防ぎ、CloudFront経由のみに制限 |
| 演習結果 | S3 + CloudFront構成でSPA/静的Webを高速配信可能 |
霊夢「S3とCloudFrontで、まるでGitHub Pagesみたいな環境ができちゃうね!」
魔理沙「それどころか、CDKなら自動更新・バージョン管理・セキュリティ設定まで含めてコード化できるんだぜ!」
第9章 マルチステージ/環境別構成管理
🌱 1. 開発/ステージング/本番環境の分離設計
霊夢「CDKって、一つのコードで環境ごとに構成を変えられるの?」
魔理沙「もちろんだ。dev・stg・prod の3環境を1つのコードで管理できるぜ。」
📁 ディレクトリ構成例
cdk_project/ ├── app.py ├── stacks/ │ ├── network_stack.py │ ├── compute_stack.py │ └── storage_stack.py └── cdk.json
💡 CDKの環境管理の基本
| 要素 | 役割 |
|---|---|
| Context | CDK実行時に渡すパラメータ (cdk.json / --context) |
| Environment | AWSアカウントとリージョン情報 |
| StackProps | スタック間で共通の設定を受け渡す |
📄 cdk.json に環境設定を記述
{ "app": "python3 app.py", "context": { "environments": { "dev": { "account": "111111111111", "region": "ap-northeast-1" }, "stg": { "account": "222222222222", "region": "ap-northeast-1" }, "prod": { "account": "333333333333", "region": "us-east-1" } } } }
霊夢「Contextに全部の環境情報を書いておくのね!」
魔理沙「そう。cdk deploy --context stage=dev で切り替え可能だぜ。」
⚙️ 2. パラメータ化(Context, Environment, StackProps)
霊夢「環境ごとにバケット名やインスタンスタイプを変えるには?」
魔理沙「StackPropsを使ってパラメータを渡すんだ。」
📄 stacks/app_stack.py
from aws_cdk import ( Stack, aws_s3 as s3, ) from constructs import Construct class AppStack(Stack): def __init__(self, scope: Construct, id: str, env_name: str, **kwargs): super().__init__(scope, id, **kwargs) # --- S3 バケット名を環境ごとに変える --- s3.Bucket( self, "AppBucket", bucket_name=f"my-app-bucket-{env_name}", versioned=True )
📄 app.py
#!/usr/bin/env python3 from aws_cdk import App, Environment from stacks.app_stack import AppStack import json, os app = App() # --- Contextから環境を取得 --- stage = app.node.try_get_context("stage") or "dev" # --- cdk.jsonの環境定義を読み込み --- env_config = app.node.try_get_context("environments")[stage] env = Environment( account=env_config["account"], region=env_config["region"] ) AppStack( app, f"MyApp-{stage}", env_name=stage, env=env ) app.synth()
💻 デプロイコマンド
cdk deploy --context stage=dev cdk deploy --context stage=stg cdk deploy --context stage=prod
霊夢「同じコードで環境を切り替えられるなんて便利ね!」
魔理沙「これがIaCの真価だ。マルチアカウントにも対応できるぜ。」
💡 StackPropsを使った例(明示的に渡す)
from dataclasses import dataclass @dataclass class AppStackProps: env_name: str bucket_suffix: str
props = AppStackProps(env_name=stage, bucket_suffix="001") AppStack(app, f"MyApp-{stage}", **vars(props), env=env)
🌍 3. ブートストラップとクロスアカウント・クロスリージョン構成
霊夢「マルチアカウントにデプロイするときの準備ってあるの?」
魔理沙「あるぜ。cdk bootstrap で“デプロイ用リソース”を先に作っておくんだ。」
💡 ブートストラップとは?
CDKが内部で使う以下のリソースを作成する:
- CDKデプロイ用S3バケット
- CloudFormation実行ロール
- File Asset用ECRリポジトリ
📘 各アカウント・リージョンで実行
cdk bootstrap aws://111111111111/ap-northeast-1 # dev cdk bootstrap aws://222222222222/ap-northeast-1 # stg cdk bootstrap aws://333333333333/us-east-1 # prod
霊夢「ブートストラップしないとデプロイできないの?」
魔理沙「そうだ。CDKが使う“中間リソース”を準備しておく必要があるんだ。」
💡 クロスアカウント構成例(Pipelineなど)
from aws_cdk import pipelines as pipelines pipeline = pipelines.CodePipeline( self, "MyPipeline", synth=pipelines.ShellStep("Synth", input=pipelines.CodePipelineSource.git_hub("user/repo", "main"), commands=["npm install -g aws-cdk", "cdk synth"] ), cross_account_keys=True )
霊夢「cross_account_keys=True でクロスアカウント対応なのね!」
魔理沙「そう、複数AWSアカウントにデプロイする時に必要な設定だぜ。」
🧪 4. 演習:dev/stg/prod 環境を CDK でテンプレート化
霊夢「じゃあ、環境ごとにリソースを分けて自動で作る構成をやってみよう!」
魔理沙「3つの環境をループで定義して、それぞれ別のAWS環境にデプロイするぜ。」
📄 multi_env_app.py
#!/usr/bin/env python3 from aws_cdk import App, Environment from stacks.app_stack import AppStack app = App() # --- 環境設定をループで定義 --- environments = { "dev": {"account": "111111111111", "region": "ap-northeast-1"}, "stg": {"account": "222222222222", "region": "ap-northeast-1"}, "prod": {"account": "333333333333", "region": "us-east-1"}, } for stage, cfg in environments.items(): AppStack( app, f"MyApp-{stage}", env_name=stage, env=Environment(account=cfg["account"], region=cfg["region"]) ) app.synth()
💻 一括デプロイ(個別またはまとめて)
cdk deploy MyApp-dev cdk deploy MyApp-stg cdk deploy MyApp-prod
📦 出力例
✅ MyApp-dev: Deployed ✅ MyApp-stg: Deployed ✅ MyApp-prod: Deployed
💬 各環境で異なるS3バケット作成
| 環境 | バケット名 |
|---|---|
| dev | my-app-bucket-dev |
| stg | my-app-bucket-stg |
| prod | my-app-bucket-prod |
霊夢「おぉ、3環境が自動で分かれた!」
魔理沙「一度作れば、チーム全員が同じIaCテンプレートで環境を再現できるぜ。」
💡 さらに発展:環境ごとの設定ファイルを外部管理
# config/dev.yaml bucket_suffix: dev instance_type: t3.micro
import yaml config = yaml.safe_load(open(f"config/{stage}.yaml")) AppStack(app, f"MyApp-{stage}", **config)
霊夢「YAMLで管理すればOpsチームも編集しやすいね。」
魔理沙「IaCとConfigの分離、これぞプロのCDK設計だぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| 環境分離 | dev / stg / prod を1コードで管理 |
| Context & StackProps | パラメータを動的に切り替える仕組み |
| Bootstrap | 各アカウントでCDKが動くための下準備 |
| クロスアカウント | 複数AWSアカウント間でデプロイ |
| 演習 | 3環境をループで生成、テンプレート化可能 |
霊夢「CDKって、まるでインフラをプログラムみたいに構築できるのね!」
魔理沙「その通り。次章では“CI/CDパイプライン構築”をやって、
コード変更→自動デプロイまで一気通貫で仕上げるぜ!」
第10章 CI/CD パイプラインの構築
🪄 1. AWS CodePipeline/AWS CodeBuild/GitHub Actions 連携
霊夢「いよいよCI/CDね。コードをpushしたら自動でデプロイしたい!」
魔理沙「そのための最強コンビが CodePipeline + CodeBuild だぜ。」
💡 CDKでCI/CDを構築する流れ
- CodeCommit/GitHub などのソースをトリガーに
- CodeBuild でテスト・ビルドを実行
- CodePipeline がステージ(dev→prod)に自動デプロイ
📄 pipeline_stack.py
from aws_cdk import ( Stack, pipelines as pipelines, ) from constructs import Construct class PipelineStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- GitHub ソース設定 --- source = pipelines.CodePipelineSource.git_hub( "your-github-user/cdk-sample-app", "main", authentication=cdk.SecretValue.secrets_manager("github-token") ) # --- Synth ステップ(CDKビルド) --- synth = pipelines.ShellStep( "Synth", input=source, commands=[ "npm install -g aws-cdk", "python -m venv .venv", "source .venv/bin/activate", "pip install -r requirements.txt", "cdk synth" ] ) # --- CodePipeline定義 --- self.pipeline = pipelines.CodePipeline( self, "CdkPipeline", pipeline_name="MyAppPipeline", synth=synth )
霊夢「GitHubのmainブランチをトリガーに自動でCDKがビルドされるのね!」
魔理沙「そう。Secrets ManagerにGitHubトークンを入れておけば、認証も安全だぜ。」
💡 GitHubトークン登録
AWS CLIから:
aws secretsmanager create-secret \ --name github-token \ --secret-string "ghp_xxxxxxxxxxxxxxxxxxxxx"
🧪 2. CDK アプリのテスト自動化(ユニットテスト/アサーション)
霊夢「CIにテストも入れたいわ!」
魔理沙「CDKにはassertionsを使ったテストも書けるんだ。」
📄 tests/test_cdk_stack.py
import aws_cdk as cdk import aws_cdk.assertions as assertions from stacks.app_stack import AppStack def test_s3_bucket_created(): app = cdk.App() stack = AppStack(app, "TestStack", env_name="dev") template = assertions.Template.from_stack(stack) template.has_resource_properties("AWS::S3::Bucket", { "VersioningConfiguration": {"Status": "Enabled"} })
霊夢「これで“S3バケットのバージョニング有効化”を自動チェックできるのね!」
魔理沙「CIでpytestを走らせるだけでIaCの構成も検証できるんだぜ。」
📄 buildspec.yml(CodeBuild用)
version: 0.2 phases: install: commands: - python -m venv .venv - source .venv/bin/activate - pip install -r requirements.txt - pip install pytest build: commands: - pytest -v - cdk synth
霊夢「CodeBuildでpytestまで回すのか!」
魔理沙「IaCの品質保証もCIに組み込むのが今風だぜ。」
🔁 3. デプロイ戦略(ブルーグリーン/ローリング)/ロールバック構成
霊夢「本番デプロイって、一気に入れ替えるのは怖いよね…?」
魔理沙「そこで“ブルーグリーン”や“ローリングデプロイ”の出番だ!」
💡 デプロイ戦略の違い
| 戦略 | 内容 | 利点 |
|---|---|---|
| ブルーグリーン | 新旧環境を並行稼働し、切り替え | 即時ロールバック可能 |
| ローリング | 段階的に入れ替え | リソース節約・安定運用 |
| カナリア | 一部トラフィックだけ新環境へ | 検証が容易 |
📄 Lambdaのブルーグリーンデプロイ例
from aws_cdk import ( Stack, aws_codedeploy as codedeploy, aws_lambda as _lambda, ) from constructs import Construct class BlueGreenStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- Lambda定義 --- fn = _lambda.Function( self, "BlueGreenLambda", runtime=_lambda.Runtime.PYTHON_3_11, handler="handler.handler", code=_lambda.Code.from_asset("lambda_app") ) # --- エイリアス作成 --- alias = _lambda.Alias( self, "LambdaAlias", alias_name="live", version=fn.current_version ) # --- CodeDeployでブルーグリーン管理 --- codedeploy.LambdaDeploymentGroup( self, "DeploymentGroup", alias=alias, deployment_config=codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE, alarms=[], auto_rollback=codedeploy.AutoRollbackConfig( deployment_in_alarm=True, failed_deployment=True ) )
霊夢「auto_rollback=True で失敗時に自動で戻せるのね!」
魔理沙「そうだ。エラーが出たら即座に旧バージョンへ切り替わるぜ。」
📄 ECSローリングデプロイ例(Fargate)
from aws_cdk import aws_ecs_patterns as ecs_patterns ecs_patterns.ApplicationLoadBalancedFargateService( self, "AppService", desired_count=3, circuit_breaker=ecs.DeploymentCircuitBreaker(rollback=True), )
霊夢「circuit_breaker(rollback=True)、まさに自動安全装置!」
魔理沙「CI/CDの“C”がContinuousだけじゃなくて“Careful”のCにもなるぜ。」
🚀 4. 演習:GitHub プッシュ時に dev→prod へのパイプライン構築
霊夢「実際にGitHub push → dev → prod 自動デプロイを組みたい!」
魔理沙「よっしゃ、実戦構成を見せるぜ。」
📄 multi_stage_pipeline_stack.py
from aws_cdk import ( Stack, pipelines as pipelines, aws_codebuild as codebuild, ) from constructs import Construct class MultiStagePipelineStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- ソース (GitHub) --- source = pipelines.CodePipelineSource.git_hub( "your-github-user/cdk-sample-app", "main", authentication=cdk.SecretValue.secrets_manager("github-token") ) # --- Synth ステップ --- synth_step = pipelines.ShellStep( "Synth", input=source, commands=[ "python -m venv .venv", "source .venv/bin/activate", "pip install -r requirements.txt", "pytest -v", "cdk synth" ] ) # --- パイプライン作成 --- pipeline = pipelines.CodePipeline( self, "Pipeline", synth=synth_step, cross_account_keys=True ) # --- devステージ --- dev_stage = MyAppStage(self, "DevStage", env_name="dev") pipeline.add_stage(dev_stage) # --- 手動承認を挟んでprodへ --- pipeline.add_stage( MyAppStage(self, "ProdStage", env_name="prod"), pre=[pipelines.ManualApprovalStep("PromoteToProd")] )
📄 stages/my_app_stage.py
from aws_cdk import Stage from stacks.app_stack import AppStack from constructs import Construct class MyAppStage(Stage): def __init__(self, scope: Construct, id: str, env_name: str, **kwargs): super().__init__(scope, id, **kwargs) AppStack(self, f"MyApp-{env_name}", env_name=env_name)
💻 デプロイ
cdk deploy MultiStagePipelineStack
💡 動作イメージ
GitHub push (main) ↓ CodePipeline 起動 ↓ CodeBuild で pytest & synth ↓ DevStage 自動デプロイ ↓ 手動承認 ↓ ProdStage デプロイ
霊夢「GitHubにpushしただけで自動でdev→prodデプロイされるんだ!」
魔理沙「しかもテスト→承認→ロールバックまで自動。
もう“デプロイ職人”いらずだぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| CodePipeline/CodeBuild | CDKで構築可能。CI/CDを自動化 |
| GitHub Actions連携 | Secrets Manager経由で安全に認証 |
| テスト統合 | pytest + assertionsでIaCテスト |
| デプロイ戦略 | ブルーグリーン・ローリング・ロールバック |
| 演習構成 | GitHub push → dev → 承認 → prod 自動展開 |
霊夢「CI/CDがここまでコードで書けるなんてすごいね!」
魔理沙「CDKはただのIaCじゃない。“デプロイオーケストレーション”までできるんだぜ!」
第11章 テストとトラブルシューティング
🧩 1. CDK アプリのテスト手法(Assertions, Snapshot)
霊夢「CDKのテストって、コードの挙動じゃなくて“インフラ構成”を検証するんでしょ?」
魔理沙「その通りだ。アプリコードじゃなく“CloudFormationテンプレートが正しいか”をテストするのがCDK流だぜ。」
📁 テスト用ディレクトリ構成
tests/
├── test_app_stack.py
└── snapshots/
└── test_app_stack.snapshot.json
📄 tests/test_app_stack.py
import aws_cdk as cdk import aws_cdk.assertions as assertions from stacks.app_stack import AppStack def test_s3_bucket_properties(): app = cdk.App() stack = AppStack(app, "TestStack", env_name="dev") template = assertions.Template.from_stack(stack) # --- S3バケットが作成されていることを検証 --- template.resource_count_is("AWS::S3::Bucket", 1) # --- バージョニングが有効であることを確認 --- template.has_resource_properties("AWS::S3::Bucket", { "VersioningConfiguration": {"Status": "Enabled"} })
霊夢「これでCDKの出力テンプレートを直接検証できるのね!」
魔理沙「そう。コード変更による構成差分を安全にチェックできる。」
💡 Snapshotテスト(テンプレート丸ごと比較)
def test_template_snapshot(snapshot): app = cdk.App() stack = AppStack(app, "SnapshotStack", env_name="dev") template = assertions.Template.from_stack(stack) snapshot.assert_match(template.to_json(), "snapshot.json")
霊夢「テンプレート全体をJSONとしてスナップショット保存するのね。」
魔理沙「構成変更時に差分が出るから、レビューにも便利だぜ。」
📄 pytest.ini
[pytest] addopts = --tb=short -v python_files = test_*.py
💻 実行例
pytest -v
出力:
tests/test_app_stack.py::test_s3_bucket_properties PASSED tests/test_app_stack.py::test_template_snapshot PASSED
🪵 2. デプロイ時のログ/CloudFormation スタック失敗対応
霊夢「テストは通っても、cdk deployで失敗することあるよね…」
魔理沙「あるある。CloudFormationログの見方を知っておくと安心だぜ。」
💻 デプロイ時のエラー確認
cdk deploy --verbose
出力例:
CREATE_FAILED AWS::IAM::Role MyRole Resource creation cancelled
💡 CloudFormationコンソールで詳細確認
AWSマネジメントコンソール → 「CloudFormation」 → スタックを選択 → 「イベント」 タブ
CREATE_FAILED: AccessDenied - IAMポリシーが不足
霊夢「CDKのデプロイエラーはCloudFormationの裏で起きてるのね!」
魔理沙「そう。cdkはあくまでテンプレートを流してるだけだから、根本はCloudFormationだ。」
📦 CloudFormationのスタック削除忘れ対応
aws cloudformation delete-stack --stack-name MyApp-dev
霊夢「ROLLBACK_COMPLETE で止まったスタック、削除できない時あるんだよね。」
魔理沙「その場合は依存リソース(S3、VPC等)を手動で削除してから再試行だぜ。」
💡 CDKデプロイの出力ログを保存する例
cdk deploy --require-approval never --outputs-file ./cdk-outputs.json
出力例:
{ "MyApp-dev": { "BucketName": "my-app-bucket-dev" } }
⚠️ 3. よくある落とし穴とアンチパターン
霊夢「実際に使ってて失敗するポイントってある?」
魔理沙「めちゃくちゃある!IaC初心者がよくハマるやつを紹介するぜ。」
🧱 アンチパターン①:リソース肥大化
for i in range(1000): s3.Bucket(self, f"Bucket{i}")
霊夢「これ…スタック1つで1000個のバケット?」
魔理沙「CloudFormationにはリソース数制限がある(約500個)。
分割(Nested Stack)するか、S3だけ別スタックに切り出そう。」
🧹 アンチパターン②:削除ポリシーの設定忘れ
# ❌ remove_policy 未設定 s3.Bucket(self, "Bucket")
魔理沙「デフォルトだと削除時にバケットが残るぞ。」
✅ 正解:
s3.Bucket(
self, "Bucket",
removal_policy=cdk.RemovalPolicy.DESTROY
)
🔐 アンチパターン③:SecretやKeyを直書き
# ❌ 秘密情報をコードにベタ書き db_password = "supersecret123"
✅ 修正版:
from aws_cdk import aws_secretsmanager as secretsmanager secret = secretsmanager.Secret.from_secret_name_v2( self, "DbSecret", "my-db-secret" )
💣 アンチパターン④:CDK diff未確認でデプロイ
魔理沙「cdk diffで差分を見ずにdeployすると痛い目見るぜ。」
✅ 正解:
cdk diff
出力例:
IAM Policy Changes [+] Policy: Add s3:DeleteObject permission
⚙️ ベストプラクティスまとめ
| 分野 | ベストプラクティス |
|---|---|
| スタック設計 | リソースは100〜200個以内/機能単位で分割 |
| 削除ポリシー | 明示的にDESTROY or RETAINを指定 |
| 機密情報 | Secrets Manager or Parameter Storeで管理 |
| デプロイ前 | cdk diff と pytest を必ず実行 |
| ログ分析 | CloudFormation「イベント」 +cdk --verbose |
🧰 4. 演習:テスト付き構成をリファクタリング
霊夢「じゃあ実際に“テストしやすいCDK構成”にリファクタリングしてみよう!」
魔理沙「おう。モノリシックなスタックを分割して、
テストを分離しやすい形にするんだ。」
📁 構成
stacks/ ├── network_stack.py ├── app_stack.py └── storage_stack.py tests/ └── test_storage_stack.py
📄 stacks/storage_stack.py
from aws_cdk import Stack, aws_s3 as s3 from constructs import Construct class StorageStack(Stack): def __init__(self, scope: Construct, id: str, env_name: str, **kwargs): super().__init__(scope, id, **kwargs) self.bucket = s3.Bucket( self, "AppBucket", bucket_name=f"my-storage-{env_name}", versioned=True, removal_policy=None )
📄 tests/test_storage_stack.py
import aws_cdk as cdk import aws_cdk.assertions as assertions from stacks.storage_stack import StorageStack def test_storage_bucket_created(): app = cdk.App() stack = StorageStack(app, "StorageTest", env_name="dev") template = assertions.Template.from_stack(stack) template.resource_count_is("AWS::S3::Bucket", 1) template.has_resource_properties("AWS::S3::Bucket", { "VersioningConfiguration": {"Status": "Enabled"} })
💻 実行
pytest -v
出力例:
tests/test_storage_stack.py::test_storage_bucket_created PASSED
📄 改善例:テストと依存関係を明示する構成
# app.py from aws_cdk import App from stacks.network_stack import NetworkStack from stacks.storage_stack import StorageStack app = App() network = NetworkStack(app, "Network") StorageStack(app, "Storage", env_name="dev", tags={"DependsOn": network.stack_name}) app.synth()
霊夢「これならネットワークとストレージの関係もテスト単位で分けられるね!」
魔理沙「その通り。大規模プロジェクトでは“スタックごとの責務分離”が超重要だぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| テスト | Assertions/SnapshotでIaC構成を検証 |
| ログ分析 | CloudFormationイベント/--verbose/Outputs |
| 落とし穴 | リソース肥大化・削除忘れ・秘密情報直書き |
| リファクタリング演習 | スタック分割+pytest検証で品質を担保 |
霊夢「CDKは“書いたあとが本番”って感じね!」
魔理沙「そう。テストと監視を組み合わせれば、IaCもプロダクトコードと同じ品質で運用できるぜ。」
第12章 再利用可能な Construct ライブラリ設計
🧱 1. L1/L2/L3 Construct の違いと使い分け
霊夢「CDKで“Construct”ってよく出てくるけど、どんな種類があるの?」
魔理沙「CDKのリソースは抽象度ごとに3つの階層に分かれてるんだぜ。」
📘 CDK Construct の階層構造
| レベル | 名前 | 説明 | 例 |
|---|---|---|---|
| L1 | CloudFormation Resource | AWSのリソースをそのまま表す低レベルAPI | CfnBucket, CfnInstance |
| L2 | High-level Construct | L1を包んで使いやすくした抽象化 | s3.Bucket, ec2.Instance |
| L3 | Pattern Construct | 複数のL2を組み合わせた便利パターン | ecs_patterns.ApplicationLoadBalancedFargateService |
霊夢「なるほど、L1がCloudFormationそのもの、L2が“素のCDK”、L3が“便利セット”ね。」
魔理沙「そう。実務ではL2/L3を使うのが基本だが、L1も拡張時に重要だぜ。」
💡 例:L1 と L2 の違い
# --- L1 (低レベル) --- from aws_cdk import aws_s3 as s3 bucket_l1 = s3.CfnBucket(self, "MyBucketL1", bucket_name="raw-l1-bucket") # --- L2 (高レベル) --- bucket_l2 = s3.Bucket(self, "MyBucketL2", versioned=True)
💡 例:L3 のパターン(ECS + ALB)
from aws_cdk import aws_ecs_patterns as ecs_patterns service = ecs_patterns.ApplicationLoadBalancedFargateService( self, "WebApp", desired_count=2, public_load_balancer=True, )
霊夢「この数行でECS+ALBが全部できるの、やっぱ便利すぎる…!」
魔理沙「そう。L3は“構築テンプレート”として再利用するのに最適なんだぜ。」
🧠 2. 自作 Construct/モジュール化のための設計ガイド
霊夢「自分の社内用やチーム用の構成をモジュール化できたら便利そう!」
魔理沙「それがこの章の本題、“自作Construct”だぜ。」
📁 プロジェクト構成例
cdk_constructs/ ├── my_constructs/ │ ├── __init__.py │ └── web_bucket_construct.py ├── app.py └── requirements.txt
📄 my_constructs/web_bucket_construct.py
from aws_cdk import ( aws_s3 as s3, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, ) from constructs import Construct class WebBucketConstruct(Construct): """静的サイト配信用のS3+CloudFrontセットを1コンポーネント化""" def __init__(self, scope: Construct, id: str, *, bucket_name: str): super().__init__(scope, id) self.bucket = s3.Bucket( self, "SiteBucket", bucket_name=bucket_name, block_public_access=s3.BlockPublicAccess.BLOCK_ALL ) oai = cloudfront.OriginAccessIdentity(self, "OAI") self.bucket.grant_read(oai) self.distribution = cloudfront.Distribution( self, "Distribution", default_behavior=cloudfront.BehaviorOptions( origin=origins.S3Origin(self.bucket, origin_access_identity=oai), viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS ), default_root_object="index.html" )
霊夢「おお!これでS3とCloudFrontをまとめて“1行で使える部品”になるのね!」
魔理沙「そう。再利用性が爆上がりするぜ。」
📄 app.py(再利用例)
from aws_cdk import App, Stack from my_constructs.web_bucket_construct import WebBucketConstruct from constructs import Construct class MainStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) WebBucketConstruct(self, "WebSite", bucket_name="demo-site-2025") app = App() MainStack(app, "ConstructDemo") app.synth()
💡 パラメータ渡しのベストプラクティス
- 明示的に
__init__の引数に*を入れてキーワード専用引数にする self.<property>で外部から参照可能にする- type hintを明確に記述(mypy/Pyright対応)
🧪 3. バージョン管理/テスト付きライブラリ化/公開までの流れ
霊夢「作ったConstructをpipで使えるようにしたい!」
魔理沙「よし、CDK Construct Library化の手順を見ていこう。」
📁 ライブラリ構成例
my-cdk-lib/ ├── my_cdk_lib/ │ ├── __init__.py │ └── web_bucket_construct.py ├── tests/ │ └── test_web_bucket_construct.py ├── pyproject.toml └── README.md
📄 pyproject.toml
[project] name = "my-cdk-lib" version = "0.1.0" description = "My custom AWS CDK constructs" authors = [{ name="Hiromichi Nomata", email="you@example.com" }] dependencies = ["aws-cdk-lib>=2.150.0", "constructs>=10.0.0"] readme = "README.md" [build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta"
📄 tests/test_web_bucket_construct.py
import aws_cdk as cdk import aws_cdk.assertions as assertions from my_cdk_lib.web_bucket_construct import WebBucketConstruct from constructs import Construct class DummyStack(cdk.Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) WebBucketConstruct(self, "Demo", bucket_name="demo-bucket") def test_bucket_created(): app = cdk.App() stack = DummyStack(app, "Dummy") template = assertions.Template.from_stack(stack) template.resource_count_is("AWS::S3::Bucket", 1)
💻 ビルド&テスト
pytest -v python -m build
出力例:
tests/test_web_bucket_construct.py::test_bucket_created PASSED Successfully built my-cdk-lib-0.1.0.tar.gz
📦 PyPI公開(または社内PyPI)
twine upload dist/*
霊夢「これでpip installできるの!?」
魔理沙「そうだ。チーム内で共有したい場合は社内PyPIやCodeArtifactに置くのがいいぜ。」
📄 社内配布例(AWS CodeArtifact)
aws codeartifact login --tool pip --repository my-repo --domain my-domain pip install my-cdk-lib --extra-index-url https://my-domain-123.d.codeartifact.ap-northeast-1.amazonaws.com/pypi/my-repo/simple/
⚙️ 4. 演習:自分用の CDK Construct パッケージを作る
霊夢「じゃあ実際に“自分のCDKライブラリ”を作ってみよう!」
魔理沙「おう。S3+CloudFrontを自作Constructにして公開してみるぜ。」
📁 演習構成
custom_cdk_lib/ ├── custom_cdk_lib/ │ └── static_site_construct.py ├── tests/ │ └── test_static_site_construct.py └── pyproject.toml
📄 custom_cdk_lib/static_site_construct.py
from aws_cdk import ( aws_s3 as s3, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, ) from constructs import Construct class StaticSiteConstruct(Construct): """S3 + CloudFrontで静的サイトをホスティングする自作Construct""" def __init__(self, scope: Construct, id: str, *, site_name: str): super().__init__(scope, id) self.bucket = s3.Bucket( self, "SiteBucket", bucket_name=f"{site_name}-bucket", block_public_access=s3.BlockPublicAccess.BLOCK_ALL ) oai = cloudfront.OriginAccessIdentity(self, "OAI") self.bucket.grant_read(oai) self.distribution = cloudfront.Distribution( self, "Distribution", default_behavior=cloudfront.BehaviorOptions( origin=origins.S3Origin(self.bucket, origin_access_identity=oai), viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS ) )
📄 tests/test_static_site_construct.py
import aws_cdk as cdk import aws_cdk.assertions as assertions from custom_cdk_lib.static_site_construct import StaticSiteConstruct from constructs import Construct class DummyStack(cdk.Stack): def __init__(self, scope: Construct, id: str): super().__init__(scope, id) StaticSiteConstruct(self, "Demo", site_name="testsite") def test_bucket_and_distribution(): app = cdk.App() stack = DummyStack(app, "TestStack") template = assertions.Template.from_stack(stack) template.resource_count_is("AWS::S3::Bucket", 1) template.resource_count_is("AWS::CloudFront::Distribution", 1)
💻 ビルド・テスト・公開(ローカル用)
pytest -v python -m build pip install dist/custom_cdk_lib-0.1.0-py3-none-any.whl
📄 利用例(他プロジェクトで)
from aws_cdk import App, Stack from custom_cdk_lib.static_site_construct import StaticSiteConstruct from constructs import Construct class DemoStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) StaticSiteConstruct(self, "MySite", site_name="portfolio") app = App() DemoStack(app, "MyDemo") app.synth()
📘 まとめ
| 項目 | 内容 |
|---|---|
| L1/L2/L3の違い | CloudFormation直層/抽象化API/便利パターン |
| 自作Construct設計 | S3+CloudFrontなどを1クラスにまとめる |
| ライブラリ化 | pyproject.toml + pytest + build + twine |
| 再利用 | pip install / CodeArtifactで社内配布 |
| 演習 | 自作CDKパッケージでS3+CloudFront構成を自動生成 |
霊夢「自作Constructを作ると、会社でもOSSでも使い回せるんだね!」
魔理沙「そうだ。CDKの真骨頂は“インフラ設計そのものをコード資産化できる”ことなんだぜ!」
第13章 大規模組織向けアーキテクチャ設計
🌍 1. 複数アカウント・複数リージョン展開戦略
霊夢「CDKって、1つのAWSアカウントにしかデプロイできないの?」
魔理沙「いや、複数アカウント・リージョンを跨いで構築できるぜ!」
📁 構成イメージ
app.py stacks/ ├── network_stack.py ├── app_stack.py └── logging_stack.py
📄 app.py
from aws_cdk import App, Environment from stacks.network_stack import NetworkStack from stacks.app_stack import AppStack from stacks.logging_stack import LoggingStack app = App() # --- 各アカウント/リージョン設定 --- env_dev = Environment(account="111111111111", region="ap-northeast-1") env_prod = Environment(account="222222222222", region="us-east-1") # --- スタック展開 --- network_dev = NetworkStack(app, "Network-Dev", env=env_dev) app_dev = AppStack(app, "App-Dev", env=env_dev) logging_global = LoggingStack(app, "Logging-Global", env=env_prod) app.synth()
霊夢「これで環境ごとにアカウントもリージョンも変えられるのね!」
魔理沙「うむ。Environmentを明示的に渡せば、CDKが自動で分離デプロイしてくれる。」
💡 クロスアカウント依存関係の注意点
- CDKは同一リージョン内の依存関係のみ自動解決できる
- 異なるリージョン/アカウント間の値共有は SSM Parameter Store や S3出力ファイルを経由
📄 例:SSMでクロスリージョン情報共有
from aws_cdk import aws_ssm as ssm # devアカウント ssm.StringParameter(self, "VpcIdParam", parameter_name="/network/vpc_id", string_value=vpc.vpc_id ) # prodアカウント側 vpc_id = ssm.StringParameter.from_string_parameter_name( self, "ImportVpcId", parameter_name="/network/vpc_id" ).string_value
霊夢「なるほど。Parameter Storeを“共有メモリ”として使うわけね。」
魔理沙「そう。組織全体で安全に構成値を渡せるんだぜ。」
🧑💼 2. セキュリティ・ガバナンス(IAM、Organization、SSO)
霊夢「大規模組織だと、誰が何を操作できるかが重要よね。」
魔理沙「その通り。CDKでもガバナンス設計をコードで管理できるんだぜ。」
📄 例:IAMロールとポリシーの明示的設計
from aws_cdk import ( aws_iam as iam, Stack, ) from constructs import Construct class GovernanceStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- 開発者用ロール --- dev_role = iam.Role( self, "DeveloperRole", assumed_by=iam.AccountRootPrincipal(), description="Developer access for sandbox" ) dev_role.add_managed_policy( iam.ManagedPolicy.from_aws_managed_policy_name("PowerUserAccess") ) # --- 本番ロール (読み取り専用) --- prod_role = iam.Role( self, "ProdReadOnly", assumed_by=iam.ServicePrincipal("ec2.amazonaws.com") ) prod_role.add_managed_policy( iam.ManagedPolicy.from_aws_managed_policy_name("ReadOnlyAccess") )
霊夢「CDKでIAMロール設計もできるのね!」
魔理沙「うむ。さらにaws_organizationsやaws_ssoモジュールを組み合わせれば、
組織単位でアカウントをプロビジョニングできるんだ。」
📄 Organizationユニット構成例
from aws_cdk import aws_organizations as org org.Organization( self, "MyOrg", feature_set=org.FeatureSet.ALL )
霊夢「これで新しいAWSアカウント作成も自動化できちゃうの!?」
魔理沙「ああ、組織単位で構成管理が可能だぜ。」
💡 SSO連携設計ポイント
- AWS IAM Identity Center (旧SSO) を活用し、ロール権限をコードで管理
cdk diffで権限差分をレビュー(ヒューマンエラー防止)- 環境ごとにRole ARNをContextで管理
💰 3. コスト最適化/モニタリング/ログ管理
霊夢「大規模構成だとコストも気になるよね。」
魔理沙「もちろん。CDKでもコストを意識したリソース設計ができる。」
📄 CloudWatch+Cost Anomaly Detection
from aws_cdk import ( aws_cloudwatch as cw, aws_cloudwatch_actions as cw_actions, aws_sns as sns, ) from constructs import Construct class MonitoringStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) topic = sns.Topic(self, "AlertTopic") topic.add_subscription( sns.Subscription(email_address="admin@example.com") ) # --- CloudWatchアラーム(S3 APIコスト監視) --- metric = cw.Metric( namespace="AWS/S3", metric_name="NumberOfObjects", statistic="Sum", period=cdk.Duration.hours(1) ) cw.Alarm( self, "S3ObjectAlarm", metric=metric, threshold=1_000_000, evaluation_periods=1, comparison_operator=cw.ComparisonOperator.GREATER_THAN_THRESHOLD ).add_alarm_action(cw_actions.SnsAction(topic))
💡 コスト最適化チェックリスト
| 項目 | チェック内容 |
|---|---|
| EC2 | スポットインスタンス・Savings Plan検討 |
| Lambda | メモリ過剰割り当てを防ぐ(実行時間×メモリ) |
| S3 | ライフサイクルルール・Intelligent Tiering |
| CloudFront | キャッシュポリシーで転送コスト削減 |
| DynamoDB | オートスケーリング/オンデマンド課金 |
💾 ログ管理設計例
from aws_cdk import aws_logs as logs logs.LogGroup( self, "AppLogs", retention=logs.RetentionDays.THREE_MONTHS, removal_policy=None )
霊夢「ログ保持期間もコードで決められるのね!」
魔理沙「そう。監査対応・コスト両方を満たせる設計だぜ。」
🧱 4. 演習:Well-Architected フレームワークに沿った構成設計
霊夢「最後はAWS公式の“ベストプラクティス”に沿って構成を作るのね!」
魔理沙「そうだ。5つの柱に対応するCDK設計を実践してみよう。」
📘 AWS Well-Architected 5つの柱
| 柱 | 内容 | CDKでの実践例 |
|---|---|---|
| 運用優秀性 (Operational Excellence) | IaCで再現性を担保 | cdk synth, cdk diff |
| セキュリティ (Security) | IAM最小権限、Secrets Manager | aws_iam, aws_secretsmanager |
| 信頼性 (Reliability) | 冗長構成、AutoScaling | aws_autoscaling, Multi-AZ |
| パフォーマンス効率 (Performance) | キャッシュ・スケーリング | aws_cloudfront, lambda |
| コスト最適化 (Cost Optimization) | サーバレス・スポット活用 | aws_lambda, SavingsPlan |
📄 well_arch_stack.py(例)
from aws_cdk import ( Stack, aws_lambda as _lambda, aws_s3 as s3, aws_cloudfront as cloudfront, aws_cloudfront_origins as origins, aws_iam as iam, ) from constructs import Construct class WellArchStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # --- セキュアなS3バケット --- bucket = s3.Bucket( self, "SecureBucket", encryption=s3.BucketEncryption.S3_MANAGED, block_public_access=s3.BlockPublicAccess.BLOCK_ALL ) # --- Lambda関数 (最小権限) --- fn = _lambda.Function( self, "LambdaFn", runtime=_lambda.Runtime.PYTHON_3_11, handler="handler.main", code=_lambda.Code.from_inline("def main(event, ctx): return 'OK'"), ) fn.add_to_role_policy( iam.PolicyStatement(actions=["s3:ListBucket"], resources=[bucket.bucket_arn]) ) # --- CloudFrontキャッシュで性能最適化 --- cloudfront.Distribution( self, "AppDist", default_behavior=cloudfront.BehaviorOptions( origin=origins.S3Origin(bucket), viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, ) )
霊夢「セキュリティ・信頼性・パフォーマンス・コスト、全部カバーできるのね!」
魔理沙「そうだ。Well-Architectedの5本柱はCDK設計のチェックリストとして超便利なんだぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| マルチアカウント構成 | Environment で分離、SSMで共有 |
| セキュリティ設計 | IAM、Organization、SSOをコード管理 |
| コスト最適化 | CloudWatch+S3ライフサイクルで自動化 |
| Well-Architected準拠 | 5つの柱で構成設計をレビュー |
霊夢「これでエンタープライズ級のAWS設計もできるね!」
魔理沙「ああ。CDKを使えば“設計ドキュメント”すらコード化できるんだぜ!」
第14章 将来的な技術動向と CDK 活用展望
🧭 1. CDK v2 の最新動向とアップグレード戦略
霊夢「CDK v2 って、v1 と何が違うの?」
魔理沙「v2 では“統合・安定・整理”が進んでる。もう“モジュール地獄”とはおさらばだぜ。」
💡 v1 → v2 の主な変更点
| 項目 | v1 | v2 |
|---|---|---|
| モジュール構成 | @aws-cdk/aws-s3 など個別 |
aws-cdk-lib に統合 |
| Constructバージョン | constructs@3 系など混在 |
constructs@10 に統一 |
| coreモジュール | core |
Stack, App へ直接統合 |
| API安定性 | 各L2 APIが頻繁変更 | 安定LTSベース |
📄 v1 コード(旧)
from aws_cdk import core from aws_cdk import aws_s3 as s3 class OldStack(core.Stack): def __init__(self, scope: core.Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyBucket")
📄 v2 コード(新)
from aws_cdk import ( Stack, aws_s3 as s3, ) from constructs import Construct class NewStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) s3.Bucket(self, "MyBucket")
⚙️ v2 へのアップグレード手順
pip uninstall aws-cdk.core aws-cdk.aws-* -y pip install aws-cdk-lib constructs cdk doctor
# synthテスト cdk synth # 差分確認 cdk diff # 実デプロイ cdk deploy
霊夢「移行って思ったより簡単ね!」
魔理沙「v2は“複雑さの整理”が目的だからな。構成もシンプルで堅牢だぜ。」
🧰 2. 他の IaC ツールとの比較(Terraform/Pulumi)
霊夢「CDKって他のツールより何がいいの?」
魔理沙「TerraformとPulumiと比べながら整理してみよう。」
📊 IaCツール比較表
| 項目 | AWS CDK | Terraform | Pulumi |
|---|---|---|---|
| 言語 | TypeScript / Python / Go / Java | 独自HCL言語 | TypeScript / Python / Go |
| 管理方式 | CloudFormationベース | 独自Stateファイル(tfstate) | 独自バックエンド |
| 冪等性 | AWS標準の差分管理 | HCL定義に準拠 | 動的言語で柔軟 |
| 拡張性 | Constructライブラリ化 | Module再利用 | SDKで自由度高 |
| マルチクラウド | AWS中心 | 強い(Azure/GCP対応) | 強い(API直結) |
| OSSエコシステム | Construct Hub | Terraform Registry | Pulumi Registry |
霊夢「Terraformはマルチクラウド強いけど、AWS中心ならCDKが自然ね。」
魔理沙「そうだな。Pulumiはコードベース柔軟性が高いけど、AWS公式のCDKは安定性が強みだぜ。」
💡 CDK + Terraform の併用例(tf module呼び出し)
from aws_cdk import ( Stack, custom_resources as cr, ) from constructs import Construct class HybridStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # Terraform module呼び出し例 cr.AwsCustomResource( self, "TFBridge", on_create=cr.AwsSdkCall( service="S3", action="createBucket", parameters={"Bucket": "tf-shared-bucket"}, physical_resource_id=cr.PhysicalResourceId.of("tf-bucket") ), policy=cr.AwsCustomResourcePolicy.from_sdk_calls(resources=cr.AwsCustomResourcePolicy.ANY_RESOURCE) )
霊夢「TerraformやPulumiのいいとこも組み合わせられるのね!」
魔理沙「そうだ。CDKはAWSの中核に最も近いレイヤーだから、拡張性が高いんだ。」
🔁 3. 継続的な運用・リファクタリング・技術負債回避
霊夢「長期運用してるとCDKプロジェクトも肥大化するよね。」
魔理沙「うむ。IaCも“継続的リファクタリング”が大事だぜ。」
💡 構成の分割戦略(Modular Monolithスタイル)
stacks/
├── network/
│ ├── vpc_stack.py
│ └── sg_stack.py
├── app/
│ ├── api_stack.py
│ └── worker_stack.py
└── shared/
├── s3_stack.py
└── kms_stack.py
# app.py from aws_cdk import App from stacks.network.vpc_stack import VpcStack from stacks.app.api_stack import ApiStack app = App() vpc = VpcStack(app, "Vpc") ApiStack(app, "Api", vpc=vpc) app.synth()
💡 リファクタリングのテスト支援
import aws_cdk as cdk import aws_cdk.assertions as assertions from stacks.app.api_stack import ApiStack def test_api_resources(): app = cdk.App() stack = ApiStack(app, "Test") template = assertions.Template.from_stack(stack) template.resource_count_is("AWS::Lambda::Function", 2)
霊夢「CIで構成テストできれば安心だね!」
魔理沙「IaCもコード。テスト、レビュー、リリース管理の文化を取り入れるんだぜ。」
💡 技術負債を避ける3原則
| 原則 | 内容 |
|---|---|
| 1. Infrastructure as Software | IaCをアプリコード同様にリファクタ・Lint |
| 2. Version as Policy | Constructのバージョン固定・アップデート管理 |
| 3. Reuse over Rebuild | L3 Construct再利用で重複削減 |
🏗️ 4. 小規模から大規模へ、次のステップを見据えて
霊夢「個人プロジェクトでもCDK使える?」
魔理沙「もちろんだ!CDKは“スモールスタート→エンタープライズ化”が得意なんだぜ。」
📘 ステップアップのロードマップ
| フェーズ | 規模 | 目標 | 活用ポイント |
|---|---|---|---|
| Phase 1:個人/小規模 | 1〜2人 | 小規模Webサービス構築 | S3, Lambda, API Gateway |
| Phase 2:チーム開発 | 3〜10人 | CI/CD自動化 | CodePipeline, CDK Pipelines |
| Phase 3:組織運用 | 10人〜 | 権限/コスト管理 | Organization, SSO, Cost Anomaly Detection |
| Phase 4:グローバル展開 | 100人〜 | マルチリージョン最適化 | multi-env, cross-account |
📄 例:CDK Pipelines + GitOps連携
from aws_cdk import pipelines as pipelines from constructs import Construct from aws_cdk import Stack class PipelineStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) synth = pipelines.ShellStep( "Synth", input=pipelines.CodePipelineSource.git_hub( "hiromichinomata/aws-cdk-sample", "main" ), commands=[ "python -m venv .venv", "source .venv/bin/activate", "pip install -r requirements.txt", "pytest -v", "cdk synth" ] ) pipeline = pipelines.CodePipeline(self, "Pipeline", synth=synth)
霊夢「コードとパイプラインを同じリポジトリで管理できるんだ!」
魔理沙「そう。これが“GitOps with CDK”。未来の運用はこれが標準になるぜ。」
📘 まとめ
| 項目 | 内容 |
|---|---|
| CDK v2の進化 | 統合・安定・単一ライブラリ構成 |
| 他IaC比較 | Terraformはマルチクラウド、Pulumiは柔軟、CDKはAWS最適化 |
| 運用・負債回避 | テスト+リファクタ+Construct再利用 |
| 未来展望 | GitOps/マルチアカウント/自作ライブラリ運用 |
霊夢「CDKって、ただのIaCツールじゃなくて“クラウド開発のOS”って感じね。」
魔理沙「まさに!コードでクラウドを設計・構築・進化させる──
それがCDKの未来であり、次世代インフラエンジニアの武器なんだぜ。」
🌟 最終章まとめ:
「CDKでインフラを“資産”として育てよう。」
コードで設計し、テストで守り、リファクタで進化させる。 それが、現代クラウドエンジニアリングの理想形だ。
💬 霊夢「次は“CDKライブラリ開発者”としてConstruct Hubデビューしようか!」
💬 魔理沙「いいな。AWSの未来をコードで描こうぜ!」
付録 A〜D
🧰 A. CDK CLI コマンドリファレンス
霊夢「CDKのコマンド、結構いろいろあるよね…」
魔理沙「うむ。IaCの“gitコマンド”みたいなもんだ。
ここで一気にまとめておこう。」
💡 基本構文
cdk [COMMAND] [OPTIONS]
🧱 CDKプロジェクト操作コマンド
# 新規プロジェクト作成 cdk init app --language python # 必要な依存関係をインストール pip install -r requirements.txt
🏗️ スタック生成・展開関連
# CloudFormationテンプレート生成(dry-run) cdk synth # 実際にデプロイ(初回はbootstrapが必要) cdk deploy # 複数スタック指定 cdk deploy NetworkStack AppStack
🧨 スタック削除・差分確認
# 削除 cdk destroy # 差分確認(本番前チェック) cdk diff
🧾 環境確認・ドキュメント
# 環境診断 cdk doctor # 使用リージョン・アカウント確認 cdk context # コマンド一覧 cdk --help
霊夢「cdk diff と cdk synth は絶対習慣にしたいね。」
魔理沙「そうだな。デプロイ前のcdk diffは“プルリクレビュー”と同じくらい大事だぜ。」
🐍 B. Python 開発環境ベストプラクティス
霊夢「CDKのPython環境って、どう整えるのがいいの?」
魔理沙「CDKはAWS SDKも絡むから、“隔離された仮想環境”が基本だぜ。」
💡 仮想環境構築手順
python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install --upgrade pip pip install aws-cdk-lib constructs
📦 推奨 requirements.txt
aws-cdk-lib==2.150.0 constructs>=10.0.0,<11.0.0 pytest black flake8 boto3 mypy
💡 開発補助ツール
| ツール | 用途 | コマンド例 |
|---|---|---|
| black | 自動整形 | black . |
| flake8 | スタイルチェック | flake8 stacks/ |
| pytest | テスト | pytest -v |
| mypy | 型チェック | mypy stacks/ |
📄 .pre-commit-config.yaml(自動整形設定)
repos: - repo: https://github.com/psf/black rev: 23.7.0 hooks: - id: black - repo: https://github.com/pycqa/flake8 rev: 6.1.0 hooks: - id: flake8
pip install pre-commit pre-commit install
霊夢「コード整形やテストを自動で走らせるのね!」
魔理沙「CDKの品質は“インフラの安全性”に直結する。
Pythonツールで守るのがベストプラクティスだぜ。」
🧩 C. トラブルシュート用チェックリスト
霊夢「CDKデプロイでエラー出た時って、どこを見ればいいの?」
魔理沙「よくあるトラブルを一覧でまとめておいたぞ。」
⚠️ デプロイ失敗時のチェックリスト
| 症状 | 確認ポイント | 対処法 | |
|---|---|---|---|
| CREATE_FAILED | IAMポリシー不足 | 権限拡張 or --role-arn指定 |
|
| ROLLBACK_COMPLETE | 依存リソース削除済み? | aws cloudformation delete-stack |
|
| AccessDenied | CLI認証ミス | aws configure で再設定 |
|
| Too many resources | スタック肥大化 | NestedStack に分割 | |
| Timeout | NAT/ALB構成誤り | SecurityGroup確認 | |
| Version mismatch | CDK v2未統一 | `pip freeze | grep cdk` |
| context.json 不整合 | 古い値キャッシュ | cdk context --clear |
💡 デバッグコマンド
cdk deploy --verbose cdk synth --trace
📄 CloudFormationイベントの確認
aws cloudformation describe-stack-events --stack-name MyAppStack
📄 CDKリソースの調査補助スクリプト
import boto3 cf = boto3.client("cloudformation") for stack in cf.list_stacks(StackStatusFilter=["CREATE_FAILED"])["StackSummaries"]: print(stack["StackName"], stack["StackStatus"])
霊夢「CDKエラーって見た目怖いけど、原因はだいたいパターン化されてるのね。」
魔理沙「そう。焦らずcdk diffとCloudFormationイベントを見れば大体わかるぜ。」
🗂️ D. リポジトリ構成・サンプルコードへのアクセス
霊夢「本書のサンプルコードはどこにあるの?」
魔理沙「以下の構成をベースに、GitHubでも同様に提供してるぜ。」
📁 推奨ディレクトリ構成
aws-cdk-hands-on/ ├── README.md ├── app.py ├── requirements.txt ├── cdk.json ├── stacks/ │ ├── network_stack.py │ ├── compute_stack.py │ ├── storage_stack.py │ ├── app_stack.py │ └── pipeline_stack.py ├── constructs/ │ ├── web_bucket_construct.py │ └── lambda_api_construct.py ├── tests/ │ ├── test_app_stack.py │ ├── test_pipeline_stack.py │ └── snapshot/ ├── .github/ │ └── workflows/ │ └── deploy.yml └── .pre-commit-config.yaml
📄 .github/workflows/deploy.yml(GitHub Actions自動デプロイ)
name: CDK Deploy on: push: branches: [ "main" ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install dependencies run: | python -m venv .venv source .venv/bin/activate pip install -r requirements.txt - name: CDK Deploy run: | source .venv/bin/activate cdk synth cdk deploy --require-approval never
💡 GitHubリポジトリ例(サンプル)(注: まだないです)
git clone https://github.com/hiromichinomata/aws-cdk-python-hands-on.git cd aws-cdk-python-hands-on python -m venv .venv source .venv/bin/activate pip install -r requirements.txt cdk synth
霊夢「これで環境再現も一瞬ね!」
魔理沙「そう。CDKの世界では“リポジトリそのものが設計書”なんだぜ。」
🧾 付録まとめ
| 付録 | 内容 |
|---|---|
| A. CLIリファレンス | cdk synth, cdk diff, cdk deploy の使い方まとめ |
| B. Python環境ベストプラクティス | venv, Linter, pre-commit設定例 |
| C. トラブルシュート | よくあるエラーと確認ポイント |
| D. リポジトリ構成 | 実践的なスタック・テスト・CI構成例 |
💬 霊夢「これでCDKの全章コンプリートだね!」
💬 魔理沙「おう!IaCを“読む・書く・テストする・運用する”力が全部身についたぜ!」
🎉 Final Message
AWS CDKは「クラウドのためのソフトウェア工学」 。 あなたのインフラ設計が“コード”として語り継がれる時代が来た。
