<?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>MySQL - CayTech Lab</title>
	<atom:link href="https://caymezon.com/tag/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>https://caymezon.com</link>
	<description></description>
	<lastBuildDate>Sat, 09 May 2026 04:16:13 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://caymezon.com/wp-content/uploads/2026/01/cropped-CayTechLab-32x32.jpg</url>
	<title>MySQL - 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>EC2 + CodePipeline（Code 3兄弟）で WebアプリをCI/CDデプロイする【完全 AWS CI/CD 体験】</title>
		<link>https://caymezon.com/aws-handson-ec2-codepipeline/</link>
					<comments>https://caymezon.com/aws-handson-ec2-codepipeline/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 09 May 2026 04:16:13 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[appspec]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[buildspec]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[CodeArtifact]]></category>
		<category><![CDATA[CodeBuild]]></category>
		<category><![CDATA[CodeCommit]]></category>
		<category><![CDATA[CodeDeploy]]></category>
		<category><![CDATA[CodePipeline]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[SSM Parameter Store]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20417</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説① 全体構成を理解するCodeArtifact とはbuildspec.yml の処理フロー② CloudFormation スタックを作成する③ Outputs を確認する④ git-rem [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-ec2-codepipeline/">EC2 + CodePipeline（Code 3兄弟）で WebアプリをCI/CDデプロイする【完全 AWS CI/CD 体験】</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">① 全体構成を理解する</a><ol><li><a href="#toc4" tabindex="0">CodeArtifact とは</a></li><li><a href="#toc5" tabindex="0">buildspec.yml の処理フロー</a></li></ol></li><li><a href="#toc6" tabindex="0">② CloudFormation スタックを作成する</a></li><li><a href="#toc7" tabindex="0">③ Outputs を確認する</a></li><li><a href="#toc8" tabindex="0">④ git-remote-codecommit をインストールする</a></li><li><a href="#toc9" tabindex="0">⑤ ソースコードを CodeCommit に push する</a><ol><li><a href="#toc10" tabindex="0">⑤-1 CodeCommit リポジトリを clone する</a></li><li><a href="#toc11" tabindex="0">⑤-2 ソースコードをコピーして push する</a></li></ol></li><li><a href="#toc12" tabindex="0">⑥ CodePipeline の動作を確認する</a></li><li><a href="#toc13" tabindex="0">⑦ CodeBuild のログを確認する</a></li><li><a href="#toc14" tabindex="0">⑧ CodeDeploy の状況を確認する</a></li><li><a href="#toc15" tabindex="0">⑨ アプリの動作確認をする</a></li><li><a href="#toc16" tabindex="0">⑩ CI/CD サイクルを体験する</a></li><li><a href="#toc17" tabindex="0">⑪ CodeArtifact のパッケージを確認する</a></li><li><a href="#toc18" tabindex="0">⑫ SSM Session Manager で EC2 に接続する（オプション）</a></li><li><a href="#toc19" tabindex="0">⑬ リソースを削除する</a></li><li><a href="#toc20" tabindex="0">まとめ：学んだ概念</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「GitHub Actions は GitHub に依存してしまう。AWS だけで CI/CD を完結させたい」——そんなニーズに応えるのが <strong>AWS Code 4 兄弟（CodeCommit・CodeBuild・CodeDeploy・CodePipeline）</strong> です。さらに共通ライブラリは <strong>AWS CodeArtifact</strong> で管理するフルマネージドな AWS CI/CD パイプラインを構築します。</p>
<p>前のハンズオン（<a href="https://caymezon.com/aws-handson-ec2-github-actions/">EC2 + GitHub Actions</a>）との最大の違いは、<strong>すべてが AWS の中で完結すること</strong>です。ソース管理・ビルド・ライブラリ管理・デプロイ、すべてを AWS サービスで担います。</p>
<pre><code class="language-plaintext">[CodeCommit リポジトリ] ← git push
         ↓
[CodePipeline: my-cp-pipeline]
  Stage 1: Source  — CodeCommit からソースを取得
  Stage 2: Build   — CodeBuild で mvn package
                        ├─ CodeArtifact から認証トークン取得
                        ├─ common-lib を CodeArtifact に publish
                        ├─ CodeArtifact から common-lib を取得
                        └─ webapp.war を生成
  Stage 3: Deploy  — CodeDeploy で EC2 に配備
                        ├─ Tomcat を停止
                        ├─ WAR を配置
                        └─ Tomcat を再起動
         ↓
[EC2: Tomcat + Spring Boot]  my-cpapp
         ↓
[ALB: my-cp-alb] ← インターネット
         ↓
[RDS MySQL: my-cp-rds]</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>CodeCommit + CodeBuild + CodeDeploy + CodePipeline による完全 AWS CI/CD</li>
<li>CodeArtifact による Maven ライブラリの社内管理（Maven Central のプロキシも体験）</li>
<li><code>buildspec.yml</code> でビルド手順をコード化する</li>
<li><code>git-remote-codecommit</code> で AWS CLI 認証のまま CodeCommit に push する</li>
<li>CloudFormation で全リソース（パイプラインを含む）を一括構築</li>
</ul>
<hr>
<blockquote>
<p>コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/ec2-codepipeline">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![CodePipelineの3ステージ全成功とブラウザ確認画面](images/codepipeline-success.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>CodeCommit</strong></td>
<td>Git リポジトリを AWS 内でフルマネージドに管理するサービス。GitHub の AWS 版</td>
</tr>
<tr>
<td><strong>CodeBuild</strong></td>
<td><code>buildspec.yml</code> でビルド手順をコード化し、マネージド環境でビルドを実行するサービス</td>
</tr>
<tr>
<td><strong>CodeDeploy</strong></td>
<td>EC2 への WAR 配置・サービス再起動・ヘルスチェックを自動化するサービス</td>
</tr>
<tr>
<td><strong>CodePipeline</strong></td>
<td>Source → Build → Deploy のステージを自動で連結する CI/CD オーケストレーター</td>
</tr>
<tr>
<td><strong>CodeArtifact</strong></td>
<td>Maven/npm/PyPI ライブラリを AWS 内で管理・共有するマネージドリポジトリ。Maven Central のプロキシとしても機能</td>
</tr>
<tr>
<td><strong>buildspec.yml</strong></td>
<td>CodeBuild が実行するビルドステップをコードで定義するファイル</td>
</tr>
<tr>
<td><strong>appspec.yml</strong></td>
<td>CodeDeploy がデプロイ時に実行するファイル配置とフックスクリプトを定義するファイル</td>
</tr>
<tr>
<td><strong>git-remote-codecommit</strong></td>
<td>AWS CLI の認証情報を使って CodeCommit に push できるツール（Git 専用認証情報の設定不要）</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DB パスワードなどの設定値を暗号化して管理し、EC2 から安全に取得できる AWS のサービス</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">① 全体構成を理解する</span></h2>
<h3><span id="toc4">CodeArtifact とは</span></h3>
<p>CodeArtifact は AWS が提供するマネージドな Maven リポジトリです。GitHub Packages との大きな違いの一つが <strong>Maven Central のプロキシ機能</strong>です。</p>
<pre><code class="language-plaintext">mvn package
  └─ CodeArtifact (my-cp-libs)
       ├── 社内ライブラリ: com.example:common-utils:1.0.0  ← CodeBuild が publish
       └── 外部ライブラリ: org.springframework.boot:*      ← Maven Central から透過的に取得</code></pre>
<p>外部依存関係も CodeArtifact 経由でキャッシュされるため、<strong>インターネット接続なしでも再現性の高いビルドが可能</strong>になります。</p>
<h3><span id="toc5">buildspec.yml の処理フロー</span></h3>
<p><code>buildspec.yml</code> は CodeBuild が実行するビルド手順を定義します：</p>
<pre><code class="language-yaml">phases:
  install:
    runtime-versions:
      java: corretto17    # Amazon Corretto 17 を使用

  pre_build:
    commands:
      # settings.xml に書き込んで Maven 認証を設定
      - CODEARTIFACT_TOKEN=$(aws codeartifact get-authorization-token ...)
      - CODEARTIFACT_URL=$(aws codeartifact get-repository-endpoint ...)

  build:
    commands:
      # common-lib を CodeArtifact に publish（すでに存在する場合はスキップ）
      - mvn deploy -f common-lib/pom.xml -Dcodeartifact.url="$CODEARTIFACT_URL"
      # app の WAR をビルド（CodeArtifact から common-lib を取得）
      - mvn package -f app/pom.xml -DskipTests -Dcodeartifact.url="$CODEARTIFACT_URL"
      - cp app/target/webapp.war webapp.war

artifacts:
  files:
    - webapp.war
    - appspec.yml
    - scripts/**/*   # CodeDeploy のフックスクリプト</code></pre>
<p><strong>ポイント</strong>：pom.xml の CodeArtifact URL はプレースホルダー（<code>${codeartifact.url}</code>）で、buildspec.yml が <code>-Dcodeartifact.url=</code> で実行時に渡します。pom.xml を事前に編集する必要はありません。</p>
<hr>
<h2><span id="toc6">② CloudFormation スタックを作成する</span></h2>
<p>自分の IP アドレスを確認し（<code>curl https://checkip.amazonaws.com</code>）、CloudFormation でインフラを一括作成します：</p>
<pre><code class="language-cmd">aws cloudformation deploy ^
  --template-file ec2-codepipeline/infra/template.yaml ^
  --stack-name my-cpapp-stack ^
  --parameter-overrides EmployeeId=my DBPassword=Handson1234! MyIP=123.456.78.901/32 ^
  --capabilities CAPABILITY_NAMED_IAM ^
  --region ap-northeast-1</code></pre>
<blockquote>
<p><strong>所要時間：約 10〜15 分</strong>（VPC・EC2・RDS の作成に時間がかかります）</p>
</blockquote>
<p>CloudFormation スタックが作成するリソース：</p>
<table>
<thead>
<tr>
<th>リソース</th>
<th>名前</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC / サブネット / IGW</td>
<td><code>my-cp-vpc</code></td>
</tr>
<tr>
<td>EC2（Amazon Linux 2023 + Tomcat 10.1）</td>
<td><code>my-cpapp</code></td>
</tr>
<tr>
<td>ALB</td>
<td><code>my-cp-alb</code></td>
</tr>
<tr>
<td>RDS MySQL 8.0</td>
<td><code>my-cp-rds</code></td>
</tr>
<tr>
<td>S3（パイプラインアーティファクト）</td>
<td><code>my-cp-artifacts-{アカウントID}</code></td>
</tr>
<tr>
<td>CodeCommit リポジトリ</td>
<td><code>my-cpapp</code></td>
</tr>
<tr>
<td>CodeArtifact ドメイン / リポジトリ</td>
<td><code>my-domain</code> / <code>my-libs</code></td>
</tr>
<tr>
<td>CodeBuild プロジェクト</td>
<td><code>my-cpapp-build</code></td>
</tr>
<tr>
<td>CodePipeline</td>
<td><code>my-cp-pipeline</code></td>
</tr>
<tr>
<td>CodeDeploy Application + Deployment Group</td>
<td><code>my-cpapp</code></td>
</tr>
<tr>
<td>SSM Parameter Store</td>
<td><code>/my/cpapp/db-host</code>, <code>/my/cpapp/db-password</code></td>
</tr>
<tr>
<td>IAM ロール（EC2・CodeBuild・CodeDeploy・CodePipeline 用）</td>
<td>—</td>
</tr>
</tbody>
</table>
<p><!-- ![CloudFormationスタック作成完了とリソース一覧](images/cloudformation-stack-complete.jpg) --></p>
<hr>
<h2><span id="toc7">③ Outputs を確認する</span></h2>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-cpapp-stack ^
  --query "Stacks[0].Outputs" ^
  --output table ^
  --region ap-northeast-1</code></pre>
<p><code>CodeCommitCloneUrlHttp</code> と <code>ALBEndpoint</code> の値を控えておきます。</p>
<hr>
<h2><span id="toc8">④ git-remote-codecommit をインストールする</span></h2>
<p>CodeCommit への push には <strong>git-remote-codecommit</strong> を使います。AWS CLI の認証情報をそのまま使えるため、Git 専用の認証情報を別途作成する必要がありません：</p>
<pre><code class="language-cmd">pip install git-remote-codecommit</code></pre>
<p>インストール後、<code>codecommit::</code> プレフィックスの URL で clone・push が可能になります。</p>
<hr>
<h2><span id="toc9">⑤ ソースコードを CodeCommit に push する</span></h2>
<h3><span id="toc10">⑤-1 CodeCommit リポジトリを clone する</span></h3>
<pre><code class="language-cmd">git clone codecommit::ap-northeast-1://my-cpapp %TEMP%\my-cpapp</code></pre>
<p><code>codecommit::</code> プレフィックスを使うことで、AWS CLI の認証情報が自動的に使われます（認証プロンプトは出ません）。</p>
<h3><span id="toc11">⑤-2 ソースコードをコピーして push する</span></h3>
<pre><code class="language-cmd">xcopy ec2-codepipeline\* %TEMP%\my-cpapp\ /E /I /H /Y
cd %TEMP%\my-cpapp

git add .
git commit -m "initial commit"
git branch -M main
git push origin main</code></pre>
<p>push が完了すると <strong>CodePipeline が自動的に起動</strong>します。</p>
<hr>
<h2><span id="toc12">⑥ CodePipeline の動作を確認する</span></h2>
<p>AWS コンソール → <strong>CodePipeline</strong> → <code>my-cp-pipeline</code></p>
<p>3 つのステージが順番に実行されます：</p>
<table>
<thead>
<tr>
<th>ステージ</th>
<th>処理内容</th>
<th>目安時間</th>
</tr>
</thead>
<tbody>
<tr>
<td>Source</td>
<td>CodeCommit からソース取得</td>
<td>数秒</td>
</tr>
<tr>
<td>Build</td>
<td>CodeBuild が <code>buildspec.yml</code> を実行（common-lib publish → WAR ビルド）</td>
<td>5〜8 分</td>
</tr>
<tr>
<td>Deploy</td>
<td>CodeDeploy が EC2 に配備（stop → install → start → validate）</td>
<td>2〜3 分</td>
</tr>
</tbody>
</table>
<p><strong>合計：約 8〜10 分</strong></p>
<p><!-- ![CodePipeline 3ステージ実行中の画面](images/codepipeline-stages.jpg) --></p>
<hr>
<h2><span id="toc13">⑦ CodeBuild のログを確認する</span></h2>
<p>AWS コンソール → <strong>CodeBuild</strong> → <code>my-cpapp-build</code> → 「ビルド履歴」タブ</p>
<p>ビルド実行をクリックするとインラインログが確認できます。以下が表示されれば正常です：</p>
<pre><code class="language-plaintext">=== CodeArtifact 認証トークン取得 ===
CodeArtifact URL = https://my-domain-XXXX.d.codeartifact.ap-northeast-1.amazonaws.com/maven/my-libs/
Maven settings.xml 生成完了
=== common-lib を CodeArtifact に publish ===
[INFO] BUILD SUCCESS
=== webapp WAR をビルド ===
[INFO] BUILD SUCCESS
=== デプロイパッケージを準備 ===
ビルド完了</code></pre>
<p>CodeArtifact の認証トークンは 12 時間有効な一時トークンです。有効期限が切れても次のビルド時に自動取得されるため、管理の手間がありません。</p>
<hr>
<h2><span id="toc14">⑧ CodeDeploy の状況を確認する</span></h2>
<p>AWS コンソール → <strong>CodeDeploy</strong> → アプリケーション → <code>my-cpapp</code> → デプロイグループ → 最新のデプロイメント</p>
<p>各フックスクリプト（stop_server → install → start_server → validate_service）の実行結果が確認できます。デプロイが失敗した場合はここでエラー詳細を確認します。</p>
<hr>
<h2><span id="toc15">⑨ アプリの動作確認をする</span></h2>
<p>パイプラインが成功したら ALB 経由でアクセスします：</p>
<pre><code class="language-cmd">curl http://&lt;ALBEndpoint の値&gt;/api/health
rem → {"status":"ok","message":null,"data":"OK"}

curl http://&lt;ALBEndpoint の値&gt;/api/items
rem → {"status":"ok","message":null,"data":[]}</code></pre>
<p>ブラウザで <code>ALBEndpoint</code> にアクセスするとアイテム管理 UI が表示されます。</p>
<p><!-- ![アプリ初期画面のブラウザ表示](images/app-initial-screen.jpg) --></p>
<hr>
<h2><span id="toc16">⑩ CI/CD サイクルを体験する</span></h2>
<p>CodeCommit にコードを push するだけでパイプラインが自動実行される CI/CD を体験します：</p>
<pre><code class="language-cmd">cd %TEMP%\my-cpapp</code></pre>
<p><code>app\src\main\resources\templates\index.html</code> を開いてタイトルを変更します：</p>
<pre><code class="language-html">&lt;!-- 変更後 --&gt;
&lt;h1&gt;Spring Boot + RDS Item Manager (EC2 v2) &lt;span class="badge"&gt;CodePipeline&lt;/span&gt;&lt;/h1&gt;</code></pre>
<pre><code class="language-cmd">git add app\src\main\resources\templates\index.html
git commit -m "feat: update title to EC2 v2"
git push origin main</code></pre>
<p>CodePipeline コンソール（<code>my-cp-pipeline</code>）で全ステージが成功になるまで待ちます（約 8〜10 分）。ALB にアクセスしてタイトルが変わっていることを確認します。</p>
<hr>
<h2><span id="toc17">⑪ CodeArtifact のパッケージを確認する</span></h2>
<p>AWS コンソール → <strong>CodeArtifact</strong> → ドメイン <code>my-domain</code> → リポジトリ <code>my-libs</code> → パッケージ一覧</p>
<p><code>com.example:common-utils:1.0.0</code> が登録されていることを確認します。CLI でも確認できます：</p>
<pre><code class="language-cmd">aws codeartifact list-packages ^
  --domain my-domain ^
  --domain-owner 123456789012 ^
  --repository my-libs ^
  --region ap-northeast-1</code></pre>
<p>また、Maven Central のプロキシとして機能しているため、<code>org.springframework.boot</code> なども一覧に表示されます。これらは外部から初回取得時にキャッシュされたものです。</p>
<hr>
<h2><span id="toc18">⑫ SSM Session Manager で EC2 に接続する（オプション）</span></h2>
<p>キーペアなしで EC2 内部を確認できます：</p>
<pre><code class="language-cmd">aws ssm start-session ^
  --target i-xxxxxxxxxxxxxxxxx ^
  --region ap-northeast-1</code></pre>
<pre><code class="language-bash"># Tomcat のステータス確認
sudo systemctl status tomcat

# CodeDeploy エージェントのログ確認
sudo cat /var/log/aws/codedeploy-agent/codedeploy-agent.log

# SSM から取得した DB 接続情報の確認
sudo cat /opt/tomcat/current/bin/setenv.sh

# Tomcat ログの確認
sudo journalctl -u tomcat -n 50</code></pre>
<hr>
<h2><span id="toc19">⑬ リソースを削除する</span></h2>
<p>CodeArtifact にパッケージが存在するとドメインが削除できないため、先にパッケージを手動削除します。</p>
<p><strong>① CodeArtifact のパッケージを削除する</strong></p>
<pre><code class="language-cmd">aws codeartifact delete-package-versions ^
  --domain my-domain ^
  --domain-owner 123456789012 ^
  --repository my-libs ^
  --format maven ^
  --namespace com.example ^
  --package common-utils ^
  --versions 1.0.0 ^
  --region ap-northeast-1

aws codeartifact delete-package ^
  --domain my-domain ^
  --domain-owner 123456789012 ^
  --repository my-libs ^
  --format maven ^
  --namespace com.example ^
  --package common-utils ^
  --region ap-northeast-1</code></pre>
<p><strong>② S3 バケットを空にする</strong></p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-cpapp-stack ^
  --query "Stacks[0].Outputs[?OutputKey=='ArtifactBucketName'].OutputValue" ^
  --output text ^
  --region ap-northeast-1

aws s3 rm s3://&lt;ArtifactBucketName の値&gt; --recursive</code></pre>
<p><strong>③ CloudFormation スタックを削除する</strong></p>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-cpapp-stack ^
  --region ap-northeast-1

aws cloudformation wait stack-delete-complete ^
  --stack-name my-cpapp-stack ^
  --region ap-northeast-1

echo 削除完了</code></pre>
<p><strong>④ ローカルの clone ディレクトリを削除する</strong></p>
<pre><code class="language-cmd">rmdir /S /Q %TEMP%\my-cpapp</code></pre>
<hr>
<h2><span id="toc20">まとめ：学んだ概念</span></h2>
<table>
<thead>
<tr>
<th>概念</th>
<th>ポイント</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CodeCommit</strong></td>
<td>Git リポジトリを AWS 内でフルマネージドに管理。<code>git-remote-codecommit</code> で AWS CLI 認証のまま push できる</td>
</tr>
<tr>
<td><strong>CodeBuild</strong></td>
<td><code>buildspec.yml</code> でビルド手順をコード化。CodeArtifact の認証トークン取得→ publish → WAR ビルドまで自動化</td>
</tr>
<tr>
<td><strong>CodeDeploy</strong></td>
<td>EC2 への WAR 配置・サービス再起動・ヘルスチェックを自動化。<code>appspec.yml</code> でフックスクリプトを定義</td>
</tr>
<tr>
<td><strong>CodePipeline</strong></td>
<td>Source → Build → Deploy のステージを自動で連結する CI/CD オーケストレーター</td>
</tr>
<tr>
<td><strong>CodeArtifact</strong></td>
<td>Maven ライブラリを AWS 内で管理。Maven Central のプロキシとしても機能し、外部依存関係もキャッシュ</td>
</tr>
<tr>
<td><strong>buildspec.yml</strong></td>
<td>CodeBuild が実行するビルドステップをコードで定義するファイル</td>
</tr>
<tr>
<td><strong>git-remote-codecommit</strong></td>
<td>AWS CLI 認証情報を使って CodeCommit に push できるツール（Git 専用認証情報不要）</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DB パスワードをインスタンスに安全に渡す仕組み</td>
</tr>
</tbody>
</table>
<p>GitHub Actions を使った<a href="https://caymezon.com/aws-handson-ec2-github-actions/">前のハンズオン</a>と比較すると、CodePipeline は <strong>すべてが AWS 内で完結</strong>する一方、GitHub Actions は <strong>Git リポジトリと AWS の柔軟な組み合わせ</strong>が強みです。どちらを使うかはチームの方針や既存インフラに合わせて選択しましょう。</p>
<p>次のステップとして、<a href="https://caymezon.com/aws-handson-cdk-ecs-webapp/">CDK + ECS Fargate ハンズオン</a>で EC2 からコンテナ化への移行も体験してみましょう。</p><p>The post <a href="https://caymezon.com/aws-handson-ec2-codepipeline/">EC2 + CodePipeline（Code 3兄弟）で WebアプリをCI/CDデプロイする【完全 AWS CI/CD 体験】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-ec2-codepipeline/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>EC2 + GitHub Actions で WebアプリをCI/CDデプロイする【GitHub Packages + CodeDeploy 体験】</title>
		<link>https://caymezon.com/aws-handson-ec2-github-actions/</link>
					<comments>https://caymezon.com/aws-handson-ec2-github-actions/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 09 May 2026 04:16:05 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[CodeDeploy]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[GitHub Actions]]></category>
		<category><![CDATA[GitHub Packages]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[OIDC]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[SSM Parameter Store]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20419</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説① 全体構成を理解する② OIDC 認証と IAM ロールを設定する②-1 OIDC プロバイダーを登録する（初回のみ）②-2 IAM ロールを作成する③ GitHub Variables を [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-ec2-github-actions/">EC2 + GitHub Actions で WebアプリをCI/CDデプロイする【GitHub Packages + CodeDeploy 体験】</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-4" checked><label class="toc-title" for="toc-checkbox-4">目次</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">① 全体構成を理解する</a></li><li><a href="#toc4" tabindex="0">② OIDC 認証と IAM ロールを設定する</a><ol><li><a href="#toc5" tabindex="0">②-1 OIDC プロバイダーを登録する（初回のみ）</a></li><li><a href="#toc6" tabindex="0">②-2 IAM ロールを作成する</a></li></ol></li><li><a href="#toc7" tabindex="0">③ GitHub Variables を設定する</a></li><li><a href="#toc8" tabindex="0">④ CloudFormation スタックを作成する</a></li><li><a href="#toc9" tabindex="0">⑤ スタックの Outputs を確認する</a></li><li><a href="#toc10" tabindex="0">⑥ common-lib を GitHub Packages に publish する</a></li><li><a href="#toc11" tabindex="0">⑦ アプリを初回デプロイする</a></li><li><a href="#toc12" tabindex="0">⑧ GitHub Actions の進行を確認する</a></li><li><a href="#toc13" tabindex="0">⑨ ブラウザで動作確認する</a></li><li><a href="#toc14" tabindex="0">⑩ CI/CD サイクルを体験する</a></li><li><a href="#toc15" tabindex="0">⑪ SSM Session Manager で EC2 に接続する（オプション）</a></li><li><a href="#toc16" tabindex="0">⑫ リソースを削除する</a></li><li><a href="#toc17" tabindex="0">まとめ：学んだ概念</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「インフラは AWS でいいけど、デプロイは GitHub Actions でやりたい」——クラウドと Git を組み合わせたハイブリッドな CI/CD 構成は、多くの現場で採用されています。このハンズオンでは、<strong>EC2（Tomcat）への自動デプロイを GitHub Actions + AWS CodeDeploy で実現</strong>します。</p>
<p>さらに実務でよく見かける「<strong>社内共通ライブラリを GitHub Packages で管理・共有する</strong>」仕組みも体験できます。Spring Boot WAR をビルドする際に共通ライブラリを GitHub Packages からフェッチし、CodeDeploy で EC2 にデプロイするまでの一連のフローです。</p>
<pre><code class="language-plaintext">[GitHub リポジトリ]
  ├── common-lib/ → push → GitHub Actions → GitHub Packages に publish
  └── app/        → push → GitHub Actions
                               ├── GitHub Packages から common-utils を取得
                               ├── mvn package → webapp.war
                               ├── zip → S3 にアップロード
                               └── CodeDeploy を呼び出す
                                        ↓
                          [EC2: Tomcat + Spring Boot WAR]  my-ec2app
                                        ↓
                          [ALB: my-ec2app-alb] ← インターネット
                                        ↓
                          [RDS MySQL: my-ec2app-rds]</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>GitHub Packages を使った共通ライブラリの管理・共有</li>
<li>GitHub Actions（OIDC 認証）で AWS にキーレスアクセス</li>
<li>CodeDeploy + appspec.yml を使った EC2 へのデプロイ自動化</li>
<li>SSM Parameter Store でデータベースパスワードを安全に管理</li>
<li>CloudFormation によるインフラ一括構築（VPC / EC2 / ALB / RDS / CodeDeploy）</li>
</ul>
<hr>
<blockquote>
<p>コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/ec2-github-actions">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![GitHub ActionsのCI/CDパイプライン完了とブラウザ確認画面](images/github-actions-deploy-complete.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>GitHub Packages</strong></td>
<td>GitHub リポジトリ内で Maven/npm などのパッケージを管理・共有するレジストリ。社内共通ライブラリの配布に使える</td>
</tr>
<tr>
<td><strong>GitHub Actions OIDC</strong></td>
<td>アクセスキーをリポジトリに保存せず、JWT トークンで AWS を操作するキーレス認証方式</td>
</tr>
<tr>
<td><strong>CodeDeploy</strong></td>
<td>EC2 への WAR 配置・サービス停止/起動・ヘルスチェックを自動化する AWS のデプロイサービス</td>
</tr>
<tr>
<td><strong>appspec.yml</strong></td>
<td>CodeDeploy がデプロイ時に実行するファイル配置先とフックスクリプト（BeforeInstall / AfterInstall / ValidateService）を定義するファイル</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DB パスワードなどの設定値を暗号化して管理し、EC2 から安全に取得できる AWS のサービス</td>
</tr>
<tr>
<td><strong>CloudFormation</strong></td>
<td>AWS リソースをコード（YAML/JSON）で定義・一括管理する IaC サービス</td>
</tr>
<tr>
<td><strong>ALB（Application Load Balancer）</strong></td>
<td>HTTP/HTTPS をリスンして EC2 にリクエストを振り分けるロードバランサー</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">① 全体構成を理解する</span></h2>
<p>このハンズオンの主役は 2 つのワークフローです。</p>
<p><strong>①-1 common-lib のパブリッシュ（<code>ec2-publish-lib.yml</code>）</strong></p>
<p><code>common-lib/</code> が変更されると起動し、Maven ライブラリを GitHub Packages に publish します。<code>actions/setup-java</code> が <code>settings.xml</code> を自動生成するため、認証設定は不要です。</p>
<p><strong>①-2 アプリのデプロイ（<code>ec2-app-deploy.yml</code>）</strong></p>
<p><code>app/</code> が変更されると起動します。処理の流れは次の通りです：</p>
<pre><code class="language-plaintext">① OIDC で AWS に認証
② GitHub Packages から common-utils を取得しながら mvn package で WAR をビルド
③ zip 化して S3 にアップロード（webapp.war + appspec.yml + scripts/）
④ CodeDeploy でローリングデプロイ（〜1〜2 分）</code></pre>
<p><strong>デプロイパッケージの構造：</strong></p>
<pre><code class="language-plaintext">deploy.zip
├── webapp.war         ← Tomcat の webapps/ に配置
├── appspec.yml        ← CodeDeploy の設定
└── scripts/
    ├── stop_server.sh      ← Tomcat を停止・古い WAR を削除
    ├── start_server.sh     ← SSM から DB 情報を取得・Tomcat 起動
    └── validate_service.sh ← /api/health を最大 60 秒リトライ確認</code></pre>
<hr>
<h2><span id="toc4">② OIDC 認証と IAM ロールを設定する</span></h2>
<p>GitHub Actions が AWS に安全にアクセスするための <strong>OIDC（OpenID Connect）認証</strong> を設定します。アクセスキーをリポジトリに保存せずに AWS を操作できる現代的な方式です。</p>
<h3><span id="toc5">②-1 OIDC プロバイダーを登録する（初回のみ）</span></h3>
<p>AWS コンソール → <strong>IAM</strong> → 「IDプロバイダー」→「プロバイダーを追加」</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>プロバイダータイプ</td>
<td>OpenID Connect</td>
</tr>
<tr>
<td>プロバイダー URL</td>
<td><code>https://token.actions.githubusercontent.com</code></td>
</tr>
<tr>
<td>対象者（Audience）</td>
<td><code>sts.amazonaws.com</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>ECS ハンズオン（cdk-ecs-webapp）完了済みの場合</strong>はスキップ可。</p>
</blockquote>
<h3><span id="toc6">②-2 IAM ロールを作成する</span></h3>
<p>AWS CloudShell で以下を実行します（<code>GITHUB_OWNER</code> と <code>REPO</code> は自分のリポジトリに合わせて変更）：</p>
<pre><code class="language-bash">ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
GITHUB_OWNER="your-github-username"   # ← 自分の GitHub ユーザー名
REPO="your-github-repo"               # ← 自分の GitHub リポジトリ名
EMPLOYEE_ID="my"                      # ← 好きなプレフィックス（例: 123456）
ROLE_NAME="${EMPLOYEE_ID}-github-actions-role"

cat &gt; /tmp/trust-policy.json &lt;&lt; EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
      },
      "StringLike": {
        "token.actions.githubusercontent.com:sub": "repo:${GITHUB_OWNER}/${REPO}:ref:refs/heads/main"
      }
    }
  }]
}
EOF

aws iam create-role \
  --role-name $ROLE_NAME \
  --assume-role-policy-document file:///tmp/trust-policy.json

# 権限ポリシーを付与（S3 + CodeDeploy）
cat &gt; /tmp/permissions.json &lt;&lt; EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::${EMPLOYEE_ID}-ec2app-deploy-*/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "codedeploy:CreateDeployment",
        "codedeploy:GetDeployment",
        "codedeploy:GetDeploymentConfig",
        "codedeploy:RegisterApplicationRevision"
      ],
      "Resource": "*"
    }
  ]
}
EOF

aws iam put-role-policy \
  --role-name $ROLE_NAME \
  --policy-name "${ROLE_NAME}-policy" \
  --policy-document file:///tmp/permissions.json

# ロール ARN を表示
aws iam get-role --role-name $ROLE_NAME --query Role.Arn --output text</code></pre>
<p>出力された <strong>ロール ARN</strong>（<code>arn:aws:iam::...</code>）をコピーしておきます。</p>
<hr>
<h2><span id="toc7">③ GitHub Variables を設定する</span></h2>
<p>GitHub リポジトリ → <strong>Settings</strong> → <strong>Secrets and variables</strong> → <strong>Actions</strong> → <strong>「Variables」タブ</strong></p>
<p>「New repository variable」で以下を追加します：</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>例</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>EMPLOYEE_ID</code></td>
<td><code>my</code></td>
<td>リソース名プレフィックス</td>
</tr>
<tr>
<td><code>AWS_ROLE_ARN</code></td>
<td><code>arn:aws:iam::...</code></td>
<td>②-2 で表示されたロール ARN</td>
</tr>
<tr>
<td><code>AWS_ACCOUNT_ID</code></td>
<td><code>123456789012</code></td>
<td>AWS アカウント ID（S3 バケット名に使用）</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Variables vs Secrets</strong>：ARN や ID は機密情報ではないため Variables に保存します（パスワード類は Secrets を使う）。</p>
</blockquote>
<hr>
<h2><span id="toc8">④ CloudFormation スタックを作成する</span></h2>
<p>自分の IP アドレスを確認し（<code>curl https://checkip.amazonaws.com</code>）、CloudFormation でインフラを一括作成します：</p>
<pre><code class="language-cmd">aws cloudformation deploy ^
  --template-file ec2-github-actions/infra/template.yaml ^
  --stack-name my-ec2app ^
  --parameter-overrides EmployeeId=my DBPassword=Handson1234! MyIP=123.456.78.901/32 ^
  --capabilities CAPABILITY_NAMED_IAM ^
  --region ap-northeast-1</code></pre>
<blockquote>
<p><strong>所要時間：約 10〜15 分</strong>（RDS の作成に時間がかかります）</p>
</blockquote>
<p>CloudFormation スタックが作成するリソース：</p>
<table>
<thead>
<tr>
<th>リソース</th>
<th>名前</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC / サブネット / IGW</td>
<td><code>my-ec2app-vpc</code></td>
</tr>
<tr>
<td>EC2（Amazon Linux 2023 + Tomcat 10.1）</td>
<td><code>my-ec2app</code></td>
</tr>
<tr>
<td>ALB</td>
<td><code>my-ec2app-alb</code></td>
</tr>
<tr>
<td>RDS MySQL 8.0</td>
<td><code>my-ec2app-rds</code></td>
</tr>
<tr>
<td>S3（デプロイアーティファクト保管）</td>
<td><code>my-ec2app-deploy-{アカウントID}</code></td>
</tr>
<tr>
<td>CodeDeploy Application + Deployment Group</td>
<td><code>my-ec2app</code></td>
</tr>
<tr>
<td>SSM Parameter Store（DB 接続情報）</td>
<td><code>/my/ec2app/db-host</code>, <code>/my/ec2app/db-password</code></td>
</tr>
</tbody>
</table>
<p><!-- ![CloudFormationスタック作成完了の画面](images/cloudformation-stack-complete.jpg) --></p>
<hr>
<h2><span id="toc9">⑤ スタックの Outputs を確認する</span></h2>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-ec2app ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs" ^
  --output table</code></pre>
