はじめに
「CDKで書いたインフラコードをdev環境とprod環境で使い回したい」「スタックがどんどん肥大化してきた」——これを解決するのが CDK カスタム Construct です。
この記事では、CDK版ハンズオンで構築した ALB + EC2(Tomcat) + RDS の3層構成を、カスタム L3 Construct(ThreeTierWebConstruct) に切り出します。同じインフラを「再利用可能な部品」として定義することで、スタックのコード量が約170行 → 約30行に削減されることを体験します。
インターネット
↓ HTTP(80)
[ALB: my-cdk2-alb]
↓ HTTP(8080) ← SG-to-SG 制御
[EC2: my-cdk2-ap-instance] ← Tomcat
↓ MySQL(3306) ← SG-to-SG 制御
[RDS: my-cdk2-rds-mysql] ← MySQL 8.0
┌─ ThreeTierWebConstruct ────────────────────────────────────────┐
│ VPC / SG / SSM / RDS / EC2 Role / EC2 / ALB をすべて内包 │
└────────────────────────────────────────────────────────────────┘このハンズオンで体験できること:
Constructクラスを継承して独自の L3 Construct を自作する方法@dataclassで設定値(Props)を型安全にまとめる CDK の慣習を理解- スタックを「Construct を呼ぶだけの30行」に削減し、インフラ定義とデプロイ単位を分離する設計を体験
- 同じ Construct を dev/prod など複数環境で使い回す拡張イメージをつかむ
このハンズオンの特徴:
- CDK版(cdk-alb-ec2-rds)の約170行スタックが 約30行に削減される
- インフラの構成はまったく同じ(ALB + EC2 + RDS)でコード構造だけが変わる
ThreeTierWebConstructを複数スタックから呼び出すだけで環境を増やせる設計になる
この記事は CDK版ハンズオン(cdk-alb-ec2-rds) の発展記事です。
CDK の基本(Bootstrap・synth・deploy)を体験済みの方向けです。コード詳細は GitHub を参照してください。
-->
キーワード解説
| 用語 | 意味 |
|---|---|
| カスタム Construct | Construct を継承して作る独自の再利用可能コンポーネント。複数の L2 Construct を組み合わせて L3 Construct を自作できる |
| L3 Construct | 複数サービスを組み合わせた再利用可能なパターン。今回自作する ThreeTierWebConstruct がこれにあたる |
| Props パターン | @dataclass で Construct の設定値をまとめる CDK の慣習。型ヒントでどんな値が必要か一目で分かる |
| 公開属性 | self.alb のように self 属性に保存することで、スタック側から Construct 内のリソースを参照できるようにしたもの |
| Construct ID | 同じスタック内でリソースを一意に識別するための文字列(例: "Web")。CDK はこの ID を使って CloudFormation リソース名を自動生成する |
| 責務の分離 | インフラ定義(Construct)とデプロイ単位(スタック)を別ファイルに分けて管理する設計原則 |
cdk-alb-ec2-rds との比較
| 比較項目 | cdk-alb-ec2-rds(単一スタック) | cdk-custom-constructs(カスタム Construct) |
|---|---|---|
| コード構成 | 全リソースを1ファイルに記述 | components/ に Construct、stacks/ にスタックを分離 |
| スタックの行数 | 約170行 | 約30行(定義は Construct に委譲) |
| 再利用性 | なし | 同じ Construct を dev/prod などで使い回せる |
| 責務の分離 | なし | インフラ定義(Construct)とデプロイ単位(スタック)を分ける |
| テスト容易性 | スタック全体のみ | Construct 単体でユニットテストを書ける |
| 生成されるインフラ | ALB + EC2 + RDS 3層構成 | 同じ ALB + EC2 + RDS 3層構成 |
インフラの構成は cdk-alb-ec2-rds と完全に同等です。変わるのはコードの書き方(設計)だけです。
ファイル構成と役割
cdk-custom-constructs/
├── app.py ← CDK アプリ エントリポイント
├── cdk.json ← CDK 設定・Context 変数
├── requirements.txt
├── components/
│ └── three_tier_web.py ← カスタム L3 Construct(本ハンズオンの主役)
│ ├── ThreeTierWebProps ← Construct に渡す設定値(dataclass)
│ └── ThreeTierWebConstruct ← ALB + EC2 + RDS をカプセル化した Construct
└── stacks/
└── three_tier_stack.py ← Props を組み立て、Construct を呼ぶだけの約30行スタック| ファイル | 役割 |
|---|---|
components/three_tier_web.py | インフラ定義(VPC・SG・RDS・EC2・ALB を全部 Construct に詰め込む) |
stacks/three_tier_stack.py | デプロイ単位(Props の組み立て + ThreeTierWebConstruct を1行で呼ぶ + Outputs) |
app.py | CDK アプリのエントリポイント(スタックを登録する) |
詳細なコードは GitHub を参照してください。
使用するAWSサービス
| サービス | 役割 | 料金 |
|---|---|---|
| VPC | カスタムネットワーク空間 | 無料 |
| EC2(t2.micro) | APサーバ(Tomcat) | 月750時間まで無料枠あり |
| RDS MySQL(db.t3.micro) | マネージドDBサーバ | 月750時間まで無料枠あり |
| ALB | ロードバランサー | 約$0.008/時間 + LCU料金(無料枠なし) |
| SSM Parameter Store | DBパスワードの安全な保管 | スタンダード層は無料 |
| IAM | EC2にSSM・Parameter Store権限を付与 | 無料 |
注意: ALBは無料枠がありません。ハンズオン後は必ず
cdk destroyで削除してください。
前提条件
| ツール | 確認コマンド |
|---|---|
| AWS CLI v2 | aws --version |
| Python 3.9 以上 | python --version |
| Node.js 18 以上 | node --version |
| CDK CLI | cdk --version |
AWS 認証確認:
aws sts get-caller-identityCDK Bootstrap 確認(CDKToolkit スタックが存在すれば実施済み):
aws cloudformation describe-stacks ^
--stack-name CDKToolkit ^
--query "Stacks[0].StackStatus" ^
--output text ^
--region ap-northeast-1作業順序
⓪ 自分のIPアドレスを確認する
↓
① キーペアを作成する(AWS CLI で実施)
↓
② プロジェクトのセットアップ(Python 仮想環境)
↓
③ cdk.json を設定する
↓
④ コードの構造を理解する(デプロイ前に読む)
↓
⑤ CDK Synth(テンプレート確認)
↓
⑥ デプロイ(cdk deploy)
↓
⑦ 動作確認(ALBアクセス・SSH・RDS接続)
↓
⑧ Construct の再利用性を確認する(参考)
↓
⑨ CDK diff(参考)
↓
⑩ リソース削除(cdk destroy)【重要】⓪ 自分のIPアドレスを確認する
curl https://checkip.amazonaws.com控えておく情報: 自分のIPアドレス(例: 203.0.113.1)
① キーペアの作成
aws ec2 create-key-pair ^
--key-name my-cdk2-key ^
--query KeyMaterial ^
--output text ^
--region ap-northeast-1 > %USERPROFILE%\.ssh\my-cdk2-key.pem
%USERPROFILE%\.ssh\が存在しない場合は先にmkdir %USERPROFILE%\.sshを実行してください。
② プロジェクトのセットアップ
cd C:\my-aws\aws-learning-projects\cdk-custom-constructs
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txtプロンプトの先頭に (.venv) が表示されれば仮想環境が有効になっています。
重要: CDK コマンド(synth / deploy / diff / destroy)はすべてこの仮想環境が有効な状態で実行する必要があります。
ターミナルを開き直した際は必ず最初に以下を実行してください。cd C:\my-aws\aws-learning-projects\cdk-custom-constructs .venv\Scripts\activate
③ cdk.json の設定
cdk.json の context セクションを自分の環境に合わせて編集します。
{
"context": {
"employee_id": "my",
"my_ip": "203.0.113.1/32",
"db_password": "Handson1234!",
"key_name": "my-cdk2-key",
"tomcat_version": "10.1.28"
}
}| 設定項目 | 値 | 説明 |
|---|---|---|
employee_id | my | リソース名のプレフィックス(my-cdk2-xxx という名前になる) |
my_ip | ⓪で確認したIP /32 | EC2へのSSHを許可するIP |
db_password | Handson1234! | RDS MySQL のパスワード |
key_name | my-cdk2-key | ①で作成したキーペア名 |
tomcat_version | 10.1.28 | Tomcat バージョン |
cdk-alb-ec2-rds(前回)と同時にデプロイしてもリソース名が競合しません。前回は
my-cdk-xxx、今回はmy-cdk2-xxxというプレフィックスになります。
④ コードの構造を理解する(デプロイ前に読む)
このハンズオンの主役は コードの設計です。デプロイ前にファイルの役割を確認しておきます。
Props クラス(components/three_tier_web.py)
@dataclass
class ThreeTierWebProps:
employee_id: str
my_ip: str
db_password: str
key_name: str
tomcat_version: str = field(default="10.1.28") # 省略可能@dataclass で設定値の型と省略時デフォルトを明示しています。スタック側から ThreeTierWebProps(employee_id=..., ...) として渡します。
Props とは: Construct に「何を作るか」を伝える設定値のまとまりです。型ヒントがあることで、どんな値が必要かが一目で分かります。
Construct クラス(components/three_tier_web.py)
class ThreeTierWebConstruct(Construct):
def __init__(self, scope, id, *, props: ThreeTierWebProps):
super().__init__(scope, id)
self.vpc = ec2.Vpc(...) # ← self に保存 → スタックから参照可能
self.alb = elbv2.ApplicationLoadBalancer(...)
self.instance = ec2.Instance(...)
self.db_instance = rds.DatabaseInstance(...)self.alb のように self 属性に保存した値はスタックから参照できます。ローカル変数に代入した場合はスタックから見えません。
スタック(stacks/three_tier_stack.py)
class ThreeTierStack(Stack):
def __init__(self, scope, id, **kwargs):
super().__init__(scope, id, **kwargs)
props = ThreeTierWebProps(employee_id=..., my_ip=..., ...)
# ← この1行で VPC / SG / RDS / EC2 / ALB が全部できる
web = ThreeTierWebConstruct(self, "Web", props=props)
CfnOutput(self, "ALBEndpoint",
value=f"http://{web.alb.load_balancer_dns_name}")スタックは Props の組み立てと Outputs の定義だけに集中できます。インフラの詳細は Construct にすべて委譲されています。
cdk-alb-ec2-rds との比較:
| cdk-alb-ec2-rds | cdk-custom-constructs | |
|---|---|---|
| スタック | 全リソース定義を直接記述(約170行) | Props 組み立て + Construct を1行 + Outputs(約30行) |
| Construct | なし(スタックに全部書く) | ThreeTierWebConstruct が全リソースをカプセル化 |
⑤ CDK Synth(テンプレート確認)
cdk synthcdk.out/ に CloudFormation テンプレートが生成されます。この段階では AWS へのデプロイは行われません。
cdk-alb-ec2-rds の
cdk synthと同じ CloudFormation テンプレートが生成されることを確認できます。Construct を使っても最終的な CloudFormation リソース数は変わりません。CDK の抽象化はコードの書き方を変えますが、生成されるインフラは同等です。
⑥ デプロイ
cdk deploy「Do you wish to deploy these changes?」に y を入力します。
所要時間: RDS の作成に 15〜20 分かかります。
デプロイ完了後、Outputs が表示されます:
Outputs:
CdkCustomConstructsStack.ALBEndpoint = http://my-cdk2-alb-xxxx.ap-northeast-1.elb.amazonaws.com
CdkCustomConstructsStack.EC2PublicIP = x.x.x.x
CdkCustomConstructsStack.RDSEndpoint = my-cdk2-rds-mysql.xxxx.ap-northeast-1.rds.amazonaws.com
CdkCustomConstructsStack.SSHCommand = ssh -i %USERPROFILE%\.ssh\my-cdk2-key.pem ec2-user@x.x.x.x
CdkCustomConstructsStack.MySQLConnectCommand = mysql -h my-cdk2-rds-mysql.xxxx... -u admin -pHandson1234! sampledb控えておく情報: ALBEndpoint、EC2PublicIP、RDSEndpoint
⑦ 動作確認
7-1. ALB のヘルスチェック確認
EC2 → ターゲットグループ → 「ターゲット」タブ
EC2 のステータスが 「healthy」 になるまで待ちます(EC2 起動後 5〜10 分)。
| ステータス | 意味 |
|---|---|
initial | 初期ヘルスチェック中(待つ) |
healthy | 正常(トラフィック転送される) |
unhealthy | 異常(Tomcat が起動していない可能性) |
7-2. ALB 経由でWebアクセス確認
Outputs の ALBEndpoint をブラウザで開きます。
http://(ALBEndpoint の DNS 名)以下が表示されれば正常です:
AP Server - my-cdk2 3-Tier Web
Deployed via AWS CDK Custom Construct (ThreeTierWebConstruct).7-3. EC2 に SSH 接続する
ssh -i %USERPROFILE%\.ssh\my-cdk2-key.pem ec2-user@(EC2PublicIP)7-4. EC2 から RDS への接続確認
EC2 に SSH 接続後(以下は EC2 上での操作):
sudo dnf install -y mariadb105
DB_PASSWORD=$(aws ssm get-parameter \
--name /my/cdk2/db-password \
--query Parameter.Value \
--output text \
--region ap-northeast-1)
mysql -h <RDSEndpoint> -u admin -p${DB_PASSWORD} sampledb-- テーブル作成
CREATE TABLE items (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- データ挿入
INSERT INTO items (name) VALUES ('Apple'), ('Banana');
-- データ確認
SELECT * FROM items;
-- 後片付け
DROP TABLE items;
EXIT;7-5. SSH 接続を切断する
exit⑧ Construct の再利用性を確認する(参考)
カスタム Construct の最大の利点は 同じ定義を複数回使い回せる ことです。app.py を以下のように書き換えると、同じインフラを dev / prod の2環境同時にデプロイできます。
# app.py(参考: 複数環境デプロイの例)
ThreeTierStack(app, "CdkConstructsDevStack",
env=cdk.Environment(region="ap-northeast-1"),
# cdk deploy CdkConstructsDevStack -c employee_id=dev01 ...
)
ThreeTierStack(app, "CdkConstructsProdStack",
env=cdk.Environment(region="ap-northeast-1"),
# cdk deploy CdkConstructsProdStack -c employee_id=prod01 ...
)CloudFormation では同じ YAML を2つコピーするか、パラメータで分岐させる必要がありました。CDK では Construct を使うことでコードの重複なしに複数環境を管理できます。
⑨ CDK の差分確認(参考)
cdk diffコードを変更した後、デプロイ前に差分を確認できます。
⑩ リソース削除
課金を止めるために、ハンズオン完了後は必ず削除します。
cdk destroy「Are you sure you want to delete?」に y を入力します。
所要時間: RDS の削除に 10〜15 分かかります。
削除されるリソース一覧
| リソース | 削除タイミング |
|---|---|
| ALB・リスナー・ターゲットグループ | cdk destroy 実行直後 |
| EC2 インスタンス | cdk destroy 実行直後 |
| RDS インスタンス | 10〜15 分(削除ポリシー: DESTROY) |
| VPC・サブネット・SG | RDS 削除後 |
| SSM Parameter Store | スタック削除と同時 |
| IAM ロール | スタック削除と同時 |
キーペアの削除(手動)
CDK が管理していないリソースは手動で削除します:
aws ec2 delete-key-pair ^
--key-name my-cdk2-key ^
--region ap-northeast-1
del %USERPROFILE%\.ssh\my-cdk2-key.pem仮想環境の終了
deactivate(.venv) が消えて通常のプロンプトに戻れば完了です。
削除完了の確認
aws cloudformation describe-stacks ^
--stack-name CdkCustomConstructsStack ^
--region ap-northeast-1「Stack with id CdkCustomConstructsStack does not exist」エラーが出れば削除完了です。
Construct のポイント解説
なぜ self.alb のように属性に保存するのか
Construct 内のリソースをスタックから参照するには self 属性に保存する必要があります。
# Construct 内(three_tier_web.py)
self.alb = elbv2.ApplicationLoadBalancer(...) # ← self に保存 → スタックから参照可能
listener = alb.add_listener(...) # ← ローカル変数 → スタックからは見えないスタック側では web.alb.load_balancer_dns_name のようにアクセスできます。
props パターン
Construct の設定値は @dataclass で Props クラスにまとめるのが CDK の慣習です。型ヒントでどんな値が必要か一目で分かります。
@dataclass
class ThreeTierWebProps:
employee_id: str # 必須(型ヒントあり)
my_ip: str # 必須
tomcat_version: str = field(default="10.1.28") # 省略可能(デフォルト値あり)Construct ID の役割
ThreeTierWebConstruct(self, "Web", props=props)
# ^^^^
# Construct ID(同じスタック内で一意にする)CDK はこの ID を使って CloudFormation リソース名を自動生成します。同じスタックに同じ Construct を複数配置する場合は ID を変えます(例: "DevWeb", "ProdWeb")。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
ModuleNotFoundError: components | 仮想環境が無効 | .venv\Scripts\activate を実行してから再試行 |
cdk deploy で認証エラー | AWS 認証情報が未設定 | aws sts get-caller-identity で確認 |
cdk bootstrap が必要と言われる | Bootstrap 未実施 | cdk bootstrap aws://アカウントID/ap-northeast-1 を実行 |
| ALB にアクセスすると 502 | Tomcat 起動前 | 3〜5 分待ってリトライ |
| SSH できない | my_ip または key_name が違う | cdk.json の my_ip と key_name を確認 |
| RDS に接続できない | SG 設定の問題 | EC2 SG から RDS SG への 3306 ルールを確認 |
cdk destroy が途中で止まる | RDS の削除待ち | 10〜15 分待つ(自動的に続行される) |
No match for argument: mysql | AL2023 では mysql パッケージが存在しない | sudo dnf install -y mariadb105 を使う |
デプロイ失敗時のログ確認:
aws cloudformation describe-stack-events ^
--stack-name CdkCustomConstructsStack ^
--region ap-northeast-1 ^
--query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[ResourceType,ResourceStatusReason]" ^
--output tableまとめ
| ステップ | 内容 |
|---|---|
| ①② | キーペア作成 + Python 仮想環境のセットアップ |
| ③ | cdk.json に自分の環境(IP・パスワード・キー名)を設定 |
| ④ | ThreeTierWebProps(dataclass)と ThreeTierWebConstruct(Construct)のコード構造を理解 |
| ⑤ | cdk synth で CloudFormation テンプレートを確認 |
| ⑥ | cdk deploy でデプロイ(RDS 待ち 15〜20 分) |
| ⑦ | ALB DNS 名でアクセス → EC2 → RDS への接続テスト |
| ⑧ | 複数環境デプロイのコード例で Construct の再利用イメージをつかむ |
| ⑩ | cdk destroy で全リソースを一括削除 |
カスタム Construct の最大のメリットは インフラを「部品」として設計できる 点です。今回の ThreeTierWebConstruct は1行で3層構成のすべてを生成します。同じ Construct を dev/prod で呼ぶだけで環境を増やせ、スタックのコードは Props の組み立てと Outputs のみの約30行に収まります。
コンソールで3層構成を視覚的に学びたい場合は AWSコンソール版ハンズオン、CloudFormation での自動化は CloudFormation版ハンズオン、CDK の基本(CloudFormation との比較)は CDK版ハンズオン(cdk-alb-ec2-rds) を参照してください。
コメント