AWS CDK カスタム Construct でALB + EC2 + RDS 3層構成を再利用可能な部品に切り出す【スタック170行 → 30行】

AWS Basic
スポンサーリンク
スポンサーリンク

はじめに

「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 を参照してください。


-->

スポンサーリンク

キーワード解説

用語意味
カスタム ConstructConstruct を継承して作る独自の再利用可能コンポーネント。複数の 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.pyCDK アプリのエントリポイント(スタックを登録する)

詳細なコードは GitHub を参照してください。


使用するAWSサービス

サービス役割料金
VPCカスタムネットワーク空間無料
EC2(t2.micro)APサーバ(Tomcat)月750時間まで無料枠あり
RDS MySQL(db.t3.micro)マネージドDBサーバ月750時間まで無料枠あり
ALBロードバランサー約$0.008/時間 + LCU料金(無料枠なし
SSM Parameter StoreDBパスワードの安全な保管スタンダード層は無料
IAMEC2にSSM・Parameter Store権限を付与無料

注意: ALBは無料枠がありません。ハンズオン後は必ず cdk destroy で削除してください。


前提条件

ツール確認コマンド
AWS CLI v2aws --version
Python 3.9 以上python --version
Node.js 18 以上node --version
CDK CLIcdk --version

AWS 認証確認:

aws sts get-caller-identity

CDK 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.jsoncontext セクションを自分の環境に合わせて編集します。

{
  "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_idmyリソース名のプレフィックス(my-cdk2-xxx という名前になる)
my_ip⓪で確認したIP /32EC2へのSSHを許可するIP
db_passwordHandson1234!RDS MySQL のパスワード
key_namemy-cdk2-key①で作成したキーペア名
tomcat_version10.1.28Tomcat バージョン

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-rdscdk-custom-constructs
スタック全リソース定義を直接記述(約170行)Props 組み立て + Construct を1行 + Outputs(約30行)
Constructなし(スタックに全部書く)ThreeTierWebConstruct が全リソースをカプセル化

⑤ CDK Synth(テンプレート確認)

cdk synth

cdk.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

控えておく情報: ALBEndpointEC2PublicIPRDSEndpoint


⑦ 動作確認

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・サブネット・SGRDS 削除後
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 にアクセスすると 502Tomcat 起動前3〜5 分待ってリトライ
SSH できないmy_ip または key_name が違うcdk.jsonmy_ipkey_name を確認
RDS に接続できないSG 設定の問題EC2 SG から RDS SG への 3306 ルールを確認
cdk destroy が途中で止まるRDS の削除待ち10〜15 分待つ(自動的に続行される)
No match for argument: mysqlAL2023 では 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) を参照してください。

コメント