<table>
<thead>
<tr>
<th>キー</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ALBEndpoint</code></td>
<td>アプリ URL（後で使用）</td>
</tr>
<tr>
<td><code>DeploymentBucketName</code></td>
<td>S3 バケット名</td>
</tr>
<tr>
<td><code>CodeDeployApplicationName</code></td>
<td>CodeDeploy アプリ名</td>
</tr>
<tr>
<td><code>EC2InstanceId</code></td>
<td>EC2 インスタンス ID（SSM 接続に使用）</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc10">⑥ common-lib を GitHub Packages に publish する</span></h2>
<p><code>common-lib/pom.xml</code> と <code>app/pom.xml</code> にある GitHub ユーザー名・リポジトリ名のプレースホルダーを自分のものに変更してから push します：</p>
<pre><code class="language-xml">&lt;!-- common-lib/pom.xml と app/pom.xml の両方を変更 --&gt;
&lt;url&gt;https://maven.pkg.github.com/your-github-username/your-github-repo&lt;/url&gt;</code></pre>
<p>変更を push すると <strong><code>Publish common-utils to GitHub Packages</code></strong> ワークフローが自動起動します：</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add ec2-github-actions/common-lib/ ec2-github-actions/app/pom.xml
git commit -m "feat: configure GitHub Packages URL for ec2-github-actions"
git push</code></pre>
<p>GitHub リポジトリ → <strong>Actions タブ</strong> → <code>Publish common-utils to GitHub Packages</code> の実行を確認します。完了後、<strong>Packages タブ</strong>に <code>common-utils 1.0.0</code> が表示されます。</p>
<blockquote>
<p><strong><code>ResponseWrapper</code> クラスとは</strong>：このハンズオンの共通ライブラリは API レスポンスを <code>{&quot;status&quot;:&quot;ok&quot;,&quot;data&quot;:{...}}</code> の統一形式にラップするユーティリティです。チーム開発でレスポンス形式を統一する際によく使われるパターンです。</p>
</blockquote>
<hr>
<h2><span id="toc11">⑦ アプリを初回デプロイする</span></h2>
<p><code>app/</code> の変更を push して <strong><code>Deploy App to EC2</code></strong> ワークフローを起動します：</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add ec2-github-actions/app/
git commit -m "feat: initial deploy via GitHub Actions (EC2)"
git push</code></pre>
<blockquote>
<p><strong><code>nothing to commit</code> と表示された場合</strong>：<code>index.html</code> に空白行を追加して変更を作ります：</p>
<pre><code class="language-cmd">echo. &gt;&gt; ec2-github-actions\app\src\main\resources\templates\index.html
git add ec2-github-actions/app/src/main/resources/templates/index.html
git commit -m "chore: trigger initial deploy"
git push</code></pre>
</blockquote>
<hr>
<h2><span id="toc12">⑧ GitHub Actions の進行を確認する</span></h2>
<p>GitHub → <strong>Actions タブ</strong> → <code>Deploy App to EC2</code> で各ステップの実行状況を確認します：</p>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
<th>時間目安</th>
</tr>
</thead>
<tbody>
<tr>
<td>Checkout</td>
<td>コード取得</td>
<td>〜10秒</td>
</tr>
<tr>
<td>Configure AWS credentials</td>
<td>OIDC 認証（アクセスキー不要）</td>
<td>〜10秒</td>
</tr>
<tr>
<td>Set up Java</td>
<td>JDK 17 + settings.xml 生成</td>
<td>〜30秒</td>
</tr>
<tr>
<td>Build WAR</td>
<td>GitHub Packages から common-utils 取得 + <code>mvn package</code></td>
<td>〜1〜2分</td>
</tr>
<tr>
<td>Package and upload to S3</td>
<td>zip 化 → S3 へアップロード</td>
<td>〜10秒</td>
</tr>
<tr>
<td>Deploy via CodeDeploy</td>
<td>EC2 にデプロイ + 完了待ち</td>
<td>〜1〜2分</td>
</tr>
</tbody>
</table>
<p><strong>合計：約 3〜5 分</strong></p>
<p>ポイントは <strong>「Set up Java」ステップ</strong>です。<code>actions/setup-java</code> に <code>server-id: github</code> を指定するだけで、GitHub Packages の認証情報を含む <code>settings.xml</code> が自動生成されます。Maven のパスワード設定を手書きする必要はありません。</p>
<hr>
<h2><span id="toc13">⑨ ブラウザで動作確認する</span></h2>
<p><code>ALBEndpoint</code> の URL にアクセスすると「Spring Boot + RDS Item Manager (EC2)」が表示されます。</p>
<p><code>/api/health</code> にアクセスして以下が返ることも確認します：</p>
<pre><code class="language-json">{"status":"ok","message":null,"data":"OK"}</code></pre>
<p>このレスポンス形式こそが <strong><code>common-utils</code> の <code>ResponseWrapper</code></strong> が GitHub Packages 経由で取得・ビルドされた証拠です。</p>
<p><!-- ![アプリの初期画面とAPIヘルスチェック結果](images/app-initial-screen.jpg) --></p>
<hr>
<h2><span id="toc14">⑩ CI/CD サイクルを体験する</span></h2>
<p><code>index.html</code> のタイトルを変更して push すると、約 3〜5 分でブラウザに反映されます：</p>
<pre><code class="language-html">&lt;!-- 変更後 --&gt;
&lt;h1&gt;Spring Boot + RDS Item Manager (EC2 v2) &lt;span class="badge"&gt;GitHub Actions&lt;/span&gt;&lt;/h1&gt;</code></pre>
<pre><code class="language-cmd">git add ec2-github-actions/app/src/main/resources/templates/index.html
git commit -m "feat: update title to EC2 v2"
git push</code></pre>
<p>これが CI/CD の醍醐味です。<strong>コードを push するだけで自動的にデプロイが完了</strong>します。</p>
<hr>
<h2><span id="toc15">⑪ SSM Session Manager で EC2 に接続する（オプション）</span></h2>
<p>キーペアなしで EC2 内部を確認できます：</p>
<pre><code class="language-cmd">aws ssm start-session ^
  --target i-xxxxxxxxxxxxxxxxx ^
  --region ap-northeast-1</code></pre>
<pre><code class="language-bash"># Tomcat のステータス確認
sudo systemctl status tomcat

# デプロイログの確認
sudo cat /var/log/aws/codedeploy-agent/codedeploy-agent.log

# SSM から取得した DB 接続情報の確認
sudo cat /opt/tomcat/current/bin/setenv.sh

# Tomcat ログの確認
sudo journalctl -u tomcat -n 50</code></pre>
<p><code>setenv.sh</code> を確認すると、SSM Parameter Store から取得した DB ホスト名とパスワードが環境変数としてセットされていることがわかります。パスワードをコードに書かずに安全に注入できています。</p>
<hr>
<h2><span id="toc16">⑫ リソースを削除する</span></h2>
<p><strong>① S3 バケットを空にする</strong>（デプロイアーティファクトが蓄積されているため先に削除）</p>
<pre><code class="language-cmd">aws s3 rm s3://my-ec2app-deploy-123456789012 --recursive</code></pre>
<p><strong>② CloudFormation スタックを削除する</strong></p>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-ec2app ^
  --region ap-northeast-1

aws cloudformation wait stack-delete-complete ^
  --stack-name my-ec2app ^
  --region ap-northeast-1

