<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>L3 Construct - CayTech Lab</title>
	<atom:link href="https://caymezon.com/tag/l3-construct/feed/" rel="self" type="application/rss+xml" />
	<link>https://caymezon.com</link>
	<description></description>
	<lastBuildDate>Sun, 19 Apr 2026 04:26:26 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://caymezon.com/wp-content/uploads/2026/01/cropped-CayTechLab-32x32.jpg</url>
	<title>L3 Construct - CayTech Lab</title>
	<link>https://caymezon.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<atom:link rel='hub' href='https://caymezon.com/?pushpress=hub'/>
	<item>
		<title>AWS CDK カスタム Construct でALB + EC2 + RDS 3層構成を再利用可能な部品に切り出す【スタック170行 → 30行】</title>
		<link>https://caymezon.com/aws-handson-cdk-custom-constructs/</link>
					<comments>https://caymezon.com/aws-handson-cdk-custom-constructs/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sun, 19 Apr 2026 04:26:26 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[cdk deploy]]></category>
		<category><![CDATA[cdk destroy]]></category>
		<category><![CDATA[dataclass]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[L3 Construct]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Props]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[カスタムConstruct]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[再利用性]]></category>
		<category><![CDATA[初心者]]></category>
		<category><![CDATA[責務の分離]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20374</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説cdk-alb-ec2-rds との比較ファイル構成と役割使用するAWSサービス前提条件作業順序【重要】⓪ 自分のIPアドレスを確認する① キーペアの作成② プロジェクトのセットアップ③ cd [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">AWS CDK カスタム Construct でALB + EC2 + RDS 3層構成を再利用可能な部品に切り出す【スタック170行 → 30行】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></description>
										<content:encoded><![CDATA[<div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">キーワード解説</a></li><li><a href="#toc3" tabindex="0">cdk-alb-ec2-rds との比較</a></li><li><a href="#toc4" tabindex="0">ファイル構成と役割</a></li><li><a href="#toc5" tabindex="0">使用するAWSサービス</a></li><li><a href="#toc6" tabindex="0">前提条件</a></li><li><a href="#toc7" tabindex="0">作業順序</a></li><li><a href="#toc8" tabindex="0">【重要】⓪ 自分のIPアドレスを確認する</a></li><li><a href="#toc9" tabindex="0">① キーペアの作成</a></li><li><a href="#toc10" tabindex="0">② プロジェクトのセットアップ</a></li><li><a href="#toc11" tabindex="0">③ cdk.json の設定</a></li><li><a href="#toc12" tabindex="0">④ コードの構造を理解する（デプロイ前に読む）</a><ol><li><a href="#toc13" tabindex="0">Props クラス（components/three_tier_web.py）</a></li><li><a href="#toc14" tabindex="0">Construct クラス（components/three_tier_web.py）</a></li><li><a href="#toc15" tabindex="0">スタック（stacks/three_tier_stack.py）</a></li></ol></li><li><a href="#toc16" tabindex="0">⑤ CDK Synth（テンプレート確認）</a></li><li><a href="#toc17" tabindex="0">⑥ デプロイ</a></li><li><a href="#toc18" tabindex="0">⑦ 動作確認</a><ol><li><a href="#toc19" tabindex="0">7-1. ALB のヘルスチェック確認</a></li><li><a href="#toc20" tabindex="0">7-2. ALB 経由でWebアクセス確認</a></li><li><a href="#toc21" tabindex="0">7-3. EC2 に SSH 接続する</a></li><li><a href="#toc22" tabindex="0">7-4. EC2 から RDS への接続確認</a></li><li><a href="#toc23" tabindex="0">7-5. SSH 接続を切断する</a></li></ol></li><li><a href="#toc24" tabindex="0">⑧ Construct の再利用性を確認する（参考）</a></li><li><a href="#toc25" tabindex="0">⑨ CDK の差分確認（参考）</a></li><li><a href="#toc26" tabindex="0">⑩ リソース削除</a><ol><li><a href="#toc27" tabindex="0">削除されるリソース一覧</a></li><li><a href="#toc28" tabindex="0">キーペアの削除（手動）</a></li><li><a href="#toc29" tabindex="0">仮想環境の終了</a></li><li><a href="#toc30" tabindex="0">削除完了の確認</a></li></ol></li><li><a href="#toc31" tabindex="0">Construct のポイント解説</a><ol><li><a href="#toc32" tabindex="0">なぜ self.alb のように属性に保存するのか</a></li><li><a href="#toc33" tabindex="0">props パターン</a></li><li><a href="#toc34" tabindex="0">Construct ID の役割</a></li></ol></li><li><a href="#toc35" tabindex="0">トラブルシューティング</a></li><li><a href="#toc36" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「CDKで書いたインフラコードをdev環境とprod環境で使い回したい」「スタックがどんどん肥大化してきた」——これを解決するのが <strong>CDK カスタム Construct</strong> です。</p>
<p>この記事では、<a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン</a>で構築した <strong>ALB + EC2(Tomcat) + RDS の3層構成</strong>を、<strong>カスタム L3 Construct（<code>ThreeTierWebConstruct</code>）</strong> に切り出します。同じインフラを「再利用可能な部品」として定義することで、<strong>スタックのコード量が約170行 → 約30行</strong>に削減されることを体験します。</p>
<pre><code class="language-plaintext">インターネット
  ↓ 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 をすべて内包    │
└────────────────────────────────────────────────────────────────┘</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li><code>Construct</code> クラスを継承して独自の L3 Construct を自作する方法</li>
<li><code>@dataclass</code> で設定値（Props）を型安全にまとめる CDK の慣習を理解</li>
<li>スタックを「Construct を呼ぶだけの30行」に削減し、<strong>インフラ定義とデプロイ単位を分離</strong>する設計を体験</li>
<li>同じ Construct を dev/prod など複数環境で使い回す拡張イメージをつかむ</li>
</ul>
<p><strong>このハンズオンの特徴：</strong></p>
<ul>
<li>CDK版（cdk-alb-ec2-rds）の約170行スタックが <strong>約30行</strong>に削減される</li>
<li>インフラの構成はまったく同じ（ALB + EC2 + RDS）でコード構造だけが変わる</li>
<li><code>ThreeTierWebConstruct</code> を複数スタックから呼び出すだけで環境を増やせる設計になる</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン（cdk-alb-ec2-rds）</a> の発展記事です。</strong><br />CDK の基本（Bootstrap・synth・deploy）を体験済みの方向けです。コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-custom-constructs">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![cdk deployコマンド実行後のOutputs確認画面](images/cdk-deploy-outputs.jpg) --></p>
<p><!-- <a rel="nofollow" href="//af.moshimo.com/af/c/click?a_id=1384942&p_id=170&pc_id=185&pl_id=4062&url=https%3A%2F%2Fwww.amazon.co.jp%2Fs%3Fk%3D%25E6%259C%25AC%2BAWS%2B%25E9%2596%258B%25E7%2599%25BA%26__mk_ja_JP%3D%25E3%2582%25AB%25E3%2582%25BF%25E3%2582%25AB%25E3%2583%258A%26crid%3D1DE63UBHFOR4K%26sprefix%3D%25E6%259C%25AC%2Baws%2B%25E9%2596%258B%25E7%2599%25BA%252Caps%252C167%26ref%3Dnb_sb_noss" referrerpolicy="no-referrer-when-downgrade" attributionsrc>Amazon検索[本 AWS 開発]</a><img decoding="async" src="//i.moshimo.com/af/i/impression?a_id=1384942&p_id=170&pc_id=185&pl_id=4062" width="1" height="1" style="border:none;" alt="" loading="lazy"> --></p>
<p><!-- <!-- START MoshimoAffiliateEasyLink --><script type="text/javascript">(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;b[a]=b[a]||function(){arguments.currentScript=c.currentScript||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};c.getElementById(a)||(d=c.createElement(f),d.src=g,d.id=a,e=c.getElementsByTagName("body")[0],e.appendChild(d))})(window,document,"script","//dn.msmstatic.com/site/cardlink/bundle.js?20220329","msmaflink");msmaflink({"n":"AWSの基本・仕組み・重要用語が全部わかる教科書 (見るだけ図解)","b":"SBクリエイティブ","t":"","d":"https:\/\/m.media-amazon.com","c_p":"\/images\/I","p":["\/51DEDQXj6oL._SL500_.jpg","\/41F589smNwL._SL500_.jpg","\/41R6f9yyCWL._SL500_.jpg","\/41HqWQ9BvmL._SL500_.jpg","\/41p8p0ZU79L._SL500_.jpg","\/41qLC-fndBL._SL500_.jpg","\/41fcLv9VT5L._SL500_.jpg","\/51lRvCsvHqL._SL500_.jpg"],"u":{"u":"https:\/\/www.amazon.co.jp\/dp\/4815607850","t":"amazon","r_v":""},"v":"2.1","b_l":[{"id":1,"u_tx":"Amazonで見る","u_bc":"#f79256","u_url":"https:\/\/www.amazon.co.jp\/dp\/4815607850","a_id":1384942,"p_id":170,"pl_id":27060,"pc_id":185,"s_n":"amazon","u_so":1},{"id":2,"u_tx":"楽天市場で見る","u_bc":"#f76956","u_url":"https:\/\/search.rakuten.co.jp\/search\/mall\/AWS%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%83%BB%E4%BB%95%E7%B5%84%E3%81%BF%E3%83%BB%E9%87%8D%E8%A6%81%E7%94%A8%E8%AA%9E%E3%81%8C%E5%85%A8%E9%83%A8%E3%82%8F%E3%81%8B%E3%82%8B%E6%95%99%E7%A7%91%E6%9B%B8%20(%E8%A6%8B%E3%82%8B%E3%81%A0%E3%81%91%E5%9B%B3%E8%A7%A3)\/","a_id":1384917,"p_id":54,"pl_id":27059,"pc_id":54,"s_n":"rakuten","u_so":2},{"id":3,"u_tx":"Yahoo!ショッピングで見る","u_bc":"#66a7ff","u_url":"https:\/\/shopping.yahoo.co.jp\/search?first=1\u0026p=AWS%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%83%BB%E4%BB%95%E7%B5%84%E3%81%BF%E3%83%BB%E9%87%8D%E8%A6%81%E7%94%A8%E8%AA%9E%E3%81%8C%E5%85%A8%E9%83%A8%E3%82%8F%E3%81%8B%E3%82%8B%E6%95%99%E7%A7%91%E6%9B%B8%20(%E8%A6%8B%E3%82%8B%E3%81%A0%E3%81%91%E5%9B%B3%E8%A7%A3)","a_id":1466950,"p_id":1225,"pl_id":27061,"pc_id":1925,"s_n":"yahoo","u_so":3}],"eid":"eaCUB","s":"s"});</script></p>
<div id="msmaflink-eaCUB">リンク</div>
<p><!-- MoshimoAffiliateEasyLink END --> --></p>
<h2><span id="toc2">キーワード解説</span></h2>
<table>
<thead>
<tr>
<th>用語</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>カスタム Construct</strong></td>
<td><code>Construct</code> を継承して作る独自の再利用可能コンポーネント。複数の L2 Construct を組み合わせて L3 Construct を自作できる</td>
</tr>
<tr>
<td><strong>L3 Construct</strong></td>
<td>複数サービスを組み合わせた再利用可能なパターン。今回自作する <code>ThreeTierWebConstruct</code> がこれにあたる</td>
</tr>
<tr>
<td><strong>Props パターン</strong></td>
<td><code>@dataclass</code> で Construct の設定値をまとめる CDK の慣習。型ヒントでどんな値が必要か一目で分かる</td>
</tr>
<tr>
<td><strong>公開属性</strong></td>
<td><code>self.alb</code> のように <code>self</code> 属性に保存することで、スタック側から Construct 内のリソースを参照できるようにしたもの</td>
</tr>
<tr>
<td><strong>Construct ID</strong></td>
<td>同じスタック内でリソースを一意に識別するための文字列（例: <code>&quot;Web&quot;</code>）。CDK はこの ID を使って CloudFormation リソース名を自動生成する</td>
</tr>
<tr>
<td><strong>責務の分離</strong></td>
<td>インフラ定義（Construct）とデプロイ単位（スタック）を別ファイルに分けて管理する設計原則</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">cdk-alb-ec2-rds との比較</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>cdk-alb-ec2-rds（単一スタック）</th>
<th>cdk-custom-constructs（カスタム Construct）</th>
</tr>
</thead>
<tbody>
<tr>
<td>コード構成</td>
<td>全リソースを1ファイルに記述</td>
<td><code>components/</code> に Construct、<code>stacks/</code> にスタックを分離</td>
</tr>
<tr>
<td>スタックの行数</td>
<td>約170行</td>
<td><strong>約30行</strong>（定義は Construct に委譲）</td>
</tr>
<tr>
<td>再利用性</td>
<td>なし</td>
<td>同じ Construct を dev/prod などで使い回せる</td>
</tr>
<tr>
<td>責務の分離</td>
<td>なし</td>
<td>インフラ定義（Construct）とデプロイ単位（スタック）を分ける</td>
</tr>
<tr>
<td>テスト容易性</td>
<td>スタック全体のみ</td>
<td>Construct 単体でユニットテストを書ける</td>
</tr>
<tr>
<td>生成されるインフラ</td>
<td>ALB + EC2 + RDS 3層構成</td>
<td><strong>同じ</strong> ALB + EC2 + RDS 3層構成</td>
</tr>
</tbody>
</table>
<blockquote>
<p>インフラの構成は cdk-alb-ec2-rds と完全に同等です。変わるのは<strong>コードの書き方（設計）だけ</strong>です。</p>
</blockquote>
<hr>
<h2><span id="toc4">ファイル構成と役割</span></h2>
<pre><code class="language-plaintext">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行スタック</code></pre>
<table>
<thead>
<tr>
<th>ファイル</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>components/three_tier_web.py</code></td>
<td>インフラ定義（VPC・SG・RDS・EC2・ALB を全部 Construct に詰め込む）</td>
</tr>
<tr>
<td><code>stacks/three_tier_stack.py</code></td>
<td>デプロイ単位（Props の組み立て + <code>ThreeTierWebConstruct</code> を1行で呼ぶ + Outputs）</td>
</tr>
<tr>
<td><code>app.py</code></td>
<td>CDK アプリのエントリポイント（スタックを登録する）</td>
</tr>
</tbody>
</table>
<p>詳細なコードは <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-custom-constructs">GitHub</a> を参照してください。</p>
<hr>
<h2><span id="toc5">使用するAWSサービス</span></h2>
<table>
<thead>
<tr>
<th>サービス</th>
<th>役割</th>
<th>料金</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>VPC</strong></td>
<td>カスタムネットワーク空間</td>
<td>無料</td>
</tr>
<tr>
<td><strong>EC2</strong>（t2.micro）</td>
<td>APサーバ（Tomcat）</td>
<td>月750時間まで無料枠あり</td>
</tr>
<tr>
<td><strong>RDS MySQL</strong>（db.t3.micro）</td>
<td>マネージドDBサーバ</td>
<td>月750時間まで無料枠あり</td>
</tr>
<tr>
<td><strong>ALB</strong></td>
<td>ロードバランサー</td>
<td>約$0.008/時間 + LCU料金（<strong>無料枠なし</strong>）</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DBパスワードの安全な保管</td>
<td>スタンダード層は無料</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>EC2にSSM・Parameter Store権限を付与</td>
<td>無料</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>: ALBは無料枠がありません。ハンズオン後は必ず <code>cdk destroy</code> で削除してください。</p>
</blockquote>
<hr>
<h2><span id="toc6">前提条件</span></h2>
<table>
<thead>
<tr>
<th>ツール</th>
<th>確認コマンド</th>
</tr>
</thead>
<tbody>
<tr>
<td>AWS CLI v2</td>
<td><code>aws --version</code></td>
</tr>
<tr>
<td>Python 3.9 以上</td>
<td><code>python --version</code></td>
</tr>
<tr>
<td>Node.js 18 以上</td>
<td><code>node --version</code></td>
</tr>
<tr>
<td>CDK CLI</td>
<td><code>cdk --version</code></td>
</tr>
</tbody>
</table>
<p>AWS 認証確認:</p>
<pre><code class="language-cmd">aws sts get-caller-identity</code></pre>
<p>CDK Bootstrap 確認（<code>CDKToolkit</code> スタックが存在すれば実施済み）:</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name CDKToolkit ^
  --query "Stacks[0].StackStatus" ^
  --output text ^
  --region ap-northeast-1</code></pre>
<p><!-- > Node.js・CDK CLI・Bootstrap の手順は [docs/setup/09_nodejs-cdk.md](https://github.com/caymezon/aws-handson/tree/main/docs/setup/09_nodejs-cdk.md) を参照してください。 --></p>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（AWS CLI で実施）
      ↓
② プロジェクトのセットアップ（Python 仮想環境）
      ↓
③ cdk.json を設定する
      ↓
④ コードの構造を理解する（デプロイ前に読む）
      ↓
⑤ CDK Synth（テンプレート確認）
      ↓
⑥ デプロイ（cdk deploy）
      ↓
⑦ 動作確認（ALBアクセス・SSH・RDS接続）
      ↓
⑧ Construct の再利用性を確認する（参考）
      ↓
⑨ CDK diff（参考）
      ↓
⑩ リソース削除（cdk destroy）</code></pre>
<hr>
<h2><span id="toc8">【重要】⓪ 自分のIPアドレスを確認する</span></h2>
<pre><code class="language-cmd">curl https://checkip.amazonaws.com</code></pre>
<p><strong>控えておく情報</strong>: 自分のIPアドレス（例: <code>203.0.113.1</code>）</p>
<hr>
<h2><span id="toc9">① キーペアの作成</span></h2>
<pre><code class="language-cmd">aws ec2 create-key-pair ^
  --key-name my-cdk2-key ^
  --query KeyMaterial ^
  --output text ^
  --region ap-northeast-1 &gt; %USERPROFILE%\.ssh\my-cdk2-key.pem</code></pre>
<blockquote>
<p><code>%USERPROFILE%\.ssh\</code> が存在しない場合は先に <code>mkdir %USERPROFILE%\.ssh</code> を実行してください。</p>
</blockquote>
<hr>
<h2><span id="toc10">② プロジェクトのセットアップ</span></h2>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-custom-constructs

python -m venv .venv

.venv\Scripts\activate

pip install -r requirements.txt</code></pre>
<p>プロンプトの先頭に <code>(.venv)</code> が表示されれば仮想環境が有効になっています。</p>
<blockquote>
<p><strong>重要</strong>: CDK コマンド（synth / deploy / diff / destroy）はすべてこの仮想環境が有効な状態で実行する必要があります。<br />ターミナルを開き直した際は必ず最初に以下を実行してください。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-custom-constructs
.venv\Scripts\activate</code></pre>
</blockquote>
<hr>
<h2><span id="toc11">③ cdk.json の設定</span></h2>
<p><code>cdk.json</code> の <code>context</code> セクションを自分の環境に合わせて編集します。</p>
<pre><code class="language-json">{
  "context": {
    "employee_id": "my",
    "my_ip": "203.0.113.1/32",
    "db_password": "Handson1234!",
    "key_name": "my-cdk2-key",
    "tomcat_version": "10.1.28"
  }
}</code></pre>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>employee_id</code></td>
<td><code>my</code></td>
<td>リソース名のプレフィックス（<code>my-cdk2-xxx</code> という名前になる）</td>
</tr>
<tr>
<td><code>my_ip</code></td>
<td>⓪で確認したIP <code>/32</code></td>
<td>EC2へのSSHを許可するIP</td>
</tr>
<tr>
<td><code>db_password</code></td>
<td><code>Handson1234!</code></td>
<td>RDS MySQL のパスワード</td>
</tr>
<tr>
<td><code>key_name</code></td>
<td><code>my-cdk2-key</code></td>
<td>①で作成したキーペア名</td>
</tr>
<tr>
<td><code>tomcat_version</code></td>
<td><code>10.1.28</code></td>
<td>Tomcat バージョン</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>cdk-alb-ec2-rds（前回）と同時にデプロイしてもリソース名が競合しません</strong>。前回は <code>my-cdk-xxx</code>、今回は <code>my-cdk2-xxx</code> というプレフィックスになります。</p>
</blockquote>
<hr>
<h2><span id="toc12">④ コードの構造を理解する（デプロイ前に読む）</span></h2>
<p>このハンズオンの主役は <strong>コードの設計</strong>です。デプロイ前にファイルの役割を確認しておきます。</p>
<h3><span id="toc13">Props クラス（components/three_tier_web.py）</span></h3>
<pre><code class="language-python">@dataclass
class ThreeTierWebProps:
    employee_id: str
    my_ip: str
    db_password: str
    key_name: str
    tomcat_version: str = field(default="10.1.28")  # 省略可能</code></pre>
<p><code>@dataclass</code> で設定値の型と省略時デフォルトを明示しています。スタック側から <code>ThreeTierWebProps(employee_id=..., ...)</code> として渡します。</p>
<blockquote>
<p><strong>Props とは</strong>: Construct に「何を作るか」を伝える設定値のまとまりです。型ヒントがあることで、どんな値が必要かが一目で分かります。</p>
</blockquote>
<h3><span id="toc14">Construct クラス（components/three_tier_web.py）</span></h3>
<pre><code class="language-python">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(...)</code></pre>
<p><code>self.alb</code> のように <strong><code>self</code> 属性に保存した値はスタックから参照できます</strong>。ローカル変数に代入した場合はスタックから見えません。</p>
<h3><span id="toc15">スタック（stacks/three_tier_stack.py）</span></h3>
<pre><code class="language-python">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}")</code></pre>
<p>スタックは <strong>Props の組み立てと Outputs の定義だけ</strong>に集中できます。インフラの詳細は Construct にすべて委譲されています。</p>
<p><strong>cdk-alb-ec2-rds との比較:</strong></p>
<table>
<thead>
<tr>
<th></th>
<th>cdk-alb-ec2-rds</th>
<th>cdk-custom-constructs</th>
</tr>
</thead>
<tbody>
<tr>
<td>スタック</td>
<td>全リソース定義を直接記述（約170行）</td>
<td>Props 組み立て + Construct を1行 + Outputs（約30行）</td>
</tr>
<tr>
<td>Construct</td>
<td>なし（スタックに全部書く）</td>
<td><code>ThreeTierWebConstruct</code> が全リソースをカプセル化</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc16">⑤ CDK Synth（テンプレート確認）</span></h2>
<pre><code class="language-cmd">cdk synth</code></pre>
<p><code>cdk.out/</code> に CloudFormation テンプレートが生成されます。<strong>この段階では AWS へのデプロイは行われません。</strong></p>
<blockquote>
<p>cdk-alb-ec2-rds の <code>cdk synth</code> と同じ CloudFormation テンプレートが生成されることを確認できます。Construct を使っても最終的な CloudFormation リソース数は変わりません。<strong>CDK の抽象化はコードの書き方を変えますが、生成されるインフラは同等です。</strong></p>
</blockquote>
<hr>
<h2><span id="toc17">⑥ デプロイ</span></h2>
<pre><code class="language-cmd">cdk deploy</code></pre>
<p>「Do you wish to deploy these changes?」に <code>y</code> を入力します。</p>
<blockquote>
<p><strong>所要時間</strong>: RDS の作成に 15〜20 分かかります。</p>
</blockquote>
<p>デプロイ完了後、Outputs が表示されます:</p>
<pre><code class="language-plaintext">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</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>EC2PublicIP</code>、<code>RDSEndpoint</code></p>
<p><!-- ![cdk deployのOutputs確認画面](images/cdk-deploy-outputs.jpg) --></p>
<hr>
<h2><span id="toc18">⑦ 動作確認</span></h2>
<h3><span id="toc19">7-1. ALB のヘルスチェック確認</span></h3>
<p><strong>EC2 → ターゲットグループ → 「ターゲット」タブ</strong></p>
<p>EC2 のステータスが <strong>「healthy」</strong> になるまで待ちます（EC2 起動後 5〜10 分）。</p>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>initial</code></td>
<td>初期ヘルスチェック中（待つ）</td>
</tr>
<tr>
<td><code>healthy</code></td>
<td>正常（トラフィック転送される）</td>
</tr>
<tr>
<td><code>unhealthy</code></td>
<td>異常（Tomcat が起動していない可能性）</td>
</tr>
</tbody>
</table>
<h3><span id="toc20">7-2. ALB 経由でWebアクセス確認</span></h3>
<p>Outputs の <code>ALBEndpoint</code> をブラウザで開きます。</p>
<pre><code class="language-plaintext">http://（ALBEndpoint の DNS 名）</code></pre>
<p>以下が表示されれば正常です:</p>
<pre><code class="language-plaintext">AP Server - my-cdk2 3-Tier Web
Deployed via AWS CDK Custom Construct (ThreeTierWebConstruct).</code></pre>
<p><!-- ![ALB経由でTomcat画面が表示されている様子](images/alb-healthy-access.jpg) --></p>
<h3><span id="toc21">7-3. EC2 に SSH 接続する</span></h3>
<pre><code class="language-cmd">ssh -i %USERPROFILE%\.ssh\my-cdk2-key.pem ec2-user@（EC2PublicIP）</code></pre>
<h3><span id="toc22">7-4. EC2 から RDS への接続確認</span></h3>
<p>EC2 に SSH 接続後（以下は EC2 上での操作）:</p>
<pre><code class="language-bash">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 &lt;RDSEndpoint&gt; -u admin -p${DB_PASSWORD} sampledb</code></pre>
<pre><code class="language-sql">-- テーブル作成
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;</code></pre>
<p><!-- ![EC2からRDS MySQLへの接続成功画面](images/rds-mysql-connect.jpg) --></p>
<h3><span id="toc23">7-5. SSH 接続を切断する</span></h3>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc24">⑧ Construct の再利用性を確認する（参考）</span></h2>
<p>カスタム Construct の最大の利点は <strong>同じ定義を複数回使い回せる</strong> ことです。<br /><code>app.py</code> を以下のように書き換えると、同じインフラを dev / prod の2環境同時にデプロイできます。</p>
<pre><code class="language-python"># 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 ...
)</code></pre>
<p>CloudFormation では同じ YAML を2つコピーするか、パラメータで分岐させる必要がありました。CDK では Construct を使うことで<strong>コードの重複なし</strong>に複数環境を管理できます。</p>
<hr>
<h2><span id="toc25">⑨ CDK の差分確認（参考）</span></h2>
<pre><code class="language-cmd">cdk diff</code></pre>
<p>コードを変更した後、デプロイ前に差分を確認できます。</p>
<hr>
<h2><span id="toc26">⑩ リソース削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<pre><code class="language-cmd">cdk destroy</code></pre>
<p>「Are you sure you want to delete?」に <code>y</code> を入力します。</p>
<blockquote>
<p><strong>所要時間</strong>: RDS の削除に 10〜15 分かかります。</p>
</blockquote>
<h3><span id="toc27">削除されるリソース一覧</span></h3>
<table>
<thead>
<tr>
<th>リソース</th>
<th>削除タイミング</th>
</tr>
</thead>
<tbody>
<tr>
<td>ALB・リスナー・ターゲットグループ</td>
<td>cdk destroy 実行直後</td>
</tr>
<tr>
<td>EC2 インスタンス</td>
<td>cdk destroy 実行直後</td>
</tr>
<tr>
<td>RDS インスタンス</td>
<td>10〜15 分（削除ポリシー: DESTROY）</td>
</tr>
<tr>
<td>VPC・サブネット・SG</td>
<td>RDS 削除後</td>
</tr>
<tr>
<td>SSM Parameter Store</td>
<td>スタック削除と同時</td>
</tr>
<tr>
<td>IAM ロール</td>
<td>スタック削除と同時</td>
</tr>
</tbody>
</table>
<h3><span id="toc28">キーペアの削除（手動）</span></h3>
<p>CDK が管理していないリソースは手動で削除します:</p>
<pre><code class="language-cmd">aws ec2 delete-key-pair ^
  --key-name my-cdk2-key ^
  --region ap-northeast-1

del %USERPROFILE%\.ssh\my-cdk2-key.pem</code></pre>
<h3><span id="toc29">仮想環境の終了</span></h3>
<pre><code class="language-cmd">deactivate</code></pre>
<p><code>(.venv)</code> が消えて通常のプロンプトに戻れば完了です。</p>
<h3><span id="toc30">削除完了の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name CdkCustomConstructsStack ^
  --region ap-northeast-1</code></pre>
<p>「Stack with id CdkCustomConstructsStack does not exist」エラーが出れば削除完了です。</p>
<hr>
<h2><span id="toc31">Construct のポイント解説</span></h2>
<h3><span id="toc32">なぜ self.alb のように属性に保存するのか</span></h3>
<p>Construct 内のリソースをスタックから参照するには <code>self</code> 属性に保存する必要があります。</p>
<pre><code class="language-python"># Construct 内（three_tier_web.py）
self.alb = elbv2.ApplicationLoadBalancer(...)   # ← self に保存 → スタックから参照可能
listener  = alb.add_listener(...)               # ← ローカル変数  → スタックからは見えない</code></pre>
<p>スタック側では <code>web.alb.load_balancer_dns_name</code> のようにアクセスできます。</p>
<h3><span id="toc33">props パターン</span></h3>
<p>Construct の設定値は <code>@dataclass</code> で Props クラスにまとめるのが CDK の慣習です。型ヒントでどんな値が必要か一目で分かります。</p>
<pre><code class="language-python">@dataclass
class ThreeTierWebProps:
    employee_id: str        # 必須（型ヒントあり）
    my_ip: str              # 必須
    tomcat_version: str = field(default="10.1.28")  # 省略可能（デフォルト値あり）</code></pre>
<h3><span id="toc34">Construct ID の役割</span></h3>
<pre><code class="language-python">ThreeTierWebConstruct(self, "Web", props=props)
#                           ^^^^
#                           Construct ID（同じスタック内で一意にする）</code></pre>
<p>CDK はこの ID を使って CloudFormation リソース名を自動生成します。同じスタックに同じ Construct を複数配置する場合は ID を変えます（例: <code>&quot;DevWeb&quot;</code>, <code>&quot;ProdWeb&quot;</code>）。</p>
<hr>
<h2><span id="toc35">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ModuleNotFoundError: components</code></td>
<td>仮想環境が無効</td>
<td><code>.venv\Scripts\activate</code> を実行してから再試行</td>
</tr>
<tr>
<td><code>cdk deploy</code> で認証エラー</td>
<td>AWS 認証情報が未設定</td>
<td><code>aws sts get-caller-identity</code> で確認</td>
</tr>
<tr>
<td><code>cdk bootstrap</code> が必要と言われる</td>
<td>Bootstrap 未実施</td>
<td><code>cdk bootstrap aws://アカウントID/ap-northeast-1</code> を実行</td>
</tr>
<tr>
<td>ALB にアクセスすると 502</td>
<td>Tomcat 起動前</td>
<td>3〜5 分待ってリトライ</td>
</tr>
<tr>
<td>SSH できない</td>
<td>my_ip または key_name が違う</td>
<td><code>cdk.json</code> の <code>my_ip</code> と <code>key_name</code> を確認</td>
</tr>
<tr>
<td>RDS に接続できない</td>
<td>SG 設定の問題</td>
<td>EC2 SG から RDS SG への 3306 ルールを確認</td>
</tr>
<tr>
<td><code>cdk destroy</code> が途中で止まる</td>
<td>RDS の削除待ち</td>
<td>10〜15 分待つ（自動的に続行される）</td>
</tr>
<tr>
<td><code>No match for argument: mysql</code></td>
<td>AL2023 では mysql パッケージが存在しない</td>
<td><code>sudo dnf install -y mariadb105</code> を使う</td>
</tr>
</tbody>
</table>
<p>デプロイ失敗時のログ確認:</p>
<pre><code class="language-cmd">aws cloudformation describe-stack-events ^
  --stack-name CdkCustomConstructsStack ^
  --region ap-northeast-1 ^
  --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[ResourceType,ResourceStatusReason]" ^
  --output table</code></pre>
<hr>
<h2><span id="toc36">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①②</td>
<td>キーペア作成 + Python 仮想環境のセットアップ</td>
</tr>
<tr>
<td>③</td>
<td><code>cdk.json</code> に自分の環境（IP・パスワード・キー名）を設定</td>
</tr>
<tr>
<td>④</td>
<td><code>ThreeTierWebProps</code>（dataclass）と <code>ThreeTierWebConstruct</code>（Construct）のコード構造を理解</td>
</tr>
<tr>
<td>⑤</td>
<td><code>cdk synth</code> で CloudFormation テンプレートを確認</td>
</tr>
<tr>
<td>⑥</td>
<td><code>cdk deploy</code> でデプロイ（RDS 待ち 15〜20 分）</td>
</tr>
<tr>
<td>⑦</td>
<td>ALB DNS 名でアクセス → EC2 → RDS への接続テスト</td>
</tr>
<tr>
<td>⑧</td>
<td>複数環境デプロイのコード例で Construct の再利用イメージをつかむ</td>
</tr>
<tr>
<td>⑩</td>
<td><code>cdk destroy</code> で全リソースを一括削除</td>
</tr>
</tbody>
</table>
<p>カスタム Construct の最大のメリットは <strong>インフラを「部品」として設計できる</strong> 点です。今回の <code>ThreeTierWebConstruct</code> は1行で3層構成のすべてを生成します。同じ Construct を dev/prod で呼ぶだけで環境を増やせ、スタックのコードは Props の組み立てと Outputs のみの約30行に収まります。</p>
<p>コンソールで3層構成を視覚的に学びたい場合は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>、CloudFormation での自動化は <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormation版ハンズオン</a>、CDK の基本（CloudFormation との比較）は <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン（cdk-alb-ec2-rds）</a> を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">AWS CDK カスタム Construct でALB + EC2 + RDS 3層構成を再利用可能な部品に切り出す【スタック170行 → 30行】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cdk-custom-constructs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