echo スタック削除完了</code></pre>
<hr>
<h2><span id="toc17">まとめ：学んだ概念</span></h2>
<table>
<thead>
<tr>
<th>概念</th>
<th>ポイント</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>GitHub Packages</strong></td>
<td>Maven ライブラリを GitHub リポジトリ内で管理・共有する仕組み。社内共通ライブラリの配布に活用できる</td>
</tr>
<tr>
<td><strong>GitHub Actions OIDC</strong></td>
<td>アクセスキー不要で AWS に認証するキーレス方式。<code>aws-actions/configure-aws-credentials</code> で簡単に設定可</td>
</tr>
<tr>
<td><strong>CodeDeploy</strong></td>
<td>EC2 への WAR 配置・サービス再起動・ヘルスチェックを自動化するサービス</td>
</tr>
<tr>
<td><strong>appspec.yml</strong></td>
<td>CodeDeploy がデプロイ時に実行するファイル配置とフックスクリプトを定義するファイル</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DB パスワードをインスタンスに安全に渡す仕組み。<code>start_server.sh</code> から取得して <code>setenv.sh</code> に書き込む</td>
</tr>
<tr>
<td><strong>CloudFormation</strong></td>
<td>VPC・EC2・RDS・ALB・CodeDeploy を 1 つの YAML テンプレートで一括構築</td>
</tr>
</tbody>
</table>
<p>GitHub Actions と AWS を組み合わせることで、<strong>Git push だけでインフラから独立してアプリをデプロイできる</strong>柔軟な CI/CD パイプラインを構築できます。次のステップとして、<a href="https://caymezon.com/aws-handson-ec2-codepipeline/">EC2 + CodePipeline ハンズオン</a>で完全 AWS ネイティブな CI/CD も体験してみましょう。</p><p>The post <a href="https://caymezon.com/aws-handson-ec2-github-actions/">EC2 + GitHub Actions で WebアプリをCI/CDデプロイする【GitHub Packages + CodeDeploy 体験】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-ec2-github-actions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<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-6" checked><label class="toc-title" for="toc-checkbox-6">目次</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>
		<item>
		<title>AWS CDK（Python）でALB + EC2(Tomcat) + RDS 3層構成を構築する手順【CloudFormation 460行 → CDK 200行】</title>
		<link>https://caymezon.com/aws-handson-cdk-alb-ec2-rds/</link>
					<comments>https://caymezon.com/aws-handson-cdk-alb-ec2-rds/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 18 Apr 2026 08:14:06 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[3層構成]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Bootstrap]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[cdk deploy]]></category>
		<category><![CDATA[cdk destroy]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[L2 Construct]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20369</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説CloudFormation vs CDK：どれだけ違うか使用するAWSサービス前提条件構築されるリソース作業順序【重要】⓪ 自分のIPアドレスを確認する① キーペアの作成② プロジェクトのセ [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">AWS CDK（Python）でALB + EC2(Tomcat) + RDS 3層構成を構築する手順【CloudFormation 460行 → CDK 200行】</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-8" checked><label class="toc-title" for="toc-checkbox-8">目次</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">CloudFormation vs CDK：どれだけ違うか</a></li><li><a href="#toc4" tabindex="0">使用するAWSサービス</a></li><li><a href="#toc5" tabindex="0">前提条件</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">④ CDK Bootstrap（初回のみ）</a></li><li><a href="#toc13" tabindex="0">⑤ CDK Synth（テンプレート確認）</a></li><li><a href="#toc14" tabindex="0">⑥ デプロイ</a></li><li><a href="#toc15" tabindex="0">⑦ 動作確認</a><ol><li><a href="#toc16" tabindex="0">7-1. ALB のヘルスチェック確認</a></li><li><a href="#toc17" tabindex="0">7-2. ALB 経由でWebアクセス確認</a></li><li><a href="#toc18" tabindex="0">7-3. EC2 に SSH 接続する</a></li><li><a href="#toc19" tabindex="0">7-4. EC2 から RDS への接続確認</a></li><li><a href="#toc20" tabindex="0">7-5. SSH 接続を切断する</a></li><li><a href="#toc21" tabindex="0">7-6. UserData の実行ログ確認</a></li></ol></li><li><a href="#toc22" tabindex="0">⑧ CDK の差分確認（参考）</a></li><li><a href="#toc23" tabindex="0">⑨ リソース削除</a><ol><li><a href="#toc24" tabindex="0">削除されるリソース一覧</a></li><li><a href="#toc25" tabindex="0">キーペアの削除（手動）</a></li><li><a href="#toc26" tabindex="0">仮想環境の終了</a></li><li><a href="#toc27" tabindex="0">削除完了の確認</a></li><li><a href="#toc28" tabindex="0">CDK Bootstrap リソースの削除（任意）</a></li></ol></li><li><a href="#toc29" tabindex="0">CDK のポイント解説</a><ol><li><a href="#toc30" tabindex="0">L1 / L2 / L3 Construct の違い</a></li><li><a href="#toc31" tabindex="0">CDK Context 変数</a></li><li><a href="#toc32" tabindex="0">cdk synth で CloudFormation テンプレートを確認</a></li></ol></li><li><a href="#toc33" tabindex="0">トラブルシューティング</a></li><li><a href="#toc34" tabindex="0">コンソール版・CloudFormation版との比較</a></li><li><a href="#toc35" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「CloudFormation の YAML 460行をもっとシンプルに書けないか」——それを実現するのが <strong>AWS CDK（Cloud Development Kit）</strong> です。</p>
<p>この記事では、<a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormation版ハンズオン</a>で構築した <strong>ALB + EC2(Tomcat) + RDS の3層構成</strong>を、<strong>AWS CDK（Python）</strong> で再現します。YAML を Python コードで書き直すことで、<strong>460行 → 約200行</strong>に削減できることを体験します。</p>
<pre><code class="language-plaintext">インターネット
  ↓ HTTP(80)
[ALB: my-cdk-alb-alb]  ← インターネット向け / パブリックサブネット 2AZ
  ↓ HTTP(8080)  ← ALB SG → EC2 SG（SG-to-SG 制御）
[EC2: my-cdk-alb-ap-instance]  ← Tomcat / パブリックサブネット AZ-a
  ↓ MySQL(3306)  ← EC2 SG → RDS SG（SG-to-SG 制御）
[RDS: my-cdk-alb-rds-mysql]  ← MySQL 8.0 / プライベートサブネット AZ-a

[VPC: my-cdk-alb-vpc (10.0.0.0/16)]
  ├── パブリックサブネット (10.0.0.0/24) [AZ-a]  ← ALB + EC2
  ├── パブリックサブネット (10.0.1.0/24) [AZ-b]  ← ALB（2AZ 必須）
  ├── プライベートサブネット (10.0.2.0/24) [AZ-a]  ← RDS 配置
  └── プライベートサブネット (10.0.3.0/24) [AZ-b]  ← DBサブネットグループ用</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>Python コードで AWS インフラを定義する CDK の世界観を理解</li>
<li><code>ec2.Vpc</code> 1つでVPC・IGW・ルートテーブルを自動生成する L2 Construct の威力を体験</li>
<li><code>cdk synth</code> で CloudFormation テンプレートを生成・確認</li>
<li><code>cdk diff</code> でデプロイ前の差分確認（CloudFormation の変更セット相当）</li>
<li><code>cdk deploy</code> / <code>cdk destroy</code> でワンコマンドデプロイ・削除</li>
</ul>
<p><strong>このハンズオンの特徴：</strong></p>
<ul>
<li>CloudFormation 版で460行必要だった YAML が Python コード約200行で記述できる</li>
<li><code>cdk destroy</code> 1本でALB・RDS・IAMなど全リソースを自動削除</li>
<li>コードの変更差分を <code>cdk diff</code> で即確認できる（CloudFormation の変更セットより簡単）</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormation版ハンズオン</a> の発展記事です。</strong><br />同じ構成を CDK で再現し、IaC の進化を体験したい方向けです。CDK のコード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-alb-ec2-rds">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>AWS CDK</strong></td>
<td>Python・TypeScript などのプログラミング言語で AWS インフラを定義する IaC フレームワーク</td>
</tr>
<tr>
<td><strong>CDK App</strong></td>
<td><code>app.py</code> がエントリポイント。<code>cdk synth</code> でここから CloudFormation テンプレートを生成</td>
</tr>
<tr>
<td><strong>CDK Stack</strong></td>
<td>1つの CloudFormation スタックに対応する単位。今回は <code>CdkAlbEc2RdsStack</code></td>
</tr>
<tr>
<td><strong>L2 Construct</strong></td>
<td>AWS がベストプラクティスを組み込んだ高レベルな抽象クラス（<code>ec2.Vpc</code> 等）。内部で複数の AWS リソースを自動生成</td>
</tr>
<tr>
<td><strong>CDK Context</strong></td>
<td><code>cdk.json</code> に書くデプロイ設定。<code>-c key=value</code> で上書き可能</td>
</tr>
<tr>
<td><strong>CDK Bootstrap</strong></td>
<td>CDK が AWS にデプロイするために必要な事前準備（S3 バケット等を作成）。<strong>同じアカウント・リージョンで初回のみ</strong></td>
</tr>
<tr>
<td><strong>cdk synth</strong></td>
<td>Python コードから CloudFormation テンプレートを生成（デプロイなし）</td>
</tr>
<tr>
<td><strong>cdk diff</strong></td>
<td>現在のデプロイ状態と新しいコードの差分を表示</td>
</tr>
<tr>
<td><strong>cdk deploy</strong></td>
<td>CloudFormation スタックをデプロイ</td>
</tr>
<tr>
<td><strong>cdk destroy</strong></td>
<td>CloudFormation スタックとその全リソースを削除</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">CloudFormation vs CDK：どれだけ違うか</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>CloudFormation（alb-ec2-rds）</th>
<th>CDK（このハンズオン）</th>
</tr>
</thead>
<tbody>
<tr>
<td>記述言語</td>
<td>YAML（宣言型）</td>
<td>Python（プログラミング言語）</td>
</tr>
<tr>
<td>コード量</td>
<td>約460行</td>
<td><strong>約200行</strong></td>
</tr>
<tr>
<td>VPC 定義</td>
<td>15+ リソースを手動定義</td>
<td><code>ec2.Vpc</code> 1つで IGW・ルートテーブル等を自動生成</td>
</tr>
<tr>
<td>SG-to-SG 制御</td>
<td><code>SourceSecurityGroupId: !GetAtt</code> で参照</td>
<td><code>add_ingress_rule(alb_sg, ...)</code> で直感的に記述</td>
</tr>
<tr>
<td>ALB + TG + リスナー</td>
<td>3リソースを別々に定義</td>
<td><code>add_listener → add_targets</code> でメソッドチェーン</td>
</tr>
<tr>
<td>変数・ループ</td>
<td><code>!Sub</code> / <code>!If</code> でのみ表現</td>
<td>Python の変数・f-string・for文が使える</td>
</tr>
<tr>
<td>デプロイコマンド</td>
<td><code>aws cloudformation deploy ...</code></td>
<td><code>cdk deploy</code></td>
</tr>
<tr>
<td>削除コマンド</td>
<td><code>aws cloudformation delete-stack ...</code></td>
<td><code>cdk destroy</code></td>
</tr>
<tr>
<td>差分確認</td>
<td>変更セット（手動作成）</td>
<td><code>cdk diff</code>（自動）</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">使用する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="toc5">前提条件</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><!-- > Node.js と CDK CLI のインストール手順は [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="toc6">構築されるリソース</span></h2>
<p>CDK が <code>cdk deploy</code> 時に自動生成するリソース一覧です。</p>
<table>
<thead>
<tr>
<th>カテゴリ</th>
<th>リソース</th>
</tr>
</thead>
<tbody>
<tr>
<td>ネットワーク</td>
<td>VPC / IGW / パブリックサブネット×2 / プライベートサブネット×2 / ルートテーブル×2</td>
</tr>
<tr>
<td>セキュリティ</td>
<td>ALB SG / EC2 SG / RDS SG</td>
</tr>
<tr>
<td>IAM</td>
<td>EC2ロール / インスタンスプロファイル</td>
</tr>
<tr>
<td>パラメータ</td>
<td>SSM Parameter Store（DBパスワード）</td>
</tr>
<tr>
<td>データベース</td>
<td>RDS DBサブネットグループ / RDS MySQL 8.0</td>
</tr>
<tr>
<td>ロードバランサー</td>
<td>ターゲットグループ / ALB / リスナー</td>
</tr>
<tr>
<td>コンピュート</td>
<td>EC2インスタンス（Tomcat）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>CloudFormation 版では約27リソースを YAML に個別定義しましたが、CDK では <code>ec2.Vpc</code> 1つがVPC・IGW・ルートテーブル等を自動生成するため、Python コードとして書くのは主要リソースのみです。</p>
</blockquote>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（AWS CLI で実施）
      ↓
② プロジェクトのセットアップ（Python 仮想環境）
      ↓
③ cdk.json を設定する
      ↓
④ CDK Bootstrap（初回のみ）
      ↓
⑤ CDK Synth（テンプレート確認）
      ↓
⑥ デプロイ（cdk deploy）
      ↓
⑦ 動作確認（ALBアクセス・SSH・RDS接続）
      ↓
⑧ 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>
<blockquote>
<p>セキュリティグループで <code>203.0.113.1/32</code> の形式（末尾に <code>/32</code>）で使用します。</p>
</blockquote>
<hr>
<h2><span id="toc9">① キーペアの作成</span></h2>
<p>CDK では <code>cdk deploy</code> 時にキーペアのダウンロードができないため、<strong>事前に AWS CLI で作成</strong>します。</p>
<blockquote>
<p><code>%USERPROFILE%\.ssh\</code> が存在しない場合は先に <code>mkdir %USERPROFILE%\.ssh</code> を実行してください。</p>
</blockquote>
<pre><code class="language-cmd">aws ec2 create-key-pair ^
  --key-name my-cdk-alb-key ^
  --query KeyMaterial ^
  --output text ^
  --region ap-northeast-1 &gt; %USERPROFILE%\.ssh\my-cdk-alb-key.pem</code></pre>
<p><strong>控えておく情報</strong>: キーペア名 <code>my-cdk-alb-key</code></p>
<hr>
<h2><span id="toc10">② プロジェクトのセットアップ</span></h2>
<p>CDK の Python プロジェクトを実行するために、Python 仮想環境（venv）を作成してパッケージをインストールします。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-alb-ec2-rds

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 />ターミナルを閉じると仮想環境の有効化が解除されるため、<strong>ターミナルを開き直した際は必ず最初に以下を実行してください</strong>。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-alb-ec2-rds
.venv\Scripts\activate</code></pre>
<p>プロンプトに <code>(.venv)</code> が付いていることを確認してから CDK コマンドを実行してください。</p>
</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-cdk-alb-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-cdk-alb-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-cdk-alb-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><code>cdk.json</code> はバージョン管理に含めます</strong>（<code>cdk.context.json</code> は <code>.gitignore</code> 対象）。</p>
</blockquote>
<blockquote>
<p>CDK の詳細なコード（<code>stacks/alb_ec2_rds_stack.py</code> など）は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-alb-ec2-rds">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<h2><span id="toc12">④ CDK Bootstrap（初回のみ）</span></h2>
<p>CDK が AWS 環境にデプロイするために必要なリソース（S3 バケット等）を事前にセットアップします。<br /><strong>同じアカウント・リージョンで一度だけ実行すれば以降は不要</strong>です。</p>
<blockquote>
<p><code>cdk bootstrap</code> は <code>app.py</code> を実行しないため、仮想環境の有効・無効に関わらず実行できます。② で有効にした仮想環境はそのままで進めてください。</p>
</blockquote>
<pre><code class="language-cmd">rem アカウントIDを確認
aws sts get-caller-identity --query Account --output text

rem Bootstrap を実行（アカウントIDを置き換えて実行）
cdk bootstrap aws://123456789012/ap-northeast-1</code></pre>
<p>正常完了すると「CDKToolkit」という CloudFormation スタックが作成されます。</p>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_COMPLETE</code></td>
<td>Bootstrap 完了</td>
</tr>
<tr>
<td><code>ALREADY_EXISTS</code></td>
<td>既に Bootstrap 済み（スキップ可）</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc13">⑤ CDK Synth（テンプレート確認）</span></h2>
<p>デプロイ前に CDK が生成する CloudFormation テンプレートを確認します。<strong>この段階では AWS へのデプロイは行われません。</strong></p>
<pre><code class="language-cmd">cdk synth</code></pre>
<p><code>cdk.out/</code> ディレクトリに CloudFormation テンプレート（JSON）が生成されます。</p>
<p>CloudFormation 版との比較ポイント:</p>
<ul>
<li>CDK が自動生成したVPC・ルートテーブル等のリソースが含まれていることを確認</li>
<li>Python コード（約200行）から生成されたテンプレートの行数を確認（はるかに多い）</li>
</ul>
<blockquote>
<p><strong>なぜテンプレートは増えるのか</strong>: CDK の Python コードは「人間が書く量」を減らすための抽象化です。実際に AWS に渡される CloudFormation テンプレートは CDK が自動生成するため、行数は多くなります。</p>
</blockquote>
<pre><code class="language-cmd">rem 生成されたテンプレートをテキストエディタで確認
notepad cdk.out\CdkAlbEc2RdsStack.template.json</code></pre>
<hr>
<h2><span id="toc14">⑥ デプロイ</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:
CdkAlbEc2RdsStack.ALBEndpoint = http://my-cdk-alb-alb-xxxx.ap-northeast-1.elb.amazonaws.com
CdkAlbEc2RdsStack.EC2PublicIP = x.x.x.x
CdkAlbEc2RdsStack.RDSEndpoint = my-cdk-alb-rds-mysql.xxxx.ap-northeast-1.rds.amazonaws.com
CdkAlbEc2RdsStack.SSHCommand = ssh -i %USERPROFILE%\.ssh\my-cdk-alb-key.pem ec2-user@x.x.x.x
CdkAlbEc2RdsStack.MySQLConnectCommand = mysql -h my-cdk-alb-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="toc15">⑦ 動作確認</span></h2>
<h3><span id="toc16">7-1. ALB のヘルスチェック確認</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-cdk-alb-tg</code> → 「ターゲット」タブ</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>
<blockquote>
<p>Tomcat の起動はEC2が「実行中」になってからさらに 3〜5 分かかります。</p>
</blockquote>
<p>ターゲットグループの ARN を CLI で確認する場合:</p>
<pre><code class="language-cmd">aws elbv2 describe-target-groups ^
  --names my-cdk-alb-tg ^
  --query TargetGroups[0].TargetGroupArn ^
  --output text ^
  --region ap-northeast-1</code></pre>
<h3><span id="toc17">7-2. ALB 経由でWebアクセス確認</span></h3>
<p>Outputs の <code>ALBEndpoint</code> の URL をブラウザで開きます。</p>
<pre><code class="language-plaintext">http://（ALBEndpoint の DNS 名）</code></pre>
<p>以下が表示されれば正常です:</p>
<pre><code class="language-plaintext">AP Server - my-cdk ALB + EC2 + RDS
Deployed via AWS CDK (Python). Connects to RDS MySQL in the private subnet.</code></pre>
<blockquote>
<p>EC2 のパブリック IP に直接アクセスしても表示されません（ポート 8080 は ALB SG からのみ許可しているため）。</p>
</blockquote>
<p><!-- ![ALB経由でTomcat画面が表示されている様子](images/alb-healthy-access.jpg) --></p>
<h3><span id="toc18">7-3. EC2 に SSH 接続する</span></h3>
<p>Outputs の <code>SSHCommand</code> を実行します（パスは実際のパスに修正します）。</p>
<pre><code class="language-cmd">ssh -i %USERPROFILE%\.ssh\my-cdk-alb-key.pem ec2-user@（EC2PublicIP）</code></pre>
<h3><span id="toc19">7-4. EC2 から RDS への接続確認</span></h3>
<p>EC2 に SSH 接続後（以下は EC2 上での操作）:</p>
<pre><code class="language-bash">sudo dnf install -y mariadb105

# SSM Parameter Store からパスワードを取得
DB_PASSWORD=$(aws ssm get-parameter \
  --name /my/cdk-alb/db-password \
  --query Parameter.Value \
  --output text \
  --region ap-northeast-1)

# RDS に接続（Outputs の RDSEndpoint を使用）
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="toc20">7-5. SSH 接続を切断する</span></h3>
<pre><code class="language-bash">exit</code></pre>
<h3><span id="toc21">7-6. UserData の実行ログ確認</span></h3>
<p>EC2 に SSH 接続後（以下は EC2 上での操作）:</p>
<pre><code class="language-bash">sudo cat /var/log/user-data.log</code></pre>
<p>エラーなく最後まで実行されていれば正常です。</p>
<hr>
<h2><span id="toc22">⑧ CDK の差分確認（参考）</span></h2>
<p>コードを変更した後、デプロイ前に差分を確認できます。これが CDK の強力な機能の1つです。</p>
<pre><code class="language-cmd">cdk diff</code></pre>
<p>CloudFormation の変更セット相当の情報が自動で表示されます。</p>
<pre><code class="language-plaintext">Stack CdkAlbEc2RdsStack
Resources
[~] AWS::EC2::Instance EC2Instance
 └─ [~] UserData
     └─ [~] .Fn::Base64
         └─ ...</code></pre>
<hr>
<h2><span id="toc23">⑨ リソース削除</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="toc24">削除されるリソース一覧</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="toc25">キーペアの削除（手動）</span></h3>
<p>CDK が管理していないリソースは手動で削除します:</p>
<pre><code class="language-cmd">aws ec2 delete-key-pair ^
  --key-name my-cdk-alb-key ^
  --region ap-northeast-1

del %USERPROFILE%\.ssh\my-cdk-alb-key.pem</code></pre>
<h3><span id="toc26">仮想環境の終了</span></h3>
<p>ハンズオンが終わったらプロンプトの <code>(.venv)</code> を外します。</p>
<pre><code class="language-cmd">deactivate</code></pre>
<p><code>(.venv)</code> が消えて通常のプロンプトに戻れば完了です。</p>
<blockquote>
<p><strong><code>(.venv)</code> が付いている状態とは</strong>: このプロジェクト専用の Python 環境（venv）が有効な状態です。<code>deactivate</code> しなくてもターミナルを閉じれば自動的に解除されます。次回 CDK コマンドを使う際は再度 <code>.venv\Scripts\activate</code> が必要です。</p>
</blockquote>
<h3><span id="toc27">削除完了の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name CdkAlbEc2RdsStack ^
  --region ap-northeast-1</code></pre>
<p>「Stack with id CdkAlbEc2RdsStack does not exist」エラーが出れば削除完了です。</p>
<h3><span id="toc28">CDK Bootstrap リソースの削除（任意）</span></h3>
<p>CDK を今後も使う場合は残したままでよいです。<br />削除する場合は AWS コンソールで <code>CDKToolkit</code> スタックを手動削除します。</p>
<hr>
<h2><span id="toc29">CDK のポイント解説</span></h2>
<h3><span id="toc30">L1 / L2 / L3 Construct の違い</span></h3>
<table>
<thead>
<tr>
<th>レベル</th>
<th>名前</th>
<th>説明</th>
<th>例</th>
</tr>
</thead>
<tbody>
<tr>
<td>L1</td>
<td>Cfn Construct</td>
<td>CloudFormation リソースと 1:1 対応。<code>Cfn</code> プレフィックス</td>
<td><code>ec2.CfnVPC</code></td>
</tr>
<tr>
<td>L2</td>
<td>Default Construct</td>
<td>AWS のベストプラクティスを組み込んだ高レベル抽象</td>
<td><code>ec2.Vpc</code></td>
</tr>
<tr>
<td>L3</td>
<td>Pattern</td>
<td>複数サービスを組み合わせた再利用可能なパターン</td>
<td><code>ecs_patterns.ApplicationLoadBalancedFargateService</code></td>
</tr>
</tbody>
</table>
<p>このハンズオンでは主に <strong>L2 Construct</strong> を使用しています。<code>ec2.Vpc</code> 1つを書くだけで、CloudFormation では15+ リソースが必要だったVPC・IGW・ルートテーブル・サブネット設定を自動生成します。</p>
<h3><span id="toc31">CDK Context 変数</span></h3>
<p><code>cdk.json</code> の <code>context</code> に設定した値は <code>self.node.try_get_context(&quot;key&quot;)</code> で取得できます。<br />デプロイ時に <code>-c key=value</code> で上書きも可能です:</p>
<pre><code class="language-cmd">cdk deploy -c my_ip=1.2.3.4/32 -c db_password=MyPass123!</code></pre>
<h3><span id="toc32">cdk synth で CloudFormation テンプレートを確認</span></h3>
<pre><code class="language-cmd">rem cdk.out/ に生成されたテンプレートをテキストエディタで確認
notepad cdk.out\CdkAlbEc2RdsStack.template.json</code></pre>
<p>CDK コードの詳細（<code>stacks/alb_ec2_rds_stack.py</code> 全文）は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-alb-ec2-rds">GitHub</a> を参照してください。</p>
<hr>
<h2><span id="toc33">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<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>④の手順を実施</td>
</tr>
<tr>
<td><code>ModuleNotFoundError: aws_cdk</code></td>
<td>仮想環境が無効</td>
<td><code>.venv\Scripts\activate</code> を実行してから再度 CDK コマンドを実行</td>
</tr>
<tr>
<td>ALB にアクセスすると 502</td>
<td>Tomcat 起動前</td>
<td>3〜5 分待ってリトライ</td>
</tr>
<tr>
<td>SSH できない</td>
<td>my_ip が違う、またはキー名が違う</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 CdkAlbEc2RdsStack ^
  --region ap-northeast-1 ^
  --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[ResourceType,ResourceStatusReason]" ^
  --output table</code></pre>
<hr>
<h2><span id="toc34">コンソール版・CloudFormation版との比較</span></h2>
<table>
<thead>
<tr>
<th>作業</th>
<th>コンソール（手動）</th>
<th>CloudFormation</th>
<th><strong>CDK（今回）</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC + サブネット4つ + IGW + RT</td>
<td>約20〜30分</td>
<td>template.yaml に定義済み</td>
<td><strong><code>ec2.Vpc</code> 1行で自動生成</strong></td>
</tr>
<tr>
<td>SG 3つ（ALB/EC2/RDS）の作成</td>
<td>3画面で個別設定</td>
<td>template.yaml に定義済み</td>
<td><strong>Python コードで直感的に記述</strong></td>
</tr>
<tr>
<td>ALB + TG + Listener の作成</td>
<td>3つの画面で設定</td>
<td>template.yaml に定義済み</td>
<td><strong>メソッドチェーンで接続</strong></td>
</tr>
<tr>
<td>コード量</td>
<td>—</td>
<td>約460行（YAML）</td>
<td><strong>約200行（Python）</strong></td>
</tr>
<tr>
<td>全体のデプロイ</td>
<td><strong>約40〜50分</strong></td>
<td>コマンド1本（RDS待ち15〜20分）</td>
<td><code>cdk deploy</code> 1本（同等）</td>
</tr>
<tr>
<td>削除</td>
<td><strong>13ステップ手動</strong></td>
<td><code>delete-stack</code> 1本</td>
<td><code>cdk destroy</code> 1本</td>
</tr>
<tr>
<td>差分確認</td>
<td>—</td>
<td>変更セット（手動作成）</td>
<td><code>cdk diff</code>（自動・即時）</td>
</tr>
<tr>
<td>バージョン管理</td>
<td>不可</td>
<td>Gitで管理可能</td>
<td>Gitで管理可能</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc35">まとめ</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>cdk bootstrap</code>（初回のみ。以降は不要）</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 → Parameter Store → RDS への接続テスト</td>
</tr>
<tr>
<td>⑨</td>
<td><code>cdk destroy</code> で全リソースを一括削除</td>
</tr>
</tbody>
</table>
<p>CDK の最大の魅力は <strong>プログラミング言語でインフラを書ける</strong>点です。CloudFormation の460行の YAML が Python 約200行に削減されただけでなく、<code>cdk diff</code> による差分確認や L2 Construct による抽象化など、開発者体験が大きく向上します。</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> を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">AWS CDK（Python）でALB + EC2(Tomcat) + RDS 3層構成を構築する手順【CloudFormation 460行 → CDK 200行】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cdk-alb-ec2-rds/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWSコンソールでSpring Boot WebアプリをALB + EC2(Tomcat) + RDS にデプロイする手順【WAR / JdbcTemplate / CRUD】</title>
		<link>https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/</link>
					<comments>https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 18 Apr 2026 04:45:30 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[3層構成]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWSコンソール]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[JDBC]]></category>
		<category><![CDATA[JdbcTemplate]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[SCP]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[Thymeleaf]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[WAR]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[ロードバランサー]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20365</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説前フェーズとの違い使用するAWSサービス構築するリソース一覧全体の作業順序⓪ Webアプリのビルド⓪-2 自分のIPアドレスの確認① キーペアの作成② VPCの作成③ サブネットの作成（4つ） [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/">AWSコンソールでSpring Boot WebアプリをALB + EC2(Tomcat) + RDS にデプロイする手順【WAR / JdbcTemplate / CRUD】</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-10" checked><label class="toc-title" for="toc-checkbox-10">目次</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">前フェーズとの違い</a></li><li><a href="#toc4" tabindex="0">使用するAWSサービス</a></li><li><a href="#toc5" tabindex="0">構築するリソース一覧</a></li><li><a href="#toc6" tabindex="0">全体の作業順序</a></li><li><a href="#toc7" tabindex="0">⓪ Webアプリのビルド</a></li><li><a href="#toc8" tabindex="0">⓪-2 自分のIPアドレスの確認</a></li><li><a href="#toc9" tabindex="0">① キーペアの作成</a></li><li><a href="#toc10" tabindex="0">② VPCの作成</a></li><li><a href="#toc11" tabindex="0">③ サブネットの作成（4つ）</a><ol><li><a href="#toc12" tabindex="0">パブリックサブネットのIPv4自動割り当てを有効化</a></li></ol></li><li><a href="#toc13" tabindex="0">④ インターネットゲートウェイの作成・アタッチ</a><ol><li><a href="#toc14" tabindex="0">作成</a></li><li><a href="#toc15" tabindex="0">VPCへのアタッチ</a></li></ol></li><li><a href="#toc16" tabindex="0">⑤ ルートテーブルの設定</a><ol><li><a href="#toc17" tabindex="0">5-1. パブリック用ルートテーブル</a></li><li><a href="#toc18" tabindex="0">5-2. プライベート用ルートテーブル</a></li></ol></li><li><a href="#toc19" tabindex="0">⑥ セキュリティグループの作成</a><ol><li><a href="#toc20" tabindex="0">6-1. ALB用セキュリティグループ</a></li><li><a href="#toc21" tabindex="0">6-2. EC2用セキュリティグループ</a></li><li><a href="#toc22" tabindex="0">6-3. RDS用セキュリティグループ</a></li></ol></li><li><a href="#toc23" tabindex="0">⑦ IAMロールの作成</a><ol><li><a href="#toc24" tabindex="0">信頼されたエンティティ</a></li><li><a href="#toc25" tabindex="0">許可ポリシー</a></li><li><a href="#toc26" tabindex="0">ロール名</a></li></ol></li><li><a href="#toc27" tabindex="0">⑧ SSM Parameter Storeの作成</a></li><li><a href="#toc28" tabindex="0">⑨ RDS DBサブネットグループの作成</a></li><li><a href="#toc29" tabindex="0">⑩ RDS MySQL インスタンスの作成</a><ol><li><a href="#toc30" tabindex="0">エンジンの選択</a></li><li><a href="#toc31" tabindex="0">テンプレート</a></li><li><a href="#toc32" tabindex="0">設定</a></li><li><a href="#toc33" tabindex="0">インスタンスの設定</a></li><li><a href="#toc34" tabindex="0">ストレージ</a></li><li><a href="#toc35" tabindex="0">接続</a></li><li><a href="#toc36" tabindex="0">追加設定</a></li></ol></li><li><a href="#toc37" tabindex="0">⑪ EC2インスタンスの起動（Tomcat）</a><ol><li><a href="#toc38" tabindex="0">基本設定</a></li><li><a href="#toc39" tabindex="0">ネットワーク設定</a></li><li><a href="#toc40" tabindex="0">IAMロール</a></li><li><a href="#toc41" tabindex="0">UserData</a></li></ol></li><li><a href="#toc42" tabindex="0">⑫ ターゲットグループの作成</a><ol><li><a href="#toc43" tabindex="0">ヘルスチェック</a></li><li><a href="#toc44" tabindex="0">ターゲットの登録</a></li></ol></li><li><a href="#toc45" tabindex="0">⑬ ALBの作成・リスナー設定</a><ol><li><a href="#toc46" tabindex="0">基本的な設定</a></li><li><a href="#toc47" tabindex="0">ネットワークマッピング</a></li><li><a href="#toc48" tabindex="0">セキュリティグループ</a></li><li><a href="#toc49" tabindex="0">リスナーとルーティング</a></li></ol></li><li><a href="#toc50" tabindex="0">⑭ Webアプリのデプロイ</a><ol><li><a href="#toc51" tabindex="0">14-1. EC2にSSH接続する</a></li><li><a href="#toc52" tabindex="0">14-2. setenv.sh を作成してDB接続情報を注入する</a></li><li><a href="#toc53" tabindex="0">14-3. WARファイルをEC2に転送する</a></li><li><a href="#toc54" tabindex="0">14-4. Tomcatを停止してWARをデプロイする</a></li><li><a href="#toc55" tabindex="0">14-5. デプロイを確認する</a></li></ol></li><li><a href="#toc56" tabindex="0">⑮ 動作確認</a><ol><li><a href="#toc57" tabindex="0">15-1. ALBのヘルスチェック確認</a></li><li><a href="#toc58" tabindex="0">15-2. ALB経由でWebアプリにアクセスする</a></li><li><a href="#toc59" tabindex="0">15-3. アイテムの追加・削除を試す</a></li><li><a href="#toc60" tabindex="0">15-4. RDSのデータをSQLで直接確認する（任意）</a></li></ol></li><li><a href="#toc61" tabindex="0">⑯ リソースの削除</a><ol><li><a href="#toc62" tabindex="0">削除順序</a></li><li><a href="#toc63" tabindex="0">1. ALBを削除する</a></li><li><a href="#toc64" tabindex="0">2. ターゲットグループを削除する</a></li><li><a href="#toc65" tabindex="0">3. EC2インスタンスを終了する</a></li><li><a href="#toc66" tabindex="0">4. RDSインスタンスを削除する</a></li><li><a href="#toc67" tabindex="0">5. RDS DBサブネットグループを削除する</a></li><li><a href="#toc68" tabindex="0">6. SSM Parameter Storeを削除する</a></li><li><a href="#toc69" tabindex="0">7. IAMロールを削除する</a></li><li><a href="#toc70" tabindex="0">8. セキュリティグループを削除する</a></li><li><a href="#toc71" tabindex="0">9. VPCを削除する</a></li><li><a href="#toc72" tabindex="0">10. キーペアを削除する（任意）</a></li></ol></li><li><a href="#toc73" tabindex="0">トラブルシューティング</a></li><li><a href="#toc74" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>前回の<a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン（ALB + EC2 + RDS）</a>では、Tomcatに静的HTMLを配置して3層構成を体験しました。</p>
<p>今回はそこからさらに一歩進め、<strong>Spring Boot + Maven</strong> で作成したWebアプリケーションをWARファイルとしてビルドし、EC2（Tomcat）にデプロイして <strong>RDS MySQLにJDBCで接続するCRUDアプリ</strong>を動かすハンズオンを紹介します。</p>
<pre><code class="language-plaintext">インターネット
  ↓ HTTP(80)  ← ALB SGで全IPから許可
ALB（my-webapp-alb）
  [パブリックサブネット1: 10.0.1.0/24 AZ-a]
  [パブリックサブネット2: 10.0.2.0/24 AZ-c]  ← ALBの2AZ要件
  ↓ HTTP(8080)  ← EC2 SGでALB SGからのみ許可（SG-to-SG）
EC2（my-webapp-ap-instance）
  Tomcat 10 + webapp.war（Spring Boot）
  [パブリックサブネット1: 10.0.1.0/24 AZ-a]
  ↓ JDBC/MySQL(3306)  ← RDS SGでEC2 SGからのみ許可（SG-to-SG）
RDS MySQL（my-webapp-rds-mysql）← sampledb / items テーブル
  [プライベートサブネット1: 10.0.3.0/24 AZ-a]</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>Spring Boot WebアプリをMavenでWARファイルにビルドする方法</li>
<li>SCPでWARをEC2に転送してTomcatにデプロイする手順</li>
<li>SSM Parameter StoreのDB接続情報をEC2上の環境変数として注入する方法</li>
<li>JdbcTemplateでRDS MySQLにCRUD操作するSpring Bootアプリの動作</li>
</ul>
<p><strong>アプリのソースコード・CloudFormationテンプレートはGitHubで公開しています：</strong></p>
<blockquote>

<a rel="noopener" href="https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp" title="aws-handson/alb-ec2-rds-webapp at main · caymezon/aws-handson" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Fcaymezon%2Faws-handson%2Ftree%2Fmain%2Falb-ec2-rds-webapp?w=160&h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">aws-handson/alb-ec2-rds-webapp at main · caymezon/aws-handson</div><div class="blogcard-snippet external-blogcard-snippet">AWS HANDSON. Contribute to caymezon/aws-handson development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
</blockquote>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/">CloudFormation版ハンズオン</a> の比較記事です。</strong><br />コンソール操作でWARのデプロイ手順を手を動かして確認したい方向けです。</p>
</blockquote>
<hr>
<p><!-- ![Spring Boot WebアプリのCRUD画面（アイテム追加・削除）](images/webapp-crud.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>Spring Boot</strong></td>
<td>Javaの代表的なWebアプリフレームワーク。設定ファイルを最小限にしてWebアプリを素早く作れる</td>
</tr>
<tr>
<td><strong>Maven</strong></td>
<td>Javaのビルドツール。<code>mvn package</code> でWARファイルを生成する</td>
</tr>
<tr>
<td><strong>WAR（Web Application Archive）</strong></td>
<td>JavaのWebアプリをTomcatにデプロイするためのパッケージ形式</td>
</tr>
<tr>
<td><strong>JdbcTemplate</strong></td>
<td>Spring FrameworkのDB操作クラス。SQLを直接書いてDBを操作できる</td>
</tr>
<tr>
<td><strong>Thymeleaf</strong></td>
<td>Spring Bootで使われるHTMLテンプレートエンジン</td>
</tr>
<tr>
<td><strong>SCP</strong></td>
<td>SSH経由でファイルを転送するコマンド。<code>scp</code> でWARをEC2に送る</td>
</tr>
<tr>
<td><strong>setenv.sh</strong></td>
<td>Tomcatが起動時に読み込むシェルスクリプト。環境変数を設定してJVMに渡す</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">前フェーズとの違い</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>前回（ALB + EC2 + RDS）</th>
<th>今回（Webアプリ版）</th>
</tr>
</thead>
<tbody>
<tr>
<td>EC2のコンテンツ</td>
<td>静的HTML（手書き）</td>
<td><strong>Spring Boot WARアプリ</strong></td>
</tr>
<tr>
<td>DB接続</td>
<td>なし</td>
<td><strong>JdbcTemplateでRDS接続</strong></td>
</tr>
<tr>
<td>画面</td>
<td>テキストのみ</td>
<td><strong>アイテム一覧・追加・削除（CRUD）</strong></td>
</tr>
<tr>
<td>WARデプロイ</td>
<td>なし</td>
<td><strong>SCPで転送 → Tomcatに配置</strong></td>
</tr>
<tr>
<td>DB設定の渡し方</td>
<td>なし</td>
<td><strong>SSMから取得して環境変数（setenv.sh）で注入</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">使用する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は無料枠がありません。ハンズオン後は必ず削除してください。</p>
</blockquote>
<hr>
<h2><span id="toc5">構築するリソース一覧</span></h2>
<table>
<thead>
<tr>
<th>順序</th>
<th>リソース</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td>⓪</td>
<td>（前提）Webアプリのビルド</td>
<td><code>webapp.war</code> の生成</td>
</tr>
<tr>
<td>⓪-2</td>
<td>自分のIPアドレスの確認</td>
<td>SGのSSHルール設定に使用</td>
</tr>
<tr>
<td>①</td>
<td>キーペア（<code>my-webapp-key</code>）</td>
<td>EC2へのSSH・SCP認証鍵</td>
</tr>
<tr>
<td>②</td>
<td>VPC（<code>my-webapp-vpc</code>）</td>
<td>独立したネットワーク空間</td>
</tr>
<tr>
<td>③</td>
<td>サブネット × 4</td>
<td>パブリック2 + プライベート2</td>
</tr>
<tr>
<td>④</td>
<td>インターネットゲートウェイ（<code>my-webapp-igw</code>）</td>
<td>VPCをインターネットに接続</td>
</tr>
<tr>
<td>⑤</td>
<td>ルートテーブル × 2</td>
<td>パブリック/プライベートの通信経路</td>
</tr>
<tr>
<td>⑥</td>
<td>セキュリティグループ × 3</td>
<td>ALB用・EC2用・RDS用</td>
</tr>
<tr>
<td>⑦</td>
<td>IAMロール（<code>my-webapp-ec2-role</code>）</td>
<td>EC2がSSM・Parameter Storeを使う権限</td>
</tr>
<tr>
<td>⑧</td>
<td>SSM Parameter Store（<code>/my/webapp/db-password</code>）</td>
<td>DBパスワードの保管</td>
</tr>
<tr>
<td>⑨</td>
<td>RDS DBサブネットグループ</td>
<td>RDSを配置できるサブネットを登録</td>
</tr>
<tr>
<td>⑩</td>
<td>RDS MySQL インスタンス（<code>my-webapp-rds-mysql</code>）</td>
<td>マネージドDBサーバ（作成に10〜15分）</td>
</tr>
<tr>
<td>⑪</td>
<td>EC2インスタンス（<code>my-webapp-ap-instance</code>）</td>
<td>APサーバ（Tomcat）</td>
</tr>
<tr>
<td>⑫</td>
<td>ターゲットグループ（<code>my-webapp-tg</code>）</td>
<td>ALBのトラフィック転送先の定義</td>
</tr>
<tr>
<td>⑬</td>
<td>ALB（<code>my-webapp-alb</code>） + リスナー</td>
<td>ロードバランサー本体</td>
</tr>
<tr>
<td>⑭</td>
<td>Webアプリのデプロイ</td>
<td>WARをEC2に転送・Tomcatに配置</td>
</tr>
<tr>
<td>⑮</td>
<td>動作確認</td>
<td>ALB経由でWebアプリにアクセス</td>
</tr>
<tr>
<td>⑯</td>
<td>リソース削除</td>
<td>課金停止</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc6">全体の作業順序</span></h2>
<pre><code class="language-plaintext">⓪ Webアプリをビルドする（mvn package）
      ↓
⓪-2 自分のIPアドレスを確認する
      ↓
① キーペア作成
      ↓
② VPC作成
      ↓
③ サブネット作成（4つ）
      ↓
④ インターネットゲートウェイ作成・アタッチ
      ↓
⑤ ルートテーブル設定
      ↓
⑥ セキュリティグループ作成（ALB用・EC2用・RDS用）
      ↓
⑦ IAMロール作成
      ↓
⑧ SSM Parameter Store作成
      ↓
⑨ RDS DBサブネットグループ作成
      ↓
⑩ RDS MySQL インスタンス作成（※約10〜15分かかる）
      ↓
⑪ EC2インスタンス起動（Tomcat）
      ↓
⑫ ターゲットグループ作成
      ↓
⑬ ALB作成・リスナー設定
      ↓
⑭ Webアプリのデプロイ（WAR転送・Tomcat起動）
      ↓
⑮ 動作確認（ALB → EC2 → RDS CRUD）
      ↓
⑯ リソース削除</code></pre>
<hr>
<h2><span id="toc7">⓪ Webアプリのビルド</span></h2>
<p>Webアプリのソースコードはこちら：</p>
<blockquote>

<a rel="noopener" href="https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp/app" title="aws-handson/alb-ec2-rds-webapp/app at main ?? caymezon/aws-handson" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Fcaymezon%2Faws-handson%2Ftree%2Fmain%2Falb-ec2-rds-webapp%2Fapp?w=160&h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">aws-handson/alb-ec2-rds-webapp/app at main ?? caymezon/aws-handson</div><div class="blogcard-snippet external-blogcard-snippet">AWS HANDSON. Contribute to caymezon/aws-handson development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp/app" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
</blockquote>
<p>GitHubからクローンするか、ローカルにすでにあるソースコードを使います。</p>
<p><strong>ビルド前の確認：</strong></p>
<pre><code class="language-cmd">java -version
mvn -version</code></pre>
<p>Java 17以上、Maven 3.x が表示されることを確認します。</p>
<p><strong>WARファイルをビルドする：</strong></p>
<pre><code class="language-cmd">cd C:\（プロジェクトフォルダ）\alb-ec2-rds-webapp\app
mvn package -DskipTests</code></pre>
<p><code>-DskipTests</code> はローカルにDBがないためテストをスキップします。初回は依存ライブラリのダウンロードで数分かかります。</p>
<pre><code class="language-plaintext">[INFO] BUILD SUCCESS</code></pre>
<p><strong>ビルド成果物を確認する：</strong></p>
<pre><code class="language-cmd">dir target\webapp.war</code></pre>
<p><code>webapp.war</code>（約20〜30MB）が生成されていれば成功です。</p>
<blockquote>
<p><strong>アプリの機能</strong>: アイテム一覧表示・追加・削除の3機能。Spring Boot + JdbcTemplate + Thymeleaf で構成されています。DB接続情報は環境変数（<code>DB_HOST</code>・<code>DB_PASSWORD</code>）から読み込みます。</p>
</blockquote>
<hr>
<h2><span id="toc8">⓪-2 自分のIPアドレスの確認</span></h2>
<pre><code class="language-plaintext">https://checkip.amazonaws.com</code></pre>
<p><strong>控えておく情報</strong>: 自分のIPアドレス（例: <code>203.0.113.1</code>）</p>
<blockquote>
<p>セキュリティグループで <code>203.0.113.1/32</code> の形式（末尾に <code>/32</code>）で使用します。</p>
</blockquote>
<hr>
<h2><span id="toc9">① キーペアの作成</span></h2>
<p><strong>AWSコンソール → EC2 → キーペア → 「キーペアを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-webapp-key</code></td>
</tr>
<tr>
<td>キーペアのタイプ</td>
<td><strong>RSA</strong></td>
</tr>
<tr>
<td>プライベートキーファイル形式</td>
<td><strong>.pem</strong></td>
</tr>
</tbody>
</table>
<p>「キーペアを作成」をクリック。ダウンロードされた <code>.pem</code> ファイルを保存します。</p>
<pre><code class="language-plaintext">C:\Users\ユーザー名\.ssh\my-webapp-key.pem</code></pre>
<hr>
<h2><span id="toc10">② VPCの作成</span></h2>
<p><strong>AWSコンソール → VPC → 「VPCを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>作成するリソース</td>
<td><strong>VPCのみ</strong></td>
</tr>
<tr>
<td>名前タグ</td>
<td><code>my-webapp-vpc</code></td>
</tr>
<tr>
<td>IPv4 CIDR</td>
<td><code>10.0.0.0/16</code></td>
</tr>
</tbody>
</table>
<p>「VPCを作成」をクリック。</p>
<hr>
<h2><span id="toc11">③ サブネットの作成（4つ）</span></h2>
<p><strong>AWSコンソール → VPC → サブネット → 「サブネットを作成」</strong></p>
<p>VPCに <code>my-webapp-vpc</code> を選択して、以下の4つを1回の操作で作成します（「新しいサブネットを追加」で追加できます）。</p>
<table>
<thead>
<tr>
<th>名前</th>
<th>AZ</th>
<th>IPv4 CIDR</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>my-webapp-public-subnet-1</code></td>
<td>ap-northeast-1<strong>a</strong></td>
<td><code>10.0.1.0/24</code></td>
<td>ALB + EC2</td>
</tr>
<tr>
<td><code>my-webapp-public-subnet-2</code></td>
<td>ap-northeast-1<strong>c</strong></td>
<td><code>10.0.2.0/24</code></td>
<td>ALB（2AZ必須）</td>
</tr>
<tr>
<td><code>my-webapp-private-subnet-1</code></td>
<td>ap-northeast-1<strong>a</strong></td>
<td><code>10.0.3.0/24</code></td>
<td>RDS配置</td>
</tr>
<tr>
<td><code>my-webapp-private-subnet-2</code></td>
<td>ap-northeast-1<strong>c</strong></td>
<td><code>10.0.4.0/24</code></td>
<td>DBサブネットグループ用</td>
</tr>
</tbody>
</table>
<p>「サブネットを作成」をクリック。</p>
<h3><span id="toc12">パブリックサブネットのIPv4自動割り当てを有効化</span></h3>
<p><code>my-webapp-public-subnet-1</code> と <code>my-webapp-public-subnet-2</code> それぞれに対して：</p>
<p>サブネットを選択 → 「アクション」→「サブネットの設定を編集」→ <strong>「パブリックIPv4アドレスの自動割り当てを有効化」</strong> にチェック → 「保存」</p>
<hr>
<h2><span id="toc13">④ インターネットゲートウェイの作成・アタッチ</span></h2>
<h3><span id="toc14">作成</span></h3>
<p><strong>AWSコンソール → VPC → インターネットゲートウェイ → 「インターネットゲートウェイを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前タグ</td>
<td><code>my-webapp-igw</code></td>
</tr>
</tbody>
</table>
<p>「インターネットゲートウェイを作成」をクリック。</p>
<h3><span id="toc15">VPCへのアタッチ</span></h3>
<p>作成したIGWを選択 → 「アクション」→「VPCにアタッチ」→ <code>my-webapp-vpc</code> を選択 → 「インターネットゲートウェイのアタッチ」</p>
<hr>
<h2><span id="toc16">⑤ ルートテーブルの設定</span></h2>
<blockquote>
<p><strong>注意</strong>: EC2を起動する前にこの手順を完了させてください。IGWルートがない状態でEC2を起動すると、UserDataのTomcatインストールが失敗します。</p>
</blockquote>
<h3><span id="toc17">5-1. パブリック用ルートテーブル</span></h3>
<p><strong>AWSコンソール → VPC → ルートテーブル → 「ルートテーブルを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-webapp-public-rt</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
</tbody>
</table>
<p>作成後、「ルート」タブ → 「ルートを編集」→「ルートを追加」</p>
<table>
<thead>
<tr>
<th>送信先</th>
<th>ターゲット</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0.0.0.0/0</code></td>
<td>インターネットゲートウェイ <code>my-webapp-igw</code></td>
</tr>
</tbody>
</table>
<p>「変更を保存」をクリック。</p>
<p>「サブネットの関連付け」タブ → 「サブネットの関連付けを編集」→ パブリックサブネット2つにチェック → 保存</p>
<ul>
<li><code>my-webapp-public-subnet-1</code></li>
<li><code>my-webapp-public-subnet-2</code></li>
</ul>
<h3><span id="toc18">5-2. プライベート用ルートテーブル</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-webapp-private-rt</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
</tbody>
</table>
<p>「サブネットの関連付けを編集」→ プライベートサブネット2つにチェック → 保存</p>
<ul>
<li><code>my-webapp-private-subnet-1</code></li>
<li><code>my-webapp-private-subnet-2</code></li>
</ul>
<hr>
<h2><span id="toc19">⑥ セキュリティグループの作成</span></h2>
<h3><span id="toc20">6-1. ALB用セキュリティグループ</span></h3>
<p><strong>AWSコンソール → EC2 → セキュリティグループ → 「セキュリティグループを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-webapp-alb-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>ALB SG - HTTP:80 from internet</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
</tbody>
</table>
<p>インバウンドルール:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>ポート</th>
<th>ソース</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTTP</td>
<td>80</td>
<td><code>0.0.0.0/0</code></td>
<td>HTTP from internet</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>なぜ全開放か</strong>: ALBはWebサービスの入口なので不特定多数を許可。EC2はALB SGからのみ8080番を受け付けるSG-to-SG制御で守られています。</p>
</blockquote>
<h3><span id="toc21">6-2. EC2用セキュリティグループ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-webapp-ec2-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>EC2 Tomcat SG - Tomcat:8080 from ALB SG, SSH from MyIP</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
</tbody>
</table>
<p>インバウンドルール:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>ポート</th>
<th>ソース</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>カスタムTCP</td>
<td>8080</td>
<td><code>my-webapp-alb-sg</code>（<strong>SGを選択</strong>）</td>
<td>Tomcat from ALB SG only</td>
</tr>
<tr>
<td>SSH</td>
<td>22</td>
<td><code>自分のIP/32</code></td>
<td>SSH from my IP（SCPにも必要）</td>
</tr>
</tbody>
</table>
<p>アウトバウンドルール（<strong>変更しない</strong>）:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>プロトコル</th>
<th>送信先</th>
</tr>
</thead>
<tbody>
<tr>
<td>すべてのトラフィック</td>
<td>すべて</td>
<td><code>0.0.0.0/0</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>アウトバウンドルールを削除してはいけない</strong>: EC2からのTomcatインストール（パッケージダウンロード）に必要です。</p>
</blockquote>
<h3><span id="toc22">6-3. RDS用セキュリティグループ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-webapp-rds-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS MySQL SG - MySQL:3306 from EC2 SG only</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
</tbody>
</table>
<p>インバウンドルール:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>ポート</th>
<th>ソース</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL/Aurora</td>
<td>3306</td>
<td><code>my-webapp-ec2-sg</code>（<strong>SGを選択</strong>）</td>
<td>MySQL from EC2 SG only</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc23">⑦ IAMロールの作成</span></h2>
<p><strong>AWSコンソール → IAM → ロール → 「ロールを作成」</strong></p>
<h3><span id="toc24">信頼されたエンティティ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>信頼されたエンティティタイプ</td>
<td><strong>AWSのサービス</strong></td>
</tr>
<tr>
<td>サービス</td>
<td><strong>EC2</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc25">許可ポリシー</span></h3>
<table>
<thead>
<tr>
<th>ポリシー名</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AmazonSSMManagedInstanceCore</code></td>
<td>Session Manager接続用</td>
</tr>
<tr>
<td><code>AmazonSSMReadOnlyAccess</code></td>
<td>Parameter Storeからパスワードを読み取る</td>
</tr>
</tbody>
</table>
<h3><span id="toc26">ロール名</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ロール名</td>
<td><code>my-webapp-ec2-role</code></td>
</tr>
</tbody>
</table>
<p>「ロールを作成」をクリック。</p>
<hr>
<h2><span id="toc27">⑧ SSM Parameter Storeの作成</span></h2>
<p><strong>AWSコンソール → Systems Manager → パラメータストア → 「パラメータの作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>/my/webapp/db-password</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS MySQL master password for webapp hands-on</code></td>
</tr>
<tr>
<td>層</td>
<td><strong>スタンダード</strong></td>
</tr>
<tr>
<td>タイプ</td>
<td><strong>安全な文字列</strong>（SecureString・推奨）</td>
</tr>
<tr>
<td>KMS キーID</td>
<td><code>alias/aws/ssm</code>（デフォルト）</td>
</tr>
<tr>
<td>値</td>
<td>（任意のパスワード。例: <code>Handson1234!</code>）</td>
</tr>
</tbody>
</table>
<p>「パラメータの作成」をクリック。</p>
<blockquote>
<p><strong>SecureStringとは</strong>: KMSで暗号化して保存するタイプです。取得時は <code>--with-decryption</code> オプションが必要です。CloudFormation版はString型になりますが、コンソールではSecureStringを推奨します。</p>
</blockquote>
<hr>
<h2><span id="toc28">⑨ RDS DBサブネットグループの作成</span></h2>
<p><strong>AWSコンソール → Aurora and RDS → サブネットグループ → 「DBサブネットグループの作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-webapp-rds-subnet-group</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>webapp RDS subnet group</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
</tbody>
</table>
<p>アベイラビリティーゾーンとサブネットの追加:</p>
<table>
<thead>
<tr>
<th>AZ</th>
<th>サブネット</th>
</tr>
</thead>
<tbody>
<tr>
<td>ap-northeast-1a</td>
<td><code>my-webapp-private-subnet-1</code></td>
</tr>
<tr>
<td>ap-northeast-1c</td>
<td><code>my-webapp-private-subnet-2</code></td>
</tr>
</tbody>
</table>
<p>「作成」をクリック。</p>
<hr>
<h2><span id="toc29">⑩ RDS MySQL インスタンスの作成</span></h2>
<p><strong>AWSコンソール → Aurora and RDS → データベース → 「データベースの作成 ▲」→「フル設定」</strong></p>
<blockquote>
<p><strong>「フル設定」を選ぶ</strong>: VPC・サブネットグループ・SGなどを細かく指定するため、必ず「フル設定」を選択します。</p>
<p><strong>注意</strong>: RDSの作成は<strong>約10〜15分</strong>かかります。</p>
</blockquote>
<h3><span id="toc30">エンジンの選択</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>エンジンのタイプ</td>
<td><strong>MySQL</strong></td>
</tr>
<tr>
<td>エンジンバージョン</td>
<td><strong>MySQL 8.0.x</strong>（最新）</td>
</tr>
</tbody>
</table>
<h3><span id="toc31">テンプレート</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>テンプレート</td>
<td><strong>無料利用枠</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc32">設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBインスタンス識別子</td>
<td><code>my-webapp-rds-mysql</code></td>
</tr>
<tr>
<td>マスターユーザー名</td>
<td><code>admin</code></td>
</tr>
<tr>
<td>マスターパスワード</td>
<td>⑧で設定したパスワード</td>
</tr>
</tbody>
</table>
<h3><span id="toc33">インスタンスの設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBインスタンスクラス</td>
<td><strong>db.t3.micro</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc34">ストレージ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ストレージタイプ</td>
<td><strong>汎用SSD (gp2)</strong></td>
</tr>
<tr>
<td>割り当てられたストレージ</td>
<td><code>20</code> GiB</td>
</tr>
<tr>
<td>ストレージの自動スケーリング</td>
<td>チェックを<strong>外す</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc35">接続</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>コンピューティングリソース</td>
<td><strong>EC2コンピューティングリソースに接続しない</strong></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
<tr>
<td>DBサブネットグループ</td>
<td><code>my-webapp-rds-subnet-group</code></td>
</tr>
<tr>
<td>パブリックアクセス</td>
<td><strong>なし</strong></td>
</tr>
<tr>
<td>VPCセキュリティグループ</td>
<td><strong>既存の選択</strong> → <code>my-webapp-rds-sg</code>（<code>default</code> は削除）</td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc36">追加設定</span></h3>
<p>「追加設定」を開きます。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>最初のデータベース名</td>
<td><code>sampledb</code></td>
</tr>
<tr>
<td>自動バックアップを有効にする</td>
<td>チェックを<strong>外す</strong></td>
</tr>
</tbody>
</table>
<p>「データベースの作成」をクリック。</p>
<p><strong>控えておく情報</strong>: 作成完了後の「エンドポイント」ホスト名</p>
<pre><code class="language-plaintext">my-webapp-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com</code></pre>
<hr>
<h2><span id="toc37">⑪ EC2インスタンスの起動（Tomcat）</span></h2>
<blockquote>
<p><strong>注意</strong>: EC2を起動する前に⑤のルートテーブル設定が完了していることを確認してください。</p>
</blockquote>
<p><strong>AWSコンソール → EC2 → インスタンス → 「インスタンスを起動」</strong></p>
<h3><span id="toc38">基本設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-webapp-ap-instance</code></td>
</tr>
<tr>
<td>AMI</td>
<td><strong>Amazon Linux 2023 AMI</strong></td>
</tr>
<tr>
<td>インスタンスタイプ</td>
<td><strong>t2.micro</strong></td>
</tr>
<tr>
<td>キーペア</td>
<td><code>my-webapp-key</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc39">ネットワーク設定</span></h3>
<p>「編集」をクリックして以下を設定します。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
<tr>
<td>サブネット</td>
<td><code>my-webapp-public-subnet-1</code></td>
</tr>
<tr>
<td>パブリックIPの自動割り当て</td>
<td><strong>有効化</strong></td>
</tr>
<tr>
<td>セキュリティグループ</td>
<td><strong>既存のセキュリティグループを選択</strong> → <code>my-webapp-ec2-sg</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc40">IAMロール</span></h3>
<p>「高度な詳細」→「IAMインスタンスプロファイル」→ <code>my-webapp-ec2-role</code> を選択</p>
<h3><span id="toc41">UserData</span></h3>
<p>「高度な詳細」→「ユーザーデータ」に以下を貼り付けます。</p>
<blockquote>
<p><strong>このUserDataではTomcatをインストールするのみです。アプリのデプロイは⑭で手動で行います。</strong></p>
</blockquote>
<pre><code class="language-bash">#!/bin/bash
set -xe
exec &gt; &gt;(tee /var/log/user-data.log) 2&gt;&amp;1

dnf install -y java-17-amazon-corretto wget

TOMCAT_VERSION="10.1.28"
cd /opt
wget -q https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz
tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz
mv apache-tomcat-${TOMCAT_VERSION} tomcat
chmod +x /opt/tomcat/bin/*.sh

cat &gt; /opt/tomcat/webapps/ROOT/index.html &lt;&lt; 'HTMLEOF'
&lt;html&gt;&lt;body&gt;&lt;h1&gt;Tomcat is ready. Deploy webapp.war to complete setup.&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;
HTMLEOF

/opt/tomcat/bin/startup.sh</code></pre>
<p>「インスタンスを起動」をクリック。</p>
<p><strong>控えておく情報</strong>: インスタンスのパブリックIPアドレス</p>
<hr>
<h2><span id="toc42">⑫ ターゲットグループの作成</span></h2>
<p><strong>AWSコンソール → EC2 → ターゲットグループ → 「ターゲットグループの作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ターゲットタイプ</td>
<td><strong>インスタンス</strong></td>
</tr>
<tr>
<td>ターゲットグループ名</td>
<td><code>my-webapp-tg</code></td>
</tr>
<tr>
<td>プロトコル</td>
<td><strong>HTTP</strong></td>
</tr>
<tr>
<td>ポート</td>
<td><strong>8080</strong></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
<tr>
<td>プロトコルバージョン</td>
<td><strong>HTTP1</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc43">ヘルスチェック</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ヘルスチェックプロトコル</td>
<td>HTTP</td>
</tr>
<tr>
<td>ヘルスチェックパス</td>
<td><code>/</code></td>
</tr>
</tbody>
</table>
<p>「次へ」をクリック。</p>
<h3><span id="toc44">ターゲットの登録</span></h3>
<p>一覧から <code>my-webapp-ap-instance</code> を選択 → ポートが <strong>8080</strong> であることを確認 → 「保留中として以下を含める」をクリック → 「ターゲットグループの作成」</p>
<hr>
<h2><span id="toc45">⑬ ALBの作成・リスナー設定</span></h2>
<p><strong>AWSコンソール → EC2 → ロードバランサー → 「ロードバランサーの作成」</strong></p>
<p>「Application Load Balancer」の「作成」をクリック。</p>
<h3><span id="toc46">基本的な設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ロードバランサー名</td>
<td><code>my-webapp-alb</code></td>
</tr>
<tr>
<td>スキーム</td>
<td><strong>インターネット向け</strong></td>
</tr>
<tr>
<td>IPアドレスタイプ</td>
<td><strong>IPv4</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc47">ネットワークマッピング</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC</td>
<td><code>my-webapp-vpc</code></td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code>（<code>my-webapp-public-subnet-1</code>）と <code>ap-northeast-1c</code>（<code>my-webapp-public-subnet-2</code>）の<strong>両方</strong>を選択</td>
</tr>
</tbody>
</table>
<h3><span id="toc48">セキュリティグループ</span></h3>
<p><code>my-webapp-alb-sg</code> のみを選択（デフォルトSGは削除）。</p>
<h3><span id="toc49">リスナーとルーティング</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>プロトコル</td>
<td>HTTP</td>
</tr>
<tr>
<td>ポート</td>
<td>80</td>
</tr>
<tr>
<td>デフォルトアクション</td>
<td><code>my-webapp-tg</code> に転送</td>
</tr>
</tbody>
</table>
<p>「ロードバランサーの作成」をクリック。</p>
<p><strong>控えておく情報</strong>: 「DNS名」（例: <code>my-webapp-alb-1234567890.ap-northeast-1.elb.amazonaws.com</code>）</p>
<hr>
<h2><span id="toc50">⑭ Webアプリのデプロイ</span></h2>
<p>これが前回のハンズオンとの最大の違いです。WARファイルをEC2に転送してTomcatにデプロイします。</p>
<h3><span id="toc51">14-1. EC2にSSH接続する</span></h3>
<pre><code class="language-cmd">ssh -i C:\Users\ユーザー名\.ssh\my-webapp-key.pem ec2-user@（EC2のパブリックIP）</code></pre>
<h3><span id="toc52">14-2. setenv.sh を作成してDB接続情報を注入する</span></h3>
<p>TomcatはEC2起動時に <code>/opt/tomcat/bin/setenv.sh</code> を読み込み、環境変数をJVMに渡します。Spring Bootアプリは <code>DB_HOST</code> と <code>DB_PASSWORD</code> 環境変数からDB接続情報を取得します。</p>
<pre><code class="language-bash">DB_PASSWORD=$(aws ssm get-parameter \
  --name "/my/webapp/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1 \
  --with-decryption)

# setenv.shを作成（RDSエンドポイントは⑩で控えた値に置き換える）
sudo tee /opt/tomcat/bin/setenv.sh &lt;&lt; EOF
export DB_HOST=my-webapp-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
export DB_PASSWORD=$DB_PASSWORD
EOF

sudo chmod +x /opt/tomcat/bin/setenv.sh</code></pre>
<p>内容を確認します。</p>
<pre><code class="language-bash">sudo cat /opt/tomcat/bin/setenv.sh</code></pre>
<pre><code class="language-plaintext">export DB_HOST=my-webapp-rds-mysql.xxxxx.ap-northeast-1.rds.amazonaws.com
export DB_PASSWORD=（設定したパスワード）</code></pre>
<h3><span id="toc53">14-3. WARファイルをEC2に転送する</span></h3>
<p>**ローカルの別ターミナル（SSH接続していないターミナル）**で実行します。</p>
<pre><code class="language-cmd">scp -i C:\Users\ユーザー名\.ssh\my-webapp-key.pem ^
  C:\（プロジェクトフォルダ）\alb-ec2-rds-webapp\app\target\webapp.war ^
  ec2-user@（EC2のパブリックIP）:/home/ec2-user/</code></pre>
<p>転送完了後、SSH接続中のターミナルで確認します。</p>
<pre><code class="language-bash">ls -lh ~/webapp.war</code></pre>
<h3><span id="toc54">14-4. Tomcatを停止してWARをデプロイする</span></h3>
<pre><code class="language-bash"># Tomcatを停止
sudo /opt/tomcat/bin/shutdown.sh
sleep 3

# 既存のROOTアプリを削除してWARをデプロイ
sudo rm -rf /opt/tomcat/webapps/ROOT /opt/tomcat/webapps/ROOT.war
sudo cp ~/webapp.war /opt/tomcat/webapps/ROOT.war

# Tomcatを起動（setenv.shが読み込まれる）
sudo /opt/tomcat/bin/startup.sh</code></pre>
<h3><span id="toc55">14-5. デプロイを確認する</span></h3>
<p>Tomcatは <code>ROOT.war</code> を自動展開してアプリを起動します。1〜2分待ってから確認します。</p>
<pre><code class="language-bash"># Tomcatログを確認
sudo tail -50 /opt/tomcat/logs/catalina.out</code></pre>
<p>以下のログが表示されれば成功です。</p>
<pre><code class="language-plaintext">INFO: Starting ProtocolHandler ["http-nio-8080"]
INFO: Server startup in [XXXX] milliseconds</code></pre>
<pre><code class="language-bash"># EC2内からの疎通確認
curl -s http://localhost:8080/ | head -5</code></pre>
<p>HTMLが返ってくればデプロイ成功です。</p>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc56">⑮ 動作確認</span></h2>
<h3><span id="toc57">15-1. ALBのヘルスチェック確認</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-webapp-tg</code> → 「ターゲット」タブ</strong></p>
<p>EC2のステータスが <strong>「healthy」</strong> になるまで待ちます（Tomcat起動後1〜3分）。</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>異常（⑭のデプロイ手順を確認）</td>
</tr>
</tbody>
</table>
<p><!-- ![ターゲットグループのHealthy確認画面](images/target-healthy.jpg) --></p>
<h3><span id="toc58">15-2. ALB経由でWebアプリにアクセスする</span></h3>
<p>⑬で控えたALBのDNS名をブラウザで開きます。</p>
<pre><code class="language-plaintext">http://（ALBのDNS名）</code></pre>
<p>以下の画面が表示されれば成功です。</p>
<pre><code class="language-plaintext">Spring Boot + RDS Item Manager
ALB → EC2(Tomcat) → RDS MySQL の3層構成で動作しています。
アイテムの追加・削除がRDSに永続化されます。</code></pre>
<h3><span id="toc59">15-3. アイテムの追加・削除を試す</span></h3>
<ol>
<li>テキストボックスにアイテム名（例: <code>Apple</code>）を入力して「追加」をクリック</li>
<li>アイテムが一覧に表示されることを確認</li>
<li>「削除」ボタンでアイテムが削除されることを確認</li>
</ol>
<p>ブラウザをリロードしてもデータが保持されていれば、RDSへの永続化が成功しています。</p>
<p><!-- ![アイテム追加・削除のCRUD動作確認](images/crud-demo.jpg) --></p>
<h3><span id="toc60">15-4. RDSのデータをSQLで直接確認する（任意）</span></h3>
<pre><code class="language-bash">ssh -i C:\Users\ユーザー名\.ssh\my-webapp-key.pem ec2-user@（EC2のパブリックIP）</code></pre>
<pre><code class="language-bash">sudo dnf install -y mariadb105

DB_PASSWORD=$(aws ssm get-parameter \
  --name "/my/webapp/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1 \
  --with-decryption)

mysql -h （RDSエンドポイント） -u admin -p"$DB_PASSWORD" sampledb</code></pre>
<pre><code class="language-sql">SELECT * FROM items;
EXIT;</code></pre>
<p>Webアプリで追加したデータが表示されれば完全成功です。</p>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc61">⑯ リソースの削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p><strong>ALBの課金</strong>: インスタンスが稼働中は課金されます（約$0.008/時間 + LCU料金）。</p>
</blockquote>
<h3><span id="toc62">削除順序</span></h3>
<pre><code class="language-plaintext">1. ALBを削除
2. ターゲットグループを削除
3. EC2インスタンスを終了
4. RDSインスタンスを削除（約10〜15分）
5. RDS DBサブネットグループを削除
6. SSM Parameter Storeのパラメータを削除
7. IAMロールを削除
8. セキュリティグループを削除（RDS用 → EC2用 → ALB用の順）
9. VPCを削除（サブネット・ルートテーブルも一括削除）
10. キーペアを削除（任意）</code></pre>
<h3><span id="toc63">1. ALBを削除する</span></h3>
<p><strong>EC2 → ロードバランサー → <code>my-webapp-alb</code> を選択 → 「アクション」→「削除」</strong></p>
<h3><span id="toc64">2. ターゲットグループを削除する</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-webapp-tg</code> を選択 → 「アクション」→「削除」</strong></p>
<h3><span id="toc65">3. EC2インスタンスを終了する</span></h3>
<p><strong>EC2 → インスタンス → <code>my-webapp-ap-instance</code> を選択 → 「インスタンスの状態」→「インスタンスを終了」</strong></p>
<h3><span id="toc66">4. RDSインスタンスを削除する</span></h3>
<p><strong>Aurora and RDS → データベース → <code>my-webapp-rds-mysql</code> を選択 → 「アクション」→「削除」</strong></p>
<table>
<thead>
<tr>
<th>項目</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>最終スナップショットを作成</td>
<td><strong>チェックを外したまま</strong></td>
</tr>
<tr>
<td>「私は〜を了承します。」</td>
<td><strong>チェックを入れる</strong></td>
</tr>
<tr>
<td>確認フィールド</td>
<td><code>delete me</code> と入力</td>
</tr>
</tbody>
</table>
<h3><span id="toc67">5. RDS DBサブネットグループを削除する</span></h3>
<p><strong>Aurora and RDS → サブネットグループ → <code>my-webapp-rds-subnet-group</code> → 「削除」</strong></p>
<h3><span id="toc68">6. SSM Parameter Storeを削除する</span></h3>
<p><strong>Systems Manager → パラメータストア → <code>/my/webapp/db-password</code> → 「削除」</strong></p>
<h3><span id="toc69">7. IAMロールを削除する</span></h3>
<p><strong>IAM → ロール → <code>my-webapp-ec2-role</code> を選択 → 「削除」</strong></p>
<h3><span id="toc70">8. セキュリティグループを削除する</span></h3>
<p>依存関係があるため以下の順番で削除します。</p>
<ol>
<li><code>my-webapp-rds-sg</code></li>
<li><code>my-webapp-ec2-sg</code></li>
<li><code>my-webapp-alb-sg</code></li>
</ol>
<h3><span id="toc71">9. VPCを削除する</span></h3>
<p><strong>VPC → お使いのVPC → <code>my-webapp-vpc</code> を選択 → 「アクション」→「VPCの削除」</strong></p>
<blockquote>
<p>VPCを削除するとサブネット・ルートテーブルも一括削除されます。IGWのデタッチが必要な場合は先に行います。</p>
</blockquote>
<h3><span id="toc72">10. キーペアを削除する（任意）</span></h3>
<p><strong>EC2 → キーペア → <code>my-webapp-key</code> を選択 → 「アクション」→「削除」</strong></p>
<hr>
<h2><span id="toc73">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td>ALBに繋がらない</td>
<td>TomcatまたはWebアプリが起動していない</td>
<td>TGのヘルスチェックステータスを確認</td>
</tr>
<tr>
<td>ヘルスチェックがUnhealthy</td>
<td>Tomcatの起動失敗 or WARのデプロイ失敗</td>
<td><code>sudo tail -50 /opt/tomcat/logs/catalina.out</code> を確認</td>
</tr>
<tr>
<td>アクセスはできるが500エラー</td>
<td>DB接続失敗（setenv.sh不正）</td>
<td><code>sudo cat /opt/tomcat/bin/setenv.sh</code> でDB_HOSTとDB_PASSWORDを確認</td>
</tr>
<tr>
<td>アイテムが保存されない</td>
<td>DBに接続できていない</td>
<td>catalina.outにDB接続エラーが出ていないか確認</td>
</tr>
<tr>
<td><code>scp</code> でファイル転送できない</td>
<td>SSHが通らない</td>
<td>EC2 SGのSSHルール（22番ポート）が自分のIPを許可しているか確認</td>
</tr>
<tr>
<td>Tomcat起動失敗（ポート競合）</td>
<td>すでにTomcatが動いている</td>
<td><code>sudo /opt/tomcat/bin/shutdown.sh</code> 後に起動し直す</td>
</tr>
<tr>
<td>EC2 SGアウトバウンドルール不正</td>
<td>Javaインストール・wget失敗</td>
<td>EC2 SG → アウトバウンドルールに「すべてのトラフィック 0.0.0.0/0」を追加</td>
</tr>
<tr>
<td><code>ERROR 1049: Unknown database</code></td>
<td>RDS作成時にDB名を指定し忘れた</td>
<td>MySQLに接続して <code>CREATE DATABASE sampledb;</code> を実行</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc74">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>⓪</td>
<td>Mavenで <code>webapp.war</code> をビルド（初回のみ）</td>
</tr>
<tr>
<td>①〜⑤</td>
<td>VPC / サブネット4つ / IGW / ルートテーブルの設定</td>
</tr>
<tr>
<td>⑥</td>
<td><strong>3段階のSG</strong>（ALB → EC2 → RDS）でSG-to-SG制御</td>
</tr>
<tr>
<td>⑦⑧</td>
<td>IAMロール + Parameter Store（SecureString）でパスワードを安全に管理</td>
</tr>
<tr>
<td>⑨⑩</td>
<td>DBサブネットグループ + RDS MySQL 8.0</td>
</tr>
<tr>
<td>⑪</td>
<td>EC2起動（UserDataでTomcat自動セットアップ）</td>
</tr>
<tr>
<td>⑫⑬</td>
<td>ターゲットグループ + ALB作成</td>
</tr>
<tr>
<td>⑭</td>
<td><strong>setenv.shでDB情報を注入 → SCPでWAR転送 → Tomcatにデプロイ</strong></td>
</tr>
<tr>
<td>⑮</td>
<td>ALB DNS名でアクセス → Spring Boot CRUD画面で動作確認</td>
</tr>
</tbody>
</table>
<p>前回（静的HTML）との最大の違いは、⑭のWARデプロイです。<code>setenv.sh</code> でSSMから取得したDB接続情報を環境変数としてTomcatに渡し、Spring BootアプリがRDS MySQLに接続してCRUD操作できる本格的なWebアプリになりました。</p>
<p>CloudFormationでインフラ構築からWARデプロイまで自動化したい場合は、<a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/">CloudFormation版ハンズオン</a>を参照してください。S3にWARをアップロードするだけで、UserDataが自動デプロイまで行います。</p><p>The post <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/">AWSコンソールでSpring Boot WebアプリをALB + EC2(Tomcat) + RDS にデプロイする手順【WAR / JdbcTemplate / CRUD】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CloudFormationでSpring Boot WebアプリをALB + EC2(Tomcat) + RDS に自動デプロイする手順【S3 + UserData / WAR自動配布】</title>
		<link>https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/</link>
					<comments>https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 18 Apr 2026 04:45:25 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[JDBC]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[S3]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[UserData]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[WAR]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[初心者]]></category>
		<category><![CDATA[自動デプロイ]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20363</guid>

					<description><![CDATA[<p>目次 はじめにCloudFormation vs コンソール：どれだけ違うかキーワード解説前提条件構築されるリソース（約30個）作業順序⓪ Webアプリのビルド⓪-2 自分のIPアドレスを確認する① キーペアの作成（コン [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/">CloudFormationでSpring Boot WebアプリをALB + EC2(Tomcat) + RDS に自動デプロイする手順【S3 + UserData / WAR自動配布】</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-12" checked><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">CloudFormation vs コンソール：どれだけ違うか</a></li><li><a href="#toc3" tabindex="0">キーワード解説</a></li><li><a href="#toc4" tabindex="0">前提条件</a></li><li><a href="#toc5" tabindex="0">構築されるリソース（約30個）</a></li><li><a href="#toc6" tabindex="0">作業順序</a></li><li><a href="#toc7" tabindex="0">⓪ Webアプリのビルド</a></li><li><a href="#toc8" tabindex="0">⓪-2 自分のIPアドレスを確認する</a></li><li><a href="#toc9" tabindex="0">① キーペアの作成（コンソールで実施）</a></li><li><a href="#toc10" tabindex="0">② S3バケットの作成とWARのアップロード</a><ol><li><a href="#toc11" tabindex="0">プロジェクトフォルダに移動する</a></li><li><a href="#toc12" tabindex="0">S3バケットを作成する</a></li><li><a href="#toc13" tabindex="0">WARをS3にアップロードする</a></li></ol></li><li><a href="#toc14" tabindex="0">③ スタックの作成</a></li><li><a href="#toc15" tabindex="0">④ 作成完了・Outputsの確認</a><ol><li><a href="#toc16" tabindex="0">作成状況の確認</a></li><li><a href="#toc17" tabindex="0">Outputs（接続情報）の確認</a></li></ol></li><li><a href="#toc18" tabindex="0">⑤ 動作確認</a><ol><li><a href="#toc19" tabindex="0">ALBのヘルスチェック確認</a></li><li><a href="#toc20" tabindex="0">ALB経由でWebアプリにアクセスする</a></li><li><a href="#toc21" tabindex="0">アイテムの追加・削除を試す</a></li><li><a href="#toc22" tabindex="0">（任意）ログでデプロイ過程を確認する</a></li></ol></li><li><a href="#toc23" tabindex="0">⑥ スタックの削除</a><ol><li><a href="#toc24" tabindex="0">スタックを削除する</a></li><li><a href="#toc25" tabindex="0">削除状況の確認</a></li><li><a href="#toc26" tabindex="0">S3バケットを削除する</a></li><li><a href="#toc27" tabindex="0">キーペアを削除する（任意）</a></li></ol></li><li><a href="#toc28" tabindex="0">コンソール版との比較</a></li><li><a href="#toc29" tabindex="0">トラブルシューティング</a><ol><li><a href="#toc30" tabindex="0">失敗時の詳細確認コマンド</a></li></ol></li><li><a href="#toc31" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>前回の<a href="https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/">AWSコンソール版ハンズオン（Spring Boot Webアプリ）</a>では、コンソールでインフラを手動構築してSCPでWARをデプロイしました。</p>
<p>この記事では、同じ構成を <strong>CloudFormation</strong> で自動化します。<code>template.yaml</code> 1ファイルにVPC・SG・IAM・SSM・RDS・ALB・EC2（約30リソース）を定義し、さらに <strong>EC2のUserDataがS3からWARを自動取得してTomcatにデプロイ</strong>するため、コマンド数本でWebアプリが動く状態まで一気に構築できます。</p>
<pre><code class="language-plaintext">ローカル環境（VSCode）
  ├── app/target/webapp.war（mvn packageでビルド済み）
  ├── template.yaml（約30リソースを定義）
  └── AWS CLI コマンド
        ↓ スタック作成（コマンド1本）
AWS環境
  ├── S3バケット（webapp.war置き場）
  └── VPC（10.0.0.0/16）
        ├── パブリックサブネット1・2（ALB + EC2）
        ├── プライベートサブネット1・2（RDS）
        ├── IGW + ルートテーブル
        ├── IAMロール（SSM + S3読み取り権限）
        ├── ALB SG → EC2 SG → RDS SG（SG-to-SG制御）
        ├── SSM Parameter Store（/my/webapp/db-password）
        ├── RDS MySQL 8.0（db.t3.micro / sampledb）
        ├── ALB（HTTP:80）→ TG（HTTP:8080）
        └── EC2（t2.micro）
              UserDataが自動実行:
              1. Java + Tomcat インストール
              2. SSMからDBパスワード取得 → setenv.sh作成
              3. S3からwebapp.warをダウンロード → ROOT.warとして配置
              4. Tomcat起動（Spring Bootアプリが自動展開）</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>WARのデプロイをS3 + UserDataで完全自動化する設計</li>
<li>SSMからDB接続情報を取得してsetenv.shを自動作成する仕組み</li>
<li><code>delete-stack</code> 1本でALB・RDS・IAMなど全リソースを自動削除</li>
</ul>
<p><strong>テンプレートとアプリのソースコードはGitHubで公開しています：</strong></p>
<blockquote>

<a rel="noopener" href="https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp" title="aws-handson/alb-ec2-rds-webapp at main · caymezon/aws-handson" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Fcaymezon%2Faws-handson%2Ftree%2Fmain%2Falb-ec2-rds-webapp?w=160&h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">aws-handson/alb-ec2-rds-webapp at main · caymezon/aws-handson</div><div class="blogcard-snippet external-blogcard-snippet">AWS HANDSON. Contribute to caymezon/aws-handson development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
</blockquote>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/">AWSコンソール版ハンズオン</a> の比較記事です。</strong><br />コンソール版でSCPデプロイの仕組みを理解した後に読むと理解が深まります。</p>
</blockquote>
<hr>
<p><!-- ![CloudFormationスタック作成完了・Outputs確認画面](images/cfn-create-complete.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">CloudFormation vs コンソール：どれだけ違うか</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>CloudFormation</th>
<th>コンソール（手動）</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>VPC + サブネット4つ + IGW + ルートテーブル</strong></td>
<td>template.yaml に定義済み</td>
<td>約20〜30分・複数画面</td>
</tr>
<tr>
<td><strong>SG 3つ（ALB/EC2/RDS）の作成</strong></td>
<td>template.yaml に定義済み</td>
<td>3画面で個別設定</td>
</tr>
<tr>
<td><strong>ALB + TG + Listener の作成</strong></td>
<td>template.yaml に定義済み</td>
<td>3つの画面で設定</td>
</tr>
<tr>
<td><strong>WARのデプロイ</strong></td>
<td>S3 + UserDataで<strong>自動</strong></td>
<td>SCPで転送 + SSH操作</td>
</tr>
<tr>
<td><strong>DB接続設定（setenv.sh）</strong></td>
<td>UserDataがSSMから<strong>自動</strong>取得・作成</td>
<td>SSH接続して手動作成</td>
</tr>
<tr>
<td><strong>全体のデプロイ</strong></td>
<td>コマンド数本（RDS待ち15〜20分）</td>
<td><strong>約60〜90分</strong></td>
</tr>
<tr>
<td><strong>削除</strong></td>
<td><code>delete-stack</code> 1本</td>
<td><strong>16ステップ手動</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">キーワード解説</span></h2>
<table>
<thead>
<tr>
<th>用語</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CloudFormation</strong></td>
<td>AWSが提供するIaC（Infrastructure as Code）サービス</td>
</tr>
<tr>
<td><strong>スタック</strong></td>
<td>CloudFormationが管理するリソースのまとまり。今回は約30リソースを1スタックで管理</td>
</tr>
<tr>
<td><strong>S3 VPC Gateway Endpoint</strong></td>
<td>EC2からS3にインターネットを経由せずアクセスするための無料エンドポイント</td>
</tr>
<tr>
<td><strong>UserData</strong></td>
<td>EC2初回起動時に自動実行されるシェルスクリプト</td>
</tr>
<tr>
<td><strong>CAPABILITY_NAMED_IAM</strong></td>
<td>名前付きIAMリソースを含むスタック作成に必要なフラグ</td>
</tr>
<tr>
<td><strong>DeletionPolicy: Delete</strong></td>
<td>スタック削除時にRDSも確実に削除する設定</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">前提条件</span></h2>
<pre><code class="language-cmd">aws sts get-caller-identity
aws --version
java -version
mvn -version</code></pre>
<hr>
<h2><span id="toc5">構築されるリソース（約30個）</span></h2>
<p><code>template.yaml</code> の詳細はGitHubを参照してください：</p>
<blockquote>

<a rel="noopener" href="https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp/template.yaml" title="aws-handson/alb-ec2-rds-webapp/template.yaml at main ?? caymezon/aws-handson" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://caymezon.com/wp-content/uploads/cocoon-resources/blog-card-cache/5980334f3509003dcb5664ba60ee6db4.jpg" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">aws-handson/alb-ec2-rds-webapp/template.yaml at main ?? caymezon/aws-handson</div><div class="blogcard-snippet external-blogcard-snippet">AWS HANDSON. Contribute to caymezon/aws-handson development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/caymezon/aws-handson/blob/main/alb-ec2-rds-webapp/template.yaml" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
</blockquote>
<table>
<thead>
<tr>
<th>カテゴリ</th>
<th>リソース</th>
</tr>
</thead>
<tbody>
<tr>
<td>ネットワーク</td>
<td>VPC / IGW / パブリックサブネット×2 / プライベートサブネット×2 / ルートテーブル×2 / S3 VPC Endpoint</td>
</tr>
<tr>
<td>セキュリティ</td>
<td>ALB SG / EC2 SG / RDS SG</td>
</tr>
<tr>
<td>IAM</td>
<td>EC2ロール（SSM + S3読み取り権限）+ インスタンスプロファイル</td>
</tr>
<tr>
<td>パラメータ</td>
<td>SSM Parameter Store（<code>/my/webapp/db-password</code>）</td>
</tr>
<tr>
<td>データベース</td>
<td>RDS DBサブネットグループ / RDS MySQL 8.0</td>
</tr>
<tr>
<td>ロードバランサー</td>
<td>ターゲットグループ / ALB本体 / リスナー</td>
</tr>
<tr>
<td>コンピュート</td>
<td>EC2インスタンス（Tomcat + WAR自動デプロイ）</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc6">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ Webアプリをビルドする（mvn package）
      ↓
⓪-2 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（コンソールで実施）
      ↓
② S3バケットを作成してWARをアップロードする
      ↓
③ スタックを作成する（aws cloudformation create-stack）
      ↓
④ 作成完了・Outputsを確認する（RDS待ち15〜20分）
      ↓
⑤ 動作確認（ALBアクセス・アイテム追加削除）
      ↓
⑥ スタックを削除する（S3バケットも忘れず削除）</code></pre>
<hr>
<h2><span id="toc7">⓪ Webアプリのビルド</span></h2>
<p>アプリのソースコードはGitHubで公開しています：</p>
<blockquote>

<a rel="noopener" href="https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp/app" title="aws-handson/alb-ec2-rds-webapp/app at main ?? caymezon/aws-handson" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fgithub.com%2Fcaymezon%2Faws-handson%2Ftree%2Fmain%2Falb-ec2-rds-webapp%2Fapp?w=160&h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">aws-handson/alb-ec2-rds-webapp/app at main ?? caymezon/aws-handson</div><div class="blogcard-snippet external-blogcard-snippet">AWS HANDSON. Contribute to caymezon/aws-handson development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/caymezon/aws-handson/tree/main/alb-ec2-rds-webapp/app" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>
</blockquote>
<pre><code class="language-cmd">cd C:\（プロジェクトフォルダ）\alb-ec2-rds-webapp\app
mvn package -DskipTests
dir target\webapp.war</code></pre>
<p><code>webapp.war</code>（約20〜30MB）が生成されていれば成功です。</p>
<hr>
<h2><span id="toc8">⓪-2 自分の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>
<p>CloudFormationではキーペアのダウンロードができないため、コンソールで先に作成します。</p>
<p><strong>AWSコンソール → EC2 → キーペア → 「キーペアを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-webapp-key</code></td>
</tr>
<tr>
<td>キーペアのタイプ</td>
<td><strong>RSA</strong></td>
</tr>
<tr>
<td>プライベートキーファイル形式</td>
<td><strong>.pem</strong></td>
</tr>
</tbody>
</table>
<p>ダウンロードされた <code>my-webapp-key.pem</code> を保存します。</p>
<pre><code class="language-plaintext">C:\Users\ユーザー名\.ssh\my-webapp-key.pem</code></pre>
<hr>
<h2><span id="toc10">② S3バケットの作成とWARのアップロード</span></h2>
<p>EC2のUserDataがS3から <code>webapp.war</code> をダウンロードするため、<strong>スタック作成より前に</strong>アップロードしておく必要があります。</p>
<h3><span id="toc11">プロジェクトフォルダに移動する</span></h3>
<pre><code class="language-cmd">cd C:\（プロジェクトフォルダ）\alb-ec2-rds-webapp</code></pre>
<h3><span id="toc12">S3バケットを作成する</span></h3>
<p>バケット名はグローバルで一意である必要があります。末尾に任意の識別子を加えてユニークにしてください。</p>
<pre><code class="language-cmd">aws s3 mb s3://my-webapp-bucket-（任意の識別子） --region ap-northeast-1</code></pre>
<h3><span id="toc13">WARをS3にアップロードする</span></h3>
<pre><code class="language-cmd">aws s3 cp app\target\webapp.war s3://my-webapp-bucket-（任意の識別子）/webapp.war</code></pre>
<p>アップロードを確認します。</p>
<pre><code class="language-cmd">aws s3 ls s3://my-webapp-bucket-（任意の識別子）/</code></pre>
<pre><code class="language-plaintext">2026-04-18 10:00:00   26214400 webapp.war</code></pre>
<hr>
<h2><span id="toc14">③ スタックの作成</span></h2>
<p>以下のコマンドを実行します。各パラメータを自分の値に置き換えます。</p>
<pre><code class="language-cmd">aws cloudformation create-stack ^
  --stack-name my-webapp-stack ^
  --template-body file://template.yaml ^
  --region ap-northeast-1 ^
  --capabilities CAPABILITY_NAMED_IAM ^
  --parameters ^
    ParameterKey=KeyName,ParameterValue=my-webapp-key ^
    ParameterKey=MyIP,ParameterValue=（⓪-2で確認したIP）/32 ^
    ParameterKey=DBPassword,ParameterValue=（任意のパスワード） ^
    ParameterKey=WARBucketName,ParameterValue=my-webapp-bucket-（任意の識別子）</code></pre>
<blockquote>
<p><strong><code>CAPABILITY_NAMED_IAM</code> について</strong>: 名前を指定したIAMリソース（EC2ロールなど）を作成する場合に必要なフラグです。</p>
<p><strong><code>TomcatVersion</code> パラメータについて</strong>: デフォルト値は <code>10.1.28</code>。変更する場合は <code>ParameterKey=TomcatVersion,ParameterValue=10.1.xx</code> を追加します。利用可能なバージョンは <a href="https://archive.apache.org/dist/tomcat/tomcat-10/">https://archive.apache.org/dist/tomcat/tomcat-10/</a> で確認します。</p>
</blockquote>
<p>成功すると以下のような StackId が表示されます。</p>
<pre><code class="language-json">{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/my-webapp-stack/xxxxx"
}</code></pre>
<hr>
<h2><span id="toc15">④ 作成完了・Outputsの確認</span></h2>
<blockquote>
<p><strong>注意</strong>: RDSの作成には<strong>約15〜20分</strong>かかります。EC2のUserDataでWARのデプロイも行うため、さらに数分かかります。</p>
</blockquote>
<h3><span id="toc16">作成状況の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-webapp-stack ^
  --query "Stacks[0].StackStatus" ^
  --output text</code></pre>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_IN_PROGRESS</code></td>
<td>作成中（待つ）</td>
</tr>
<tr>
<td><code>CREATE_COMPLETE</code></td>
<td>作成完了</td>
</tr>
<tr>
<td><code>ROLLBACK_COMPLETE</code></td>
<td>失敗してロールバック完了（トラブルシューティングを参照）</td>
</tr>
</tbody>
</table>
<h3><span id="toc17">Outputs（接続情報）の確認</span></h3>
<p><code>CREATE_COMPLETE</code> になったら以下を実行します。</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-webapp-stack ^
  --query "Stacks[0].Outputs" ^
  --output table</code></pre>
<pre><code class="language-plaintext">+--------------------+-------------------------------------------------------------------+
|  OutputKey         |  OutputValue                                                      |
+--------------------+-------------------------------------------------------------------+
|  ALBEndpoint       |  http://my-webapp-alb-1234567890.ap-northeast-1.elb.amazonaws.com |
|  EC2PublicIP       |  x.x.x.x                                                         |
|  RDSEndpoint       |  my-webapp-rds-mysql.xxxxx.ap-northeast-1.rds.amazonaws.com      |
|  DBPasswordParamName |  /my/webapp/db-password                                         |
|  SSHCommand        |  ssh -i ~/.ssh/my-webapp-key.pem ec2-user@x.x.x.x               |
+--------------------+-------------------------------------------------------------------+</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>EC2PublicIP</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CloudFormation Outputs確認コマンドの実行結果](images/cfn-outputs.jpg) --></p>
<hr>
<h2><span id="toc18">⑤ 動作確認</span></h2>
<h3><span id="toc19">ALBのヘルスチェック確認</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-webapp-tg</code> → 「ターゲット」タブ</strong></p>
<p>EC2のステータスが <strong>「healthy」</strong> になるまで待ちます（EC2起動後5〜10分）。</p>
<blockquote>
<p>Tomcatの起動 + WARのデプロイはEC2「実行中」からさらに数分かかります。<code>initial</code> や <code>unhealthy</code> が続く場合はしばらく待ちます。</p>
</blockquote>
<h3><span id="toc20">ALB経由でWebアプリにアクセスする</span></h3>
<p>Outputsに表示された <code>ALBEndpoint</code> をブラウザで開きます。</p>
<pre><code class="language-plaintext">http://（ALBEndpointのDNS名）</code></pre>
<pre><code class="language-plaintext">Spring Boot + RDS Item Manager
ALB → EC2(Tomcat) → RDS MySQL の3層構成で動作しています。</code></pre>
<p>が表示されれば成功です。</p>
<h3><span id="toc21">アイテムの追加・削除を試す</span></h3>
<ol>
<li>テキストボックスにアイテム名を入力して「追加」をクリック</li>
<li>アイテムが一覧に表示されることを確認</li>
<li>「削除」ボタンでアイテムが削除されることを確認</li>
</ol>
<p>ブラウザをリロードしてもデータが保持されていれば、RDSへの永続化が成功しています。</p>
<h3><span id="toc22">（任意）ログでデプロイ過程を確認する</span></h3>
<p>OutputsのSSHCommandでEC2に接続して確認します。</p>
<pre><code class="language-bash">sudo cat /var/log/user-data.log

# Tomcatのログ（Spring Bootのスタートアップログ）
sudo tail -50 /opt/tomcat/logs/catalina.out

# setenv.shの内容確認（DB接続情報が正しく設定されているか）
sudo cat /opt/tomcat/bin/setenv.sh

exit</code></pre>
<hr>
<h2><span id="toc23">⑥ スタックの削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<h3><span id="toc24">スタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-webapp-stack ^
  --region ap-northeast-1</code></pre>
<blockquote>
<p><strong>注意</strong>: RDS削除には<strong>約10〜15分</strong>かかります。</p>
</blockquote>
<h3><span id="toc25">削除状況の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-webapp-stack ^
  --query "Stacks[0].StackStatus" ^
  --output text</code></pre>
<p>削除完了の確認（以下のエラーが表示されれば削除完了）:</p>
<pre><code class="language-plaintext">An error occurred (ValidationError) when calling the DescribeStacks operation:
Stack with id my-webapp-stack does not exist</code></pre>
<h3><span id="toc26">S3バケットを削除する</span></h3>
<p>S3バケットはCloudFormationで管理していないため手動で削除します。</p>
<pre><code class="language-cmd">aws s3 rm s3://my-webapp-bucket-（任意の識別子） --recursive
aws s3 rb s3://my-webapp-bucket-（任意の識別子）</code></pre>
<h3><span id="toc27">キーペアを削除する（任意）</span></h3>
<p><strong>EC2 → キーペア → <code>my-webapp-key</code> を選択 → 「アクション」→「削除」</strong></p>
<hr>
<h2><span id="toc28">コンソール版との比較</span></h2>
<table>
<thead>
<tr>
<th>作業</th>
<th>コンソール版</th>
<th>CloudFormation版</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC + サブネット + IGW + RT</td>
<td>約20〜30分</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>SG 3つの作成</td>
<td>3画面で個別設定</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>ALB + TG + Listener</td>
<td>3つの画面で設定</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>WARのデプロイ</td>
<td>SCP + SSH操作</td>
<td>S3 + UserDataで<strong>自動</strong></td>
</tr>
<tr>
<td>DB接続設定（setenv.sh）</td>
<td>SSH接続して手動作成</td>
<td>UserDataがSSMから<strong>自動</strong>取得・作成</td>
</tr>
<tr>
<td>全体のデプロイ</td>
<td>約60〜90分</td>
<td>コマンド数本（RDS待ち15〜20分）</td>
</tr>
<tr>
<td>削除</td>
<td>16ステップ手動</td>
<td><code>delete-stack</code> 1本（+S3手動削除）</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc29">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_FAILED</code> → ROLLBACKになる</td>
<td>パラメータ不正・リソース上限など</td>
<td>コンソールのCloudFormationイベントタブでエラー詳細を確認</td>
</tr>
<tr>
<td>ALBに繋がらない</td>
<td>WARデプロイがまだ完了していない</td>
<td>TGのヘルスチェックがHealthyになるまで待つ（5〜10分）</td>
</tr>
<tr>
<td>ヘルスチェックがUnhealthy</td>
<td>WAR取得失敗 or Spring Boot起動失敗</td>
<td>SSH接続して <code>sudo cat /var/log/user-data.log</code> を確認</td>
</tr>
<tr>
<td><code>NoSuchKey</code> エラー</td>
<td>S3にwebapp.warがない</td>
<td><code>aws s3 ls s3://バケット名/</code> で存在確認</td>
</tr>
<tr>
<td>DB接続エラー（500エラー）</td>
<td>setenv.shのDB_HOSTが空</td>
<td><code>sudo cat /opt/tomcat/bin/setenv.sh</code> で確認</td>
</tr>
<tr>
<td>Tomcatバージョンエラー</td>
<td>指定バージョンがアーカイブに存在しない</td>
<td><a href="https://archive.apache.org/dist/tomcat/tomcat-10/">https://archive.apache.org/dist/tomcat/tomcat-10/</a> で確認して <code>TomcatVersion</code> パラメータを更新</td>
</tr>
<tr>
<td><code>DELETE_FAILED</code></td>
<td>リソースの依存関係が残っている</td>
<td>コンソールからALBを手動削除してから <code>delete-stack</code> を再実行</td>
</tr>
</tbody>
</table>
<h3><span id="toc30">失敗時の詳細確認コマンド</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stack-events ^
  --stack-name my-webapp-stack ^
  --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[ResourceType,ResourceStatusReason]" ^
  --output table</code></pre>
<hr>
<h2><span id="toc31">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>⓪</td>
<td>Mavenで <code>webapp.war</code> をビルド</td>
</tr>
<tr>
<td>①</td>
<td>キーペアをコンソールで作成</td>
</tr>
<tr>
<td>②</td>
<td>S3バケット作成 + WARをアップロード</td>
</tr>
<tr>
<td>③</td>
<td><code>create-stack</code> 1本（RDS待ち15〜20分）</td>
</tr>
<tr>
<td>④</td>
<td><code>CREATE_COMPLETE</code> になったらOutputsでALBエンドポイント・EC2IP・RDSエンドポイントを確認</td>
</tr>
<tr>
<td>⑤</td>
<td>ヘルスチェックがHealthyになったらブラウザでCRUD動作確認</td>
</tr>
<tr>
<td>⑥</td>
<td><code>delete-stack</code> 1本 + S3バケット手動削除</td>
</tr>
</tbody>
</table>
<p>CloudFormation版の最大のポイントは<strong>WARデプロイの自動化</strong>です。コンソール版ではSCPでWARを転送してSSHでsetenv.shを作成する必要がありましたが、今回はS3にWARをアップロードするだけでUserDataが全て自動で行います。</p>
<p>コンソール操作でSCPデプロイの仕組みを手を動かして確認したい場合は、<a href="https://caymezon.com/aws-handson-console-alb-ec2-rds-webapp/">AWSコンソール版ハンズオン</a>を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/">CloudFormationでSpring Boot WebアプリをALB + EC2(Tomcat) + RDS に自動デプロイする手順【S3 + UserData / WAR自動配布】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds-webapp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWSコンソールでALB + EC2(Tomcat) + RDS 3層構成を構築する手順【ヘルスチェック / SG-to-SG制御】</title>
		<link>https://caymezon.com/aws-handson-console-alb-ec2-rds/</link>
					<comments>https://caymezon.com/aws-handson-console-alb-ec2-rds/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Mon, 13 Apr 2026 00:09:11 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[3層構成]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWSコンソール]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[SG参照]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[セキュリティグループ]]></category>
		<category><![CDATA[ターゲットグループ]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[ヘルスチェック]]></category>
		<category><![CDATA[ロードバランサー]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20358</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説前フェーズとの違い使用するAWSサービス構築するリソース一覧全体の作業順序⓪ 自分のIPアドレスの確認① キーペアの作成② VPCの作成③ サブネットの作成（4つ）④ インターネットゲートウェ [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソールでALB + EC2(Tomcat) + RDS 3層構成を構築する手順【ヘルスチェック / SG-to-SG制御】</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-14" checked><label class="toc-title" for="toc-checkbox-14">目次</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">前フェーズとの違い</a></li><li><a href="#toc4" tabindex="0">使用するAWSサービス</a></li><li><a href="#toc5" tabindex="0">構築するリソース一覧</a></li><li><a href="#toc6" tabindex="0">全体の作業順序</a></li><li><a href="#toc7" tabindex="0">⓪ 自分のIPアドレスの確認</a></li><li><a href="#toc8" tabindex="0">① キーペアの作成</a></li><li><a href="#toc9" tabindex="0">② VPCの作成</a></li><li><a href="#toc10" tabindex="0">③ サブネットの作成（4つ）</a></li><li><a href="#toc11" tabindex="0">④ インターネットゲートウェイの作成・アタッチ</a><ol><li><a href="#toc12" tabindex="0">作成</a></li><li><a href="#toc13" tabindex="0">VPCへのアタッチ</a></li></ol></li><li><a href="#toc14" tabindex="0">⑤ ルートテーブルの設定</a><ol><li><a href="#toc15" tabindex="0">5-1. パブリック用ルートテーブル</a></li><li><a href="#toc16" tabindex="0">5-2. プライベート用ルートテーブル</a></li></ol></li><li><a href="#toc17" tabindex="0">⑥ セキュリティグループの作成</a><ol><li><a href="#toc18" tabindex="0">6-1. ALB用セキュリティグループ</a></li><li><a href="#toc19" tabindex="0">6-2. EC2用セキュリティグループ</a></li><li><a href="#toc20" tabindex="0">6-3. RDS用セキュリティグループ</a></li></ol></li><li><a href="#toc21" tabindex="0">⑦ IAMロールの作成</a><ol><li><a href="#toc22" tabindex="0">信頼されたエンティティ</a></li><li><a href="#toc23" tabindex="0">許可ポリシー</a></li><li><a href="#toc24" tabindex="0">ロール名</a></li></ol></li><li><a href="#toc25" tabindex="0">⑧ SSM Parameter Storeの作成</a></li><li><a href="#toc26" tabindex="0">⑨ RDS DBサブネットグループの作成</a></li><li><a href="#toc27" tabindex="0">⑩ RDS MySQL インスタンスの作成</a><ol><li><a href="#toc28" tabindex="0">エンジンの選択</a></li><li><a href="#toc29" tabindex="0">テンプレート</a></li><li><a href="#toc30" tabindex="0">設定</a></li><li><a href="#toc31" tabindex="0">インスタンスの設定</a></li><li><a href="#toc32" tabindex="0">ストレージ</a></li><li><a href="#toc33" tabindex="0">接続</a></li><li><a href="#toc34" tabindex="0">追加設定</a></li></ol></li><li><a href="#toc35" tabindex="0">⑪ EC2インスタンスの起動</a><ol><li><a href="#toc36" tabindex="0">基本設定</a></li><li><a href="#toc37" tabindex="0">ネットワーク設定</a></li><li><a href="#toc38" tabindex="0">IAMロール</a></li><li><a href="#toc39" tabindex="0">UserData</a></li><li><a href="#toc40" tabindex="0">タグ</a></li></ol></li><li><a href="#toc41" tabindex="0">⑫ ターゲットグループの作成</a><ol><li><a href="#toc42" tabindex="0">ターゲットタイプとプロトコル</a></li><li><a href="#toc43" tabindex="0">ヘルスチェック</a></li><li><a href="#toc44" tabindex="0">ターゲットの登録</a></li></ol></li><li><a href="#toc45" tabindex="0">⑬ ALBの作成・リスナー設定</a><ol><li><a href="#toc46" tabindex="0">基本的な設定</a></li><li><a href="#toc47" tabindex="0">ネットワークマッピング</a></li><li><a href="#toc48" tabindex="0">セキュリティグループ</a></li><li><a href="#toc49" tabindex="0">リスナーとルーティング</a></li></ol></li><li><a href="#toc50" tabindex="0">⑭ 動作確認</a><ol><li><a href="#toc51" tabindex="0">14-1. ALBのヘルスチェック確認</a></li><li><a href="#toc52" tabindex="0">14-2. ALB経由でWebアクセス確認</a></li><li><a href="#toc53" tabindex="0">14-3. EC2にSSH接続する</a></li><li><a href="#toc54" tabindex="0">14-4. Parameter StoreからDBパスワードを取得する</a></li><li><a href="#toc55" tabindex="0">14-5. RDSへのMySQL接続テスト</a></li><li><a href="#toc56" tabindex="0">14-6. SSH接続を切断する</a></li></ol></li><li><a href="#toc57" tabindex="0">⑮ リソースの削除</a><ol><li><a href="#toc58" tabindex="0">削除順序</a></li><li><a href="#toc59" tabindex="0">1. ALBを削除する</a></li><li><a href="#toc60" tabindex="0">2. ターゲットグループを削除する</a></li><li><a href="#toc61" tabindex="0">3. EC2インスタンスを終了する</a></li><li><a href="#toc62" tabindex="0">4. RDSインスタンスを削除する</a></li><li><a href="#toc63" tabindex="0">5. RDS DBサブネットグループを削除する</a></li><li><a href="#toc64" tabindex="0">6. SSM Parameter Storeのパラメータを削除する</a></li><li><a href="#toc65" tabindex="0">7. セキュリティグループを削除する</a></li><li><a href="#toc66" tabindex="0">8. ルートテーブルを削除する</a></li><li><a href="#toc67" tabindex="0">9. インターネットゲートウェイをデタッチ・削除する</a></li><li><a href="#toc68" tabindex="0">10. サブネットを削除する（4つ）</a></li><li><a href="#toc69" tabindex="0">11. VPCを削除する</a></li><li><a href="#toc70" tabindex="0">12. IAMロールを削除する</a></li><li><a href="#toc71" tabindex="0">13. キーペアを削除する（任意）</a></li></ol></li><li><a href="#toc72" tabindex="0">トラブルシューティング</a></li><li><a href="#toc73" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「EC2のIPが変わるたびに接続先を変えるのは面倒」「EC2を複数台に増やしてトラフィックを分散したい」——これを解決するのが <strong>ALB（Application Load Balancer）</strong> です。</p>
<p>この記事では、<strong>AWSコンソール（GUI）のみ</strong>を使って、ALB・EC2（Tomcat）・RDS MySQLの3層構成をゼロから手動構築するハンズオンを紹介します。</p>
<pre><code class="language-plaintext">インターネット
  ↓ HTTP(80)  ← ALB SGで全IPから許可
ALB（my-alb-alb）
  [パブリックサブネット1: 10.0.1.0/24 AZ-a]
  [パブリックサブネット2: 10.0.2.0/24 AZ-c]  ← ALBの2AZ要件
  ↓ HTTP(8080)  ← EC2 SGでALB SGからのみ許可（SG-to-SG）
EC2（my-alb-ap-instance）
  Tomcat（Javaアプリケーションサーバ）
  [パブリックサブネット1: 10.0.1.0/24 AZ-a]
  ↓ MySQL(3306)  ← RDS SGでEC2 SGからのみ許可（SG-to-SG）
RDS MySQL（my-alb-rds-mysql）
  [プライベートサブネット1: 10.0.3.0/24 AZ-a]

DBサブネットグループ
  ├── プライベートサブネット1（10.0.3.0/24）[AZ-a]
  └── プライベートサブネット2（10.0.4.0/24）[AZ-c]  ← 2AZ必須</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>ALBのリスナー・ターゲットグループ・ヘルスチェックの概念を理解</li>
<li>EC2（Tomcat）をALBの後ろに隠して直接アクセスを遮断する設計</li>
<li>SG-to-SGを2段階で使う3層制御（インターネット→ALB→EC2→RDS）</li>
<li>ヘルスチェックで「Healthy」になるまでALBがトラフィックを転送しない仕組みを体験</li>
</ul>
<p><strong>このハンズオンのポイント：</strong></p>
<p>前フェーズ（EC2 + RDS 2層構成）ではEC2に直接アクセスしていましたが、今回はALBを前段に追加します。ALBのDNS名でアクセスし、EC2のIPアドレスが変わっても影響を受けない設計になります。</p>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormation版ハンズオン</a> の比較記事です。</strong><br />コンソール操作でALB・ターゲットグループ・ヘルスチェックを視覚的に学びたい方向けです。</p>
</blockquote>
<hr>
<p><!-- ![ALBのターゲットグループでHealthy確認後、ブラウザでTomcat画面が表示されている](images/alb-healthy-access.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>ALB（Application Load Balancer）</strong></td>
<td>HTTP/HTTPSトラフィックをEC2に振り分けるL7ロードバランサー。EC2の死活監視も行う</td>
</tr>
<tr>
<td><strong>ターゲットグループ</strong></td>
<td>ALBのトラフィック転送先（EC2インスタンスなど）をまとめたグループ</td>
</tr>
<tr>
<td><strong>リスナー</strong></td>
<td>ALBがトラフィックを受け付けるポート・プロトコルの設定（今回はHTTP:80）</td>
</tr>
<tr>
<td><strong>ヘルスチェック</strong></td>
<td>ALBがバックエンドEC2に定期的にHTTPリクエストを送り正常性を確認する仕組み。Unhealthyなインスタンスへはトラフィックを転送しない</td>
</tr>
<tr>
<td><strong>Tomcat</strong></td>
<td>JavaアプリケーションサーバーのOSS実装。デフォルトポート8080</td>
</tr>
<tr>
<td><strong>SG-to-SG制御</strong></td>
<td>セキュリティグループのルールでソースにIPでなく別SGを指定する設定</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">前フェーズとの違い</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>Phase 2-1（EC2 + RDS）</th>
<th>Phase 2-2（ALB + EC2 + RDS）今回</th>
</tr>
</thead>
<tbody>
<tr>
<td>アクセス方法</td>
<td>EC2のパブリックIPに直接アクセス</td>
<td><strong>ALBのDNS名でアクセス</strong></td>
</tr>
<tr>
<td>Webサーバ</td>
<td>Apache httpd</td>
<td><strong>Tomcat（Java APサーバ）</strong></td>
</tr>
<tr>
<td>ロードバランシング</td>
<td>なし（EC2 1台）</td>
<td><strong>ALBによるHTTP負荷分散</strong></td>
</tr>
<tr>
<td>ヘルスチェック</td>
<td>なし</td>
<td><strong>ALBが定期的にEC2の死活監視</strong></td>
</tr>
<tr>
<td>EC2の直接公開</td>
<td>ポート80で直接公開</td>
<td><strong>ポート8080はALB SGからのみ許可</strong></td>
</tr>
<tr>
<td>サブネット数</td>
<td>3つ</td>
<td><strong>4つ（ALBの2AZ要件のため）</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">使用する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は無料枠がありません。ハンズオン後は必ず削除してください。</p>
</blockquote>
<hr>
<h2><span id="toc5">構築するリソース一覧</span></h2>
<table>
<thead>
<tr>
<th>順序</th>
<th>リソース</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td>⓪</td>
<td>（確認）自分のIPアドレス</td>
<td>SGの設定に使用</td>
</tr>
<tr>
<td>①</td>
<td>キーペア（<code>my-alb-key</code>）</td>
<td>EC2へのSSH認証鍵</td>
</tr>
<tr>
<td>②</td>
<td>VPC（<code>my-alb-vpc</code>）</td>
<td>独立したネットワーク空間</td>
</tr>
<tr>
<td>③</td>
<td>サブネット × 4</td>
<td>パブリック2 + プライベート2</td>
</tr>
<tr>
<td>④</td>
<td>インターネットゲートウェイ（<code>my-alb-igw</code>）</td>
<td>VPCをインターネットに接続</td>
</tr>
<tr>
<td>⑤</td>
<td>ルートテーブル × 2</td>
<td>パブリック/プライベートの通信経路</td>
</tr>
<tr>
<td>⑥</td>
<td>セキュリティグループ × 3</td>
<td>ALB用・EC2用・RDS用</td>
</tr>
<tr>
<td>⑦</td>
<td>IAMロール（<code>my-alb-ec2-role</code>）</td>
<td>EC2がSSM・Parameter Storeを使う権限</td>
</tr>
<tr>
<td>⑧</td>
<td>SSM Parameter Store</td>
<td>DBパスワードの保管</td>
</tr>
<tr>
<td>⑨</td>
<td>RDS DBサブネットグループ</td>
<td>RDSを配置できるサブネットを登録</td>
</tr>
<tr>
<td>⑩</td>
<td>RDS MySQL インスタンス</td>
<td>マネージドDBサーバ（作成に10〜15分）</td>
</tr>
<tr>
<td>⑪</td>
<td>EC2インスタンス（<code>my-alb-ap-instance</code>）</td>
<td>APサーバ（Tomcat）</td>
</tr>
<tr>
<td>⑫</td>
<td>ターゲットグループ（<code>my-alb-tg</code>）</td>
<td>ALBのトラフィック転送先の定義</td>
</tr>
<tr>
<td>⑬</td>
<td>ALB（<code>my-alb-alb</code>）+ リスナー</td>
<td>ロードバランサー本体</td>
</tr>
<tr>
<td>⑭</td>
<td>動作確認</td>
<td>ALB → EC2 → RDS接続テスト</td>
</tr>
<tr>
<td>⑮</td>
<td>リソース削除</td>
<td>課金停止</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc6">全体の作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認
      ↓
① キーペア作成
      ↓
② VPC作成
      ↓
③ サブネット作成（4つ）
      ↓
④ インターネットゲートウェイ作成・アタッチ
      ↓
⑤ ルートテーブル設定（IGWルート追加 → サブネット関連付け）
      ↓
⑥ セキュリティグループ作成（ALB用・EC2用・RDS用）
      ↓
⑦ IAMロール作成
      ↓
⑧ SSM Parameter Store作成
      ↓
⑨ RDS DBサブネットグループ作成
      ↓
⑩ RDS MySQL インスタンス作成（※約10〜15分かかる）
      ↓
⑪ EC2インスタンス起動（Tomcat）
      ↓
⑫ ターゲットグループ作成
      ↓
⑬ ALB作成・リスナー設定
      ↓
⑭ 動作確認（ALB → EC2 → RDS）
      ↓
⑮ リソース削除</code></pre>
<hr>
<h2><span id="toc7">⓪ 自分のIPアドレスの確認</span></h2>
<p>ブラウザで以下のURLを開くか、コマンドで確認します。</p>
<pre><code class="language-plaintext">https://checkip.amazonaws.com</code></pre>
<p><strong>控えておく情報</strong>: 自分のIPアドレス（例: <code>203.0.113.1</code>）</p>
<blockquote>
<p>セキュリティグループで <code>203.0.113.1/32</code> の形式（末尾に <code>/32</code>）で使用します。</p>
</blockquote>
<hr>
<h2><span id="toc8">① キーペアの作成</span></h2>
<p><strong>AWSコンソール → EC2 → キーペア → 「キーペアを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-alb-key</code></td>
</tr>
<tr>
<td>キーペアのタイプ</td>
<td><strong>RSA</strong></td>
</tr>
<tr>
<td>プライベートキーファイル形式</td>
<td><strong>.pem</strong></td>
</tr>
</tbody>
</table>
<p>「キーペアを作成」をクリック。ダウンロードされた <code>.pem</code> ファイルを保存します。</p>
<pre><code class="language-plaintext">C:\Users\ユーザー名\.ssh\my-alb-key.pem</code></pre>
<hr>
<h2><span id="toc9">② VPCの作成</span></h2>
<p><strong>AWSコンソール → VPC → VPC → 「VPCを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>作成するリソース</td>
<td><strong>VPCのみ</strong></td>
</tr>
<tr>
<td>名前タグ</td>
<td><code>my-alb-vpc</code></td>
</tr>
<tr>
<td>IPv4 CIDR</td>
<td><code>10.0.0.0/16</code></td>
</tr>
</tbody>
</table>
<p>「VPCを作成」をクリック。</p>
<hr>
<h2><span id="toc10">③ サブネットの作成（4つ）</span></h2>
<p><strong>AWSコンソール → VPC → サブネット → 「サブネットを作成」</strong></p>
<p>VPCに <code>my-alb-vpc</code> を選択して、以下の4つを1回の操作で作成します（「新しいサブネットを追加」で追加できます）。</p>
<table>
<thead>
<tr>
<th>名前</th>
<th>AZ</th>
<th>IPv4 CIDR</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>my-alb-public-subnet-1</code></td>
<td>ap-northeast-1<strong>a</strong></td>
<td><code>10.0.1.0/24</code></td>
<td>ALB + EC2</td>
</tr>
<tr>
<td><code>my-alb-public-subnet-2</code></td>
<td>ap-northeast-1<strong>c</strong></td>
<td><code>10.0.2.0/24</code></td>
<td>ALB（2AZ必須）</td>
</tr>
<tr>
<td><code>my-alb-private-subnet-1</code></td>
<td>ap-northeast-1<strong>a</strong></td>
<td><code>10.0.3.0/24</code></td>
<td>RDS配置</td>
</tr>
<tr>
<td><code>my-alb-private-subnet-2</code></td>
<td>ap-northeast-1<strong>c</strong></td>
<td><code>10.0.4.0/24</code></td>
<td>DBサブネットグループ用</td>
</tr>
</tbody>
</table>
<p>「サブネットを作成」をクリック。</p>
<blockquote>
<p><strong>ALBが2AZを必要とする理由</strong>: ALBは高可用性のため複数AZにまたがって配置されます。VPCを選択すると「少なくとも2つのAZのサブネットを指定してください」というバリデーションが入ります。</p>
</blockquote>
<p><!-- ![4つのサブネット作成画面](images/subnet-create.jpg) --></p>
<hr>
<h2><span id="toc11">④ インターネットゲートウェイの作成・アタッチ</span></h2>
<h3><span id="toc12">作成</span></h3>
<p><strong>AWSコンソール → VPC → インターネットゲートウェイ → 「インターネットゲートウェイを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前タグ</td>
<td><code>my-alb-igw</code></td>
</tr>
</tbody>
</table>
<p>「インターネットゲートウェイを作成」をクリック。</p>
<h3><span id="toc13">VPCへのアタッチ</span></h3>
<p>作成したIGWを選択 → 「アクション」→「VPCにアタッチ」→ <code>my-alb-vpc</code> を選択 → 「インターネットゲートウェイのアタッチ」</p>
<hr>
<h2><span id="toc14">⑤ ルートテーブルの設定</span></h2>
<blockquote>
<p><strong>注意</strong>: EC2を起動する前にこの手順を完了させてください。IGWルートがない状態でEC2を起動すると、UserDataのTomcatインストールが失敗します。</p>
</blockquote>
<h3><span id="toc15">5-1. パブリック用ルートテーブル</span></h3>
<p><strong>AWSコンソール → VPC → ルートテーブル → 「ルートテーブルを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-alb-public-rt</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
</tbody>
</table>
<p>作成後、<code>my-alb-public-rt</code> を選択 → 「ルート」タブ → 「ルートを編集」→「ルートを追加」</p>
<table>
<thead>
<tr>
<th>送信先</th>
<th>ターゲット</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0.0.0.0/0</code></td>
<td>インターネットゲートウェイ <code>my-alb-igw</code></td>
</tr>
</tbody>
</table>
<p>「変更を保存」をクリック。</p>
<p>次に「サブネットの関連付け」タブ → 「サブネットの関連付けを編集」→ <strong>パブリックサブネット2つ</strong>にチェック → 保存</p>
<ul>
<li><code>my-alb-public-subnet-1</code></li>
<li><code>my-alb-public-subnet-2</code></li>
</ul>
<h3><span id="toc16">5-2. プライベート用ルートテーブル</span></h3>
<p>同様に「ルートテーブルを作成」</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-alb-private-rt</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
</tbody>
</table>
<p>「サブネットの関連付けを編集」→ <strong>プライベートサブネット2つ</strong>にチェック → 保存</p>
<ul>
<li><code>my-alb-private-subnet-1</code></li>
<li><code>my-alb-private-subnet-2</code></li>
</ul>
<hr>
<h2><span id="toc17">⑥ セキュリティグループの作成</span></h2>
<h3><span id="toc18">6-1. ALB用セキュリティグループ</span></h3>
<p><strong>AWSコンソール → EC2 → セキュリティグループ → 「セキュリティグループを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-alb-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>ALB SG - HTTP from internet</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
</tbody>
</table>
<p>インバウンドルール:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>ポート</th>
<th>ソース</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>HTTP</td>
<td>80</td>
<td><code>0.0.0.0/0</code></td>
<td>HTTP from internet</td>
</tr>
</tbody>
</table>
<p>「セキュリティグループを作成」をクリック。</p>
<blockquote>
<p><strong>なぜ <code>0.0.0.0/0</code>（全開放）なのか</strong>: これまでのハンズオンではEC2が直接インターネットに露出していたため自分のIPだけに制限していました。今回はEC2の前にALBを置く3層構成のため、不特定多数のユーザーが使うWebサービスの入口であるALBは全開放が正しい設計です。</p>
<p>EC2（Tomcat）のセキュリティは次の6-2で設定するEC2 SGの「ALB SGからのみ8080を許可」によって守られます。</p>
</blockquote>
<h3><span id="toc19">6-2. EC2用セキュリティグループ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-alb-ec2-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>EC2 Tomcat SG - Tomcat:8080 from ALB SG, SSH from MyIP</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
</tbody>
</table>
<p>インバウンドルール:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>ポート</th>
<th>ソース</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>カスタムTCP</td>
<td>8080</td>
<td><code>my-alb-sg</code>（<strong>SGを選択</strong>）</td>
<td>Tomcat from ALB SG only</td>
</tr>
<tr>
<td>SSH</td>
<td>22</td>
<td><code>自分のIP/32</code></td>
<td>SSH from my IP</td>
</tr>
</tbody>
</table>
<p>アウトバウンドルール（<strong>変更しない</strong>）:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>プロトコル</th>
<th>送信先</th>
</tr>
</thead>
<tbody>
<tr>
<td>すべてのトラフィック</td>
<td>すべて</td>
<td><code>0.0.0.0/0</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>アウトバウンドルールを変更してはいけない理由</strong>: EC2がTomcatのインストール（Javaのダウンロードなど）でインターネットにアクセスするために必要です。誤って削除するとEC2からの通信が遮断されます。</p>
</blockquote>
<blockquote>
<p><strong>SG-to-SG制御</strong>: ポート8080のソースにIPアドレスではなく <code>my-alb-sg</code> のSGを指定します。ALBを経由したトラフィックのみEC2に到達でき、インターネットから直接8080番ポートへのアクセスは不可になります。</p>
</blockquote>
<h3><span id="toc20">6-3. RDS用セキュリティグループ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-alb-rds-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS MySQL SG - MySQL:3306 from EC2 SG only</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
</tbody>
</table>
<p>インバウンドルール:</p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>ポート</th>
<th>ソース</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL/Aurora</td>
<td>3306</td>
<td><code>my-alb-ec2-sg</code>（<strong>SGを選択</strong>）</td>
<td>MySQL from EC2 SG only</td>
</tr>
</tbody>
</table>
<p><!-- ![3段階のSG設定（ALB SG → EC2 SG → RDS SG）](images/sg-layers.jpg) --></p>
<hr>
<h2><span id="toc21">⑦ IAMロールの作成</span></h2>
<p><strong>AWSコンソール → IAM → ロール → 「ロールを作成」</strong></p>
<h3><span id="toc22">信頼されたエンティティ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>信頼されたエンティティタイプ</td>
<td><strong>AWSのサービス</strong></td>
</tr>
<tr>
<td>サービス</td>
<td><strong>EC2</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc23">許可ポリシー</span></h3>
<p>以下の2つのポリシーを検索して追加します。</p>
<table>
<thead>
<tr>
<th>ポリシー名</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AmazonSSMManagedInstanceCore</code></td>
<td>Session Manager接続用</td>
</tr>
<tr>
<td><code>AmazonSSMReadOnlyAccess</code></td>
<td>Parameter Storeからパスワードを読み取る</td>
</tr>
</tbody>
</table>
<h3><span id="toc24">ロール名</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ロール名</td>
<td><code>my-alb-ec2-role</code></td>
</tr>
</tbody>
</table>
<p>「ロールを作成」をクリック。</p>
<hr>
<h2><span id="toc25">⑧ SSM Parameter Storeの作成</span></h2>
<p><strong>AWSコンソール → Systems Manager → パラメータストア → 「パラメータの作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>/my/alb/db-password</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS MySQL master password for ALB hands-on</code></td>
</tr>
<tr>
<td>層</td>
<td><strong>スタンダード</strong></td>
</tr>
<tr>
<td>タイプ</td>
<td><strong>安全な文字列</strong>（SecureString・推奨）</td>
</tr>
<tr>
<td>KMS キーID</td>
<td><code>alias/aws/ssm</code>（デフォルト）</td>
</tr>
<tr>
<td>値</td>
<td><code>Handson1234!</code></td>
</tr>
</tbody>
</table>
<p>「パラメータの作成」をクリック。</p>
<blockquote>
<p><strong>「安全な文字列」（SecureString）とは</strong>: パスワードなどの機密情報をKMSで暗号化して保存するタイプです。取得時は <code>--with-decryption</code> オプションが必要です。CloudFormation版はString型で作成しますが、コンソールではSecureStringを推奨します。</p>
</blockquote>
<hr>
<h2><span id="toc26">⑨ RDS DBサブネットグループの作成</span></h2>
<p><strong>AWSコンソール → Aurora and RDS → サブネットグループ → 「DBサブネットグループの作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-alb-rds-subnet-group</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS subnet group for ALB hands-on</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
</tbody>
</table>
<p>アベイラビリティーゾーンとサブネットの追加:</p>
<table>
<thead>
<tr>
<th>AZ</th>
<th>サブネット</th>
</tr>
</thead>
<tbody>
<tr>
<td>ap-northeast-1a</td>
<td><code>my-alb-private-subnet-1</code></td>
</tr>
<tr>
<td>ap-northeast-1c</td>
<td><code>my-alb-private-subnet-2</code></td>
</tr>
</tbody>
</table>
<p>「作成」をクリック。</p>
<hr>
<h2><span id="toc27">⑩ RDS MySQL インスタンスの作成</span></h2>
<p><strong>AWSコンソール → Aurora and RDS → データベース → 「データベースの作成 ▲」→「フル設定」</strong></p>
<blockquote>
<p><strong>注意</strong>: RDSの作成は<strong>約10〜15分</strong>かかります。</p>
</blockquote>
<h3><span id="toc28">エンジンの選択</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>エンジンのタイプ</td>
<td><strong>MySQL</strong></td>
</tr>
<tr>
<td>エンジンバージョン</td>
<td><strong>MySQL 8.0.x</strong>（最新）</td>
</tr>
</tbody>
</table>
<h3><span id="toc29">テンプレート</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>テンプレート</td>
<td><strong>無料利用枠</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc30">設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBインスタンス識別子</td>
<td><code>my-alb-rds-mysql</code></td>
</tr>
<tr>
<td>マスターユーザー名</td>
<td><code>admin</code></td>
</tr>
<tr>
<td>マスターパスワード</td>
<td><code>Handson1234!</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc31">インスタンスの設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBインスタンスクラス</td>
<td><strong>db.t3.micro</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc32">ストレージ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ストレージタイプ</td>
<td><strong>汎用SSD (gp2)</strong></td>
</tr>
<tr>
<td>割り当てられたストレージ</td>
<td><code>20</code> GiB</td>
</tr>
<tr>
<td>ストレージの自動スケーリング</td>
<td>チェックを<strong>外す</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc33">接続</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>コンピューティングリソース</td>
<td><strong>EC2コンピューティングリソースに接続しない</strong></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
<tr>
<td>DBサブネットグループ</td>
<td><code>my-alb-rds-subnet-group</code></td>
</tr>
<tr>
<td>パブリックアクセス</td>
<td><strong>なし</strong></td>
</tr>
<tr>
<td>VPCセキュリティグループ</td>
<td><strong>既存の選択</strong> → <code>my-alb-rds-sg</code>（<code>default</code> は削除）</td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc34">追加設定</span></h3>
<p>「追加設定」を開きます。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>最初のデータベース名</td>
<td><code>sampledb</code></td>
</tr>
<tr>
<td>自動バックアップを有効にする</td>
<td>チェックを<strong>外す</strong></td>
</tr>
</tbody>
</table>
<p>「データベースの作成」をクリック。</p>
<p><strong>控えておく情報</strong>: 作成完了後に表示される「エンドポイント」のホスト名</p>
<pre><code class="language-plaintext">my-alb-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com</code></pre>
<hr>
<h2><span id="toc35">⑪ EC2インスタンスの起動</span></h2>
<blockquote>
<p><strong>注意</strong>: EC2を起動する前に⑤のルートテーブル設定が完了していることを確認してください。IGWルートがない状態で起動するとTomcatのインストールが失敗します。</p>
</blockquote>
<p><strong>AWSコンソール → EC2 → インスタンス → 「インスタンスを起動」</strong></p>
<h3><span id="toc36">基本設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-alb-ap-instance</code></td>
</tr>
<tr>
<td>AMI</td>
<td><strong>Amazon Linux 2023 AMI</strong></td>
</tr>
<tr>
<td>インスタンスタイプ</td>
<td><strong>t2.micro</strong></td>
</tr>
<tr>
<td>キーペア</td>
<td><code>my-alb-key</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc37">ネットワーク設定</span></h3>
<p>「編集」をクリックして以下を設定します。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
<tr>
<td>サブネット</td>
<td><code>my-alb-public-subnet-1</code></td>
</tr>
<tr>
<td>パブリックIPの自動割り当て</td>
<td><strong>有効化</strong></td>
</tr>
<tr>
<td>ファイアウォール（セキュリティグループ）</td>
<td><strong>既存のセキュリティグループを選択</strong></td>
</tr>
<tr>
<td>セキュリティグループ</td>
<td><code>my-alb-ec2-sg</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc38">IAMロール</span></h3>
<p>「高度な詳細」→「IAMインスタンスプロファイル」→ <code>my-alb-ec2-role</code> を選択</p>
<h3><span id="toc39">UserData</span></h3>
<p>「高度な詳細」→「ユーザーデータ」に以下を貼り付けます。</p>
<blockquote>
<p><strong>Tomcatバージョンの確認</strong>: <code>TOMCAT_VERSION</code> の値は <a href="https://archive.apache.org/dist/tomcat/tomcat-10/">https://archive.apache.org/dist/tomcat/tomcat-10/</a> で利用可能なバージョンを確認して設定してください。</p>
</blockquote>
<pre><code class="language-bash">#!/bin/bash
set -xe
exec &gt; &gt;(tee /var/log/user-data.log) 2&gt;&amp;1

dnf update -y
dnf install -y java-17-amazon-corretto wget

# https://archive.apache.org/dist/tomcat/tomcat-10/
TOMCAT_VERSION="10.1.28"
cd /opt
wget -q https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz
tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz
mv apache-tomcat-${TOMCAT_VERSION} tomcat
chmod +x /opt/tomcat/bin/*.sh

# テスト用Webアプリのデプロイ
cat &gt; /opt/tomcat/webapps/ROOT/index.html &lt;&lt; 'HTMLEOF'
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;AP Server - Phase 2-2 ALB + Tomcat + RDS&lt;/h1&gt;
&lt;p&gt;This server is load balanced by ALB and connects to RDS MySQL.&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
HTMLEOF

# Tomcat起動
/opt/tomcat/bin/startup.sh</code></pre>
<h3><span id="toc40">タグ</span></h3>
<table>
<thead>
<tr>
<th>キー</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Cost</code></td>
<td><code>（任意）</code></td>
</tr>
</tbody>
</table>
<p>「インスタンスを起動」をクリック。起動完了まで約2〜3分待ちます。</p>
<blockquote>
<p><strong>Tomcatの起動について</strong>: EC2の「実行中」状態になった後、UserDataによるTomcatのインストール・起動に<strong>さらに約5〜10分</strong>かかります。ALBのヘルスチェックが「Healthy」になるまで待ってからアクセスしてください。</p>
</blockquote>
<p><strong>控えておく情報</strong>: インスタンスのパブリックIPアドレス</p>
<hr>
<h2><span id="toc41">⑫ ターゲットグループの作成</span></h2>
<p><strong>AWSコンソール → EC2 → ターゲットグループ → 「ターゲットグループの作成」</strong></p>
<h3><span id="toc42">ターゲットタイプとプロトコル</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ターゲットタイプ</td>
<td><strong>インスタンス</strong></td>
</tr>
<tr>
<td>ターゲットグループ名</td>
<td><code>my-alb-tg</code></td>
</tr>
<tr>
<td>プロトコル</td>
<td><strong>HTTP</strong></td>
</tr>
<tr>
<td>ポート</td>
<td><strong>8080</strong></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
<tr>
<td>プロトコルバージョン</td>
<td><strong>HTTP1</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc43">ヘルスチェック</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ヘルスチェックプロトコル</td>
<td>HTTP</td>
</tr>
<tr>
<td>ヘルスチェックパス</td>
<td><code>/</code></td>
</tr>
</tbody>
</table>
<p>「次へ」をクリック。</p>
<h3><span id="toc44">ターゲットの登録</span></h3>
<p>「使用可能なインスタンス」から <code>my-alb-ap-instance</code> を選択します。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ポート</td>
<td><code>8080</code></td>
</tr>
</tbody>
</table>
<p>「保留中として以下を含める」をクリック → 下部の「ターゲットを確認」にインスタンスが表示されたことを確認して「次へ」をクリック。</p>
<p>「ターゲットグループの作成」をクリック。</p>
<blockquote>
<p><strong>ヘルスチェックとは</strong>: ALBが定期的にEC2のポート8080の <code>/</code> にHTTPリクエストを送り、正常（HTTP 200）が返るか確認する仕組みです。ステータスが「Healthy」になるまではALBはそのEC2にトラフィックを転送しません。</p>
</blockquote>
<p><!-- ![ターゲットグループの作成・EC2登録画面](images/target-group-create.jpg) --></p>
<hr>
<h2><span id="toc45">⑬ ALBの作成・リスナー設定</span></h2>
<p><strong>AWSコンソール → EC2 → ロードバランサー → 「ロードバランサーの作成」</strong></p>
<p>「Application Load Balancer」の「作成」をクリック。</p>
<h3><span id="toc46">基本的な設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ロードバランサー名</td>
<td><code>my-alb-alb</code></td>
</tr>
<tr>
<td>スキーム</td>
<td><strong>インターネット向け</strong></td>
</tr>
<tr>
<td>IPアドレスタイプ</td>
<td><strong>IPv4</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc47">ネットワークマッピング</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC</td>
<td><code>my-alb-vpc</code></td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code>（<code>my-alb-public-subnet-1</code>）と <code>ap-northeast-1c</code>（<code>my-alb-public-subnet-2</code>）の<strong>両方</strong>を選択</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>ALBは必ず2つ以上のAZを選択します。</strong> 1つのAZだけを選ぶとエラーになります。</p>
</blockquote>
<h3><span id="toc48">セキュリティグループ</span></h3>
<p><code>my-alb-sg</code> のみを選択（デフォルトSGは削除）。</p>
<h3><span id="toc49">リスナーとルーティング</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>プロトコル</td>
<td>HTTP</td>
</tr>
<tr>
<td>ポート</td>
<td>80</td>
</tr>
<tr>
<td>デフォルトアクション</td>
<td><code>my-alb-tg</code> に転送</td>
</tr>
</tbody>
</table>
<p>「ロードバランサーの作成」をクリック。</p>
<p><strong>控えておく情報</strong>: 「DNS名」（例: <code>my-alb-alb-1234567890.ap-northeast-1.elb.amazonaws.com</code>）</p>
<p><!-- ![ALB作成完了・DNS名の確認画面](images/alb-created.jpg) --></p>
<hr>
<h2><span id="toc50">⑭ 動作確認</span></h2>
<h3><span id="toc51">14-1. ALBのヘルスチェック確認</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-alb-tg</code> → 「ターゲット」タブ</strong></p>
<p><code>my-alb-ap-instance</code> のステータスが <strong>「Healthy」</strong> になるまで待ちます（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>
<blockquote>
<p><code>unhealthy</code> が続く場合はEC2にSSH接続して <code>sudo cat /var/log/user-data.log</code> でUserDataのエラーを確認してください。</p>
</blockquote>
<p><!-- ![ターゲットグループのHealthy確認画面](images/target-healthy.jpg) --></p>
<h3><span id="toc52">14-2. ALB経由でWebアクセス確認</span></h3>
<p>ブラウザで以下のURLにアクセスします。</p>
<pre><code class="language-plaintext">http://（⑬で控えたALBのDNS名）</code></pre>
<p>「AP Server - Phase 2-2 ALB + Tomcat + RDS」と表示されれば正常です。</p>
<blockquote>
<p>EC2のパブリックIPに直接アクセスしても表示されません（ポート8080はALB SGからのみ許可しているため）。これが3層構成のポイントです。</p>
</blockquote>
<h3><span id="toc53">14-3. EC2にSSH接続する</span></h3>
<pre><code class="language-cmd">ssh -i C:\Users\ユーザー名\.ssh\my-alb-key.pem ec2-user@（EC2パブリックIP）</code></pre>
<h3><span id="toc54">14-4. Parameter StoreからDBパスワードを取得する</span></h3>
<pre><code class="language-bash">aws ssm get-parameter \
  --name "/my/alb/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1 \
  --with-decryption</code></pre>
<p><code>Handson1234!</code> と表示されれば成功です。</p>
<blockquote>
<p><strong><code>--with-decryption</code> について</strong>: コンソールでSecureStringとして作成した場合は必須です。このフラグがないと暗号化されたままの文字列が返ってきます。</p>
</blockquote>
<h3><span id="toc55">14-5. RDSへのMySQL接続テスト</span></h3>
<p>MySQLクライアントをインストールします（Amazon Linux 2023では <code>mariadb105</code> を使います）。</p>
<pre><code class="language-bash">sudo dnf install -y mariadb105</code></pre>
<pre><code class="language-bash"># 変数にエンドポイントを代入（⑩で控えた値に置き換える）
RDS_ENDPOINT="my-alb-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com"

# Parameter StoreからDBパスワードを取得して接続
DB_PASSWORD=$(aws ssm get-parameter \
  --name "/my/alb/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1 \
  --with-decryption)

mysql -h $RDS_ENDPOINT -u admin -p"$DB_PASSWORD" sampledb</code></pre>
<p>接続成功後、SQL操作を確認します。</p>
<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="toc56">14-6. SSH接続を切断する</span></h3>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc57">⑮ リソースの削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p><strong>ALBの課金について</strong>: ALBはインスタンスが稼働している間は課金されます（約$0.008/時間 + LCU料金）。不要になったら必ず削除してください。</p>
</blockquote>
<h3><span id="toc58">削除順序</span></h3>
<p>依存関係があるため、<strong>必ずこの順番</strong>で削除します。</p>
<pre><code class="language-plaintext">1. ALBを削除
2. ターゲットグループを削除
3. EC2インスタンスを終了
4. RDSインスタンスを削除（約10〜15分）
5. RDS DBサブネットグループを削除
6. SSM Parameter Storeのパラメータを削除
7. セキュリティグループを削除（RDS用 → EC2用 → ALB用の順）
8. ルートテーブルの関連付け解除・削除
9. インターネットゲートウェイをデタッチ・削除
10. サブネットを削除（4つ）
11. VPCを削除
12. IAMロールを削除
13. キーペアを削除（任意）</code></pre>
<h3><span id="toc59">1. ALBを削除する</span></h3>
<p><strong>EC2 → ロードバランサー → <code>my-alb-alb</code> を選択 → 「アクション」→「削除」</strong></p>
<p>確認フィールドに <code>確認</code> と入力 → 「削除」をクリック。</p>
<blockquote>
<p>ALBを削除するとリスナーも一緒に削除されます。</p>
</blockquote>
<h3><span id="toc60">2. ターゲットグループを削除する</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-alb-tg</code> を選択 → 「アクション」→「削除」</strong></p>
<h3><span id="toc61">3. EC2インスタンスを終了する</span></h3>
<p><strong>EC2 → インスタンス → <code>my-alb-ap-instance</code> を選択 → 「インスタンスの状態」→「インスタンスを終了」</strong></p>
<p>「終了済み」になるまで待ちます（2〜5分）。</p>
<h3><span id="toc62">4. RDSインスタンスを削除する</span></h3>
<p><strong>Aurora and RDS → データベース → <code>my-alb-rds-mysql</code> を選択 → 「アクション」→「削除」</strong></p>
<table>
<thead>
<tr>
<th>項目</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>最終スナップショットを作成</td>
<td><strong>チェックを外したまま</strong></td>
</tr>
<tr>
<td>「私は〜を了承します。」</td>
<td><strong>チェックを入れる</strong></td>
</tr>
<tr>
<td>確認フィールド</td>
<td><code>delete me</code> と入力</td>
</tr>
</tbody>
</table>
<p>「削除」をクリック。削除完了まで<strong>約10〜15分</strong>かかります。</p>
<h3><span id="toc63">5. RDS DBサブネットグループを削除する</span></h3>
<p><strong>Aurora and RDS → サブネットグループ → <code>my-alb-rds-subnet-group</code> → 「削除」</strong></p>
<blockquote>
<p>RDSインスタンスが完全に削除されてから行います。</p>
</blockquote>
<h3><span id="toc64">6. SSM Parameter Storeのパラメータを削除する</span></h3>
<p><strong>Systems Manager → パラメータストア → <code>/my/alb/db-password</code> → 「削除」</strong></p>
<h3><span id="toc65">7. セキュリティグループを削除する</span></h3>
<p><strong>EC2 → セキュリティグループ</strong></p>
<p>依存関係があるため以下の順番で削除します。</p>
<ol>
<li><code>my-alb-rds-sg</code> → 「アクション」→「セキュリティグループを削除」</li>
<li><code>my-alb-ec2-sg</code> → 「アクション」→「セキュリティグループを削除」</li>
<li><code>my-alb-sg</code> → 「アクション」→「セキュリティグループを削除」</li>
</ol>
<h3><span id="toc66">8. ルートテーブルを削除する</span></h3>
<p><strong>VPC → ルートテーブル</strong></p>
<ol>
<li><code>my-alb-public-rt</code> → 「サブネットの関連付け」→「編集」→ チェックを全て外す → 「保存」→「アクション」→「ルートテーブルの削除」</li>
<li><code>my-alb-private-rt</code> → 同様に操作</li>
</ol>
<h3><span id="toc67">9. インターネットゲートウェイをデタッチ・削除する</span></h3>
<p><strong>VPC → インターネットゲートウェイ</strong></p>
<ol>
<li><code>my-alb-igw</code> を選択 → 「アクション」→「VPCからデタッチ」→「デタッチ」</li>
<li>「アクション」→「インターネットゲートウェイの削除」</li>
</ol>
<h3><span id="toc68">10. サブネットを削除する（4つ）</span></h3>
<p><strong>VPC → サブネット</strong></p>
<p>4つのサブネットをすべて選択 → 「アクション」→「サブネットの削除」</p>
<ul>
<li><code>my-alb-public-subnet-1</code></li>
<li><code>my-alb-public-subnet-2</code></li>
<li><code>my-alb-private-subnet-1</code></li>
<li><code>my-alb-private-subnet-2</code></li>
</ul>
<h3><span id="toc69">11. VPCを削除する</span></h3>
<p><strong>VPC → お使いのVPC → <code>my-alb-vpc</code> を選択 → 「アクション」→「VPCの削除」</strong></p>
<h3><span id="toc70">12. IAMロールを削除する</span></h3>
<p><strong>IAM → ロール → <code>my-alb-ec2-role</code> を選択 → 「削除」</strong></p>
<h3><span id="toc71">13. キーペアを削除する（任意）</span></h3>
<p><strong>EC2 → キーペア → <code>my-alb-key</code> を選択 → 「アクション」→「削除」</strong></p>
<hr>
<h2><span id="toc72">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td>ALBに繋がらない（タイムアウト）</td>
<td>ALB SGのインバウンドルールにHTTP:80がない</td>
<td>ALB SGを確認・修正</td>
</tr>
<tr>
<td>ALBに繋がるが502エラー</td>
<td>TomcatがまだEC2で起動していない</td>
<td>5〜10分待つか <code>sudo cat /var/log/user-data.log</code> 確認</td>
</tr>
<tr>
<td>ALBに繋がるが503エラー</td>
<td>ターゲットグループにHealthyなインスタンスがない</td>
<td>TG → ターゲットのステータスを確認</td>
</tr>
<tr>
<td>Unhealthyが続く</td>
<td>Tomcatの起動失敗</td>
<td>SSH接続して <code>sudo /opt/tomcat/bin/startup.sh</code> を手動実行</td>
</tr>
<tr>
<td>Unhealthyが続く（UserDataも失敗）</td>
<td>EC2 SGのアウトバウンドルールが削除されている</td>
<td>EC2 SG → アウトバウンドルールに「すべてのトラフィック 0.0.0.0/0」を追加</td>
</tr>
<tr>
<td>EC2にSSH接続できない</td>
<td>SGのSSH許可IPが違う</td>
<td>EC2 SGのインバウンドSSH規則を現在のIPに更新</td>
</tr>
<tr>
<td>MySQLに接続できない</td>
<td>RDS SGのソースがEC2 SGのIDでない</td>
<td>RDS SGのインバウンドルールを確認</td>
</tr>
<tr>
<td><code>ERROR 1049: Unknown database &#39;sampledb&#39;</code></td>
<td>RDS作成時にDB名を指定し忘れた</td>
<td>MySQLに接続して <code>CREATE DATABASE sampledb;</code> を実行</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc73">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①〜⑤</td>
<td>VPC / サブネット4つ / IGW / ルートテーブルの設定</td>
</tr>
<tr>
<td>⑥</td>
<td><strong>3段階のSG</strong>（ALB SG → EC2 SG → RDS SG）でSG-to-SG制御</td>
</tr>
<tr>
<td>⑦⑧</td>
<td>IAMロール + Parameter Store（SecureString）でパスワードを安全に管理</td>
</tr>
<tr>
<td>⑨⑩</td>
<td>DBサブネットグループ + RDS MySQL 8.0</td>
</tr>
<tr>
<td>⑪</td>
<td>EC2起動（UserDataでTomcat自動セットアップ）</td>
</tr>
<tr>
<td>⑫⑬</td>
<td>ターゲットグループ + ALB作成。Healthyになるまで待つ</td>
</tr>
<tr>
<td>⑭</td>
<td>ALB DNS名でアクセス → EC2 → Parameter Store → RDS への接続テスト</td>
</tr>
</tbody>
</table>
<p><strong>前フェーズ（EC2 + RDS）との最大の違い</strong>は、EC2がインターネットに直接露出しない点です。ALBがHTTP:80のトラフィックを受け取り、EC2にはALBを経由したポート8080のみが開放されています。スケールアウト時はターゲットグループにEC2を追加するだけで負荷分散できます。</p>
<p>CloudFormationで同じ構成を自動化したい場合は、<a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormation版ハンズオン</a>を参照してください。コマンド1本で27リソースを一括デプロイできます。</p><p>The post <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソールでALB + EC2(Tomcat) + RDS 3層構成を構築する手順【ヘルスチェック / SG-to-SG制御】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-console-alb-ec2-rds/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CloudFormationでALB + EC2(Tomcat) + RDS 3層構成を自動構築する手順【27リソース一括デプロイ / コンソール版との比較付き】</title>
		<link>https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/</link>
					<comments>https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Mon, 13 Apr 2026 00:09:05 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[3層構成]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWSCLI]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[ターゲットグループ]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[ヘルスチェック]]></category>
		<category><![CDATA[ロードバランサー]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20356</guid>

					<description><![CDATA[<p>目次 はじめにCloudFormation vs コンソール：どれだけ違うかキーワード解説前提条件構築されるリソース（約27個）template.yaml（完全版）作業順序【重要】⓪ 自分のIPアドレスを確認する① キー [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormationでALB + EC2(Tomcat) + RDS 3層構成を自動構築する手順【27リソース一括デプロイ / コンソール版との比較付き】</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-16" checked><label class="toc-title" for="toc-checkbox-16">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">CloudFormation vs コンソール：どれだけ違うか</a></li><li><a href="#toc3" tabindex="0">キーワード解説</a></li><li><a href="#toc4" tabindex="0">前提条件</a></li><li><a href="#toc5" tabindex="0">構築されるリソース（約27個）</a></li><li><a href="#toc6" tabindex="0">template.yaml（完全版）</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">③ スタックの作成</a></li><li><a href="#toc12" tabindex="0">④ 作成完了・Outputsの確認</a><ol><li><a href="#toc13" tabindex="0">作成状況の確認</a></li><li><a href="#toc14" tabindex="0">Outputs（接続情報）の確認</a></li></ol></li><li><a href="#toc15" tabindex="0">⑤ 動作確認</a><ol><li><a href="#toc16" tabindex="0">ALBのヘルスチェック確認</a></li><li><a href="#toc17" tabindex="0">ALB経由でWebアクセス確認</a></li><li><a href="#toc18" tabindex="0">EC2にSSH接続する</a></li><li><a href="#toc19" tabindex="0">Parameter StoreからDBパスワードを取得する</a></li><li><a href="#toc20" tabindex="0">RDSへのMySQL接続テスト</a></li></ol></li><li><a href="#toc21" tabindex="0">⑥ スタックの削除</a><ol><li><a href="#toc22" tabindex="0">削除状況の確認</a></li><li><a href="#toc23" tabindex="0">キーペアの手動削除</a></li></ol></li><li><a href="#toc24" tabindex="0">コンソール版との比較</a></li><li><a href="#toc25" tabindex="0">トラブルシューティング</a><ol><li><a href="#toc26" tabindex="0">失敗時の詳細確認コマンド</a></li></ol></li><li><a href="#toc27" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「コンソールで40〜50分かかったALB + EC2(Tomcat) + RDS環境をコードで再現したい」——それを実現するのが <strong>AWS CloudFormation</strong> です。</p>
<p>この記事では、<code>template.yaml</code> 1ファイルにVPC・サブネット・セキュリティグループ・IAMロール・SSM Parameter Store・RDS・ALB・ターゲットグループ・EC2（約27リソース）を定義し、<strong>コマンド1本で3層構成を一括構築するハンズオン</strong>を紹介します。コンソール版（<a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>）と全く同じ構成をCloudFormationで自動化します。</p>
<pre><code class="language-plaintext">ローカル環境（VSCode）
  ├── template.yaml（約27リソースを定義）
  └── AWS CLI コマンド
        ↓ スタック作成（コマンド1本）
AWS環境
  └── VPC（10.0.0.0/16）
        ├── パブリックサブネット1（10.0.1.0/24 / AZ-a）← ALB + EC2
        ├── パブリックサブネット2（10.0.2.0/24 / AZ-c）← ALB（2AZ必須）
        ├── プライベートサブネット1（10.0.3.0/24 / AZ-a）← RDS配置
        ├── プライベートサブネット2（10.0.4.0/24 / AZ-c）← DBサブネットグループ用
        ├── ALB SG → EC2 SG → RDS SG（3段階のSG-to-SG制御）
        ├── SSM Parameter Store: /my/alb/db-password
        ├── RDS MySQL 8.0（db.t3.micro）
        ├── ALB（HTTP:80 → ターゲットグループ → EC2:8080）
        └── EC2（t2.micro / Tomcat / UserDataで自動セットアップ）</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>ALB・ターゲットグループ・リスナーを <code>template.yaml</code> で一括定義する方法</li>
<li><code>TomcatVersion</code> パラメータでTomcatのバージョンを切り替える設計</li>
<li>ALBの2AZ要件をCloudFormationでどう表現するか</li>
<li><code>delete-stack</code> 1本でALB・RDS・IAMなど27リソースを自動削除</li>
</ul>
<p><strong>このハンズオンの特徴：</strong></p>
<ul>
<li>コンソール版では40〜50分・13ステップかかった作業が、コマンド1本（RDS待ち15〜20分込み）で完了</li>
<li><code>delete-stack</code> 1本でALBのリスナー含む全リソースの依存関係を自動解決して削除</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a> の比較記事です。</strong><br />コンソール版でALB・ターゲットグループ・ヘルスチェックを視覚的に学んだ後に読むと理解が深まります。</p>
</blockquote>
<hr>
<p><!-- ![CloudFormationスタック作成完了・Outputs確認画面](images/cfn-create-complete.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">CloudFormation vs コンソール：どれだけ違うか</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>CloudFormation</th>
<th>コンソール（手動）</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>VPC + サブネット4つ + IGW + ルートテーブル</strong></td>
<td>template.yaml に定義済み</td>
<td>約20〜30分・複数画面</td>
</tr>
<tr>
<td><strong>SG 3つ（ALB/EC2/RDS）の作成</strong></td>
<td>template.yaml に定義済み</td>
<td>3画面で個別設定</td>
</tr>
<tr>
<td><strong>ALB + TG + Listener の作成</strong></td>
<td>template.yaml に定義済み</td>
<td>3つの画面で設定</td>
</tr>
<tr>
<td><strong>Tomcat UserDataの貼り付け</strong></td>
<td>template.yaml に組み込み済み</td>
<td>手動コピペ</td>
</tr>
<tr>
<td><strong>全体のデプロイ</strong></td>
<td>コマンド1本（RDS待ち15〜20分）</td>
<td><strong>約40〜50分</strong></td>
</tr>
<tr>
<td><strong>削除</strong></td>
<td><code>delete-stack</code> 1本（自動解決）</td>
<td><strong>13ステップ手動</strong></td>
</tr>
<tr>
<td><strong>再現性</strong></td>
<td>高い（何度でも同じ構成を再現可能）</td>
<td>低い（手順漏れのリスク大）</td>
</tr>
<tr>
<td><strong>バージョン管理</strong></td>
<td>Gitで管理可能</td>
<td>不可</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">キーワード解説</span></h2>
<table>
<thead>
<tr>
<th>用語</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CloudFormation</strong></td>
<td>AWSが提供するIaC（Infrastructure as Code）サービス</td>
</tr>
<tr>
<td><strong>スタック</strong></td>
<td>CloudFormationが管理するリソースのまとまり。今回は約27リソースを1スタックで管理</td>
</tr>
<tr>
<td><strong>AWS::ElasticLoadBalancingV2::LoadBalancer</strong></td>
<td>ALB本体を定義するリソース型</td>
</tr>
<tr>
<td><strong>AWS::ElasticLoadBalancingV2::TargetGroup</strong></td>
<td>ターゲットグループを定義するリソース型</td>
</tr>
<tr>
<td><strong>AWS::ElasticLoadBalancingV2::Listener</strong></td>
<td>ALBのリスナー（受付ルール）を定義するリソース型</td>
</tr>
<tr>
<td><strong>DeletionPolicy: Delete</strong></td>
<td>スタック削除時にRDSも確実に削除する設定</td>
</tr>
<tr>
<td><strong>CAPABILITY_NAMED_IAM</strong></td>
<td>名前付きIAMリソースを含むスタックの作成に必要なフラグ</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">前提条件</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>
</tbody>
</table>
<p>AWS認証確認:</p>
<pre><code class="language-cmd">aws sts get-caller-identity</code></pre>
<hr>
<h2><span id="toc5">構築されるリソース（約27個）</span></h2>
<table>
<thead>
<tr>
<th>カテゴリ</th>
<th>リソース</th>
<th>論理ID</th>
</tr>
</thead>
<tbody>
<tr>
<td>ネットワーク</td>
<td>VPC</td>
<td><code>VPC</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>インターネットゲートウェイ</td>
<td><code>InternetGateway</code> + <code>IGWAttachment</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>パブリックサブネット × 2</td>
<td><code>PublicSubnet1</code> + <code>PublicSubnet2</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>プライベートサブネット × 2</td>
<td><code>PrivateSubnet1</code> + <code>PrivateSubnet2</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>パブリックルートテーブル</td>
<td><code>PublicRouteTable</code> + <code>PublicRoute</code> + <code>PublicSubnet1RTA</code> + <code>PublicSubnet2RTA</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>プライベートルートテーブル</td>
<td><code>PrivateRouteTable</code> + <code>PrivateSubnet1RTA</code> + <code>PrivateSubnet2RTA</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>S3 VPC Gateway Endpoint</td>
<td><code>S3VPCEndpoint</code></td>
</tr>
<tr>
<td>セキュリティ</td>
<td>ALBセキュリティグループ</td>
<td><code>ALBSecurityGroup</code></td>
</tr>
<tr>
<td>セキュリティ</td>
<td>EC2セキュリティグループ</td>
<td><code>EC2SecurityGroup</code></td>
</tr>
<tr>
<td>セキュリティ</td>
<td>RDSセキュリティグループ</td>
<td><code>RDSSecurityGroup</code></td>
</tr>
<tr>
<td>IAM</td>
<td>EC2ロール + インスタンスプロファイル</td>
<td><code>EC2Role</code> + <code>EC2InstanceProfile</code></td>
</tr>
<tr>
<td>パラメータ</td>
<td>SSM Parameter Store</td>
<td><code>DBPasswordParam</code></td>
</tr>
<tr>
<td>データベース</td>
<td>RDS DBサブネットグループ</td>
<td><code>DBSubnetGroup</code></td>
</tr>
<tr>
<td>データベース</td>
<td>RDS MySQLインスタンス</td>
<td><code>RDSInstance</code></td>
</tr>
<tr>
<td>ロードバランサー</td>
<td>ターゲットグループ</td>
<td><code>TargetGroup</code></td>
</tr>
<tr>
<td>ロードバランサー</td>
<td>ALB本体</td>
<td><code>ALB</code></td>
</tr>
<tr>
<td>ロードバランサー</td>
<td>リスナー</td>
<td><code>ALBListener</code></td>
</tr>
<tr>
<td>コンピュート</td>
<td>EC2インスタンス</td>
<td><code>EC2Instance</code></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc6">template.yaml（完全版）</span></h2>
<p>このファイルをそのまま使ってハンズオンを実施できます。<code>C:\my-aws\aws-learning-projects\alb-ec2-rds\template.yaml</code> として保存してください。</p>
<pre><code class="language-yaml">AWSTemplateFormatVersion: '2010-09-09'
Description: 'ALB + EC2(Tomcat) + RDS 3-tier architecture hands-on (Phase 2-2)'

Parameters:
  EmployeeId:
    Type: String
    Default: '123456'
    Description: Employee ID used as a prefix for resource names

  KeyName:
    Type: String
    Default: 'my-alb-key'
    Description: Name of an existing EC2 KeyPair

  MyIP:
    Type: String
    Default: '203.0.113.1/32'    # [IMPORTANT] Replace with your own IP (check: curl https://checkip.amazonaws.com)
    Description: Your IP address to allow SSH access (CIDR format)

  DBPassword:
    Type: String
    Default: 'Handson1234!'
    NoEcho: true
    Description: Master password for RDS MySQL (also stored in SSM Parameter Store)

  TomcatVersion:
    Type: String
    Default: '10.1.28'
    Description: Apache Tomcat version (check latest at https://archive.apache.org/dist/tomcat/tomcat-10/)

Resources:

  # VPC and Network
  # ============================================================

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-vpc'
        - Key: Cost
          Value: !Ref EmployeeId

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-igw'
        - Key: Cost
          Value: !Ref EmployeeId

  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # Public Subnet 1 (AZ-a): ALB + EC2 Tomcat
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-public-subnet-1'
        - Key: Cost
          Value: !Ref EmployeeId

  # Public Subnet 2 (AZ-b): ALB requires subnets in at least 2 AZs
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-public-subnet-2'
        - Key: Cost
          Value: !Ref EmployeeId

  # Private Subnet 1 (AZ-a): RDS instance is placed here
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-private-subnet-1'
        - Key: Cost
          Value: !Ref EmployeeId

  # Private Subnet 2 (AZ-b): required for RDS Subnet Group (minimum 2 AZs)
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.4.0/24
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-private-subnet-2'
        - Key: Cost
          Value: !Ref EmployeeId

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-public-rt'
        - Key: Cost
          Value: !Ref EmployeeId

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: IGWAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-private-rt'
        - Key: Cost
          Value: !Ref EmployeeId

  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable

  # S3 VPC Gateway Endpoint: free, allows EC2 to reach S3 for dnf package downloads
  S3VPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPC
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
      VpcEndpointType: Gateway
      RouteTableIds:
        - !Ref PublicRouteTable
        - !Ref PrivateRouteTable

  # ============================================================
  # IAM
  # ============================================================

  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'n${EmployeeId}-alb-ec2-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-ec2-role'
        - Key: Cost
          Value: !Ref EmployeeId

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub 'n${EmployeeId}-alb-ec2-profile'
      Roles:
        - !Ref EC2Role

  # ============================================================
  # Security Groups
  # ============================================================

  # ALB Security Group: accept HTTP:80 from the internet
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'n${EmployeeId} ALB SG - HTTP:80 from internet'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
          Description: HTTP from internet
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-sg'
        - Key: Cost
          Value: !Ref EmployeeId

  # EC2 Security Group: Tomcat:8080 from ALB SG only, SSH from MyIP
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'n${EmployeeId} EC2 Tomcat SG'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourceSecurityGroupId: !GetAtt ALBSecurityGroup.GroupId
          Description: Tomcat from ALB SG only (SG-to-SG control)
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP
          Description: SSH from my IP
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-ec2-sg'
        - Key: Cost
          Value: !Ref EmployeeId

  # RDS Security Group: MySQL:3306 from EC2 SG only
  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'n${EmployeeId} RDS MySQL SG (EC2-to-RDS only)'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !GetAtt EC2SecurityGroup.GroupId
          Description: MySQL from EC2 SG only (SG-to-SG control)
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-rds-sg'
        - Key: Cost
          Value: !Ref EmployeeId

  # ============================================================
  # SSM Parameter Store
  # NOTE: CloudFormation can only create String type, not SecureString.
  # ============================================================
  DBPasswordParam:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub '/n${EmployeeId}/alb/db-password'
      Type: String
      Value: !Ref DBPassword
      Description: !Sub 'RDS MySQL master password for n${EmployeeId} ALB hands-on'
      Tags:
        Cost: !Ref EmployeeId

  # ============================================================
  # RDS
  # ============================================================

  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: !Sub 'n${EmployeeId} RDS subnet group (private subnets in 2 AZs)'
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-rds-subnet-group'
        - Key: Cost
          Value: !Ref EmployeeId

  RDSInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      DBInstanceIdentifier: !Sub 'n${EmployeeId}-alb-rds-mysql'
      DBInstanceClass: db.t3.micro
      Engine: mysql
      EngineVersion: '8.0'
      MasterUsername: admin
      MasterUserPassword: !Ref DBPassword
      AllocatedStorage: '20'
      StorageType: gp2
      DBName: sampledb
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !GetAtt RDSSecurityGroup.GroupId
      MultiAZ: false
      PubliclyAccessible: false
      BackupRetentionPeriod: 0
      DeleteAutomatedBackups: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-rds-mysql'
        - Key: Cost
          Value: !Ref EmployeeId

  # ============================================================
  # ALB (Application Load Balancer)
  # ============================================================

  # Target Group: routes traffic to EC2 Tomcat on port 8080
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub 'n${EmployeeId}-alb-tg'
      Protocol: HTTP
      Port: 8080
      VpcId: !Ref VPC
      TargetType: instance
      HealthCheckProtocol: HTTP
      HealthCheckPort: '8080'
      HealthCheckPath: /
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      Targets:
        - Id: !Ref EC2Instance
          Port: 8080
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-tg'
        - Key: Cost
          Value: !Ref EmployeeId

  # ALB: internet-facing, spans 2 public subnets
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub 'n${EmployeeId}-alb-alb'
      Type: application
      Scheme: internet-facing
      IpAddressType: ipv4
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      SecurityGroups:
        - !GetAtt ALBSecurityGroup.GroupId
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-alb'
        - Key: Cost
          Value: !Ref EmployeeId

  # Listener: HTTP:80 -&gt; forward to TargetGroup
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref ALB
      Protocol: HTTP
      Port: 80
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroup

  # ============================================================
  # EC2 (AP Server with Tomcat)
  # ============================================================

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}'
      InstanceType: t2.micro
      KeyName: !Ref KeyName
      SubnetId: !Ref PublicSubnet1
      SecurityGroupIds:
        - !GetAtt EC2SecurityGroup.GroupId
      IamInstanceProfile: !Ref EC2InstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          set -xe
          exec &gt; &gt;(tee /var/log/user-data.log) 2&gt;&amp;1

          dnf update -y
          dnf install -y java-17-amazon-corretto wget

          # Install Tomcat
          # Check latest version at: https://archive.apache.org/dist/tomcat/tomcat-10/
          TOMCAT_VERSION="${TomcatVersion}"
          cd /opt
          wget -q https://archive.apache.org/dist/tomcat/tomcat-10/v${!TOMCAT_VERSION}/bin/apache-tomcat-${!TOMCAT_VERSION}.tar.gz
          tar xzf apache-tomcat-${!TOMCAT_VERSION}.tar.gz
          mv apache-tomcat-${!TOMCAT_VERSION} tomcat
          chmod +x /opt/tomcat/bin/*.sh

          # Deploy simple web application to ROOT context
          cat &gt; /opt/tomcat/webapps/ROOT/index.html &lt;&lt; 'HTMLEOF'
          &lt;!DOCTYPE html&gt;
          &lt;html&gt;
          &lt;body&gt;
          &lt;h1&gt;AP Server - Phase 2-2 ALB + Tomcat + RDS&lt;/h1&gt;
          &lt;p&gt;This server is load balanced by ALB and connects to RDS MySQL in the private subnet.&lt;/p&gt;
          &lt;/body&gt;
          &lt;/html&gt;
          HTMLEOF

          # Start Tomcat
          /opt/tomcat/bin/startup.sh
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-alb-ap-instance'
        - Key: Cost
          Value: !Ref EmployeeId

Outputs:
  ALBEndpoint:
    Description: ALB DNS name - access via browser
    Value: !Sub 'http://${ALB.DNSName}'

  EC2PublicIP:
    Description: EC2 Tomcat server public IP (for SSH)
    Value: !GetAtt EC2Instance.PublicIp

  RDSEndpoint:
    Description: RDS MySQL endpoint hostname
    Value: !GetAtt RDSInstance.Endpoint.Address

  DBPasswordParamName:
    Description: SSM Parameter Store path for RDS password
    Value: !Ref DBPasswordParam

  SSHCommand:
    Description: SSH command to EC2 AP server
    Value: !Sub 'ssh -i C:\Users\username\.ssh\${KeyName}.pem ec2-user@${EC2Instance.PublicIp}'

  MySQLConnectCommand:
    Description: MySQL connect command (run FROM the EC2 AP server)
    Value: !Sub 'mysql -h ${RDSInstance.Endpoint.Address} -u admin -pHandson1234! sampledb'</code></pre>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（コンソールで実施）
      ↓
② プロジェクトフォルダに移動する
      ↓
③ スタックを作成する（aws cloudformation create-stack）
      ↓
④ 作成完了・Outputsを確認する（RDSは15〜20分かかる）
      ↓
⑤ 動作確認（ALBアクセス・SSH・RDS接続）
      ↓
⑥ スタックを削除する（aws cloudformation delete-stack）</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>
<p>CloudFormationではキーペアのダウンロードができないため、コンソールで先に作成します。</p>
<p><strong>AWSコンソール → EC2 → キーペア → 「キーペアを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-alb-key</code></td>
</tr>
<tr>
<td>キーペアのタイプ</td>
<td><strong>RSA</strong></td>
</tr>
<tr>
<td>プライベートキーファイル形式</td>
<td><strong>.pem</strong></td>
</tr>
</tbody>
</table>
<p>ダウンロードされた <code>my-alb-key.pem</code> を保存します。</p>
<pre><code class="language-plaintext">C:\Users\ユーザー名\.ssh\my-alb-key.pem</code></pre>
<hr>
<h2><span id="toc10">② プロジェクトフォルダに移動する</span></h2>
<p>VSCodeのターミナル（CMD）を開き、プロジェクトフォルダに移動します。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\alb-ec2-rds</code></pre>
<p><code>template.yaml</code> があることを確認します。</p>
<pre><code class="language-cmd">dir template.yaml</code></pre>
<hr>
<h2><span id="toc11">③ スタックの作成</span></h2>
<p>以下のコマンドを実行します。<code>203.0.113.1</code> は⓪で確認した自分のIPアドレスに置き換えます。</p>
<pre><code class="language-cmd">aws cloudformation create-stack ^
  --stack-name my-alb-stack ^
  --template-body file://template.yaml ^
  --region ap-northeast-1 ^
  --capabilities CAPABILITY_NAMED_IAM ^
  --parameters ^
    ParameterKey=EmployeeId,ParameterValue=123456 ^
    ParameterKey=KeyName,ParameterValue=my-alb-key ^
    ParameterKey=MyIP,ParameterValue=203.0.113.1/32 ^
    ParameterKey=DBPassword,ParameterValue=Handson1234!</code></pre>
<blockquote>
<p><strong><code>TomcatVersion</code> パラメータについて</strong>: デフォルト値は <code>10.1.28</code>。変更する場合は <code>ParameterKey=TomcatVersion,ParameterValue=10.1.xx</code> を追加します。利用可能なバージョンは <a href="https://archive.apache.org/dist/tomcat/tomcat-10/">https://archive.apache.org/dist/tomcat/tomcat-10/</a> で確認します。</p>
</blockquote>
<blockquote>
<p><strong><code>CAPABILITY_NAMED_IAM</code> について</strong>: <code>EC2Role</code> のように名前を指定したIAMリソースを作成する場合に必要なフラグです。</p>
</blockquote>
<p>成功すると以下のような StackId が表示されます。</p>
<pre><code class="language-json">{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/my-alb-stack/xxxxx"
}</code></pre>
<hr>
<h2><span id="toc12">④ 作成完了・Outputsの確認</span></h2>
<blockquote>
<p><strong>注意</strong>: RDSの作成には<strong>約15〜20分</strong>かかります。<code>CREATE_IN_PROGRESS</code> が続く間は正常です。</p>
</blockquote>
<h3><span id="toc13">作成状況の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-alb-stack ^
  --query "Stacks[0].StackStatus" ^
  --output text</code></pre>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_IN_PROGRESS</code></td>
<td>作成中（待つ）</td>
</tr>
<tr>
<td><code>CREATE_COMPLETE</code></td>
<td>作成完了</td>
</tr>
<tr>
<td><code>CREATE_FAILED</code></td>
<td>作成失敗（後述のトラブルシューティングを参照）</td>
</tr>
<tr>
<td><code>ROLLBACK_COMPLETE</code></td>
<td>失敗してロールバック完了</td>
</tr>
</tbody>
</table>
<h3><span id="toc14">Outputs（接続情報）の確認</span></h3>
<p><code>CREATE_COMPLETE</code> になったら以下を実行します。</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-alb-stack ^
  --query "Stacks[0].Outputs" ^
  --output table</code></pre>
<pre><code class="language-plaintext">+----------------------+------------------------------------------------------------------+
|  OutputKey           |  OutputValue                                                     |
+----------------------+------------------------------------------------------------------+
|  ALBEndpoint         |  http://my-alb-alb-1234567890.ap-northeast-1.elb.amazonaws.com  |
|  EC2PublicIP         |  x.x.x.x                                                        |
|  RDSEndpoint         |  my-alb-rds-mysql.xxxxx.ap-northeast-1.rds.amazonaws.com        |
|  DBPasswordParamName |  /my/alb/db-password                                             |
|  SSHCommand          |  ssh -i C:\Users\...\.ssh\my-alb-key.pem ec2-user@x.x.x.x       |
+----------------------+------------------------------------------------------------------+</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>EC2PublicIP</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CloudFormation Outputs確認コマンドの実行結果](images/cfn-outputs.jpg) --></p>
<hr>
<h2><span id="toc15">⑤ 動作確認</span></h2>
<h3><span id="toc16">ALBのヘルスチェック確認</span></h3>
<p><strong>EC2 → ターゲットグループ → <code>my-alb-tg</code> → 「ターゲット」タブ</strong></p>
<p>EC2のステータスが <strong>「healthy」</strong> になるまで待ちます（EC2起動後5〜10分）。</p>
<blockquote>
<p>Tomcatの起動はEC2が「実行中」になってからさらに数分かかります。<code>initial</code> や <code>unhealthy</code> が続く場合はしばらく待ちます。</p>
</blockquote>
<p><!-- ![ターゲットグループのHealthy確認画面](images/target-healthy.jpg) --></p>
<h3><span id="toc17">ALB経由でWebアクセス確認</span></h3>
<p>Outputsに表示された <code>ALBEndpoint</code> をブラウザで開きます。</p>
<pre><code class="language-plaintext">http://（ALBEndpointのDNS名）</code></pre>
<p>「AP Server - Phase 2-2 ALB + Tomcat + RDS」と表示されれば正常です。</p>
<h3><span id="toc18">EC2にSSH接続する</span></h3>
<p>Outputsの <code>SSHCommand</code> を実行します（パスは実際のパスに修正します）。</p>
<pre><code class="language-cmd">ssh -i C:\Users\ユーザー名\.ssh\my-alb-key.pem ec2-user@（EC2PublicIP）</code></pre>
<h3><span id="toc19">Parameter StoreからDBパスワードを取得する</span></h3>
<p>EC2に接続した状態で実行します。</p>
<pre><code class="language-bash">aws ssm get-parameter \
  --name "/my/alb/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1</code></pre>
<p><code>Handson1234!</code> が表示されれば成功です。</p>
<blockquote>
<p><strong><code>--with-decryption</code> が不要な理由</strong>: CloudFormationで作成したパラメータは <code>Type: String</code>（平文）のため、フラグなしでそのまま値が返ります。コンソールで <strong>SecureString</strong> として作成した場合は <code>--with-decryption</code> が必要です（コンソール版参照）。</p>
</blockquote>
<h3><span id="toc20">RDSへのMySQL接続テスト</span></h3>
<pre><code class="language-bash"># RDSエンドポイントをOutputsの値に置き換える
RDS_ENDPOINT="my-alb-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com"

# Parameter StoreからDBパスワードを取得して接続（String型のため--with-decryption不要）
DB_PASSWORD=$(aws ssm get-parameter \
  --name "/my/alb/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1)

mysql -h $RDS_ENDPOINT -u admin -p"$DB_PASSWORD" sampledb</code></pre>
<p>接続成功後、SQL操作を確認します。</p>
<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>
<p>EC2からSSH接続を切断します。</p>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc21">⑥ スタックの削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-alb-stack ^
  --region ap-northeast-1</code></pre>
<blockquote>
<p><strong>注意</strong>: RDS削除には<strong>約10〜15分</strong>かかります。コマンド実行後もしばらく待つ必要があります。</p>
</blockquote>
<h3><span id="toc22">削除状況の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-alb-stack ^
  --query "Stacks[0].StackStatus" ^
  --output text</code></pre>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>DELETE_IN_PROGRESS</code></td>
<td>削除中（待つ）</td>
</tr>
<tr>
<td><code>DELETE_FAILED</code></td>
<td>削除失敗（トラブルシューティングを参照）</td>
</tr>
</tbody>
</table>
<p>削除完了の確認（以下のエラーが表示されれば削除完了）:</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-alb-stack ^
  --region ap-northeast-1</code></pre>
<pre><code class="language-plaintext">An error occurred (ValidationError) when calling the DescribeStacks operation:
Stack with id my-alb-stack does not exist</code></pre>
<h3><span id="toc23">キーペアの手動削除</span></h3>
<p>キーペアはCloudFormationで管理していないため手動で削除します。</p>
<p><strong>EC2 → キーペア → <code>my-alb-key</code> を選択 → 「アクション」→「削除」</strong></p>
<hr>
<h2><span id="toc24">コンソール版との比較</span></h2>
<table>
<thead>
<tr>
<th>作業</th>
<th>コンソール（手動）</th>
<th>CloudFormation</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC + サブネット4つ + IGW + ルートテーブル</td>
<td>約20〜30分</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>SG 3つ（ALB/EC2/RDS）の作成</td>
<td>3画面で個別設定</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>ALB + TG + Listener の作成</td>
<td>3つの画面で設定</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>Tomcat UserDataの貼り付け</td>
<td>手動コピペ</td>
<td>template.yaml に組み込み済み</td>
</tr>
<tr>
<td>全体のデプロイ</td>
<td><strong>約40〜50分</strong></td>
<td>コマンド1本（RDS待ち15〜20分）</td>
</tr>
<tr>
<td>削除</td>
<td><strong>13ステップ手動</strong></td>
<td><code>delete-stack</code> 1本</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc25">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_FAILED</code> → ROLLBACKになる</td>
<td>パラメータ不正・リソース上限など</td>
<td>コンソールのCloudFormationイベントタブでエラー詳細を確認</td>
</tr>
<tr>
<td>ALBに繋がらない</td>
<td>EC2のTomcatがまだ起動中</td>
<td>TGのヘルスチェックがHealthyになるまで待つ（5〜10分）</td>
</tr>
<tr>
<td>ALBに繋がるが502エラー</td>
<td>Tomcat起動失敗</td>
<td>SSH接続して <code>sudo cat /var/log/user-data.log</code> 確認</td>
</tr>
<tr>
<td><code>ERROR 2003</code>: MySQL接続できない</td>
<td>RDS SGのソース設定ミス</td>
<td>RDS SGのインバウンドルールを確認</td>
</tr>
<tr>
<td>Tomcatバージョンエラー</td>
<td>指定バージョンがアーカイブに存在しない</td>
<td><a href="https://archive.apache.org/dist/tomcat/tomcat-10/">https://archive.apache.org/dist/tomcat/tomcat-10/</a> で確認して <code>TomcatVersion</code> パラメータを更新</td>
</tr>
<tr>
<td><code>DELETE_FAILED</code></td>
<td>リソースの依存関係が残っている</td>
<td>コンソールからALBを手動削除してから <code>delete-stack</code> を再実行</td>
</tr>
<tr>
<td>スタック作成後しばらく <code>CREATE_IN_PROGRESS</code> が続く</td>
<td>RDSの作成に時間がかかっている</td>
<td>正常。約15〜20分待つ</td>
</tr>
</tbody>
</table>
<h3><span id="toc26">失敗時の詳細確認コマンド</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stack-events ^
  --stack-name my-alb-stack ^
  --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[ResourceType,ResourceStatusReason]" ^
  --output table</code></pre>
<hr>
<h2><span id="toc27">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>キーペアをコンソールで作成</td>
</tr>
<tr>
<td>②③</td>
<td><code>alb-ec2-rds/</code> フォルダに移動し <code>create-stack</code> を実行</td>
</tr>
<tr>
<td>④</td>
<td><code>CREATE_COMPLETE</code> になったらOutputsでALBエンドポイント・EC2IP・RDSエンドポイントを確認</td>
</tr>
<tr>
<td>⑤</td>
<td>ヘルスチェックがHealthyになったらALB → EC2 → RDS MySQL への接続テスト</td>
</tr>
<tr>
<td>⑥</td>
<td><code>delete-stack</code> 1本で27リソースを一括削除</td>
</tr>
</tbody>
</table>
<p>CloudFormation版の最大のメリットは<strong>再現性</strong>と<strong>削除の簡単さ</strong>です。コンソールでは40〜50分・13ステップかかった作業が、コマンド1本（待ち時間込みで約20分）で完了します。</p>
<p>コンソール操作でALB・ターゲットグループ・ヘルスチェックを視覚的に確認したい場合は、<a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/">CloudFormationでALB + EC2(Tomcat) + RDS 3層構成を自動構築する手順【27リソース一括デプロイ / コンソール版との比較付き】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cloudformation-alb-ec2-rds/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AWSコンソールでRDS MySQL + EC2接続環境を構築する手順【DBサブネットグループ / Parameter Storeパスワード管理】</title>
		<link>https://caymezon.com/aws-handson-console-rds-mysql-ec2/</link>
					<comments>https://caymezon.com/aws-handson-console-rds-mysql-ec2/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 11 Apr 2026 23:47:54 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWSコンソール]]></category>
		<category><![CDATA[DBサブネットグループ]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[SG参照]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[セキュリティグループ]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[マネージドサービス]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20338</guid>

					<description><![CDATA[<p>目次 はじめにキーワード解説前フェーズとの違い使用するAWSサービス構築するリソース一覧全体の作業順序【重要】⓪ 自分のIPアドレスを確認する① キーペアの作成② VPCの作成③ サブネットの作成（3つ）サブネット1:  [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-console-rds-mysql-ec2/">AWSコンソールでRDS MySQL + EC2接続環境を構築する手順【DBサブネットグループ / Parameter Storeパスワード管理】</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-18" checked><label class="toc-title" for="toc-checkbox-18">目次</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">前フェーズとの違い</a></li><li><a href="#toc4" tabindex="0">使用するAWSサービス</a></li><li><a href="#toc5" tabindex="0">構築するリソース一覧</a></li><li><a href="#toc6" tabindex="0">全体の作業順序</a></li><li><a href="#toc7" tabindex="0">【重要】⓪ 自分のIPアドレスを確認する</a></li><li><a href="#toc8" tabindex="0">① キーペアの作成</a></li><li><a href="#toc9" tabindex="0">② VPCの作成</a></li><li><a href="#toc10" tabindex="0">③ サブネットの作成（3つ）</a><ol><li><a href="#toc11" tabindex="0">サブネット1: パブリックサブネット（EC2用）</a></li><li><a href="#toc12" tabindex="0">サブネット2: プライベートサブネット1（RDS配置先）</a></li><li><a href="#toc13" tabindex="0">サブネット3: プライベートサブネット2（DBサブネットグループ用）</a></li></ol></li><li><a href="#toc14" tabindex="0">④ インターネットゲートウェイの作成・アタッチ</a><ol><li><a href="#toc15" tabindex="0">作成</a></li><li><a href="#toc16" tabindex="0">VPCへのアタッチ</a></li></ol></li><li><a href="#toc17" tabindex="0">⑤ ルートテーブルの設定</a><ol><li><a href="#toc18" tabindex="0">5-1. パブリック用ルートテーブルの作成</a></li><li><a href="#toc19" tabindex="0">5-2. プライベート用ルートテーブルの作成</a></li></ol></li><li><a href="#toc20" tabindex="0">⑥ セキュリティグループの作成</a><ol><li><a href="#toc21" tabindex="0">6-1. EC2用セキュリティグループ</a></li><li><a href="#toc22" tabindex="0">6-2. RDS用セキュリティグループ</a></li></ol></li><li><a href="#toc23" tabindex="0">⑦ IAMロールの作成</a><ol><li><a href="#toc24" tabindex="0">ステップ1</a></li><li><a href="#toc25" tabindex="0">ステップ2: ポリシーのアタッチ</a></li><li><a href="#toc26" tabindex="0">ステップ3</a></li></ol></li><li><a href="#toc27" tabindex="0">⑧ SSM Parameter Store にDBパスワードを登録する</a></li><li><a href="#toc28" tabindex="0">⑨ RDS DBサブネットグループの作成</a></li><li><a href="#toc29" tabindex="0">⑩ RDS MySQL インスタンスの作成</a><ol><li><a href="#toc30" tabindex="0">エンジンの選択</a></li><li><a href="#toc31" tabindex="0">テンプレート</a></li><li><a href="#toc32" tabindex="0">設定</a></li><li><a href="#toc33" tabindex="0">インスタンスの設定</a></li><li><a href="#toc34" tabindex="0">ストレージ</a></li><li><a href="#toc35" tabindex="0">接続</a></li><li><a href="#toc36" tabindex="0">追加設定</a></li></ol></li><li><a href="#toc37" tabindex="0">⑪ EC2インスタンスの起動</a><ol><li><a href="#toc38" tabindex="0">基本設定</a></li><li><a href="#toc39" tabindex="0">ネットワーク設定</a></li><li><a href="#toc40" tabindex="0">IAMロール</a></li><li><a href="#toc41" tabindex="0">UserData</a></li><li><a href="#toc42" tabindex="0">タグ</a></li></ol></li><li><a href="#toc43" tabindex="0">⑫ 動作確認</a><ol><li><a href="#toc44" tabindex="0">12-1. WebブラウザでEC2のWebサーバ確認</a></li><li><a href="#toc45" tabindex="0">12-2. EC2にSSH接続する</a></li><li><a href="#toc46" tabindex="0">12-3. Parameter StoreからDBパスワードを取得する</a></li><li><a href="#toc47" tabindex="0">12-4. RDSへのMySQL接続テスト</a></li><li><a href="#toc48" tabindex="0">12-5. RDS上でSQL操作を確認する</a></li><li><a href="#toc49" tabindex="0">12-6. EC2からSSH接続を切断する</a></li></ol></li><li><a href="#toc50" tabindex="0">⑬ リソースの削除</a><ol><li><a href="#toc51" tabindex="0">削除順序</a></li><li><a href="#toc52" tabindex="0">1. EC2インスタンスを終了する</a></li><li><a href="#toc53" tabindex="0">2. RDSインスタンスを削除する</a></li><li><a href="#toc54" tabindex="0">3. RDS DBサブネットグループを削除する</a></li><li><a href="#toc55" tabindex="0">4. SSM Parameter Storeのパラメータを削除する</a></li><li><a href="#toc56" tabindex="0">5. セキュリティグループを削除する</a></li><li><a href="#toc57" tabindex="0">6. ルートテーブルを削除する</a></li><li><a href="#toc58" tabindex="0">7. インターネットゲートウェイをデタッチ・削除する</a></li><li><a href="#toc59" tabindex="0">8. サブネットを削除する（3つ）</a></li><li><a href="#toc60" tabindex="0">9. VPCを削除する</a></li><li><a href="#toc61" tabindex="0">10. IAMロールを削除する</a></li><li><a href="#toc62" tabindex="0">11. キーペアを削除する（任意）</a></li></ol></li><li><a href="#toc63" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「EC2にMariaDBをインストールしてDBサーバを自分で管理するのは大変……」——その悩みを解決するのが <strong>Amazon RDS（マネージド型データベースサービス）</strong> です。</p>
<p>この記事では、<strong>AWSコンソール（GUI）のみ</strong>を使って、RDS MySQL 8.0インスタンスとEC2（APサーバ）をゼロから構築し、EC2からRDSへMySQL接続を確認するハンズオンを紹介します。</p>
<pre><code class="language-plaintext">インターネット
  ↓ HTTP(80) / SSH(22)  ← 自分のIPのみ
インターネットゲートウェイ（my-rds-igw）
  ↓
[VPC: 10.0.0.0/16]
  ├── パブリックサブネット（10.0.1.0/24）[AZ-a]
  │     └── EC2 APサーバ（Apache + mariadb client）
  │           └── IAMロール → SSM Parameter Store からパスワード取得
  │                     ↓ MySQL:3306（SG-to-SG制御）
  ├── DBサブネットグループ
  │     ├── プライベートサブネット1（10.0.2.0/24）[AZ-a]  ← RDS配置
  │     │     └── RDS MySQL 8.0（db.t3.micro）
  │     └── プライベートサブネット2（10.0.3.0/24）[AZ-c]  ← 2AZ要件用
  └── SSM Parameter Store: /my/rds/db-password</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>EC2にDBをインストールする方式との違いを体感</li>
<li>RDS DBサブネットグループが「2AZ以上必須」な理由を理解</li>
<li>SSM Parameter Storeでパスワードをコードに書かずに安全に管理する方法</li>
<li>SG-to-SG制御でRDSをEC2からのみ許可する設計</li>
</ul>
<p><strong>このハンズオンのポイント：</strong></p>
<p>前フェーズ（EC2 + MariaDB）では自分でDBを管理していましたが、今回はAWSがOSのメンテナンス・パッチ・バックアップを自動管理するRDSに置き換えます。ネットワーク設計はカスタムVPC（3サブネット）で実際の本番環境に近い構成になります。</p>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/">CloudFormation版ハンズオン</a> の比較記事です。</strong><br />コンソール操作でRDS・DBサブネットグループ・Parameter Storeを視覚的に学びたい方向けです。</p>
</blockquote>
<hr>
<p><!-- ![ハンズオン完成後のRDS接続確認画面](images/rds-mysql-ec2-top.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>Amazon RDS</strong></td>
<td>AWSが提供するマネージド型のリレーショナルDBサービス。EC2へのインストールと違い、OSのメンテナンス・パッチ・バックアップをAWSが自動管理する</td>
</tr>
<tr>
<td><strong>DBサブネットグループ</strong></td>
<td>RDSを配置できるサブネットを登録した論理グループ。2つ以上の異なるAZのサブネットが必須</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>設定値・シークレットをコードに書かずに保管・取得できるサービス。SecureString型でKMS暗号化して保管できる</td>
</tr>
<tr>
<td><strong>SG-to-SG制御</strong></td>
<td>セキュリティグループのルールで、アクセス元のIPアドレスではなく別のSGを指定する設定。EC2 SGに所属するインスタンスのみRDSへの接続を許可できる</td>
</tr>
<tr>
<td><strong>エンドポイント</strong></td>
<td>RDSに接続するためのホスト名。EC2からはこのホスト名を使ってMySQLとして接続する</td>
</tr>
<tr>
<td><strong>SecureString</strong></td>
<td>Parameter StoreでKMS暗号化して保管するタイプ。パスワードやAPIキーなどの機密情報に使用する</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">前フェーズとの違い</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>（EC2 + MariaDB）</th>
<th>（RDS MySQL）今回</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBエンジン</td>
<td>MariaDB（EC2上で動作）</td>
<td><strong>MySQL 8.0（RDSマネージドサービス）</strong></td>
</tr>
<tr>
<td>DB管理</td>
<td>OS込みで自分で管理</td>
<td><strong>AWSがパッチ・バックアップを自動管理</strong></td>
</tr>
<tr>
<td>サブネット構成</td>
<td>デフォルトVPC（単一AZ）</td>
<td><strong>カスタムVPC（3サブネット・2AZ）</strong></td>
</tr>
<tr>
<td>パスワード管理</td>
<td>ハードコード</td>
<td><strong>SSM Parameter Store</strong></td>
</tr>
<tr>
<td>削除</td>
<td>EC2終了のみ</td>
<td><strong>RDS削除に10〜15分必要</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">使用する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サーバ（Apache + mariadb client）</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>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>
<tr>
<td><strong>S3 VPC Gateway Endpoint</strong></td>
<td>プライベートサブネットからS3へのアクセス</td>
<td><strong>無料</strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>: RDSを稼働させたままにすると課金されます。ハンズオン後は必ず削除してください。</p>
</blockquote>
<hr>
<h2><span id="toc5">構築するリソース一覧</span></h2>
<table>
<thead>
<tr>
<th>順序</th>
<th>リソース</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td>⓪</td>
<td>（確認）自分のIPアドレス</td>
<td>SGの設定に使用</td>
</tr>
<tr>
<td>①</td>
<td>キーペア（<code>my-rds-mysql-key</code>）</td>
<td>EC2へのSSH認証鍵</td>
</tr>
<tr>
<td>②</td>
<td>VPC（<code>my-rds-vpc</code>）</td>
<td>独立したネットワーク空間</td>
</tr>
<tr>
<td>③</td>
<td>サブネット × 3</td>
<td>パブリック1 + プライベート2</td>
</tr>
<tr>
<td>④</td>
<td>インターネットゲートウェイ（<code>my-rds-igw</code>）</td>
<td>VPCをインターネットに接続</td>
</tr>
<tr>
<td>⑤</td>
<td>ルートテーブル × 2</td>
<td>パブリック/プライベートの通信経路</td>
</tr>
<tr>
<td>⑥</td>
<td>セキュリティグループ × 2</td>
<td>EC2用SG・RDS用SG</td>
</tr>
<tr>
<td>⑦</td>
<td>IAMロール（<code>my-rds-ec2-role</code>）</td>
<td>EC2がSSM・Parameter Storeを使う権限</td>
</tr>
<tr>
<td>⑧</td>
<td>SSM Parameter Store</td>
<td>DBパスワードの保管</td>
</tr>
<tr>
<td>⑨</td>
<td>RDS DBサブネットグループ</td>
<td>RDSを配置できるサブネットを登録</td>
</tr>
<tr>
<td>⑩</td>
<td>RDS MySQL インスタンス</td>
<td>マネージドDBサーバ（作成に10〜15分）</td>
</tr>
<tr>
<td>⑪</td>
<td>EC2インスタンス（<code>my-rds-ap-instance</code>）</td>
<td>APサーバ</td>
</tr>
<tr>
<td>⑫</td>
<td>動作確認</td>
<td>EC2→RDSへのMySQL接続テスト</td>
</tr>
<tr>
<td>⑬</td>
<td>リソース削除</td>
<td>課金停止</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc6">全体の作業順序</span></h2>
<pre><code class="language-plaintext">⓪ IPアドレス確認
      ↓
① キーペア作成
      ↓
② VPC作成
      ↓
③ サブネット作成（パブリック1 + プライベート2）
      ↓
④ インターネットゲートウェイ作成・アタッチ
      ↓
⑤ ルートテーブル設定
      ↓
⑥ セキュリティグループ作成（EC2用 → RDS用）
      ↓
⑦ IAMロール作成
      ↓
⑧ SSM Parameter Store にパスワードを登録
      ↓
⑨ RDS DBサブネットグループ作成
      ↓
⑩ RDS MySQL インスタンス作成（※約10〜15分かかる）
      ↓
⑪ EC2インスタンス起動
      ↓
⑫ 動作確認（EC2 → RDS接続）
      ↓
⑬ リソース削除</code></pre>
<hr>
<h2><span id="toc7">【重要】⓪ 自分のIPアドレスを確認する</span></h2>
<pre><code class="language-cmd">curl https://checkip.amazonaws.com</code></pre>
<p>またはブラウザで <code>https://checkip.amazonaws.com</code> にアクセスして確認します。</p>
<p><strong>控えておく情報</strong>: 自分のIPアドレス（例: <code>203.0.113.1</code>）</p>
<blockquote>
<p>セキュリティグループで <code>203.0.113.1/32</code> の形式（末尾に <code>/32</code>）で使用します。</p>
</blockquote>
<hr>
<h2><span id="toc8">① キーペアの作成</span></h2>
<p><strong>AWSコンソール → EC2 → ネットワーク＆セキュリティ → キーペア → 「キーペアを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-rds-mysql-key</code></td>
</tr>
<tr>
<td>キーペアのタイプ</td>
<td><strong>RSA</strong></td>
</tr>
<tr>
<td>プライベートキーファイル形式</td>
<td><strong>.pem</strong></td>
</tr>
</tbody>
</table>
<p>「キーペアを作成」をクリックすると <code>my-rds-mysql-key.pem</code> がダウンロードされます。</p>
<pre><code class="language-plaintext">C:\Users\ユーザー名\.ssh\my-rds-mysql-key.pem</code></pre>
<hr>
<h2><span id="toc9">② VPCの作成</span></h2>
<p><strong>AWSコンソール → VPC → お使いのVPC → 「VPCを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>作成するリソース</td>
<td><strong>VPCのみ</strong></td>
</tr>
<tr>
<td>名前タグ</td>
<td><code>my-rds-vpc</code></td>
</tr>
<tr>
<td>IPv4 CIDR</td>
<td><code>10.0.0.0/16</code></td>
</tr>
</tbody>
</table>
<p>「VPCを作成」をクリック。</p>
<blockquote>
<p><strong>VPCとは</strong>: AWS内に作る仮想的なプライベートネットワーク。<code>10.0.0.0/16</code> は「10.0.0.0〜10.0.255.255」の65,536個のIPアドレスを使える範囲です。</p>
</blockquote>
<p><!-- ![VPC作成画面](images/vpc-create.jpg) --></p>
<hr>
<h2><span id="toc10">③ サブネットの作成（3つ）</span></h2>
<p><strong>AWSコンソール → VPC → サブネット → 「サブネットを作成」</strong></p>
<p>「VPC ID」に <code>my-rds-vpc</code> を選択してから、以下の3つを作成します。</p>
<blockquote>
<p>「新しいサブネットを追加」ボタンで複数まとめて作成できます。</p>
</blockquote>
<h3><span id="toc11">サブネット1: パブリックサブネット（EC2用）</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>サブネット名</td>
<td><code>my-public-subnet</code></td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code></td>
</tr>
<tr>
<td>IPv4 CIDR</td>
<td><code>10.0.1.0/24</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc12">サブネット2: プライベートサブネット1（RDS配置先）</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>サブネット名</td>
<td><code>my-private-subnet-1</code></td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code></td>
</tr>
<tr>
<td>IPv4 CIDR</td>
<td><code>10.0.2.0/24</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc13">サブネット3: プライベートサブネット2（DBサブネットグループ用）</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>サブネット名</td>
<td><code>my-private-subnet-2</code></td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1c</code></td>
</tr>
<tr>
<td>IPv4 CIDR</td>
<td><code>10.0.3.0/24</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>なぜプライベートサブネットが2つ必要か</strong>: RDS DBサブネットグループは「2つ以上の異なるAZ」にあるサブネットを登録する必要があります。RDSインスタンス自体はAZ-aの <code>private-subnet-1</code> に置きますが、グループ定義にはAZ-cの <code>private-subnet-2</code> も必要です。これはRDSのMulti-AZフェイルオーバーに備えた仕様です。</p>
</blockquote>
<p>「サブネットを作成」をクリック。</p>
<p><!-- ![サブネット3つの作成画面](images/subnet-create.jpg) --></p>
<hr>
<h2><span id="toc14">④ インターネットゲートウェイの作成・アタッチ</span></h2>
<h3><span id="toc15">作成</span></h3>
<p><strong>AWSコンソール → VPC → インターネットゲートウェイ → 「インターネットゲートウェイを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前タグ</td>
<td><code>my-rds-igw</code></td>
</tr>
</tbody>
</table>
<p>「インターネットゲートウェイを作成」をクリック。</p>
<h3><span id="toc16">VPCへのアタッチ</span></h3>
<p>作成したIGWを選択 → 「アクション」→「VPCにアタッチ」→ <code>my-rds-vpc</code> を選択 → 「インターネットゲートウェイのアタッチ」</p>
<p>ステータスが <strong>「Attached」</strong> になることを確認します。</p>
<hr>
<h2><span id="toc17">⑤ ルートテーブルの設定</span></h2>
<h3><span id="toc18">5-1. パブリック用ルートテーブルの作成</span></h3>
<p><strong>AWSコンソール → VPC → ルートテーブル → 「ルートテーブルを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-public-rt</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
</tbody>
</table>
<p>作成後、<code>my-public-rt</code> を選択 → 「ルート」タブ → 「ルートを編集」→「ルートを追加」</p>
<table>
<thead>
<tr>
<th>送信先</th>
<th>ターゲット</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0.0.0.0/0</code></td>
<td>インターネットゲートウェイ <code>my-rds-igw</code></td>
</tr>
</tbody>
</table>
<p>「変更を保存」をクリック。</p>
<p>次に「サブネットの関連付け」タブ → 「サブネットの関連付けを編集」→ <code>my-public-subnet</code> にチェック → 「関連付けを保存」</p>
<h3><span id="toc19">5-2. プライベート用ルートテーブルの作成</span></h3>
<p>同様に「ルートテーブルを作成」</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-private-rt</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
</tbody>
</table>
<p>「サブネットの関連付けを編集」→ <code>my-private-subnet-1</code> と <code>my-private-subnet-2</code> の両方にチェック → 「関連付けを保存」</p>
<blockquote>
<p>プライベートルートテーブルにはインターネットへのルートを追加しません。RDSはインターネットに出る必要がないため、VPC内のローカル通信のみで十分です。</p>
</blockquote>
<hr>
<h2><span id="toc20">⑥ セキュリティグループの作成</span></h2>
<h3><span id="toc21">6-1. EC2用セキュリティグループ</span></h3>
<p><strong>AWSコンソール → EC2 → セキュリティグループ → 「セキュリティグループを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-rds-ec2-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>EC2 AP server SG for RDS hands-on</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
</tbody>
</table>
<p><strong>インバウンドルール:</strong></p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>プロトコル</th>
<th>ポート</th>
<th>ソース</th>
</tr>
</thead>
<tbody>
<tr>
<td>SSH</td>
<td>TCP</td>
<td>22</td>
<td><code>自分のIP/32</code></td>
</tr>
<tr>
<td>HTTP</td>
<td>TCP</td>
<td>80</td>
<td><code>自分のIP/32</code></td>
</tr>
</tbody>
</table>
<p><strong>アウトバウンドルール:</strong></p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>送信先</th>
</tr>
</thead>
<tbody>
<tr>
<td>すべてのトラフィック</td>
<td><code>0.0.0.0/0</code></td>
</tr>
</tbody>
</table>
<p>「セキュリティグループを作成」をクリック。</p>
<h3><span id="toc22">6-2. RDS用セキュリティグループ</span></h3>
<p>同様に「セキュリティグループを作成」</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>セキュリティグループ名</td>
<td><code>my-rds-sg</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS MySQL SG - EC2 only</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
</tbody>
</table>
<p><strong>インバウンドルール:</strong></p>
<table>
<thead>
<tr>
<th>タイプ</th>
<th>プロトコル</th>
<th>ポート</th>
<th>ソース</th>
</tr>
</thead>
<tbody>
<tr>
<td>MYSQL/Aurora</td>
<td>TCP</td>
<td>3306</td>
<td><strong><code>my-rds-ec2-sg</code> のSG-ID</strong>（SGを選択）</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>ポイント（SG-to-SG制御）</strong>: ソースにIPアドレスではなく<strong>セキュリティグループ</strong>を指定します。<code>my-rds-ec2-sg</code> に所属するEC2インスタンスからの通信のみ許可でき、インターネットからのアクセスを完全に遮断できます。</p>
</blockquote>
<p>「セキュリティグループを作成」をクリック。</p>
<p><!-- ![RDS用SGのインバウンドルール設定画面（SG-to-SG）](images/rds-sg-create.jpg) --></p>
<hr>
<h2><span id="toc23">⑦ IAMロールの作成</span></h2>
<p><strong>AWSコンソール → IAM → ロール → 「ロールを作成」</strong></p>
<h3><span id="toc24">ステップ1</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>信頼されたエンティティタイプ</td>
<td><strong>AWSのサービス</strong></td>
</tr>
<tr>
<td>ユースケース</td>
<td><strong>EC2</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc25">ステップ2: ポリシーのアタッチ</span></h3>
<p>以下の2つを検索してチェックを入れます。</p>
<table>
<thead>
<tr>
<th>ポリシー名</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AmazonSSMManagedInstanceCore</code></td>
<td>Session Manager接続用</td>
</tr>
<tr>
<td><code>AmazonSSMReadOnlyAccess</code></td>
<td>Parameter Storeからパスワードを読み取る</td>
</tr>
</tbody>
</table>
<h3><span id="toc26">ステップ3</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ロール名</td>
<td><code>my-rds-ec2-role</code></td>
</tr>
</tbody>
</table>
<p>タグを追加: <code>Cost</code> = <code>123456</code></p>
<p>「ロールを作成」をクリック。</p>
<hr>
<h2><span id="toc27">⑧ SSM Parameter Store にDBパスワードを登録する</span></h2>
<p>Parameter Storeは、DBパスワードなどの設定値をコードに書かずに安全に管理するサービスです。EC2上のアプリケーションは実行時にParameter Storeから値を取得して使用します。</p>
<p><strong>AWSコンソール → Systems Manager → パラメータストア → 「パラメータの作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>/my/rds/db-password</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>RDS MySQL master password for my hands-on</code></td>
</tr>
<tr>
<td>層</td>
<td><strong>スタンダード</strong></td>
</tr>
<tr>
<td>タイプ</td>
<td><strong>SecureString</strong></td>
</tr>
<tr>
<td>KMS キーID</td>
<td><code>alias/aws/ssm</code>（デフォルト）</td>
</tr>
<tr>
<td>値</td>
<td><code>Handson1234!</code></td>
</tr>
</tbody>
</table>
<p>「パラメータの作成」をクリック。</p>
<blockquote>
<p><strong>SecureStringとは</strong>: KMSで暗号化して保管するタイプです。通常の String と違い、値を取得する際に適切なIAM権限が必要です。パスワードやAPIキーなどの機密情報はSecureStringを使いましょう。</p>
</blockquote>
<p><!-- ![Parameter Store SecureString設定画面](images/parameter-store-create.jpg) --></p>
<hr>
<h2><span id="toc28">⑨ RDS DBサブネットグループの作成</span></h2>
<p><strong>AWSコンソール → Aurora and RDS → サブネットグループ → 「DBサブネットグループを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-rds-subnet-group</code></td>
</tr>
<tr>
<td>説明</td>
<td><code>my RDS subnet group (private subnets in 2 AZs)</code></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
</tbody>
</table>
<p><strong>サブネットの追加:</strong></p>
<table>
<thead>
<tr>
<th>アベイラビリティーゾーン</th>
<th>サブネット</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ap-northeast-1a</code></td>
<td><code>my-private-subnet-1</code></td>
</tr>
<tr>
<td><code>ap-northeast-1c</code></td>
<td><code>my-private-subnet-2</code></td>
</tr>
</tbody>
</table>
<p>「作成」をクリック。</p>
<blockquote>
<p><strong>DBサブネットグループとは</strong>: RDSインスタンスを配置できるサブネットを登録した論理グループです。RDSはこのグループ内のサブネットにインスタンスを配置します。最低2つの異なるAZのサブネットが必要な理由は、RDSがMulti-AZフェイルオーバーに対応するための仕様です。</p>
</blockquote>
<hr>
<h2><span id="toc29">⑩ RDS MySQL インスタンスの作成</span></h2>
<p><strong>AWSコンソール → Aurora and RDS → データベース → 「データベースの作成 ▲」→「フル設定」</strong></p>
<blockquote>
<p><strong>UIの変更について（2025年以降）</strong>: サービス名が <strong>「RDS」→「Aurora and RDS」</strong> に変更されました。「データベースの作成」ボタンはドロップダウン式になっており、以下の3つが表示されます。</p>
<table>
<thead>
<tr>
<th>選択肢</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>エクスプレス設定</td>
<td>簡易設定（詳細設定不可）</td>
</tr>
<tr>
<td><strong>フル設定</strong></td>
<td><strong>← これを選ぶ</strong>（VPC・SGなどを細かく指定できる）</td>
</tr>
<tr>
<td>S3から復元</td>
<td>バックアップからの復元</td>
</tr>
</tbody>
</table>
<p>VPC・サブネットグループ・SGなどを細かく指定するため、<strong>必ず「フル設定」を選択します。</strong></p>
</blockquote>
<blockquote>
<p><strong>注意</strong>: RDSの作成は<strong>約10〜15分</strong>かかります。</p>
</blockquote>
<p><!-- ![「データベースの作成 ▲」→「フル設定」の選択画面](images/rds-create-fullsetting.jpg) --></p>
<h3><span id="toc30">エンジンの選択</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>エンジンのタイプ</td>
<td><strong>MySQL</strong></td>
</tr>
<tr>
<td>エンジンバージョン</td>
<td><strong>MySQL 8.0.x</strong>（最新）</td>
</tr>
</tbody>
</table>
<h3><span id="toc31">テンプレート</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>テンプレート</td>
<td><strong>無料利用枠</strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p>「無料利用枠」を選択すると、スペックが自動的に無料枠対象の設定に制限されます。</p>
</blockquote>
<h3><span id="toc32">設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBインスタンス識別子</td>
<td><code>my-rds-mysql</code></td>
</tr>
<tr>
<td>マスターユーザー名</td>
<td><code>admin</code></td>
</tr>
<tr>
<td>マスターパスワード</td>
<td><code>Handson1234!</code></td>
</tr>
<tr>
<td>パスワードの確認</td>
<td><code>Handson1234!</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc33">インスタンスの設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>DBインスタンスクラス</td>
<td><strong>db.t3.micro</strong>（無料枠）</td>
</tr>
</tbody>
</table>
<h3><span id="toc34">ストレージ</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>ストレージタイプ</td>
<td><strong>汎用SSD (gp2)</strong></td>
</tr>
<tr>
<td>割り当てられたストレージ</td>
<td><code>20</code> GiB</td>
</tr>
<tr>
<td>ストレージの自動スケーリング</td>
<td>チェックを<strong>外す</strong></td>
</tr>
</tbody>
</table>
<h3><span id="toc35">接続</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>コンピューティングリソース</td>
<td><strong>EC2コンピューティングリソースに接続しない</strong></td>
</tr>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
<tr>
<td>DBサブネットグループ</td>
<td><code>my-rds-subnet-group</code></td>
</tr>
<tr>
<td>パブリックアクセス</td>
<td><strong>なし</strong></td>
</tr>
<tr>
<td>VPCセキュリティグループ</td>
<td><strong>既存の選択</strong> → <code>my-rds-sg</code> を選択（<code>default</code> は削除）</td>
</tr>
<tr>
<td>アベイラビリティーゾーン</td>
<td><code>ap-northeast-1a</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc36">追加設定</span></h3>
<p>「追加設定」を開きます。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>最初のデータベース名</td>
<td><code>sampledb</code></td>
</tr>
<tr>
<td>自動バックアップを有効にする</td>
<td>チェックを<strong>外す</strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p>自動バックアップを無効にすることで、ハンズオン後の削除がよりスムーズになります。</p>
</blockquote>
<p>「データベースの作成」をクリック。</p>
<p><strong>控えておく情報</strong>: 作成完了後に表示される「エンドポイント」のホスト名</p>
<pre><code class="language-plaintext">my-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com</code></pre>
<blockquote>
<p><strong>エンドポイントとは</strong>: RDSに接続するためのホスト名です。IPアドレスが変わっても同じホスト名で接続できます。</p>
</blockquote>
<p><!-- ![RDSインスタンス詳細画面のエンドポイント確認](images/rds-endpoint.jpg) --></p>
<hr>
<h2><span id="toc37">⑪ EC2インスタンスの起動</span></h2>
<blockquote>
<p><strong>注意</strong>: EC2を起動する前に⑤のルートテーブル設定（IGWルートの追加とサブネット関連付け）が完了していることを確認してください。UserDataはEC2起動時に一度だけ実行されるため、IGWルートがない状態で起動すると <code>dnf install</code> がインターネットに到達できずApacheのインストールが失敗します。</p>
</blockquote>
<p><strong>AWSコンソール → EC2 → インスタンス → 「インスタンスを起動」</strong></p>
<h3><span id="toc38">基本設定</span></h3>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-rds-ap-instance</code></td>
</tr>
<tr>
<td>AMI</td>
<td><strong>Amazon Linux 2023 AMI</strong></td>
</tr>
<tr>
<td>インスタンスタイプ</td>
<td><strong>t2.micro</strong></td>
</tr>
<tr>
<td>キーペア</td>
<td><code>my-rds-mysql-key</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc39">ネットワーク設定</span></h3>
<p>「編集」をクリックして以下を設定します。</p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC</td>
<td><code>my-rds-vpc</code></td>
</tr>
<tr>
<td>サブネット</td>
<td><code>my-public-subnet</code></td>
</tr>
<tr>
<td>パブリックIPの自動割り当て</td>
<td><strong>有効化</strong></td>
</tr>
<tr>
<td>ファイアウォール（セキュリティグループ）</td>
<td><strong>既存のセキュリティグループを選択</strong></td>
</tr>
<tr>
<td>セキュリティグループ</td>
<td><code>my-rds-ec2-sg</code></td>
</tr>
</tbody>
</table>
<h3><span id="toc40">IAMロール</span></h3>
<p>「高度な詳細」→「IAMインスタンスプロファイル」→ <code>my-rds-ec2-role</code> を選択</p>
<h3><span id="toc41">UserData</span></h3>
<p>「高度な詳細」→「ユーザーデータ」に以下を貼り付けます。</p>
<pre><code class="language-bash">#!/bin/bash
dnf update -y
dnf install -y httpd mariadb105
systemctl start httpd
systemctl enable httpd
echo "&lt;h1&gt;AP Server - RDS MySQL Hands-on&lt;/h1&gt;" &gt; /var/www/html/index.html
echo "&lt;p&gt;This server connects to RDS MySQL in the private subnet.&lt;/p&gt;" &gt;&gt; /var/www/html/index.html</code></pre>
<h3><span id="toc42">タグ</span></h3>
<table>
<thead>
<tr>
<th>キー</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Cost</code></td>
<td><code>123456</code></td>
</tr>
</tbody>
</table>
<p>「インスタンスを起動」をクリック。起動完了まで約2〜3分待ちます。</p>
<p><strong>控えておく情報</strong>: インスタンスのパブリックIPアドレス</p>
<hr>
<h2><span id="toc43">⑫ 動作確認</span></h2>
<h3><span id="toc44">12-1. WebブラウザでEC2のWebサーバ確認</span></h3>
<p>ブラウザで以下のURLにアクセスします。</p>
<pre><code class="language-plaintext">http://（EC2のパブリックIPアドレス）</code></pre>
<p>「AP Server - RDS MySQL Hands-on」と表示されればEC2は正常に動作しています。</p>
<blockquote>
<p><strong>接続できない場合のチェックポイント:</strong></p>
<ol>
<li>EC2起動後2〜3分以上待ってから再試行（UserDataの実行に時間がかかります）</li>
<li><code>https://checkip.amazonaws.com</code> で現在のIPを確認し、EC2のSGのHTTP許可IPと一致しているか確認</li>
<li>SSHでEC2に入り <code>sudo systemctl status httpd</code> でApacheの状態を確認</li>
</ol>
</blockquote>
<h3><span id="toc45">12-2. EC2にSSH接続する</span></h3>
<pre><code class="language-cmd">ssh -i C:\Users\ユーザー名\.ssh\my-rds-mysql-key.pem ec2-user@（EC2のパブリックIPアドレス）</code></pre>
<h3><span id="toc46">12-3. Parameter StoreからDBパスワードを取得する</span></h3>
<p>EC2にSSH接続した状態で以下を実行します。</p>
<pre><code class="language-bash">aws ssm get-parameter \
  --name "/my/rds/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1 \
  --with-decryption</code></pre>
<p><code>Handson1234!</code> と表示されればParameter Storeからの取得成功です。</p>
<blockquote>
<p><strong><code>--with-decryption</code> について</strong>: SecureString型のパラメータを取得するときに必要なフラグです。このフラグがないと暗号化されたまま（base64のような長い文字列）返ってきます。</p>
</blockquote>
<blockquote>
<p>これがParameter Storeの活用方法：パスワードをコードに書かず、実行時にIAMロール経由で取得します。</p>
</blockquote>
<h3><span id="toc47">12-4. RDSへのMySQL接続テスト</span></h3>
<p>⑩で控えておいたRDSエンドポイントを使って接続します。</p>
<pre><code class="language-bash">RDS_ENDPOINT="my-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com"

# DBパスワードをParameter Storeから取得
DB_PASSWORD=$(aws ssm get-parameter \
  --name "/my/rds/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1 \
  --with-decryption)

# RDSに接続
mysql -h $RDS_ENDPOINT -u admin -p"$DB_PASSWORD" sampledb</code></pre>
<p>接続に成功するとMySQLのプロンプトが表示されます。</p>
<pre><code class="language-plaintext">Welcome to the MariaDB monitor.  Commands end with ; or \g.
...
MySQL [sampledb]&gt;</code></pre>
<p><!-- ![EC2からRDS MySQLへの接続成功画面](images/rds-mysql-connect.jpg) --></p>
<h3><span id="toc48">12-5. RDS上でSQL操作を確認する</span></h3>
<pre><code class="language-sql">-- テーブル作成
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- データ挿入
INSERT INTO users (name) VALUES ('Alice'), ('Bob');

-- データ確認
SELECT * FROM users;

-- 後片付け
DROP TABLE users;

EXIT;</code></pre>
<h3><span id="toc49">12-6. EC2からSSH接続を切断する</span></h3>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc50">⑬ リソースの削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p><strong>RDSの課金について</strong>: RDSはインスタンスが稼働している間は課金されます（db.t3.micro は無料枠外の場合 約$0.017/時間）。不要になったら必ず削除してください。</p>
</blockquote>
<h3><span id="toc51">削除順序</span></h3>
<p>依存関係があるため、<strong>必ずこの順番</strong>で削除します。</p>
<pre><code class="language-plaintext">1. EC2インスタンスを終了（→ 終了済みになるまで待機）
2. RDSインスタンスを削除（→ 削除完了まで約10〜15分待機）
3. RDS DBサブネットグループを削除
4. SSM Parameter Storeのパラメータを削除
5. セキュリティグループを削除（RDS用 → EC2用の順）
6. ルートテーブルの関連付け解除・削除
7. インターネットゲートウェイをデタッチ・削除
8. サブネットを削除（3つ）
9. VPCを削除
10. IAMロールを削除
11. キーペアを削除（任意）</code></pre>
<h3><span id="toc52">1. EC2インスタンスを終了する</span></h3>
<p><strong>EC2 → インスタンス → <code>my-rds-ap-instance</code> を選択 → 「インスタンスの状態」→「インスタンスを終了」</strong></p>
<p>確認ダイアログで「終了」をクリック。「終了済み」になるまで待ちます（2〜5分）。</p>
<h3><span id="toc53">2. RDSインスタンスを削除する</span></h3>
<p><strong>Aurora and RDS → データベース → <code>my-rds-mysql</code> を選択 → 「アクション」→「削除」</strong></p>
<table>
<thead>
<tr>
<th>項目</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>最終スナップショットを作成</td>
<td><strong>チェックを外したまま</strong>（ハンズオンなので不要）</td>
</tr>
<tr>
<td>「私は〜了承します。」</td>
<td><strong>チェックを入れる</strong>（これを入れないと削除ボタンが押せない）</td>
</tr>
<tr>
<td>確認フィールド</td>
<td><code>delete me</code> と入力</td>
</tr>
</tbody>
</table>
<p>「削除」をクリック。削除完了まで<strong>約10〜15分</strong>かかります。ステータスが「削除中」から消えるまで待ちます。</p>
<blockquote>
<p><strong>注意</strong>: 「最終スナップショットを作成」にチェックを入れると、削除後もスナップショットが残り<strong>別途保存料金が発生します</strong>。必ず外してください。</p>
</blockquote>
<h3><span id="toc54">3. RDS DBサブネットグループを削除する</span></h3>
<p><strong>Aurora and RDS → サブネットグループ → <code>my-rds-subnet-group</code> を選択 → 「削除」</strong></p>
<blockquote>
<p>RDSインスタンスが完全に削除されている必要があります。「使用中」エラーが出る場合はRDS削除完了を待ちます。</p>
</blockquote>
<h3><span id="toc55">4. SSM Parameter Storeのパラメータを削除する</span></h3>
<p><strong>Systems Manager → パラメータストア → <code>/my/rds/db-password</code> を選択 → 「削除」</strong></p>
<h3><span id="toc56">5. セキュリティグループを削除する</span></h3>
<p><strong>EC2 → セキュリティグループ</strong></p>
<blockquote>
<p>EC2のSGはRDS SGのソースとして参照されているため、RDS SGから先に削除します。</p>
</blockquote>
<ol>
<li><code>my-rds-sg</code> を選択 → 「アクション」→「セキュリティグループを削除」</li>
<li><code>my-rds-ec2-sg</code> を選択 → 「アクション」→「セキュリティグループを削除」</li>
</ol>
<h3><span id="toc57">6. ルートテーブルを削除する</span></h3>
<p><strong>VPC → ルートテーブル</strong></p>
<p>まず各ルートテーブルのサブネット関連付けを解除してから削除します。</p>
<ol>
<li><code>my-public-rt</code> を選択 → 「サブネットの関連付け」タブ → 「サブネットの関連付けを編集」→ チェックをすべて外す → 「保存」</li>
<li><code>my-private-rt</code> を選択 → 同様に関連付けを解除</li>
<li>各ルートテーブルを選択 → 「アクション」→「ルートテーブルの削除」</li>
</ol>
<h3><span id="toc58">7. インターネットゲートウェイをデタッチ・削除する</span></h3>
<p><strong>VPC → インターネットゲートウェイ → <code>my-rds-igw</code> を選択</strong></p>
<ol>
<li>「アクション」→「VPCからデタッチ」→「デタッチ」</li>
<li>「アクション」→「インターネットゲートウェイの削除」→「削除」</li>
</ol>
<h3><span id="toc59">8. サブネットを削除する（3つ）</span></h3>
<p><strong>VPC → サブネット</strong></p>
<p><code>my-public-subnet</code>・<code>my-private-subnet-1</code>・<code>my-private-subnet-2</code> を選択 → 「アクション」→「サブネットの削除」</p>
<h3><span id="toc60">9. VPCを削除する</span></h3>
<p><strong>VPC → お使いのVPC → <code>my-rds-vpc</code> を選択 → 「アクション」→「VPCの削除」</strong></p>
<h3><span id="toc61">10. IAMロールを削除する</span></h3>
<p><strong>IAM → ロール → <code>my-rds-ec2-role</code> を選択 → 「削除」</strong></p>
<h3><span id="toc62">11. キーペアを削除する（任意）</span></h3>
<p><strong>EC2 → キーペア → <code>my-rds-mysql-key</code> を選択 → 「アクション」→「削除」</strong></p>
<hr>
<h2><span id="toc63">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>キーペア作成</td>
</tr>
<tr>
<td>②③④⑤</td>
<td>VPC / サブネット3つ / IGW / ルートテーブルの設定</td>
</tr>
<tr>
<td>⑥</td>
<td>EC2用SG（HTTP:80・SSH:22）+ RDS用SG（MySQL:3306、<strong>SG-to-SG制御</strong>）</td>
</tr>
<tr>
<td>⑦⑧</td>
<td>IAMロール + Parameter Store（SecureString）でパスワードを安全に管理</td>
</tr>
<tr>
<td>⑨</td>
<td>DBサブネットグループ（2AZ必須）の作成</td>
</tr>
<tr>
<td>⑩</td>
<td>RDS MySQL 8.0 インスタンス作成（約10〜15分）</td>
</tr>
<tr>
<td>⑪</td>
<td>EC2起動（UserDataでApache自動セットアップ）</td>
</tr>
<tr>
<td>⑫</td>
<td>EC2 → Parameter Store → RDS MySQL への接続テスト</td>
</tr>
</tbody>
</table>
<p><strong>EC2にDBをインストールする方式との最大の違い</strong>は、AWSがOSのメンテナンス・パッチ・バックアップを自動管理してくれる点です。パスワードをParameter Storeで管理し、SG-to-SGでRDSへのアクセスを制御するパターンは、実際のAWS本番環境でも標準的な設計です。</p>
<p>CloudFormationで同じ構成を自動化したい場合は、<a href="https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/">CloudFormation版ハンズオン</a>を参照してください。コマンド1本で22リソースを一括デプロイできます。</p><p>The post <a href="https://caymezon.com/aws-handson-console-rds-mysql-ec2/">AWSコンソールでRDS MySQL + EC2接続環境を構築する手順【DBサブネットグループ / Parameter Storeパスワード管理】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-console-rds-mysql-ec2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CloudFormationでRDS MySQL + EC2接続環境を自動構築する手順【22リソース一括デプロイ / コンソール版との比較付き】</title>
		<link>https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/</link>
					<comments>https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 11 Apr 2026 23:47:49 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[AWSCLI]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[DBサブネットグループ]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Parameter Store]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[SG参照]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[VPC]]></category>
		<category><![CDATA[セキュリティグループ]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[マネージドサービス]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20336</guid>

					<description><![CDATA[<p>目次 はじめにCloudFormation vs コンソール：どれだけ違うかキーワード解説前提条件構築されるリソース（22個）template.yaml（完全版）作業順序【重要】⓪ 自分のIPアドレスを確認する① キーペ [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/">CloudFormationでRDS MySQL + EC2接続環境を自動構築する手順【22リソース一括デプロイ / コンソール版との比較付き】</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-20" checked><label class="toc-title" for="toc-checkbox-20">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">はじめに</a></li><li><a href="#toc2" tabindex="0">CloudFormation vs コンソール：どれだけ違うか</a></li><li><a href="#toc3" tabindex="0">キーワード解説</a></li><li><a href="#toc4" tabindex="0">前提条件</a></li><li><a href="#toc5" tabindex="0">構築されるリソース（22個）</a></li><li><a href="#toc6" tabindex="0">template.yaml（完全版）</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">③ スタックの作成</a></li><li><a href="#toc12" tabindex="0">④ 作成完了・Outputsの確認</a><ol><li><a href="#toc13" tabindex="0">作成状況の確認</a></li><li><a href="#toc14" tabindex="0">Outputs（接続情報）の確認</a></li></ol></li><li><a href="#toc15" tabindex="0">⑤ 動作確認</a><ol><li><a href="#toc16" tabindex="0">Webブラウザで確認</a></li><li><a href="#toc17" tabindex="0">EC2にSSH接続する</a></li><li><a href="#toc18" tabindex="0">Parameter StoreからDBパスワードを取得する</a></li><li><a href="#toc19" tabindex="0">RDSへのMySQL接続テスト</a></li></ol></li><li><a href="#toc20" tabindex="0">⑥ スタックの削除</a><ol><li><a href="#toc21" tabindex="0">削除状況の確認</a></li><li><a href="#toc22" tabindex="0">キーペアの手動削除</a></li></ol></li><li><a href="#toc23" tabindex="0">コンソール版との比較</a></li><li><a href="#toc24" tabindex="0">トラブルシューティング</a><ol><li><a href="#toc25" tabindex="0">失敗時の詳細確認コマンド</a></li></ol></li><li><a href="#toc26" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">はじめに</span></h2>
<p>「コンソールで40〜50分かかったRDS + EC2環境をコードで再現したい」——それを実現するのが <strong>AWS CloudFormation</strong> です。</p>
<p>この記事では、<code>template.yaml</code> 1ファイルにVPC・サブネット・セキュリティグループ・IAMロール・SSM Parameter Store・RDS MySQL・EC2（22リソース）を定義し、<strong>コマンド1本でRDS + EC2接続環境を一括構築するハンズオン</strong>を紹介します。コンソール版（<a href="https://caymezon.com/aws-handson-console-rds-mysql-ec2/">AWSコンソール版ハンズオン</a>）と全く同じ構成を、CloudFormationで自動化します。</p>
<pre><code class="language-plaintext">ローカル環境（VSCode）
  ├── template.yaml（22リソースを定義）
  └── AWS CLI コマンド
        ↓ スタック作成（コマンド1本）
AWS環境
  └── VPC（10.0.0.0/16）
        ├── パブリックサブネット（10.0.1.0/24 / AZ-a）
        │     └── EC2（Apache + mariadb client / パブリックIP あり）
        │           └── IAMロール → SSM Parameter Store
        │
        ├── DBサブネットグループ
        │     ├── プライベートサブネット1（10.0.2.0/24 / AZ-a）← RDS配置
        │     └── プライベートサブネット2（10.0.3.0/24 / AZ-c）← 2AZ要件
        │
        └── SSM Parameter Store: /my/rds/db-password</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>VPC・サブネット3つ・IGW・ルートテーブル・SG・IAM・SSM・RDS・EC2を <code>template.yaml</code> 1ファイルで定義する方法</li>
<li><code>DBPassword</code> パラメータを <code>NoEcho: true</code> でマスクしてセキュアに渡す方法</li>
<li><code>DeletionPolicy: Delete</code> + <code>BackupRetentionPeriod: 0</code> でスタック削除時にRDSを確実に削除する設定</li>
<li><code>CloudFormation</code> が <code>SecureString</code> を作れない理由と代替手段</li>
</ul>
<p><strong>このハンズオンの特徴：</strong></p>
<ul>
<li>コンソール版では40〜50分かかった作業が、コマンド1本（RDS待ち15〜20分含む）で完了</li>
<li><code>delete-stack</code> 1本でRDS・VPC・IAMなど22リソースの依存関係を自動解決して削除</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-console-rds-mysql-ec2/">AWSコンソール版ハンズオン</a> の比較記事です。</strong><br />コンソール版でRDS・DBサブネットグループ・Parameter Storeを視覚的に学んだ後に読むと理解が深まります。</p>
</blockquote>
<hr>
<p><!-- ![CloudFormationスタック作成完了・Outputs確認画面](images/cfn-create-complete.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">CloudFormation vs コンソール：どれだけ違うか</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>CloudFormation</th>
<th>コンソール（手動）</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>VPC + サブネット3つ + IGW + ルートテーブル</strong></td>
<td>template.yaml に定義済み</td>
<td>約15〜20分・複数画面</td>
</tr>
<tr>
<td><strong>IAMロール作成</strong></td>
<td>template.yaml に定義済み</td>
<td>別画面で作成</td>
</tr>
<tr>
<td><strong>SSM Parameter Store作成</strong></td>
<td>template.yaml に定義済み</td>
<td>別画面で作成</td>
</tr>
<tr>
<td><strong>RDS DBサブネットグループ</strong></td>
<td>template.yaml に定義済み</td>
<td>別画面で作成</td>
</tr>
<tr>
<td><strong>RDS作成（設定項目20以上）</strong></td>
<td><code>--parameters DBPassword=...</code> を渡すだけ</td>
<td>画面でポチポチ</td>
</tr>
<tr>
<td><strong>全体のデプロイ</strong></td>
<td>コマンド1本（RDS待ち15〜20分）</td>
<td><strong>40〜50分</strong></td>
</tr>
<tr>
<td><strong>削除（依存関係あり）</strong></td>
<td><code>delete-stack</code> 1本（自動解決）</td>
<td>11ステップ・手動管理</td>
</tr>
<tr>
<td><strong>再現性</strong></td>
<td>高い（同じ構成を何度でも再現可能）</td>
<td>低い（手順漏れのリスク大）</td>
</tr>
<tr>
<td><strong>バージョン管理</strong></td>
<td>Gitで管理可能</td>
<td>不可</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">キーワード解説</span></h2>
<table>
<thead>
<tr>
<th>用語</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CloudFormation</strong></td>
<td>AWSが提供するIaC（Infrastructure as Code）サービス。YAMLやJSONでリソースを定義し、一括作成・削除できる</td>
</tr>
<tr>
<td><strong>スタック</strong></td>
<td>CloudFormationが管理するリソースのまとまり。今回は22リソースを1スタックで管理</td>
</tr>
<tr>
<td><strong>DeletionPolicy</strong></td>
<td>スタック削除時のリソースの扱いを指定するプロパティ。<code>Delete</code>/<code>Retain</code>/<code>Snapshot</code>（RDSのみ）の3種類</td>
</tr>
<tr>
<td><strong>NoEcho</strong></td>
<td>CloudFormationパラメータの設定。<code>true</code> にするとコンソール上でパスワードが <code>****</code> にマスクされる</td>
</tr>
<tr>
<td><strong>SecureString</strong></td>
<td>Parameter StoreでKMS暗号化して保管するタイプ。<strong>CloudFormationでは作成不可</strong></td>
</tr>
<tr>
<td><strong>CAPABILITY_NAMED_IAM</strong></td>
<td>IAMリソースを含むスタックの作成・更新時に必要なフラグ</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">前提条件</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>
</tbody>
</table>
<p>AWS認証確認:</p>
<pre><code class="language-cmd">aws sts get-caller-identity</code></pre>
<hr>
<h2><span id="toc5">構築されるリソース（22個）</span></h2>
<table>
<thead>
<tr>
<th>カテゴリ</th>
<th>リソース</th>
<th>論理ID</th>
</tr>
</thead>
<tbody>
<tr>
<td>ネットワーク</td>
<td>VPC</td>
<td><code>VPC</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>インターネットゲートウェイ</td>
<td><code>InternetGateway</code> + <code>IGWAttachment</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>パブリックサブネット</td>
<td><code>PublicSubnet</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>プライベートサブネット1（RDS配置）</td>
<td><code>PrivateSubnet1</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>プライベートサブネット2（2AZ要件）</td>
<td><code>PrivateSubnet2</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>パブリックルートテーブル</td>
<td><code>PublicRouteTable</code> + <code>PublicRoute</code> + <code>PublicSubnetRTA</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>プライベートルートテーブル</td>
<td><code>PrivateRouteTable</code> + <code>PrivateSubnet1RTA</code> + <code>PrivateSubnet2RTA</code></td>
</tr>
<tr>
<td>ネットワーク</td>
<td>S3 VPC Gateway Endpoint</td>
<td><code>S3VPCEndpoint</code></td>
</tr>
<tr>
<td>セキュリティ</td>
<td>EC2セキュリティグループ</td>
<td><code>EC2SecurityGroup</code></td>
</tr>
<tr>
<td>セキュリティ</td>
<td>RDSセキュリティグループ</td>
<td><code>RDSSecurityGroup</code></td>
</tr>
<tr>
<td>IAM</td>
<td>EC2ロール + インスタンスプロファイル</td>
<td><code>EC2Role</code> + <code>EC2InstanceProfile</code></td>
</tr>
<tr>
<td>パラメータ</td>
<td>SSM Parameter Store</td>
<td><code>DBPasswordParam</code></td>
</tr>
<tr>
<td>データベース</td>
<td>RDS DBサブネットグループ</td>
<td><code>DBSubnetGroup</code></td>
</tr>
<tr>
<td>データベース</td>
<td>RDS MySQLインスタンス</td>
<td><code>RDSInstance</code></td>
</tr>
<tr>
<td>コンピュート</td>
<td>EC2インスタンス</td>
<td><code>EC2Instance</code></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc6">template.yaml（完全版）</span></h2>
<p>このファイルをそのまま使ってハンズオンを実施できます。<code>C:\my-aws\aws-learning-projects\rds-mysql-ec2\template.yaml</code> として保存してください。</p>
<pre><code class="language-yaml">AWSTemplateFormatVersion: '2010-09-09'
Description: 'RDS MySQL + EC2 connection hands-on with SSM Parameter Store password management'

Parameters:
  EmployeeId:
    Type: String
    Default: '123456'
    Description: Employee ID used as a prefix for resource names

  KeyName:
    Type: String
    Default: 'my-rds-mysql-key'
    Description: Name of an existing EC2 KeyPair

  MyIP:
    Type: String
    Default: '123.456.78.901/32'    # [IMPORTANT] Replace with your own IP (check: curl https://checkip.amazonaws.com)
    Description: Your IP address to allow SSH and HTTP access (CIDR format e.g. 123.456.78.901/32)

  DBPassword:
    Type: String
    Default: 'Handson1234!'
    NoEcho: true
    Description: Master password for RDS MySQL (also stored in SSM Parameter Store)

Resources:

  # VPC and Network
  # ============================================================

  # VPC: isolated network for this hands-on
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-vpc'
        - Key: Cost
          Value: !Ref EmployeeId

  # Internet Gateway: connects public subnet to the internet
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-igw'
        - Key: Cost
          Value: !Ref EmployeeId

  # Attach Internet Gateway to VPC
  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  # Public Subnet (AZ-a): EC2 AP server is placed here
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-public-subnet'
        - Key: Cost
          Value: !Ref EmployeeId

  # Private Subnet 1 (AZ-a): RDS instance is placed here
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-private-subnet-1'
        - Key: Cost
          Value: !Ref EmployeeId

  # Private Subnet 2 (AZ-b): required for RDS Subnet Group (minimum 2 AZs)
  # RDS Subnet Group must span at least 2 AZs even for single-AZ deployments
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-private-subnet-2'
        - Key: Cost
          Value: !Ref EmployeeId

  # Public Route Table: routes internet traffic via Internet Gateway
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-public-rt'
        - Key: Cost
          Value: !Ref EmployeeId

  # Default route for public subnet: 0.0.0.0/0 -&gt; Internet Gateway
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: IGWAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

  # Private Route Table: no internet route (intra-VPC traffic only)
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-private-rt'
        - Key: Cost
          Value: !Ref EmployeeId

  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable

  # S3 VPC Gateway Endpoint: free, allows EC2 in public subnet to reach S3 for dnf package downloads
  S3VPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPC
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
      VpcEndpointType: Gateway
      RouteTableIds:
        - !Ref PublicRouteTable
        - !Ref PrivateRouteTable

  # ============================================================
  # IAM
  # ============================================================

  # IAM Role: EC2 can use Session Manager and read SSM Parameter Store
  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'n${EmployeeId}-rds-ec2-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-ec2-role'
        - Key: Cost
          Value: !Ref EmployeeId

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub 'n${EmployeeId}-rds-ec2-profile'
      Roles:
        - !Ref EC2Role

  # ============================================================
  # Security Groups
  # ============================================================

  # EC2 Security Group: SSH and HTTP access from MyIP
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'n${EmployeeId} EC2 AP server SG'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP
          Description: SSH from my IP
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref MyIP
          Description: HTTP from my IP
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-ec2-sg'
        - Key: Cost
          Value: !Ref EmployeeId

  # RDS Security Group: MySQL(3306) from EC2 SG only - not open to the internet
  RDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'n${EmployeeId} RDS MySQL SG (EC2-to-RDS only)'
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !GetAtt EC2SecurityGroup.GroupId
          Description: MySQL from EC2 SG only (SG-to-SG control)
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-sg'
        - Key: Cost
          Value: !Ref EmployeeId

  # ============================================================
  # SSM Parameter Store (password management)
  # NOTE: CloudFormation can only create String type, not SecureString.
  # In production, create a SecureString manually and reference it with
  # {{resolve:ssm-secure:/path/to/param}}
  # ============================================================
  DBPasswordParam:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub '/n${EmployeeId}/rds/db-password'
      Type: String
      Value: !Ref DBPassword
      Description: !Sub 'RDS MySQL master password for n${EmployeeId} hands-on'
      Tags:
        Cost: !Ref EmployeeId

  # ============================================================
  # RDS
  # ============================================================

  # DB Subnet Group: registers which subnets RDS can use
  # Requires subnets in at least 2 different Availability Zones
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: !Sub 'n${EmployeeId} RDS subnet group (private subnets in 2 AZs)'
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-subnet-group'
        - Key: Cost
          Value: !Ref EmployeeId

  # RDS MySQL Instance (single-AZ, db.t3.micro - free tier eligible)
  RDSInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      DBInstanceIdentifier: !Sub 'n${EmployeeId}-rds-mysql'
      DBInstanceClass: db.t3.micro
      Engine: mysql
      EngineVersion: '8.0'
      MasterUsername: admin
      MasterUserPassword: !Ref DBPassword
      AllocatedStorage: '20'
      StorageType: gp2
      DBName: sampledb
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !GetAtt RDSSecurityGroup.GroupId
      MultiAZ: false
      PubliclyAccessible: false
      BackupRetentionPeriod: 0
      DeleteAutomatedBackups: true
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-mysql'
        - Key: Cost
          Value: !Ref EmployeeId

  # ============================================================
  # EC2 (AP Server)
  # ============================================================

  # AP Server: Apache + MySQL client in public subnet
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}'
      InstanceType: t2.micro
      KeyName: !Ref KeyName
      SubnetId: !Ref PublicSubnet
      SecurityGroupIds:
        - !GetAtt EC2SecurityGroup.GroupId
      IamInstanceProfile: !Ref EC2InstanceProfile
      UserData:
        Fn::Base64: |
          #!/bin/bash
          dnf update -y
          dnf install -y httpd mariadb105
          systemctl start httpd
          systemctl enable httpd
          echo "&lt;h1&gt;AP Server - RDS MySQL Hands-on&lt;/h1&gt;" &gt; /var/www/html/index.html
          echo "&lt;p&gt;This server connects to RDS MySQL in the private subnet.&lt;/p&gt;" &gt;&gt; /var/www/html/index.html
      Tags:
        - Key: Name
          Value: !Sub 'n${EmployeeId}-rds-ap-instance'
        - Key: Cost
          Value: !Ref EmployeeId

Outputs:
  EC2PublicIP:
    Description: EC2 AP server public IP
    Value: !GetAtt EC2Instance.PublicIp

  WebsiteURL:
    Description: EC2 AP server website URL
    Value: !Sub 'http://${EC2Instance.PublicIp}'

  RDSEndpoint:
    Description: RDS MySQL endpoint hostname (use this from EC2 to connect)
    Value: !GetAtt RDSInstance.Endpoint.Address

  DBPasswordParamName:
    Description: SSM Parameter Store path for RDS password
    Value: !Ref DBPasswordParam

  SSHCommand:
    Description: SSH command to EC2 AP server
    Value: !Sub 'ssh -i C:\Users\username\.ssh\${KeyName}.pem ec2-user@${EC2Instance.PublicIp}'

  MySQLConnectCommand:
    Description: MySQL connect command (run this FROM the EC2 AP server)
    Value: !Sub 'mysql -h ${RDSInstance.Endpoint.Address} -u admin -pHandson1234! sampledb'</code></pre>
<p><strong>template.yaml の設計ポイント（RDS固有）:</strong></p>
<p><strong><code>DeletionPolicy</code> の3種類:</strong></p>
<table>
<thead>
<tr>
<th>設定</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Delete</code>（今回）</td>
<td>スタック削除時にRDSも削除する</td>
</tr>
<tr>
<td><code>Retain</code></td>
<td>スタック削除時にRDSを残す</td>
</tr>
<tr>
<td><code>Snapshot</code>（RDSのみ）</td>
<td>スタック削除時にスナップショットを作成してから削除</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>なぜ <code>DeletionPolicy: Delete</code> を明示するか</strong>: RDSはデフォルトでスナップショット作成を要求される場合があります。明示的に <code>Delete</code> を指定することで確実にスキップできます。また <code>BackupRetentionPeriod: 0</code> で自動バックアップを無効にし、削除をよりスムーズにしています。</p>
</blockquote>
<p><strong>CloudFormationが <code>SecureString</code> を作れない理由:</strong></p>
<p><code>AWS::SSM::Parameter</code> は <code>String</code> と <code>StringList</code> のみサポートしています。<code>SecureString</code> は KMS の複雑な依存関係があるためサポート外です。本番環境では事前に SecureString を作成し <code>{{resolve:ssm-secure:/path}}</code> で参照する方法が推奨されます。</p>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（コンソールで実施）
      ↓
② プロジェクトフォルダに移動する
      ↓
③ スタックを作成する（aws cloudformation create-stack）
      ↓
④ 作成完了・Outputsを確認する（RDSは15〜20分かかる）
      ↓
⑤ 動作確認（Web・SSH・RDS接続）
      ↓
⑥ スタックを削除する（aws cloudformation delete-stack）</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>
<p>CloudFormationではキーペアのダウンロードができないため、コンソールで先に作成します。</p>
<p><strong>AWSコンソール → EC2 → キーペア → 「キーペアを作成」</strong></p>
<table>
<thead>
<tr>
<th>設定項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>名前</td>
<td><code>my-rds-mysql-key</code></td>
</tr>
<tr>
<td>キーペアのタイプ</td>
<td><strong>RSA</strong></td>
</tr>
<tr>
<td>プライベートキーファイル形式</td>
<td><strong>.pem</strong></td>
</tr>
</tbody>
</table>
<p>ダウンロードされた <code>my-rds-mysql-key.pem</code> を保存します。</p>
<pre><code class="language-plaintext">C:\Users\ユーザー名\.ssh\my-rds-mysql-key.pem</code></pre>
<hr>
<h2><span id="toc10">② プロジェクトフォルダに移動する</span></h2>
<p>VSCodeのターミナル（CMD）を開き、プロジェクトフォルダに移動します。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\rds-mysql-ec2</code></pre>
<p><code>template.yaml</code> があることを確認します。</p>
<pre><code class="language-cmd">dir template.yaml</code></pre>
<hr>
<h2><span id="toc11">③ スタックの作成</span></h2>
<p>以下のコマンドを実行します。<code>203.0.113.1</code> は⓪で確認した自分のIPアドレスに置き換えます。</p>
<pre><code class="language-cmd">aws cloudformation create-stack ^
  --stack-name my-rds-mysql-stack ^
  --template-body file://template.yaml ^
  --region ap-northeast-1 ^
  --capabilities CAPABILITY_NAMED_IAM ^
  --parameters ^
    ParameterKey=EmployeeId,ParameterValue=123456 ^
    ParameterKey=KeyName,ParameterValue=my-rds-mysql-key ^
    ParameterKey=MyIP,ParameterValue=203.0.113.1/32 ^
    ParameterKey=DBPassword,ParameterValue=Handson1234!</code></pre>
<blockquote>
<p><strong><code>DBPassword</code> パラメータについて</strong>: <code>NoEcho: true</code> が設定されているため、コンソール上では <code>****</code> とマスクされます。</p>
</blockquote>
<blockquote>
<p><strong><code>CAPABILITY_NAMED_IAM</code> について</strong>: <code>EC2Role</code> のように名前を指定したIAMリソースを作成する場合に必要なフラグです。</p>
</blockquote>
<p>成功すると以下のような StackId が表示されます。</p>
<pre><code class="language-json">{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/my-rds-mysql-stack/xxxxx"
}</code></pre>
<hr>
<h2><span id="toc12">④ 作成完了・Outputsの確認</span></h2>
<blockquote>
<p><strong>注意</strong>: RDSの作成には<strong>約15〜20分</strong>かかります。<code>CREATE_IN_PROGRESS</code> が続く間は正常です。</p>
</blockquote>
<h3><span id="toc13">作成状況の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-rds-mysql-stack ^
  --query "Stacks[0].StackStatus" ^
  --output text</code></pre>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_IN_PROGRESS</code></td>
<td>作成中（待つ）</td>
</tr>
<tr>
<td><code>CREATE_COMPLETE</code></td>
<td>作成完了</td>
</tr>
<tr>
<td><code>CREATE_FAILED</code></td>
<td>作成失敗（後述のトラブルシューティングを参照）</td>
</tr>
<tr>
<td><code>ROLLBACK_COMPLETE</code></td>
<td>失敗してロールバック完了</td>
</tr>
</tbody>
</table>
<h3><span id="toc14">Outputs（接続情報）の確認</span></h3>
<p><code>CREATE_COMPLETE</code> になったら以下を実行します。</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-rds-mysql-stack ^
  --query "Stacks[0].Outputs" ^
  --output table</code></pre>
<pre><code class="language-plaintext">+---------------------+--------------------------------------------------------------------+
|  OutputKey          |  OutputValue                                                       |
+---------------------+--------------------------------------------------------------------+
|  EC2PublicIP        |  x.x.x.x                                                          |
|  WebsiteURL         |  http://x.x.x.x                                                   |
|  RDSEndpoint        |  my-rds-mysql.xxxxx.ap-northeast-1.rds.amazonaws.com         |
|  DBPasswordParamName|  /my/rds/db-password                                         |
|  SSHCommand         |  ssh -i C:\Users\...\.ssh\...pem ec2-user@x.x.x.x                 |
|  MySQLConnectCommand|  mysql -h my-rds-mysql.xxxxx...rds.amazonaws.com -u admin ... |
+---------------------+--------------------------------------------------------------------+</code></pre>
<p><strong>控えておく情報</strong>: <code>EC2PublicIP</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CloudFormation Outputs確認コマンドの実行結果](images/cfn-outputs.jpg) --></p>
<hr>
<h2><span id="toc15">⑤ 動作確認</span></h2>
<h3><span id="toc16">Webブラウザで確認</span></h3>
<p>Outputsに表示された <code>WebsiteURL</code> をブラウザで開きます。</p>
<p>「AP Server - RDS MySQL Hands-on」と表示されればEC2は正常です。</p>
<h3><span id="toc17">EC2にSSH接続する</span></h3>
<p>Outputsの <code>SSHCommand</code> を実行します（パスは実際のパスに修正します）。</p>
<pre><code class="language-cmd">ssh -i C:\Users\ユーザー名\.ssh\my-rds-mysql-key.pem ec2-user@（EC2PublicIP）</code></pre>
<h3><span id="toc18">Parameter StoreからDBパスワードを取得する</span></h3>
<p>EC2に接続した状態で実行します。</p>
<pre><code class="language-bash">aws ssm get-parameter \
  --name "/my/rds/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1</code></pre>
<p><code>Handson1234!</code> が表示されれば成功です。</p>
<blockquote>
<p><strong><code>--with-decryption</code> が不要な理由</strong>: CloudFormationで作成したパラメータは <code>Type: String</code>（平文）のため、フラグなしでそのまま値が返ります。コンソールで <strong>SecureString</strong> として作成した場合は <code>--with-decryption</code> が必要です（コンソール版参照）。</p>
</blockquote>
<h3><span id="toc19">RDSへのMySQL接続テスト</span></h3>
<pre><code class="language-bash"># RDSエンドポイントをOutputsの値に置き換える
RDS_ENDPOINT="my-rds-mysql.xxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com"

# Parameter StoreからDBパスワードを取得して接続（String型のため--with-decryption不要）
DB_PASSWORD=$(aws ssm get-parameter \
  --name "/my/rds/db-password" \
  --query "Parameter.Value" \
  --output text \
  --region ap-northeast-1)

mysql -h $RDS_ENDPOINT -u admin -p"$DB_PASSWORD" sampledb</code></pre>
<p>または Outputsに表示された <code>MySQLConnectCommand</code> を直接実行してもかまいません。</p>
<p>接続成功後、SQL操作を確認します。</p>
<pre><code class="language-sql">-- テーブル作成
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- データ挿入
INSERT INTO users (name) VALUES ('Alice'), ('Bob');

-- データ確認
SELECT * FROM users;

-- 後片付け
DROP TABLE users;

EXIT;</code></pre>
<p><!-- ![EC2からRDS MySQLへの接続成功画面](images/rds-mysql-connect.jpg) --></p>
<p>EC2からSSH接続を切断します。</p>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc20">⑥ スタックの削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-rds-mysql-stack ^
  --region ap-northeast-1</code></pre>
<blockquote>
<p><strong>注意</strong>: RDS削除には<strong>約10〜15分</strong>かかります。コマンド実行後もしばらく待つ必要があります。</p>
</blockquote>
<h3><span id="toc21">削除状況の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-rds-mysql-stack ^
  --query "Stacks[0].StackStatus" ^
  --output text</code></pre>
<table>
<thead>
<tr>
<th>ステータス</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>DELETE_IN_PROGRESS</code></td>
<td>削除中（待つ）</td>
</tr>
<tr>
<td><code>DELETE_FAILED</code></td>
<td>削除失敗（トラブルシューティングを参照）</td>
</tr>
</tbody>
</table>
<p>削除完了の確認（以下のエラーが表示されれば削除完了）:</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-rds-mysql-stack ^
  --region ap-northeast-1</code></pre>
<pre><code class="language-plaintext">An error occurred (ValidationError) when calling the DescribeStacks operation:
Stack with id my-rds-mysql-stack does not exist</code></pre>
<p>このメッセージが表示されれば削除完了です。</p>
<h3><span id="toc22">キーペアの手動削除</span></h3>
<p>キーペアはCloudFormationで管理していないため手動で削除します。</p>
<p><strong>EC2 → キーペア → <code>my-rds-mysql-key</code> を選択 → 「アクション」→「削除」</strong></p>
<hr>
<h2><span id="toc23">コンソール版との比較</span></h2>
<table>
<thead>
<tr>
<th>作業</th>
<th>コンソール（手動）</th>
<th>CloudFormation</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPC + サブネット3つ + IGW + ルートテーブル</td>
<td>約15〜20分</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>IAMロール作成</td>
<td>別画面で作成</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>RDS DBサブネットグループ作成</td>
<td>別画面で作成</td>
<td>template.yaml に定義済み</td>
</tr>
<tr>
<td>RDS作成（設定項目20以上）</td>
<td>画面でポチポチ</td>
<td><code>--parameters DBPassword=...</code> を渡すだけ</td>
</tr>
<tr>
<td>全体のデプロイ</td>
<td><strong>約40〜50分</strong></td>
<td>コマンド1本（RDS待ち15〜20分）</td>
</tr>
<tr>
<td>削除</td>
<td><strong>11ステップ（RDS削除待ちあり）</strong></td>
<td><code>delete-stack</code> 1本</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc24">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CREATE_FAILED</code> → DBInstance失敗</td>
<td>指定AZに db.t3.micro が作成できない</td>
<td>コンソールで利用可能なAZを確認する</td>
</tr>
<tr>
<td><code>stackName failed to satisfy regular expression pattern</code></td>
<td>スタック名が数字始まり</td>
<td><code>my-rds-mysql-stack</code> のように英字で始める</td>
</tr>
<tr>
<td><code>Unable to load paramfile, text contents could not be decoded</code></td>
<td>template.yaml に日本語が含まれている</td>
<td>template.yaml のコメントは<strong>英語のみ</strong>で記述する（Windows環境の既知の問題）</td>
</tr>
<tr>
<td><code>ERROR 2003: Can&#39;t connect to MySQL server</code></td>
<td>RDSがまだ起動中またはSGの設定ミス</td>
<td>RDSのステータスが「利用可能」になっているか確認。RDS SGのソースがEC2 SGのIDになっているか確認</td>
</tr>
<tr>
<td><code>ERROR 1045: Access denied for user</code></td>
<td>パスワードが誤り</td>
<td><code>--parameters DBPassword=...</code> の値を確認</td>
</tr>
<tr>
<td><code>AccessDeniedException</code> on <code>ssm get-parameter</code></td>
<td>IAMロールに <code>AmazonSSMReadOnlyAccess</code> がない</td>
<td><code>EC2Role</code> に <code>AmazonSSMReadOnlyAccess</code> がアタッチされているか確認</td>
</tr>
<tr>
<td><code>DELETE_FAILED</code></td>
<td>スナップショット作成が失敗した</td>
<td>コンソールからRDSを手動削除（スナップショットなしで）してから <code>delete-stack</code> を再実行</td>
</tr>
<tr>
<td>スタック作成後しばらく <code>CREATE_IN_PROGRESS</code> が続く</td>
<td>RDSの作成に時間がかかっている</td>
<td>正常。約15〜20分待つ</td>
</tr>
</tbody>
</table>
<h3><span id="toc25">失敗時の詳細確認コマンド</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stack-events ^
  --stack-name my-rds-mysql-stack ^
  --query "StackEvents[?ResourceStatus=='CREATE_FAILED'].[ResourceType,ResourceStatusReason]" ^
  --output table</code></pre>
<hr>
<h2><span id="toc26">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>キーペアをコンソールで作成</td>
</tr>
<tr>
<td>②③</td>
<td><code>rds-mysql-ec2/</code> フォルダに移動し <code>create-stack</code> を実行</td>
</tr>
<tr>
<td>④</td>
<td><code>CREATE_COMPLETE</code> になったらOutputsでEC2IP・RDSエンドポイントを確認</td>
</tr>
<tr>
<td>⑤</td>
<td>EC2 → Parameter Store → RDS MySQL への接続テスト</td>
</tr>
<tr>
<td>⑥</td>
<td><code>delete-stack</code> 1本で22リソースを一括削除</td>
</tr>
</tbody>
</table>
<p>CloudFormation版の最大のメリットは<strong>再現性</strong>と<strong>削除の簡単さ</strong>です。コンソールでは40〜50分・11ステップかかった作業が、コマンド1本（待ち時間込みで約20分）で完了します。</p>
<p>コンソール操作でRDS・DBサブネットグループ・Parameter Storeを視覚的に確認したい場合は、<a href="https://caymezon.com/aws-handson-console-rds-mysql-ec2/">AWSコンソール版ハンズオン</a>を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/">CloudFormationでRDS MySQL + EC2接続環境を自動構築する手順【22リソース一括デプロイ / コンソール版との比較付き】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cloudformation-rds-mysql-ec2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
