<?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>CayTech Lab</title>
	<atom:link href="https://caymezon.com/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=6.9.4</generator>

<image>
	<url>https://caymezon.com/wp-content/uploads/2026/01/cropped-CayTechLab-32x32.jpg</url>
	<title>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>はじめに 「GitHub Actions は GitHub に依存してしまう。AWS だけで CI/CD を完結させたい」——そんなニーズに応えるのが AWS Code 4 兄弟（CodeCommit・CodeBuild [&#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[<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>はじめに 「インフラは AWS でいいけど、デプロイは GitHub Actions でやりたい」——クラウドと Git を組み合わせたハイブリッドな CI/CD 構成は、多くの現場で採用されています。このハンズオンでは [&#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[<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>CDK + ECS Fargate で Webアプリをコンテナ化する【EC2 からコンテナへの移行体験】</title>
		<link>https://caymezon.com/aws-handson-cdk-ecs-webapp/</link>
					<comments>https://caymezon.com/aws-handson-cdk-ecs-webapp/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sat, 09 May 2026 03:23:21 +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 Pipelines]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[CodeBuild]]></category>
		<category><![CDATA[CodePipeline]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[ECR]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[Fargate]]></category>
		<category><![CDATA[GitHub Actions]]></category>
		<category><![CDATA[GitOps]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[OIDC]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[コンテナ]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[ローリングデプロイ]]></category>
		<category><![CDATA[初心者]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20414</guid>

					<description><![CDATA[<p>はじめに 「EC2 の OS パッチや Tomcat のインストール設定が面倒」「スケールアウト時のインスタンス管理が複雑」——これを解決するのが ECS Fargate（サーバーレスコンテナ） です。 この記事では、A [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cdk-ecs-webapp/">CDK + ECS Fargate で Webアプリをコンテナ化する【EC2 からコンテナへの移行体験】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2><span id="toc1">はじめに</span></h2>
<p>「EC2 の OS パッチや Tomcat のインストール設定が面倒」「スケールアウト時のインスタンス管理が複雑」——これを解決するのが <strong>ECS Fargate（サーバーレスコンテナ）</strong> です。</p>
<p>この記事では、<a href="https://caymezon.com/aws-handson-cdk-asg-webapp/">ASG ハンズオン（cdk-asg-webapp）</a>で構築した <strong>EC2 + Auto Scaling Group 構成</strong>を、<strong>ECS Fargate + Docker コンテナ</strong>にアップグレードします。EC2 の代わりに Docker コンテナで Spring Boot アプリを動かし、<strong>GitHub Actions</strong> がコンテナイメージをビルドして ECS へ自動デプロイする仕組みを体験します。</p>
<pre><code class="language-plaintext">【インフラ担当】CDK Pipelines
GitHub リポジトリ
  ↓ push（常に起動）
CodePipeline (my-cdk6-pipeline)
  ├── Source:       GitHub (CodeConnections 経由)
  ├── Build(Synth): CodeBuild — cdk synth
  ├── Mutate:       パイプライン自己更新（セルフミューテーション）
  └── InfraDeploy:  CloudFormation — VPC / ECR / ECS / RDS / ALB

【アプリ担当】GitHub Actions（app/ または Dockerfile 変更時のみ起動）
  1. Docker ビルド（Maven → WAR → Tomcat コンテナ）
  2. ECR にイメージをプッシュ
  3. タスク定義を新リビジョンに更新（プレースホルダー → 実イメージ）
  4. ECS サービスをローリングデプロイ（〜2〜3分）
                ↓
       [ALB: my-cdk6-alb]
                ↓
       ECS Fargate Service (my-cdk6-service)
       ・コンテナ: Tomcat 10.1 + Spring Boot WAR
       ・CPU: 256 units / Memory: 512 MiB
                ↓
       [RDS: my-cdk6-rds-mysql]  ← MySQL 8.0</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li>Docker のマルチステージビルド（Maven → WAR → Tomcat コンテナ）</li>
<li>ECR へのイメージプッシュと ECS Fargate でのコンテナ実行</li>
<li>GitHub Actions（OIDC 認証）による Docker ビルド → ECR プッシュ → ECS ローリングデプロイ</li>
<li><strong>インフラ（CDK Pipelines）とアプリ（GitHub Actions）のデプロイを分離した実務パターン</strong></li>
<li>ECS Exec でコンテナ内のシェルに直接接続するデバッグ体験</li>
<li><code>desired_count</code> を変更するだけのシンプルなスケーリング体験</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cdk-asg-webapp/">CDK + Auto Scaling Group ハンズオン（cdk-asg-webapp）</a> の発展記事です。</strong><br />cdk-asg-webapp を体験済みの方向けです（cdk-webapp-pipeline 完了でも OK）。コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-ecs-webapp">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![CDK Pipelinesの全ステージ完了とECSコンソールの画面](images/pipeline-ecs-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>Docker / Dockerfile</strong></td>
<td>アプリとその実行環境をひとまとめにした「コンテナ」を作成する仕組み。<code>Dockerfile</code> にビルド手順を記述する</td>
</tr>
<tr>
<td><strong>マルチステージビルド</strong></td>
<td>ビルド環境（Maven）と実行環境（Tomcat）を分離して軽量なイメージを作成するテクニック</td>
</tr>
<tr>
<td><strong>ECR（Elastic Container Registry）</strong></td>
<td>Docker イメージを保管する AWS マネージドレジストリ。EC2 構成での S3 バケットに相当する</td>
</tr>
<tr>
<td><strong>ECS Fargate</strong></td>
<td>EC2 なしでコンテナを実行できるサーバーレス実行環境。OS 管理・パッチ適用が不要</td>
</tr>
<tr>
<td><strong>タスク定義</strong></td>
<td>コンテナの CPU・メモリ・環境変数・IAM ロールなどを定義する設定</td>
</tr>
<tr>
<td><strong>ECS Service</strong></td>
<td>タスクのスケーリング・ヘルスチェック・ALB 連携・ローリングデプロイを管理する</td>
</tr>
<tr>
<td><strong>ローリングデプロイ</strong></td>
<td>旧コンテナを停止しながら新コンテナを順次起動する無停止デプロイ</td>
</tr>
<tr>
<td><strong>ECS Exec</strong></td>
<td>コンテナ内のシェルに AWS CLI から直接アクセスするデバッグ機能。EC2 の SSM Session Manager に相当</td>
</tr>
<tr>
<td><strong>OIDC 認証</strong></td>
<td>アクセスキーをリポジトリに保存せず、GitHub Actions から AWS を操作できるキーレス認証</td>
</tr>
<tr>
<td><strong>インフラ/アプリ分離デプロイ</strong></td>
<td>CDK Pipelines（インフラ）と GitHub Actions（アプリ）で役割を分担する実務パターン</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">cdk-asg-webapp との比較</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>cdk-asg-webapp</th>
<th>cdk-ecs-webapp</th>
</tr>
</thead>
<tbody>
<tr>
<td>実行環境</td>
<td>EC2（Launch Template + ASG）</td>
<td><strong>ECS Fargate（サーバーレスコンテナ）</strong></td>
</tr>
<tr>
<td>デプロイ形式</td>
<td>WAR → S3 → CodeDeploy</td>
<td><strong>Docker Image → ECR → ECS タスク更新</strong></td>
</tr>
<tr>
<td>インフラ管理</td>
<td>EC2 OS パッチ・エージェント管理が必要</td>
<td><strong>サーバーレス（EC2 管理不要）</strong></td>
</tr>
<tr>
<td>スケーリング</td>
<td>CPU 自動スケール（ASG ポリシー）</td>
<td><strong><code>desired_count</code> を直接変更（シンプル）</strong></td>
</tr>
<tr>
<td>デバッグ接続</td>
<td>SSM Session Manager（EC2 に接続）</td>
<td><strong>ECS Exec（コンテナに直接接続）</strong></td>
</tr>
<tr>
<td>アプリ設定ファイル</td>
<td><code>appspec.yml</code> + <code>scripts/</code></td>
<td><strong><code>Dockerfile</code> のみ</strong></td>
</tr>
<tr>
<td>アプリデプロイ担当</td>
<td>CDK Pipelines 内の CodeDeploy</td>
<td><strong>GitHub Actions</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">ファイル構成と役割</span></h2>
<pre><code class="language-plaintext">cdk-ecs-webapp/
├── app.py                     ← CDK アプリ エントリポイント
├── cdk.json                   ← CDK 設定（asg_* → task_cpu / task_memory / desired_count）
├── Dockerfile                 ← NEW: コンテナイメージのビルド手順（cdk-asg-webapp にはない）
├── app/                       ← Spring Boot Webアプリ（Java）
├── pipeline/
│   └── pipeline_stack.py      ← CodePipeline（インフラのみ / アプリは GitHub Actions が担当）
├── stages/
│   └── deploy_stage.py        ← Stage（WebappStack を内包）
├── stacks/
│   └── webapp_stack.py        ← インフラスタック（ECR/ECS の名前を CfnOutput で公開）
└── components/
    └── webapp_construct.py    ← ECS Fargate ベースの L3 Construct</code></pre>
<table>
<thead>
<tr>
<th>ファイル</th>
<th>cdk-asg-webapp との主な違い</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Dockerfile</code></td>
<td>新規追加。マルチステージビルドで Maven → WAR → Tomcat コンテナを作成</td>
</tr>
<tr>
<td><code>webapp_construct.py</code></td>
<td><code>AutoScalingGroup + LaunchTemplate</code> → <code>FargateService + TaskDefinition + ECR</code></td>
</tr>
<tr>
<td><code>webapp_stack.py</code></td>
<td><code>ASGName</code> 等の出力を廃止 → <code>ECRRepoUri / ECSClusterName / ECSServiceName</code> を追加</td>
</tr>
<tr>
<td><code>pipeline_stack.py</code></td>
<td><code>BuildAndDeployApp</code>（CodeDeploy）を廃止 → インフラ変更のみ担当</td>
</tr>
</tbody>
</table>
<p>詳細なコードは <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-ecs-webapp">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>ECS Fargate</strong>（0.25 vCPU / 512 MiB）</td>
<td>Spring Boot コンテナを実行するサーバーレス環境</td>
<td>約$0.01/時間（<strong>無料枠なし</strong>）</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>ECR</strong></td>
<td>Docker イメージの保管場所</td>
<td>500MB/月まで無料</td>
</tr>
<tr>
<td><strong>CodePipeline</strong></td>
<td>CI/CD パイプライン管理（インフラ側）</td>
<td>月1パイプラインまで無料枠あり</td>
</tr>
<tr>
<td><strong>CodeBuild</strong></td>
<td>cdk synth を実行するビルド環境</td>
<td>月100分まで無料枠あり</td>
</tr>
<tr>
<td><strong>S3</strong></td>
<td>パイプラインのアーティファクト置き場</td>
<td>無料枠あり</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DBパスワードの安全な管理・ECS への環境変数注入</td>
<td>スタンダード層無料</td>
</tr>
<tr>
<td><strong>CloudWatch Logs</strong></td>
<td>コンテナのログ出力先</td>
<td>無料枠あり</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>各サービスの権限</td>
<td>無料</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>: ECS Fargate と ALB は無料枠がありません。ハンズオン後は必ずリソースを削除してください。</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>Java 17</td>
<td><code>java -version</code></td>
</tr>
<tr>
<td>Maven</td>
<td><code>mvn -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>
<tr>
<td>Git</td>
<td><code>git --version</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Docker Desktop は不要です</strong>。コンテナのビルドは GitHub Actions runner 上（Ubuntu）で実行されるため、ローカル PC に Docker をインストールする必要はありません。</p>
</blockquote>
<p>AWS 認証確認:</p>
<pre><code class="language-cmd">aws sts get-caller-identity</code></pre>
<p>CDK Bootstrap 確認:</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>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">① プロジェクトのセットアップ（Python 仮想環境）
      ↓
② GitHub との接続を設定する（CodeConnections）
      ↓
③ cdk.json の設定
      ↓
④ コードを GitHub にプッシュする（重要！）
      ↓
⑤ パイプラインスタックのデプロイ（初回のみ手動）
      ↓
⑥ パイプラインの実行状況を確認する
      ↓
⑦ インフラを確認する
      ↓
⑧ GitHub Actions を設定する（OIDC + IAM ロール）
      ↓
⑨ アプリを初回デプロイする
      ↓
⑩ ECS Exec でコンテナに接続する
      ↓
⑪ タスク数スケーリングを体験する
      ↓
⑫ リソースを削除する</code></pre>
<hr>
<h2><span id="toc8">① プロジェクトのセットアップ</span></h2>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-ecs-webapp

python -m venv .venv

.venv\Scripts\activate

pip install -r requirements.txt</code></pre>
<p>プロンプトの先頭に <code>(.venv)</code> が表示されれば仮想環境が有効になっています。</p>
<blockquote>
<p><strong>重要</strong>: CDK コマンドはすべてこの仮想環境が有効な状態で実行する必要があります。<br />ターミナルを開き直した際は必ず最初に以下を実行してください。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-ecs-webapp
.venv\Scripts\activate</code></pre>
</blockquote>
<hr>
<h2><span id="toc9">② GitHub との接続を設定する（CodeConnections）</span></h2>
<blockquote>
<p><strong>cdk-asg-webapp（または cdk-webapp-pipeline）を実施済みの場合</strong>: 同じリポジトリを扱うため、<br />既存の接続（<code>my-github-connection</code>）をそのまま<strong>再利用</strong>できます。<br /><strong>② はスキップして、ARN の確認（②-4）から始めてください。</strong></p>
</blockquote>
<p>CodePipeline が GitHub リポジトリを監視するために、<strong>GitHub との接続（CodeConnections）</strong> を作成します。</p>
<blockquote>
<p><strong>重要</strong>: AWS コンソールと GitHub の操作は<strong>同じブラウザ</strong>で行ってください。</p>
</blockquote>
<h3><span id="toc10">接続を作成する</span></h3>
<ol>
<li>AWS コンソール検索（<code>Alt+S</code>）で <code>CodePipeline</code> と入力 → <strong>CodePipeline</strong> を開く</li>
<li>左サイドバー → <strong>「設定」</strong> → <strong>「接続」</strong></li>
<li>リージョンが <strong>東京（ap-northeast-1）</strong> になっていることを確認する</li>
<li>「<strong>接続を作成</strong>」をクリック</li>
<li>プロバイダー: <strong>GitHub</strong> を選択</li>
<li>接続名: <code>my-github-connection</code>（任意）</li>
<li>「<strong>GitHub に接続する</strong>」をクリック</li>
</ol>
<h3><span id="toc11">GitHub App をインストールする</span></h3>
<ol start="8">
<li>「<strong>Authorize</strong>」をクリック → AWS コンソールの「GitHub 接続設定」画面に自動で戻る</li>
<li>「<strong>新しいアプリをインストールする</strong>」をクリック</li>
<li><strong>「Only select repositories」</strong> を選択 → <code>aws-learning-projects</code> を追加</li>
<li>「<strong>Install & Authorize</strong>」をクリック</li>
<li>AWS コンソールに自動でリダイレクトされる → 「<strong>接続</strong>」ボタンをクリック</li>
</ol>
<h3><span id="toc12">接続 ARN を確認する</span></h3>
<p>接続一覧から接続の ARN をコピーします。</p>
<pre><code class="language-plaintext">arn:aws:codeconnections:ap-northeast-1:123456789012:connection/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</code></pre>
<blockquote>
<p>ステータスが <strong>「利用可能」</strong> になっていることを確認してから次に進んでください。</p>
</blockquote>
<p><strong>控えておく情報</strong>: 接続 ARN（<code>arn:aws:codeconnections:...</code>）</p>
<hr>
<h2><span id="toc13">③ cdk.json の設定</span></h2>
<p><code>cdk.json</code> の <code>context</code> セクションを自分の環境に合わせて編集します。</p>
<pre><code class="language-json">{
  "context": {
    "employee_id": "my",
    "db_password": "Handson1234!",
    "task_cpu": 256,
    "task_memory": 512,
    "desired_count": 1,
    "github_owner": "your-github-username",
    "github_repo": "aws-learning-projects",
    "github_branch": "main",
    "connection_arn": "arn:aws:codeconnections:ap-northeast-1:..."
  }
}</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-cdk6-xxx</code> という名前になる）</td>
</tr>
<tr>
<td><code>task_cpu</code></td>
<td><code>256</code></td>
<td>Fargate タスクの CPU ユニット（256 = 0.25 vCPU）</td>
</tr>
<tr>
<td><code>task_memory</code></td>
<td><code>512</code></td>
<td>Fargate タスクのメモリ（MiB）</td>
</tr>
<tr>
<td><code>desired_count</code></td>
<td><code>1</code></td>
<td>ECS サービスの起動タスク数</td>
</tr>
<tr>
<td><code>github_owner</code></td>
<td>GitHub ユーザー名</td>
<td>リポジトリのオーナー名</td>
</tr>
<tr>
<td><code>connection_arn</code></td>
<td>②で確認した ARN</td>
<td>CodeConnections の接続 ARN</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong><code>task_cpu</code> と <code>task_memory</code> の組み合わせ制限（Fargate の仕様）</strong><br />CPU と Memory には決まった組み合わせがあります。256 CPU の場合、Memory は 512〜2048（512 単位）が有効です。</p>
</blockquote>
<hr>
<h2><span id="toc14">④ コードを GitHub にプッシュする（重要！）</span></h2>
<blockquote>
<p><code>cdk deploy</code> の前に必ず<strong>プロジェクト全体</strong>を push してください。<br />パイプラインが <code>cdk synth</code> を実行する際、GitHub から取得したコードを使うためです。</p>
</blockquote>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects

git add cdk-ecs-webapp/
git commit -m "feat: cdk-ecs-webapp ハンズオンを追加"
git push origin main</code></pre>
<blockquote>
<p><strong>GitHub Actions も同時に起動する場合</strong>: <code>app/</code> または <code>Dockerfile</code> の変更が含まれていると GitHub Actions（<code>Deploy App to ECS</code>）も起動しますが、この時点では ECS サービスがまだ存在しないため失敗します。インフラが完成してから <strong>⑨</strong> で改めてアプリをデプロイします。</p>
</blockquote>
<hr>
<h2><span id="toc15">⑤ パイプラインスタックのデプロイ（初回のみ）</span></h2>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-ecs-webapp

.venv\Scripts\activate

cdk synth

cdk deploy</code></pre>
<p>「Do you wish to deploy these changes?」に <code>y</code> を入力します。</p>
<p><code>my-EcsPipelineStack</code> が作成され、以降は GitHub push で自動デプロイされます。</p>
<blockquote>
<p><strong>所要時間</strong>: パイプラインスタック自体のデプロイは 3〜5 分。その後パイプラインが自動起動します。</p>
</blockquote>
<p>デプロイ完了後、CodePipeline が自動起動します:</p>
<table>
<thead>
<tr>
<th>ステージ</th>
<th>内容</th>
<th>所要時間</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>GitHub から最新コードを取得</td>
<td>〜1分</td>
</tr>
<tr>
<td><strong>Build (Synth)</strong></td>
<td><code>cdk synth</code> でテンプレート生成</td>
<td>〜3分</td>
</tr>
<tr>
<td><strong>UpdatePipeline</strong></td>
<td>パイプライン自己更新（セルフミューテーション）</td>
<td>〜2分</td>
</tr>
<tr>
<td><strong>Deploy > InfraDeploy</strong></td>
<td>CloudFormation で VPC / ECR / ECS / RDS / ALB を構築</td>
<td>〜20分</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>初回の InfraDeploy に 20 分程度かかる理由</strong>: RDS の起動と ECS サービスの安定化（プレースホルダーコンテナの起動確認）に時間がかかります。</p>
</blockquote>
<hr>
<h2><span id="toc16">⑥ パイプラインの実行状況を確認する</span></h2>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> を開く</li>
<li><code>my-cdk6-pipeline</code> を選択</li>
<li>各ステージが <strong>「成功」</strong> になるまで待つ</li>
</ol>
<h3><span id="toc17">CloudFormation スタックの命名規則</span></h3>
<table>
<thead>
<tr>
<th>要素</th>
<th>このハンズオンでの値</th>
</tr>
</thead>
<tbody>
<tr>
<td>パイプラインスタック</td>
<td><code>my-EcsPipelineStack</code></td>
</tr>
<tr>
<td>インフラスタック</td>
<td><strong><code>my-Deploy-WebappStack</code></strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p><code>my-EcsPipelineStack</code>（パイプライン）と <code>my-Deploy-WebappStack</code>（インフラ）の <strong>2つが別スタック</strong>として CloudFormation に表示されます。</p>
</blockquote>
<h3><span id="toc18">CloudFormation Outputs の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs"</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CodePipelineの全ステージが成功している画面](images/pipeline-success.jpg) --></p>
<hr>
<h2><span id="toc19">⑦ インフラを確認する</span></h2>
<h3><span id="toc20">ALB の URL を確認する</span></h3>
<p><code>ALBEndpoint</code> の URL にブラウザでアクセスします。</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs[?OutputKey=='ALBEndpoint'].OutputValue" ^
  --output text</code></pre>
<p><strong>この時点では Tomcat のデフォルト画面（または 404）が表示されます。</strong><br />ECS タスクがプレースホルダーイメージ（公式 Tomcat）で起動しているためで、<strong>正常な状態</strong>です。<br />Spring Boot アプリのデプロイは <strong>⑨</strong> で実施します。</p>
<h3><span id="toc21">ECS コンソールで確認する</span></h3>
<p>ECS → クラスター → <code>my-cdk6-cluster</code> → サービス → <code>my-cdk6-service</code></p>
<p>「タスク」タブで 1 タスクが <strong>RUNNING</strong> になっていることを確認します。<br />「タスク定義」タブでリビジョンが <strong>1</strong>（プレースホルダー）になっていることを確認します。</p>
<p><!-- ![ECSコンソールでクラスターとサービスが確認できる画面](images/ecs-service-running.jpg) --></p>
<hr>
<h2><span id="toc22">⑧ GitHub Actions を設定する（OIDC + IAM ロール）</span></h2>
<p>Spring Boot アプリのデプロイを担う <strong>GitHub Actions</strong> を設定します。<br />OIDC 認証によりアクセスキーをリポジトリに保存せずに AWS を操作できます。</p>
<h3><span id="toc23">OIDC プロバイダーを登録する</span></h3>
<p>AWS コンソール → <strong>IAM</strong> → 左メニュー「<strong>IDプロバイダー</strong>」→「<strong>プロバイダーを追加</strong>」</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>値</th>
</tr>
</thead>
<tbody>
<tr>
<td>プロバイダータイプ</td>
<td><strong>OpenID Connect</strong></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>
<p>「プロバイダーを追加」ボタンをクリックして完了。</p>
<blockquote>
<p>URL と対象者を入力するだけでよいです（現在の UI ではサムプリントの手動取得は不要）。</p>
</blockquote>
<hr>
<h3><span id="toc24">IAM ロールを作成する（CloudShell）</span></h3>
<p><strong>AWS コンソール</strong> → 右上「<strong>CloudShell</strong>」アイコン → 以下を貼り付けて実行します。<br /><code>GITHUB_OWNER</code> を自分の GitHub ユーザー名に変更してください:</p>
<pre><code class="language-bash">ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
GITHUB_OWNER="your-github-username"   # ← 自分の GitHub ユーザー名に変更
REPO="aws-learning-projects"
ROLE_NAME="my-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

# 権限ポリシーを作成（ECR push + ECS deploy に必要な最小権限）
cat &gt; /tmp/permissions.json &lt;&lt; EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["ecr:GetAuthorizationToken"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability", "ecr:PutImage",
        "ecr:InitiateLayerUpload", "ecr:UploadLayerPart", "ecr:CompleteLayerUpload"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecs:DescribeTaskDefinition", "ecs:RegisterTaskDefinition",
        "ecs:UpdateService", "ecs:DescribeServices"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["iam:PassRole"],
      "Resource": "*"
    }
  ]
}
EOF

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

echo "=== Role 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>
<h3><span id="toc25">GitHub Variables を設定する</span></h3>
<p>GitHub リポジトリ → <strong>Settings</strong> → <strong>Secrets and variables</strong> → <strong>Actions</strong> → <strong>「Variables」タブ</strong></p>
<p>「New repository variable」で以下の2つを追加します:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>EMPLOYEE_ID</code></td>
<td><code>my</code>（cdk.json の employee_id と同じ値）</td>
</tr>
<tr>
<td><code>AWS_ROLE_ARN</code></td>
<td>IAM ロール作成で表示された ARN</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Variables と Secrets の使い分け</strong>: ARN や EMPLOYEE_ID は機密情報ではないため <strong>Variables</strong> に保存します（Secrets は使いません）。</p>
</blockquote>
<hr>
<h3><span id="toc26">ワークフローファイルを push する</span></h3>
<p><code>.github/workflows/ecs-app-deploy.yml</code> は既にリポジトリに含まれています。<br />このファイルを push してワークフローをアクティブにします:</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add .github/workflows/ecs-app-deploy.yml
git commit -m "feat: add GitHub Actions ECS deploy workflow"
git push</code></pre>
<blockquote>
<p>このワークフローは <code>paths</code> フィルターにより <code>app/</code> か <code>Dockerfile</code> が変わった時だけ起動します。<br />このコミットでは GitHub Actions はスキップされ、CDK Pipelines のみが起動（インフラ差分なしで数分で完了）します。</p>
</blockquote>
<p><!-- ![GitHub ActionsワークフローがActionsタブに表示されている画面](images/github-actions-workflow.jpg) --></p>
<hr>
<h2><span id="toc27">⑨ アプリを初回デプロイする（GitHub Actions）</span></h2>
<p>GitHub Actions の設定が完了したので、Spring Boot アプリを ECS に初めてデプロイします。</p>
<h3><span id="toc28">アプリのタイトルを変更する</span></h3>
<p><code>cdk-ecs-webapp/app/src/main/resources/templates/index.html</code> を開き、以下の行を変更します:</p>
<p>変更前:</p>
<pre><code class="language-html">&lt;h1&gt;Spring Boot + RDS Item Manager (ECS)&lt;/h1&gt;</code></pre>
<p>変更後:</p>
<pre><code class="language-html">&lt;h1&gt;Spring Boot + RDS Item Manager (ECS v2)&lt;/h1&gt;</code></pre>
<h3><span id="toc29">コミット＆プッシュ</span></h3>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add cdk-ecs-webapp/app/src/main/resources/templates/index.html
git commit -m "feat: initial app deploy via GitHub Actions"
git push</code></pre>
<p><code>app/</code> の変更のため、<strong>GitHub Actions が起動</strong>します（CDK Pipelines も起動しますがインフラ差分なしで数分で完了）。</p>
<h3><span id="toc30">GitHub Actions の進行を確認する</span></h3>
<p>GitHub リポジトリ → <strong>Actions</strong> タブ → <code>Deploy App to ECS</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>Login to ECR</td>
<td>ECR ログイン</td>
<td>〜10秒</td>
</tr>
<tr>
<td>Build and push</td>
<td>docker build + ECR push</td>
<td>〜1〜2分</td>
</tr>
<tr>
<td>Update ECS task definition</td>
<td>タスク定義を新リビジョンに更新</td>
<td>〜10秒</td>
</tr>
<tr>
<td>Deploy to ECS</td>
<td>サービス更新 + 安定化待ち</td>
<td>〜2〜3分</td>
</tr>
</tbody>
</table>
<h3><span id="toc31">ブラウザで動作確認する</span></h3>
<p><code>Deploy to ECS</code> ステップが完了したら、ALB の URL にアクセスします。</p>
<p>「<strong>Spring Boot + RDS Item Manager (ECS v2)</strong>」の画面が表示されることを確認します。</p>
<blockquote>
<p><strong>ECS コンソールで確認</strong>: ECS → サービス → 「タスク」タブ<br />タスク定義のリビジョンが <strong>2</strong> 以上（GitHub Actions による更新後）になっていることを確認します。</p>
</blockquote>
<p><!-- ![GitHub Actionsのデプロイが完了してアプリ画面が表示されている画面](images/app-deployed-ecs.jpg) --></p>
<hr>
<h2><span id="toc32">⑩ ECS Exec でコンテナに接続する</span></h2>
<p>cdk-asg-webapp では SSM Session Manager で <strong>EC2</strong> に接続しましたが、ECS Fargate では <strong>ECS Exec</strong> でコンテナ内のシェルに直接接続できます。</p>
<h3><span id="toc33">実行中のタスク ARN を確認する</span></h3>
<pre><code class="language-cmd">aws ecs list-tasks ^
  --cluster my-cdk6-cluster ^
  --service-name my-cdk6-service ^
  --desired-status RUNNING ^
  --region ap-northeast-1 ^
  --query "taskArns" ^
  --output text</code></pre>
<p>出力例:</p>
<pre><code class="language-plaintext">arn:aws:ecs:ap-northeast-1:123456789012:task/my-cdk6-cluster/abcdef1234567890</code></pre>
<blockquote>
<p><strong>複数件表示される場合</strong>: ローリングデプロイ中は新旧2タスクが同時に動きます。<br />ECS コンソール → サービス → 「タスク」タブでリビジョン番号が大きい方（最新）の ARN を使ってください。</p>
</blockquote>
<h3><span id="toc34">コンテナに接続する</span></h3>
<pre><code class="language-cmd">aws ecs execute-command ^
  --cluster my-cdk6-cluster ^
  --task arn:aws:ecs:ap-northeast-1:123456789012:task/my-cdk6-cluster/abcdef1234567890 ^
  --container my-cdk6-app ^
  --interactive ^
  --command "/bin/sh" ^
  --region ap-northeast-1</code></pre>
<blockquote>
<p><strong>Session Manager Plugin が必要</strong>: 接続には AWS CLI の Session Manager Plugin が必要です。<br />インストール手順は <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html">AWS 公式ドキュメント</a> を参照してください。</p>
</blockquote>
<h3><span id="toc35">コンテナ内で確認する</span></h3>
<pre><code class="language-sh"># 環境変数（DB接続情報）を確認
echo $DB_HOST
echo $DB_PASSWORD

# デプロイされた WAR を確認（ROOT.war として配置される）
ls /usr/local/tomcat/webapps/

# 接続を終了
exit</code></pre>
<blockquote>
<p><strong><code>catalina.out</code> は存在しません</strong>: Docker コンテナの Tomcat はファイルにログを書かず、<strong>stdout（標準出力）</strong> に直接出力します。ログは CloudWatch Logs に転送されます。</p>
<pre><code class="language-cmd">aws logs tail /ecs/my-cdk6-app ^
  --follow ^
  --region ap-northeast-1</code></pre>
<p>終了は <code>Ctrl+C</code>。</p>
</blockquote>
<hr>
<h2><span id="toc36">⑪ タスク数スケーリングを体験する</span></h2>
<p>cdk-asg-webapp では <code>stress-ng</code> で CPU 負荷をかけて自動スケールを体験しましたが、<br />ECS Fargate では <strong><code>desired_count</code> を直接変更する</strong>だけでスケールできます。</p>
<h3><span id="toc37">スケールアウト（1台 → 2台）</span></h3>
<pre><code class="language-cmd">aws ecs update-service ^
  --cluster my-cdk6-cluster ^
  --service my-cdk6-service ^
  --desired-count 2 ^
  --region ap-northeast-1</code></pre>
<p>ECS コンソール → サービス → 「タスク」タブで 2 タスクが RUNNING になることを確認します。<br />ALB がどちらのタスクにもリクエストを分散するようになります（ラウンドロビン）。</p>
<h3><span id="toc38">スケールイン（2台 → 1台）</span></h3>
<pre><code class="language-cmd">aws ecs update-service ^
  --cluster my-cdk6-cluster ^
  --service my-cdk6-service ^
  --desired-count 1 ^
  --region ap-northeast-1</code></pre>
<p>数分後に 1 タスクが停止し、1 台構成に戻ります。</p>
<blockquote>
<p><strong>ECS と ASG のスケーリング比較</strong></p>
<ul>
<li>ECS: <code>desired_count</code> を変更するだけ（コマンド一発、秒単位で反映）</li>
<li>ASG: CPU 閾値・CloudWatch アラーム・スケーリングポリシーが必要（設定が複雑）</li>
</ul>
<p>ECS Fargate はコンテナ単位なので起動が速く（30秒〜）、EC2 の起動を待つ ASG より素早くスケールできます。</p>
</blockquote>
<hr>
<h2><span id="toc39">⑫ リソースを削除する</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p><strong>削除順序が重要です</strong>。インフラスタック（<code>my-Deploy-WebappStack</code>）を<strong>先に</strong>削除してから、パイプラインスタック（<code>my-EcsPipelineStack</code>）を削除します。</p>
</blockquote>
<h3><span id="toc40">1. インフラスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1

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

echo WebappStack 削除完了</code></pre>
<blockquote>
<p>RDS の削除に 10〜15 分かかります。<code>wait</code> コマンドが完了するまでそのまま待ってください。</p>
</blockquote>
<h3><span id="toc41">2. パイプラインスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-EcsPipelineStack ^
  --region ap-northeast-1

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

echo EcsPipelineStack 削除完了</code></pre>
<h3><span id="toc42">3. GitHub Actions IAM ロールを削除する</span></h3>
<p>CloudFormation の管理外のため、手動で削除します:</p>
<pre><code class="language-cmd">aws iam delete-role-policy ^
  --role-name my-github-actions-role ^
  --policy-name my-github-actions-role-policy

aws iam delete-role ^
  --role-name my-github-actions-role</code></pre>
<h3><span id="toc43">4. SSM パラメータを削除する</span></h3>
<pre><code class="language-cmd">aws ssm delete-parameter ^
  --name "/my/cdk6/db-password" ^
  --region ap-northeast-1</code></pre>
<h3><span id="toc44">5. 仮想環境の終了</span></h3>
<pre><code class="language-cmd">deactivate</code></pre>
<h3><span id="toc45">6. GitHub との接続を削除する（オプション）</span></h3>
<p>次のハンズオンでも使う場合はそのままでよいです。</p>
<p><strong>CodePipeline → 設定 → 接続 → <code>my-github-connection</code> → 「削除」</strong></p>
<h3><span id="toc46">7. OIDC プロバイダーを削除する（オプション）</span></h3>
<p>他のリポジトリでも使う場合はそのままでよいです。<br />不要な場合は <strong>IAM → IDプロバイダー → <code>token.actions.githubusercontent.com</code> → 「削除」</strong></p>
<h3><span id="toc47">削除確認</span></h3>
<pre><code class="language-cmd">aws cloudformation list-stacks ^
  --region ap-northeast-1 ^
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE ^
  --query "StackSummaries[?contains(StackName, 'my-')].StackName"</code></pre>
<p>空のリスト <code>[]</code> が返れば削除完了です。</p>
<hr>
<h2><span id="toc48">EC2 → コンテナへの移行ポイント</span></h2>
<pre><code class="language-plaintext">cdk-asg-webapp（EC2 + ASG）          cdk-ecs-webapp（ECS Fargate）
─────────────────────────────       ─────────────────────────────
Launch Template                     Dockerfile
  ↓ UserData（シェルスクリプト）        ↓ マルチステージビルド（20行）
Java インストール                     Maven → WAR → Tomcat（イメージ内）
Tomcat インストール
CodeDeploy エージェントインストール
  ↓                                   ↓
appspec.yml + scripts/*.sh           Dockerfile のみ
S3 → CodeDeploy → WAR デプロイ       ECR → ECS タスク更新 → ローリングデプロイ
  ↓                                   ↓
SSM Session Manager（EC2 に接続）     ECS Exec（コンテナに直接接続）
  ↓                                   ↓
CPU 自動スケール（ASG ポリシー）        desired_count を変更（コマンド一発）</code></pre>
<p><code>appspec.yml</code> / <code>scripts/*.sh</code> / UserData の複雑な設定が、<code>Dockerfile</code> の 20 行に集約されます。</p>
<hr>
<h2><span id="toc49">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Source</code> ステージが「失敗」</td>
<td>GitHub との接続が「保留中」</td>
<td>CodePipeline → 設定 → 接続 → 承認して「利用可能」にする</td>
</tr>
<tr>
<td>ECS タスクが <code>STOPPED</code> を繰り返す</td>
<td>プレースホルダーイメージの pull 失敗</td>
<td>ECS コンソール → 停止タスク → 「停止理由」を確認</td>
</tr>
<tr>
<td>GitHub Actions が失敗（OIDC エラー）</td>
<td>IAM ロールの信頼ポリシーが不正</td>
<td>GitHub Variables の <code>AWS_ROLE_ARN</code> と信頼ポリシーの <code>sub</code> を確認</td>
</tr>
<tr>
<td>GitHub Actions が失敗（ECR push）</td>
<td><code>ecr:GetAuthorizationToken</code> 権限不足</td>
<td>IAM ロールのポリシーを確認</td>
</tr>
<tr>
<td>アプリ画面が表示されない（404）</td>
<td>GitHub Actions がまだ実行されていない</td>
<td>Actions タブで <code>Deploy App to ECS</code> が完了するまで待つ</td>
</tr>
<tr>
<td>ECS Exec が接続できない</td>
<td><code>enable_execute_command</code> が未設定</td>
<td>ECS サービスの設定を確認（CDK で有効化済み）</td>
</tr>
<tr>
<td><code>Session Manager Plugin is not found</code></td>
<td>Plugin 未インストール</td>
<td>AWS 公式ドキュメントからインストール</td>
</tr>
<tr>
<td>RDS 接続エラー</td>
<td>DB_HOST / DB_PASSWORD が不正</td>
<td>CloudWatch Logs → <code>/ecs/my-cdk6-app</code> でログを確認</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc50">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>Python 仮想環境のセットアップ</td>
</tr>
<tr>
<td>②</td>
<td>CodeConnections で GitHub との接続を作成（cdk-asg-webapp 実施済みなら再利用）</td>
</tr>
<tr>
<td>③</td>
<td><code>cdk.json</code> を設定（<code>task_cpu / task_memory / desired_count</code>）</td>
</tr>
<tr>
<td>④</td>
<td><code>cdk-ecs-webapp/</code> 全体を GitHub に push</td>
</tr>
<tr>
<td>⑤</td>
<td><code>cdk deploy</code>（<strong>初回のみ手動</strong>）→ <code>my-EcsPipelineStack</code> 作成</td>
</tr>
<tr>
<td>⑥</td>
<td>CodePipeline → <code>my-cdk6-pipeline</code> が自動起動 → InfraDeploy（20分）完了まで待つ</td>
</tr>
<tr>
<td>⑦</td>
<td>ALB URL にアクセス（Tomcat のデフォルト画面）・ECS サービスが RUNNING を確認</td>
</tr>
<tr>
<td>⑧</td>
<td>OIDC プロバイダー登録 → IAM ロール作成 → GitHub Variables 設定 → workflow push</td>
</tr>
<tr>
<td>⑨</td>
<td><code>app/index.html</code> を変更して push → GitHub Actions が Docker build → ECR push → ECS deploy</td>
</tr>
<tr>
<td>⑩</td>
<td><code>ecs execute-command</code> でコンテナ内シェルに接続・環境変数や WAR を確認</td>
</tr>
<tr>
<td>⑪</td>
<td><code>update-service --desired-count 2</code> でスケールアウト → 1 に戻してスケールイン</td>
</tr>
<tr>
<td>⑫</td>
<td><code>delete-stack my-Deploy-WebappStack</code> → <code>delete-stack my-EcsPipelineStack</code> → IAM ロール・SSM 削除</td>
</tr>
</tbody>
</table>
<p>EC2 + CodeDeploy から ECS Fargate + GitHub Actions への移行で、<strong>EC2 の OS 管理・エージェント管理から解放</strong>され、<code>Dockerfile</code> 1ファイルにデプロイ設定が集約されることを体験できました。</p>
<p>コンソールで3層構成を視覚的に学びたい場合は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>、CDK の基本は <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン（cdk-alb-ec2-rds）</a>、インフラ CI/CD は <a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines ハンズオン</a>、アプリ CI/CD は <a href="https://caymezon.com/aws-handson-cdk-webapp-pipeline/">CDK + CodeDeploy ハンズオン</a>、ASG でのオートスケーリングは <a href="https://caymezon.com/aws-handson-cdk-asg-webapp/">CDK + ASG ハンズオン</a> を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cdk-ecs-webapp/">CDK + ECS Fargate で Webアプリをコンテナ化する【EC2 からコンテナへの移行体験】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cdk-ecs-webapp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CDK + Auto Scaling Group で Webアプリをオートスケーリング構成にする【CPU負荷でスケールアウト体験】</title>
		<link>https://caymezon.com/aws-handson-cdk-asg-webapp/</link>
					<comments>https://caymezon.com/aws-handson-cdk-asg-webapp/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Thu, 30 Apr 2026 10:14:30 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[ASG]]></category>
		<category><![CDATA[Auto Scaling Group]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[CDK Pipelines]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[CodeBuild]]></category>
		<category><![CDATA[CodeDeploy]]></category>
		<category><![CDATA[CodePipeline]]></category>
		<category><![CDATA[GitOps]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Launch Template]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Session Manager]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[SSM]]></category>
		<category><![CDATA[WAR]]></category>
		<category><![CDATA[スケーリング]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[初心者]]></category>
		<category><![CDATA[自動デプロイ]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20409</guid>

					<description><![CDATA[<p>はじめに 「EC2 が 1台では負荷が集中したときに対応できない」「障害で EC2 が落ちたら手動で復旧しなければならない」——これを解決するのが Auto Scaling Group（ASG） です。 この記事では、C [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cdk-asg-webapp/">CDK + Auto Scaling Group で Webアプリをオートスケーリング構成にする【CPU負荷でスケールアウト体験】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2><span id="toc1">はじめに</span></h2>
<p>「EC2 が 1台では負荷が集中したときに対応できない」「障害で EC2 が落ちたら手動で復旧しなければならない」——これを解決するのが <strong>Auto Scaling Group（ASG）</strong> です。</p>
<p>この記事では、<a href="https://caymezon.com/aws-handson-cdk-webapp-pipeline/">CDK Pipelines + CodeDeploy ハンズオン</a>で構築した <strong>単一 EC2 構成</strong>を、<strong>Auto Scaling Group + Launch Template</strong> にアップグレードします。CPU 負荷をかけて実際にスケールアウトを体験し、新しいインスタンスへ <strong>CodeDeploy が自動で WAR をデプロイ</strong>する様子を確認します。</p>
<pre><code class="language-plaintext">GitHub リポジトリ
  ↓ push（自動トリガー）
CodePipeline (my-cdk5-pipeline)
  ├── Source:        GitHub (CodeConnections 経由)
  ├── Build(Synth):  CodeBuild — cdk synth
  ├── Mutate:        パイプライン自己更新（セルフミューテーション）
  ├── InfraDeploy:   CloudFormation — VPC / ASG / RDS / ALB
  └── BuildAndDeployApp: ← post step
        CodeBuild: mvn package → webapp.war 生成
        CodeDeploy: ASG の全 Tomcat に一括デプロイ
                ↓
       [ALB: my-cdk5-alb]
                ↓
       ┌─────────────────────────────┐
       │  Auto Scaling Group (ASG)   │
       │  my-cdk5-asg                │
       │  ・最小 1台 / 最大 3台      │
       │  ・CPU 60% 超でスケールアウト│
       │  ├── EC2: Tomcat + WAR      │
       │  └── EC2: Tomcat + WAR（追加分）│
       └─────────────────────────────┘
                ↓
       [RDS: my-cdk5-rds-mysql]  ← MySQL 8.0</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li><code>git push</code> するだけで ASG の全インスタンスへ WAR が自動デプロイされる</li>
<li>CPU に負荷をかけてスケールアウトを体験する</li>
<li>スケールアウト後の新インスタンスへ CodeDeploy が<strong>自動で WAR をデプロイ</strong>する（ライフサイクルフック）</li>
<li>キーペア不要で <strong>SSM Session Manager</strong> からブラウザ経由でインスタンスに接続する</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cdk-webapp-pipeline/">CDK Pipelines + CodeDeploy ハンズオン（cdk-webapp-pipeline）</a> の発展記事です。</strong><br />CodeDeploy・CDK Pipelines の基本を体験済みの方向けです。コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-asg-webapp">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![CodePipelineとASGが動作している画面](images/pipeline-asg-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>Auto Scaling Group（ASG）</strong></td>
<td>EC2 インスタンスのグループを自動管理するサービス。負荷に応じてインスタンス数を増減させ、障害時は自動で置き換える</td>
</tr>
<tr>
<td><strong>Launch Template</strong></td>
<td>ASG が新しいインスタンスを起動する際に使う設定テンプレート（AMI・インスタンスタイプ・UserData 等）</td>
</tr>
<tr>
<td><strong>ターゲット追跡ポリシー</strong></td>
<td>メトリクス（CPU 使用率 60%）を目標値として維持するよう自動スケーリングするポリシー</td>
</tr>
<tr>
<td><strong>ASG ライフサイクルフック</strong></td>
<td>スケールアウト時に新しいインスタンスへ CodeDeploy が最新 WAR を自動デプロイする仕組み</td>
</tr>
<tr>
<td><strong>SSM Session Manager</strong></td>
<td>キーペアなしで EC2 インスタンスにブラウザからセキュアに接続できるサービス</td>
</tr>
<tr>
<td><strong>stress-ng</strong></td>
<td>CPU・メモリ等に人工的な負荷をかけるツール。スケーリングのトリガーとして使用する</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">cdk-webapp-pipeline との比較</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>cdk-webapp-pipeline</th>
<th>cdk-asg-webapp</th>
</tr>
</thead>
<tbody>
<tr>
<td>EC2 管理方法</td>
<td>単一 Instance リソース</td>
<td><strong>Auto Scaling Group + Launch Template</strong></td>
</tr>
<tr>
<td>EC2 台数</td>
<td>固定 1台</td>
<td><strong>最小 1台〜最大 3台（自動増減）</strong></td>
</tr>
<tr>
<td>スケーリング</td>
<td>なし</td>
<td><strong>CPU 60% 超でスケールアウト</strong></td>
</tr>
<tr>
<td>自己回復</td>
<td>なし（手動対応）</td>
<td><strong>ALB ヘルスチェックで異常インスタンスを自動置換</strong></td>
</tr>
<tr>
<td>接続方法</td>
<td>SSH（キーペア必要）</td>
<td><strong>SSM Session Manager（キーペア不要）</strong></td>
</tr>
<tr>
<td>CodeDeploy ターゲット</td>
<td>名前タグで EC2 を指定</td>
<td><strong>ASG を直接指定</strong></td>
</tr>
<tr>
<td>スケールアウト時</td>
<td>新 EC2 に WAR なし</td>
<td><strong>ライフサイクルフックで自動デプロイ</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">ファイル構成と役割</span></h2>
<pre><code class="language-plaintext">cdk-asg-webapp/
├── app.py                     ← CDK アプリ エントリポイント
├── cdk.json                   ← CDK 設定（key_name/my_ip なし・asg_* 追加）
├── appspec.yml                ← CodeDeploy: WAR 配置手順とライフサイクルフック
├── scripts/
│   ├── stop_tomcat.sh
│   ├── cleanup.sh
│   └── start_tomcat.sh
├── app/                       ← Spring Boot Webアプリ（Java）
├── pipeline/
│   └── pipeline_stack.py      ← CodePipeline + post step（ASG 対応）
├── stages/
│   └── deploy_stage.py        ← Stage（WebappStack を内包）
├── stacks/
│   └── webapp_stack.py        ← インフラスタック（ASGName を CfnOutput で公開）
└── components/
    └── webapp_construct.py    ← ASG ベースの L3 Construct</code></pre>
<table>
<thead>
<tr>
<th>ファイル</th>
<th>cdk-webapp-pipeline との主な違い</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>webapp_construct.py</code></td>
<td><code>ec2.Instance</code> → <code>autoscaling.AutoScalingGroup</code> + <code>ec2.LaunchTemplate</code></td>
</tr>
<tr>
<td><code>webapp_stack.py</code></td>
<td><code>InstanceId</code> 出力を廃止 → <code>ASGName</code> を CfnOutput で公開</td>
</tr>
<tr>
<td><code>pipeline_stack.py</code></td>
<td><code>INSTANCE_ID</code> → <code>ASG_NAME</code>（インスタンス待機の方法が変わる）</td>
</tr>
</tbody>
</table>
<p>詳細なコードは <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-asg-webapp">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>Auto Scaling Group + EC2</strong>（t2.micro）</td>
<td>APサーバ（Tomcat + Spring Boot）を自動スケール</td>
<td>インスタンス分の EC2 料金</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>CodePipeline</strong></td>
<td>CI/CD パイプライン管理</td>
<td>月1パイプラインまで無料枠あり</td>
</tr>
<tr>
<td><strong>CodeBuild</strong></td>
<td>cdk synth・Maven ビルドを実行するビルド環境</td>
<td>月100分まで無料枠あり</td>
</tr>
<tr>
<td><strong>CodeDeploy</strong></td>
<td>ASG の全インスタンスへの WAR デプロイ</td>
<td>EC2 へのデプロイは<strong>無料</strong></td>
</tr>
<tr>
<td><strong>S3</strong></td>
<td>パイプラインのアーティファクト・デプロイバンドル置き場</td>
<td>無料枠あり</td>
</tr>
<tr>
<td><strong>SSM</strong></td>
<td>Session Manager による EC2 接続・Parameter Store（DBパスワード）</td>
<td>Session Manager 無料・スタンダード層無料</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>各サービスの権限</td>
<td>無料</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>: ALBは無料枠がありません。スケールアウト中はEC2が複数台起動するため料金も増加します。ハンズオン後は必ずリソースを削除してください。</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>Java 17</td>
<td><code>java -version</code></td>
</tr>
<tr>
<td>Maven</td>
<td><code>mvn -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>
<tr>
<td>Git</td>
<td><code>git --version</code></td>
</tr>
</tbody>
</table>
<p>AWS 認証確認:</p>
<pre><code class="language-cmd">aws sts get-caller-identity</code></pre>
<p>CDK Bootstrap 確認:</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>
<blockquote>
<p><strong>SSH キーペアは不要です</strong>。このハンズオンでは SSM Session Manager を使って EC2 に接続するため、キーペアの作成・設定は必要ありません。</p>
</blockquote>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">① プロジェクトのセットアップ（Python 仮想環境）
      ↓
② GitHub との接続を作成する（CodeConnections）
      ↓
③ cdk.json を設定する
      ↓
④ コードを全て GitHub にプッシュする（重要！）
      ↓
⑤ パイプラインスタックをデプロイする（初回のみ手動）
      ↓
⑥ パイプラインの実行状況を確認する
      ↓
⑦ アプリへアクセスする
      ↓
⑧ Auto Scaling を体験する（CPU 負荷 → スケールアウト → 自動デプロイ）
      ↓
⑨ CI/CD を体験する（アプリ変更を push で自動反映）
      ↓
⑩ リソース削除</code></pre>
<hr>
<h2><span id="toc8">① プロジェクトのセットアップ</span></h2>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-asg-webapp

python -m venv .venv

.venv\Scripts\activate

pip install -r requirements.txt</code></pre>
<p>プロンプトの先頭に <code>(.venv)</code> が表示されれば仮想環境が有効になっています。</p>
<blockquote>
<p><strong>重要</strong>: CDK コマンドはすべてこの仮想環境が有効な状態で実行する必要があります。<br />ターミナルを開き直した際は必ず最初に以下を実行してください。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-asg-webapp
.venv\Scripts\activate</code></pre>
</blockquote>
<hr>
<h2><span id="toc9">② GitHub との接続を作成する（CodeConnections）</span></h2>
<blockquote>
<p><strong>cdk-webapp-pipeline を実施済みの場合</strong>: 同じリポジトリ <code>aws-learning-projects</code> を扱うため、<br />既存の接続（<code>my-github-connection</code>）をそのまま<strong>再利用</strong>できます。<br /><strong>② はスキップして、ARN の確認（②-4）から始めてください。</strong></p>
</blockquote>
<p>CodePipeline が GitHub リポジトリを監視するために、<strong>GitHub との接続（CodeConnections）</strong> を作成します。</p>
<blockquote>
<p><strong>重要</strong>: AWS コンソールと GitHub の操作は<strong>同じブラウザ</strong>で行ってください。</p>
</blockquote>
<h3><span id="toc10">接続を作成する</span></h3>
<ol>
<li>AWS コンソール検索（<code>Alt+S</code>）で <code>CodePipeline</code> と入力 → <strong>CodePipeline</strong> を開く</li>
<li>左サイドバー → <strong>「設定」</strong> → <strong>「接続」</strong></li>
<li>リージョンが <strong>東京（ap-northeast-1）</strong> になっていることを確認する</li>
<li>「<strong>接続を作成</strong>」をクリック</li>
<li>プロバイダー: <strong>GitHub</strong> を選択</li>
<li>接続名: <code>my-github-connection</code>（任意）</li>
<li>「<strong>GitHub に接続する</strong>」をクリック</li>
</ol>
<h3><span id="toc11">GitHub App をインストールする</span></h3>
<ol start="8">
<li>「<strong>Authorize</strong>」をクリック → AWS コンソールの「GitHub 接続設定」画面に自動で戻る</li>
<li>「<strong>新しいアプリをインストールする</strong>」をクリック</li>
<li><strong>「Only select repositories」</strong> を選択 → <code>aws-learning-projects</code> を追加</li>
<li>「<strong>Install & Authorize</strong>」をクリック（メール認証コードが求められる場合は入力）</li>
<li>AWS コンソールに自動でリダイレクトされる → 「<strong>接続</strong>」ボタンをクリック</li>
</ol>
<h3><span id="toc12">接続 ARN を確認する</span></h3>
<p>接続一覧から接続の ARN をコピーします。</p>
<pre><code class="language-plaintext">arn:aws:codeconnections:ap-northeast-1:123456789012:connection/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</code></pre>
<blockquote>
<p>ステータスが <strong>「利用可能」</strong> になっていることを確認してから次に進んでください。</p>
</blockquote>
<p><strong>控えておく情報</strong>: 接続 ARN（<code>arn:aws:codeconnections:...</code>）</p>
<hr>
<h2><span id="toc13">③ cdk.json の設定</span></h2>
<p><code>cdk.json</code> の <code>context</code> セクションを自分の環境に合わせて編集します。</p>
<pre><code class="language-json">{
  "context": {
    "employee_id": "my",
    "db_password": "Handson1234!",
    "tomcat_version": "10.1.28",
    "asg_min_capacity": 1,
    "asg_max_capacity": 3,
    "asg_desired_capacity": 1,
    "github_owner": "your-github-username",
    "github_repo": "aws-learning-projects",
    "github_branch": "main",
    "connection_arn": "arn:aws:codeconnections:ap-northeast-1:..."
  }
}</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-cdk5-xxx</code> という名前になる）</td>
</tr>
<tr>
<td><code>asg_min_capacity</code></td>
<td><code>1</code></td>
<td>ASG の最小インスタンス数（スケールインの下限）</td>
</tr>
<tr>
<td><code>asg_max_capacity</code></td>
<td><code>3</code></td>
<td>ASG の最大インスタンス数（スケールアウトの上限）</td>
</tr>
<tr>
<td><code>asg_desired_capacity</code></td>
<td><code>1</code></td>
<td>初期起動インスタンス数</td>
</tr>
<tr>
<td><code>github_owner</code></td>
<td>GitHub ユーザー名</td>
<td>リポジトリのオーナー名</td>
</tr>
<tr>
<td><code>connection_arn</code></td>
<td>②で確認した ARN</td>
<td>CodeConnections の接続 ARN</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong><code>key_name</code>・<code>my_ip</code> は不要です</strong>。SSH の代わりに SSM Session Manager を使うため、キーペアやSSH許可IPの設定は必要ありません。</p>
</blockquote>
<hr>
<h2><span id="toc14">④ コードを GitHub にプッシュする（重要！）</span></h2>
<blockquote>
<p><code>cdk deploy</code> の前に必ず<strong>プロジェクト全体</strong>を push してください。<br />パイプラインが <code>cdk synth</code>・Maven ビルドを実行する際、GitHub から取得したコードを使うためです。</p>
</blockquote>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects

git add cdk-asg-webapp/
git commit -m "feat: cdk-asg-webapp ハンズオンを追加"
git push origin main</code></pre>
<hr>
<h2><span id="toc15">⑤ パイプラインスタックのデプロイ（初回のみ）</span></h2>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-asg-webapp

.venv\Scripts\activate

cdk synth

cdk deploy my-AsgPipelineStack</code></pre>
<p>「Do you wish to deploy these changes?」に <code>y</code> を入力します。</p>
<blockquote>
<p><strong>所要時間</strong>: パイプラインスタック自体のデプロイは 3〜5 分。その後パイプラインが自動起動します。</p>
</blockquote>
<p>デプロイ完了後、CodePipeline が自動起動します:</p>
<table>
<thead>
<tr>
<th>ステージ</th>
<th>内容</th>
<th>所要時間</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>GitHub から最新コードを取得</td>
<td>〜1分</td>
</tr>
<tr>
<td><strong>Build (Synth)</strong></td>
<td><code>cdk synth</code> でテンプレート生成</td>
<td>〜3分</td>
</tr>
<tr>
<td><strong>UpdatePipeline</strong></td>
<td>パイプライン自己更新（セルフミューテーション）</td>
<td>〜2分</td>
</tr>
<tr>
<td><strong>Deploy > InfraDeploy</strong></td>
<td>CloudFormation で VPC / ASG / RDS / ALB を構築</td>
<td>〜15分</td>
</tr>
<tr>
<td><strong>Deploy > BuildAndDeployApp</strong></td>
<td>Maven ビルド → CodeDeploy で WAR デプロイ</td>
<td>〜10分</td>
</tr>
</tbody>
</table>
<blockquote>
<p>RDS の起動に時間がかかるため、InfraDeploy は 10〜20 分かかることがあります。</p>
</blockquote>
<hr>
<h2><span id="toc16">⑥ パイプラインの実行状況を確認する</span></h2>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> を開く</li>
<li><code>my-cdk5-pipeline</code> を選択</li>
<li>各ステージが <strong>「成功」</strong> になるまで待つ</li>
</ol>
<h3><span id="toc17">CloudFormation スタックの命名規則</span></h3>
<pre><code class="language-plaintext">{StageName}-{StackName}</code></pre>
<table>
<thead>
<tr>
<th>要素</th>
<th>このハンズオンでの値</th>
</tr>
</thead>
<tbody>
<tr>
<td>StageName</td>
<td><code>my-Deploy</code></td>
</tr>
<tr>
<td>StackName</td>
<td><code>WebappStack</code></td>
</tr>
<tr>
<td>結果</td>
<td><strong><code>my-Deploy-WebappStack</code></strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p><code>my-AsgPipelineStack</code>（パイプライン）と <code>my-Deploy-WebappStack</code>（インフラ）の <strong>2つが別スタック</strong>として CloudFormation に表示されます。</p>
</blockquote>
<h3><span id="toc18">CloudFormation Outputs の確認</span></h3>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs"</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CodePipelineの全ステージが成功している画面](images/pipeline-success.jpg) --></p>
<hr>
<h2><span id="toc19">⑦ アプリへアクセスする</span></h2>
<p><code>BuildAndDeployApp</code> ステージが完了したら、ALB 経由でアプリにアクセスします。</p>
<pre><code class="language-plaintext">http://&lt;ALBEndpoint&gt;/webapp/</code></pre>
<blockquote>
<p><code>http://&lt;ALBEndpoint&gt;/</code>（ルートパス）では Tomcat のデフォルト画面が表示されます。<code>/webapp/</code> が必要です。</p>
</blockquote>
<p>ALB の DNS 名を CLI で確認する場合:</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs[?OutputKey=='ALBEndpoint'].OutputValue" ^
  --output text</code></pre>
<h3><span id="toc20">ASG インスタンスを確認する</span></h3>
<p>AWS コンソール → <strong>EC2</strong> → <strong>Auto Scaling グループ</strong> → <code>my-cdk5-asg</code></p>
<ul>
<li><strong>インスタンス</strong> タブを開く</li>
<li>1台の EC2 が <code>InService</code> になっていることを確認する</li>
</ul>
<p><!-- ![アプリ画面とASGインスタンス一覧が表示されている画面](images/webapp-asg-access.jpg) --></p>
<hr>
<h2><span id="toc21">⑧ Auto Scaling を体験する</span></h2>
<p>ASG のスケールアウト・スケールインを実際に体験します。CPU 使用率が <strong>60%</strong> を超えると自動でインスタンスが追加されます。</p>
<h3><span id="toc22">現在の ASG 状態を確認する</span></h3>
<pre><code class="language-cmd">aws autoscaling describe-auto-scaling-groups ^
  --auto-scaling-group-names my-cdk5-asg ^
  --region ap-northeast-1 ^
  --query "AutoScalingGroups[0].{Desired:DesiredCapacity,Min:MinSize,Max:MaxSize,Instances:Instances[*].{Id:InstanceId,State:LifecycleState}}"</code></pre>
<p>出力例（初期状態: 1台が <code>InService</code>）:</p>
<pre><code class="language-json">{
    "Desired": 1,
    "Min": 1,
    "Max": 3,
    "Instances": [{ "Id": "i-0abc123...", "State": "InService" }]
}</code></pre>
<h3><span id="toc23">SSM Session Manager でインスタンスに接続する</span></h3>
<blockquote>
<p>キーペアは不要です。AWS コンソールから直接ブラウザでターミナルを開けます。</p>
</blockquote>
<ol>
<li><strong>EC2</strong> → <strong>インスタンス</strong> → 対象インスタンスを選択</li>
<li>「<strong>接続</strong>」ボタンをクリック</li>
<li>「<strong>Session Manager</strong>」タブ → 「<strong>接続</strong>」をクリック</li>
</ol>
<p>ブラウザでターミナルが開きます。</p>
<p>CLI から接続する場合:</p>
<pre><code class="language-cmd">rem インスタンス ID を取得
aws autoscaling describe-auto-scaling-groups ^
  --auto-scaling-group-names my-cdk5-asg ^
  --region ap-northeast-1 ^
  --query "AutoScalingGroups[0].Instances[0].InstanceId" ^
  --output text

rem 接続（Session Manager Plugin が必要）
aws ssm start-session ^
  --target i-0abc123def456789 ^
  --region ap-northeast-1</code></pre>
<h3><span id="toc24">CPU 負荷をかける（スケールアウトをトリガーする）</span></h3>
<p>インスタンスに接続したターミナルで実行します:</p>
<pre><code class="language-bash">cd /tmp &amp;&amp; stress-ng --cpu 2 --timeout 300s &amp;
echo "CPU stress started (PID: $!)"</code></pre>
<h3><span id="toc25">スケールアウトを監視する</span></h3>
<ol>
<li>AWS コンソール → <strong>CloudWatch</strong> → <strong>メトリクス</strong> → <strong>EC2</strong> → <strong>Auto Scaling グループ別</strong></li>
<li><code>my-cdk5-asg</code> の <code>CPUUtilization</code> を確認</li>
<li>CPU が 60% を超えると、約 1〜3 分後にスケールアウトが始まる</li>
</ol>
<p>ASG のインスタンス数を 10秒ごとに確認:</p>
<pre><code class="language-cmd">:loop
aws autoscaling describe-auto-scaling-groups ^
  --auto-scaling-group-names my-cdk5-asg ^
  --region ap-northeast-1 ^
  --query "AutoScalingGroups[0].Instances[*].{Id:InstanceId,State:LifecycleState}" ^
  --output table
timeout /t 10
goto loop</code></pre>
<p>しばらく待つと 2台目のインスタンスが起動し <code>InService</code> になります。</p>
<h3><span id="toc26">スケールアウト後の CodeDeploy 自動デプロイを確認する</span></h3>
<p>新しいインスタンスが起動すると、<strong>CodeDeploy が自動的に最新の WAR をデプロイします</strong>。</p>
<p>AWS コンソール → <strong>CodeDeploy</strong> → <code>my-cdk5-deploy-app</code><br />→ デプロイグループ → デプロイ履歴</p>
<p>新しいデプロイが自動で実行されていることを確認します。パイプラインを手動実行しなくても、ライフサイクルフックにより新インスタンスへ最新 WAR が自動デプロイされます。</p>
<p><!-- ![ASGインスタンスが2台になり、CodeDeployデプロイが自動実行された画面](images/asg-scaleout-codedeploy.jpg) --></p>
<h3><span id="toc27">スケールインを確認する</span></h3>
<p>CPU 負荷が終了（300秒後）すると CPU 使用率が下がり、スケールインが始まります。</p>
<blockquote>
<p>ターゲット追跡ポリシーのデフォルトのスケールイン保護時間は <strong>300秒</strong>。<br />負荷終了後、約 5〜10 分でインスタンス数が 1台に戻ります。</p>
</blockquote>
<p>途中で手動停止する場合:</p>
<pre><code class="language-bash">pkill stress-ng</code></pre>
<hr>
<h2><span id="toc28">⑨ CI/CD を体験する（アプリ変更を push で自動反映）</span></h2>
<p>アプリのコードを変更して push するだけで、ASG の全インスタンスへ自動デプロイされることを確認します。</p>
<h3><span id="toc29">アプリの HTML を変更する</span></h3>
<p><code>app/src/main/resources/templates/index.html</code> を開き、以下の行を変更します:</p>
<p>変更前:</p>
<pre><code class="language-html">&lt;h1&gt;Spring Boot + RDS Item Manager&lt;/h1&gt;</code></pre>
<p>変更後:</p>
<pre><code class="language-html">&lt;h1&gt;Spring Boot + RDS Item Manager (ASG)&lt;/h1&gt;</code></pre>
<h3><span id="toc30">コミット＆プッシュ</span></h3>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add cdk-asg-webapp/app/src/main/resources/templates/index.html
git commit -m "feat: update webapp UI for ASG"
git push</code></pre>
<h3><span id="toc31">パイプラインの自動起動を確認する</span></h3>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> → <code>my-cdk5-pipeline</code></li>
<li>push 後、数十秒以内にパイプラインが<strong>自動起動</strong>することを確認</li>
<li><code>BuildAndDeployApp</code> ステージが完了したらブラウザで <code>/webapp/</code> を更新する</li>
</ol>
<p>「(ASG)」が付いた見出しが表示されれば、ASG 構成での CI/CD が正常に動作しています。</p>
<blockquote>
<p><strong>注意</strong>: <code>InfraDeploy</code> ステージはインフラに変更がない場合は「変更なし」でスキップされます。</p>
</blockquote>
<hr>
<h2><span id="toc32">⑩ リソース削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p>ASG を含むスタックを削除すると、全インスタンスが自動終了されます。</p>
</blockquote>
<blockquote>
<p><strong>削除順序が重要です</strong>。インフラスタック（<code>my-Deploy-WebappStack</code>）を<strong>先に</strong>削除してから、パイプラインスタック（<code>my-AsgPipelineStack</code>）を削除します。</p>
</blockquote>
<h3><span id="toc33">1. インフラスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1

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

echo WebappStack 削除完了</code></pre>
<blockquote>
<p>RDS の削除に 10〜15 分かかります。<code>wait</code> コマンドが完了するまでそのまま待ってください。</p>
</blockquote>
<h3><span id="toc34">2. パイプラインスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-AsgPipelineStack ^
  --region ap-northeast-1

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

echo AsgPipelineStack 削除完了</code></pre>
<h3><span id="toc35">3. SSM パラメータを削除する</span></h3>
<pre><code class="language-cmd">aws ssm delete-parameter ^
  --name "/my/cdk5/db-password" ^
  --region ap-northeast-1</code></pre>
<h3><span id="toc36">4. 仮想環境の終了</span></h3>
<pre><code class="language-cmd">deactivate</code></pre>
<h3><span id="toc37">5. GitHub との接続を削除する（オプション）</span></h3>
<p>次のハンズオンでも使う場合はそのままでよいです。</p>
<p><strong>CodePipeline → 設定 → 接続 → <code>my-github-connection</code> → 「削除」</strong></p>
<h3><span id="toc38">削除確認</span></h3>
<pre><code class="language-cmd">aws cloudformation list-stacks ^
  --region ap-northeast-1 ^
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE ^
  --query "StackSummaries[?contains(StackName, 'my-')].StackName"</code></pre>
<p>空のリスト <code>[]</code> が返れば削除完了です。</p>
<h3><span id="toc39">削除されるリソース一覧</span></h3>
<table>
<thead>
<tr>
<th>リソース</th>
<th>削除方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>ALB・ASG・EC2（全インスタンス）・RDS・VPC・S3（アーティファクト）・CodeDeploy</td>
<td><code>delete-stack my-Deploy-WebappStack</code> で削除</td>
</tr>
<tr>
<td>CodePipeline・CodeBuild</td>
<td><code>delete-stack my-AsgPipelineStack</code> で削除</td>
</tr>
<tr>
<td>SSM パラメータ（DBパスワード）</td>
<td><code>aws ssm delete-parameter</code> で手動削除</td>
</tr>
<tr>
<td>GitHub との接続（CodeConnections）</td>
<td>CodePipeline コンソールから手動削除</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc40">Auto Scaling のポイント解説</span></h2>
<h3><span id="toc41">ASG ライフサイクルフックで新インスタンスへ自動デプロイ</span></h3>
<p>CDK で <code>codedeploy.ServerDeploymentGroup(auto_scaling_groups=[self.asg])</code> と指定すると、CodeDeploy が ASG にライフサイクルフックを自動設定します。</p>
<pre><code class="language-plaintext">スケールアウト発生
  ↓
ASG: 新しいインスタンスを Launch Template から起動
  ↓
EC2 UserData: Java・Tomcat・CodeDeploy エージェントをインストール
  ↓
ライフサイクルフック: CodeDeploy が最新 WAR を自動デプロイ
  ↓
ALB ターゲットグループに追加（InService）</code></pre>
<p>これにより、スケールアウト後の新インスタンスでも<strong>ユーザーは何もしなくて済みます</strong>。</p>
<h3><span id="toc42">cdk-webapp-pipeline → cdk-asg-webapp の変化</span></h3>
<pre><code class="language-plaintext">cdk-webapp-pipeline               cdk-asg-webapp
─────────────────────────         ─────────────────────────
EC2 Instance（固定 1台）           Auto Scaling Group（1〜3台）
  ↓                                 ↓
SSH（キーペア必要）                 SSM Session Manager（キーペア不要）
  ↓                                 ↓
CodeDeploy: 名前タグで指定          CodeDeploy: ASG を直接指定
  ↓                                 ↓
スケールアウト不可                  CPU 60% 超で自動スケールアウト
  ↓                                 ↓
新インスタンスに WAR なし          ライフサイクルフックで自動 WAR デプロイ</code></pre>
<hr>
<h2><span id="toc43">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td>Source ステージが「失敗」</td>
<td>GitHub との接続が「保留中」</td>
<td>CodePipeline → 設定 → 接続 → 承認して「利用可能」にする</td>
</tr>
<tr>
<td>BuildAndDeployApp が失敗</td>
<td>Java ソースが GitHub に push されていない</td>
<td><code>git add cdk-asg-webapp/</code> で全ファイルを push してから再実行</td>
</tr>
<tr>
<td>CodeDeploy が失敗する</td>
<td>UserData が完了していない</td>
<td>ASG 起動後 2〜3 分待ってから再デプロイ</td>
</tr>
<tr>
<td>スケールアウトが起きない</td>
<td>CPU が 60% に達していない</td>
<td><code>stress-ng --cpu 0 --timeout 300s &amp;</code> でコア全体に負荷をかける</td>
</tr>
<tr>
<td>スケールインが起きない</td>
<td>スケールイン保護時間（300秒）が経過していない</td>
<td>CPU 負荷停止後 5〜10 分待つ</td>
</tr>
<tr>
<td><code>/webapp/</code> で 404</td>
<td>WAR デプロイ未完了</td>
<td><code>BuildAndDeployApp</code> ステージが完了するまで待つ</td>
</tr>
<tr>
<td>ALB ヘルスチェック失敗</td>
<td>Tomcat 起動中</td>
<td>SSM Session Manager で <code>sudo cat /opt/tomcat/logs/catalina.out</code> を確認</td>
</tr>
</tbody>
</table>
<p>CodeDeploy 失敗時のログ確認:</p>
<p><strong>AWS コンソール → CodeDeploy → <code>my-cdk5-deploy-app</code> → デプロイグループ → デプロイ履歴 → 失敗したデプロイ → 「ログの表示」</strong></p>
<hr>
<h2><span id="toc44">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>Python 仮想環境のセットアップ</td>
</tr>
<tr>
<td>②</td>
<td>CodeConnections で GitHub との接続を作成・承認（cdk-webapp-pipeline 実施済みなら再利用）</td>
</tr>
<tr>
<td>③</td>
<td><code>cdk.json</code> を設定（<code>key_name</code>・<code>my_ip</code> 不要。<code>asg_min/max/desired_capacity</code> を確認）</td>
</tr>
<tr>
<td>④</td>
<td><strong><code>cdk-asg-webapp/</code> 全体</strong>を GitHub に push</td>
</tr>
<tr>
<td>⑤</td>
<td><code>cdk deploy my-AsgPipelineStack</code>（<strong>初回のみ手動</strong>）</td>
</tr>
<tr>
<td>⑥</td>
<td>CodePipeline が自動起動 → Synth → Mutate → InfraDeploy（15〜20 分）→ BuildAndDeployApp（10 分）</td>
</tr>
<tr>
<td>⑦</td>
<td><code>http://&lt;ALBEndpoint&gt;/webapp/</code> でアクセス確認・ASG インスタンス <code>InService</code> を確認</td>
</tr>
<tr>
<td>⑧</td>
<td><code>stress-ng</code> で CPU 負荷 → スケールアウト → CodeDeploy が新インスタンスへ自動デプロイ</td>
</tr>
<tr>
<td>⑨</td>
<td>HTML を変更して push → パイプライン自動起動 → 変更を確認（ASG 全インスタンスに反映）</td>
</tr>
<tr>
<td>⑩</td>
<td><code>delete-stack my-Deploy-WebappStack</code> → <code>delete-stack my-AsgPipelineStack</code> の順で削除</td>
</tr>
</tbody>
</table>
<p>ASG を導入することで「<strong>単一障害点の排除・自動スケーリング・スケールアウト時の自動デプロイ</strong>」を一度に体験できました。</p>
<p>コンソールで3層構成を視覚的に学びたい場合は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>、CDK の基本は <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン（cdk-alb-ec2-rds）</a>、カスタム Construct は <a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">CDK カスタム Construct ハンズオン</a>、インフラ CI/CD は <a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines ハンズオン</a>、アプリ CI/CD は <a href="https://caymezon.com/aws-handson-cdk-webapp-pipeline/">CDK + CodeDeploy ハンズオン</a> を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cdk-asg-webapp/">CDK + Auto Scaling Group で Webアプリをオートスケーリング構成にする【CPU負荷でスケールアウト体験】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cdk-asg-webapp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CDK Pipelines で Spring Boot を CI/CD 自動デプロイする【CodeDeploy で WAR ホットデプロイ】</title>
		<link>https://caymezon.com/aws-handson-cdk-webapp-pipeline/</link>
					<comments>https://caymezon.com/aws-handson-cdk-webapp-pipeline/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Wed, 29 Apr 2026 04:57:53 +0000</pubDate>
				<category><![CDATA[AWS Basic]]></category>
		<category><![CDATA[Cloud & Infra]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[appspec.yml]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[CDK Pipelines]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[CodeBuild]]></category>
		<category><![CDATA[CodeConnections]]></category>
		<category><![CDATA[CodeDeploy]]></category>
		<category><![CDATA[CodePipeline]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[GitOps]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Maven]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[WAR]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[ホットデプロイ]]></category>
		<category><![CDATA[初心者]]></category>
		<category><![CDATA[自動デプロイ]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20404</guid>

					<description><![CDATA[<p>はじめに 「アプリのコードを push するだけで EC2 の Tomcat に自動デプロイされる仕組みを作りたい」——これを実現するのが CDK Pipelines + CodeDeploy の組み合わせです。 この記 [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cdk-webapp-pipeline/">CDK Pipelines で Spring Boot を CI/CD 自動デプロイする【CodeDeploy で WAR ホットデプロイ】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2><span id="toc1">はじめに</span></h2>
<p>「アプリのコードを push するだけで EC2 の Tomcat に自動デプロイされる仕組みを作りたい」——これを実現するのが <strong>CDK Pipelines + CodeDeploy</strong> の組み合わせです。</p>
<p>この記事では、<a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines ハンズオン</a>で体験した <strong>インフラの CI/CD</strong> をさらに発展させ、<strong>Spring Boot（Java）アプリ（WAR ファイル）の自動デプロイ</strong>まで含めた完全な CI/CD パイプラインを構築します。GitHub に push するだけで Maven ビルドが走り、CodeDeploy が EC2/Tomcat に WAR をホットデプロイします。</p>
<pre><code class="language-plaintext">GitHub リポジトリ
  ↓ push（自動トリガー）
CodePipeline (my-cdk4-pipeline)
  ├── Source:        GitHub (CodeConnections 経由)
  ├── Build(Synth):  CodeBuild — cdk synth
  ├── Mutate:        パイプライン自己更新（セルフミューテーション）
  ├── InfraDeploy:   CloudFormation — VPC / EC2 / RDS / ALB / CodeDeploy 設定
  └── BuildAndDeployApp: ← post step
        CodeBuild: mvn package → webapp.war 生成
        CodeDeploy: EC2/Tomcat に WAR をホットデプロイ
                ↓
       [ALB: my-cdk4-alb]
                ↓
       [EC2: my-cdk4-ap-instance]  ← Tomcat + Spring Boot WAR
                ↓
       [RDS: my-cdk4-rds-mysql]    ← MySQL 8.0</code></pre>
<p><strong>このハンズオンで体験できること：</strong></p>
<ul>
<li><code>git push</code> するだけで Maven ビルド → CodeDeploy → WAR ホットデプロイが自動実行される</li>
<li>インフラ（CloudFormation）とアプリ（CodeDeploy）の両方が push で自動更新される</li>
<li>HTML を変更して push すると、数分でブラウザの表示が変わることを確認できる</li>
</ul>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines ハンズオン（cdk-pipelines）</a> の発展記事です。</strong><br />CDK Pipelines の基本（セルフミューテーション・Stage・CodeConnections）を体験済みの方向けです。コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-webapp-pipeline">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![CodePipelineの全ステージが成功し、アプリが表示されている画面](images/pipeline-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>CodeDeploy</strong></td>
<td>EC2 への Webアプリ（WAR/ZIP）のホットデプロイを管理するサービス。Tomcat を再起動せずに WAR を差し替えられる</td>
</tr>
<tr>
<td><strong>appspec.yml</strong></td>
<td>CodeDeploy のデプロイ手順を定義するファイル。ファイル配置先と各フェーズで実行するスクリプト（ライフサイクルフック）を記述する</td>
</tr>
<tr>
<td><strong>デプロイバンドル</strong></td>
<td><code>appspec.yml</code> + WAR ファイル + スクリプトを ZIP にまとめたもの。S3 に置いて CodeDeploy に渡す</td>
</tr>
<tr>
<td><strong>ライフサイクルフック</strong></td>
<td>CodeDeploy デプロイの各フェーズ（ApplicationStop・BeforeInstall・AfterInstall 等）で実行するシェルスクリプト</td>
</tr>
<tr>
<td><strong>env_from_cfn_outputs</strong></td>
<td>CDK Pipelines で CloudFormation スタックの出力値（CfnOutput）をパイプラインの後続ステップへ環境変数として渡す仕組み</td>
</tr>
<tr>
<td><strong>post step</strong></td>
<td>CDK Pipelines でステージ（Stage）のデプロイ完了後に続けて実行するステップ。ここでは InfraDeploy 後にアプリをデプロイする</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">cdk-pipelines との比較</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>cdk-pipelines</th>
<th>cdk-webapp-pipeline</th>
</tr>
</thead>
<tbody>
<tr>
<td>何を自動デプロイするか</td>
<td>インフラのみ（Tomcat は空で起動）</td>
<td><strong>インフラ ＋ Spring Boot WAR</strong></td>
</tr>
<tr>
<td>アプリコード</td>
<td>なし</td>
<td><code>app/</code> 配下に Java/Spring Boot</td>
</tr>
<tr>
<td>ビルドステップ</td>
<td><code>cdk synth</code> のみ</td>
<td><code>cdk synth</code> ＋ <strong><code>mvn package</code></strong></td>
</tr>
<tr>
<td>デプロイ手段</td>
<td>CloudFormation のみ</td>
<td>CloudFormation ＋ <strong>CodeDeploy</strong></td>
</tr>
<tr>
<td>push したとき</td>
<td>インフラが更新される</td>
<td>インフラ ＋ <strong>アプリも更新される</strong></td>
</tr>
<tr>
<td>主な学習テーマ</td>
<td>CDK Pipelines の基本</td>
<td><strong>CodeDeploy / WAR デプロイ / env_from_cfn_outputs</strong></td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc4">ファイル構成と役割</span></h2>
<pre><code class="language-plaintext">cdk-webapp-pipeline/
├── app.py                     ← CDK アプリ エントリポイント
├── cdk.json                   ← CDK 設定・Context 変数
├── appspec.yml                ← CodeDeploy: WAR 配置手順とライフサイクルフック
├── scripts/
│   ├── stop_tomcat.sh         ← CodeDeploy: ApplicationStop フック
│   ├── cleanup.sh             ← CodeDeploy: BeforeInstall フック
│   └── start_tomcat.sh        ← CodeDeploy: AfterInstall フック
├── app/                       ← Spring Boot Webアプリ（Java）
│   ├── pom.xml
│   └── src/main/...           ← ItemController / Item / ItemRepository / templates/
├── pipeline/
│   └── pipeline_stack.py      ← CodePipeline + post step 定義
├── stages/
│   └── webapp_stage.py        ← Stage（WebappStack を内包）
├── stacks/
│   └── webapp_stack.py        ← インフラスタック（CfnOutput で値を公開）
└── components/
    └── webapp_construct.py    ← カスタム L3 Construct（CodeDeploy 設定含む）</code></pre>
<table>
<thead>
<tr>
<th>ファイル</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>appspec.yml</code></td>
<td>CodeDeploy に「どこに WAR を置くか・何を実行するか」を指示する</td>
</tr>
<tr>
<td><code>scripts/*.sh</code></td>
<td>Tomcat の停止・クリーンアップ・起動をフェーズごとに実行する</td>
</tr>
<tr>
<td><code>pipeline_stack.py</code></td>
<td>InfraDeploy の <code>post</code> に <code>BuildAndDeployApp</code> ステップを追加。<code>env_from_cfn_outputs</code> でスタック出力を受け取る</td>
</tr>
<tr>
<td><code>webapp_stack.py</code></td>
<td>CodeDeploy の設定（アプリ名・デプロイグループ名）を <code>CfnOutput</code> で公開する</td>
</tr>
</tbody>
</table>
<p>詳細なコードは <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-webapp-pipeline">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 + Spring Boot）</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>CodePipeline</strong></td>
<td>CI/CD パイプライン管理</td>
<td>月1パイプラインまで無料枠あり</td>
</tr>
<tr>
<td><strong>CodeBuild</strong></td>
<td>cdk synth・Maven ビルドを実行するビルド環境</td>
<td>月100分まで無料枠あり</td>
</tr>
<tr>
<td><strong>CodeDeploy</strong></td>
<td>EC2 への WAR ホットデプロイ</td>
<td>EC2 へのデプロイは<strong>無料</strong></td>
</tr>
<tr>
<td><strong>S3</strong></td>
<td>パイプラインのアーティファクト・デプロイバンドル置き場</td>
<td>無料枠あり</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DBパスワードの安全な保管</td>
<td>スタンダード層は無料</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>各サービスの権限</td>
<td>無料</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>: ALBは無料枠がありません。ハンズオン後は必ずリソースを削除してください。</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>Java 17</td>
<td><code>java -version</code></td>
</tr>
<tr>
<td>Maven</td>
<td><code>mvn -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>
<tr>
<td>Git</td>
<td><code>git --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>
<blockquote>
<p><strong>GitHub アカウントが必要です</strong>。<code>aws-learning-projects</code> リポジトリに push できる状態にしておいてください。</p>
</blockquote>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（AWS CLI で実施）
      ↓
② プロジェクトのセットアップ（Python 仮想環境）
      ↓
③ GitHub との接続を作成する（CodeConnections）
      ↓
④ cdk.json を設定する
      ↓
⑤ コードを全て GitHub にプッシュする（重要！）
      ↓
⑥ パイプラインスタックをデプロイする（初回のみ手動）
      ↓
⑦ パイプラインの実行状況を確認する
      ↓
⑧ アプリへアクセスする（/webapp/ 必須）
      ↓
⑨ CI/CD を体験する（アプリ変更を push で自動反映）
      ↓
⑩ リソース削除</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-cdk4-key ^
  --query KeyMaterial ^
  --output text ^
  --region ap-northeast-1 &gt; %USERPROFILE%\.ssh\my-cdk4-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-webapp-pipeline

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 / destroy）はすべてこの仮想環境が有効な状態で実行する必要があります。<br />ターミナルを開き直した際は必ず最初に以下を実行してください。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-webapp-pipeline
.venv\Scripts\activate</code></pre>
</blockquote>
<hr>
<h2><span id="toc11">③ GitHub との接続を作成する（CodeConnections）</span></h2>
<blockquote>
<p><strong>cdk-pipelines を実施済みの場合</strong>: 同じリポジトリ <code>aws-learning-projects</code> を扱うため、<br />既存の接続（<code>my-github-connection</code>）をそのまま<strong>再利用</strong>できます。<br /><strong>③ はスキップして、ARN の確認（③-4）から始めてください。</strong></p>
</blockquote>
<p>CodePipeline が GitHub リポジトリを監視するために、<strong>GitHub との接続（CodeConnections）</strong> を作成します。</p>
<blockquote>
<p><strong>重要</strong>: AWS コンソールと GitHub の操作は<strong>同じブラウザ</strong>で行ってください。<br />別ブラウザで操作すると、AWS にリダイレクトされた際にデフォルトリージョン（バージニア北部等）でログインされる場合があります。</p>
</blockquote>
<h3><span id="toc12">接続を作成する</span></h3>
<ol>
<li>AWS コンソール検索（<code>Alt+S</code>）で <code>CodePipeline</code> と入力 → <strong>CodePipeline</strong> を開く</li>
<li>左サイドバー → <strong>「設定」</strong> → <strong>「接続」</strong></li>
<li>リージョンが <strong>東京（ap-northeast-1）</strong> になっていることを確認する</li>
<li>「<strong>接続を作成</strong>」をクリック</li>
<li>プロバイダー: <strong>GitHub</strong> を選択</li>
<li>接続名: <code>my-github-connection</code>（任意）</li>
<li>「<strong>GitHub に接続する</strong>」をクリック</li>
</ol>
<h3><span id="toc13">GitHub App をインストールする</span></h3>
<p>クリック後、GitHub の OAuth 認証画面が開きます。</p>
<ol start="8">
<li>「<strong>Authorize</strong>」をクリック → AWS コンソールの「GitHub 接続設定」画面に自動で戻る</li>
<li>「<strong>新しいアプリをインストールする</strong>」をクリック → GitHub の「Install & Authorize」画面が開く</li>
<li><strong>「Only select repositories」</strong> を選択 → <code>aws-learning-projects</code> を追加</li>
<li>「<strong>Install & Authorize</strong>」をクリック（メール認証コードが求められる場合は入力）</li>
<li>AWS コンソールに自動でリダイレクトされる → 「<strong>接続</strong>」ボタンをクリック</li>
</ol>
<h3><span id="toc14">接続 ARN を確認する</span></h3>
<p>接続一覧から接続の ARN をコピーします。</p>
<pre><code class="language-plaintext">arn:aws:codeconnections:ap-northeast-1:123456789012:connection/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</code></pre>
<blockquote>
<p><strong>重要</strong>: ステータスが <strong>「利用可能」</strong> になっていることを確認してから次に進んでください。</p>
</blockquote>
<p><!-- ![CodeConnectionsの接続一覧で「利用可能」になっている画面](images/connections-available.jpg) --></p>
<p><strong>控えておく情報</strong>: 接続 ARN（<code>arn:aws:codeconnections:...</code>）</p>
<hr>
<h2><span id="toc15">④ 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-cdk4-key",
    "tomcat_version": "10.1.28",
    "github_owner": "your-github-username",
    "github_repo": "aws-learning-projects",
    "github_branch": "main",
    "connection_arn": "arn:aws:codeconnections:ap-northeast-1:..."
  }
}</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-cdk4-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>key_name</code></td>
<td><code>my-cdk4-key</code></td>
<td>①で作成したキーペア名</td>
</tr>
<tr>
<td><code>github_owner</code></td>
<td>GitHub ユーザー名</td>
<td>リポジトリのオーナー名（または Org 名）</td>
</tr>
<tr>
<td><code>github_repo</code></td>
<td><code>aws-learning-projects</code></td>
<td>リポジトリ名</td>
</tr>
<tr>
<td><code>github_branch</code></td>
<td><code>main</code></td>
<td>監視するブランチ名</td>
</tr>
<tr>
<td><code>connection_arn</code></td>
<td>③で確認した ARN</td>
<td>CodeConnections の接続 ARN</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>cdk-pipelines（前回）と同時にデプロイしてもリソース名が競合しません</strong>。前回は <code>my-cdk3-xxx</code>、今回は <code>my-cdk4-xxx</code> というプレフィックスになります。</p>
</blockquote>
<hr>
<h2><span id="toc16">⑤ コードを GitHub にプッシュする（重要！）</span></h2>
<blockquote>
<p><strong>cdk-pipelines との重要な違い</strong>: このハンズオンでは <strong><code>cdk.json</code> だけでなくプロジェクト全体を push する必要があります</strong>。</p>
<p>CDK Pipelines は GitHub から取得したコードで <code>cdk synth</code> を実行し、Maven ビルドも行います。<br />Java ソース（<code>app/</code>）・<code>appspec.yml</code>・<code>scripts/</code> がリポジトリに存在しないと、パイプラインの <code>BuildAndDeployApp</code> ステップが失敗します。</p>
<p><strong><code>cdk deploy</code> の前に必ずコード全体を push してください。</strong></p>
</blockquote>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects

git add cdk-webapp-pipeline/
git commit -m "feat: cdk-webapp-pipeline ハンズオンを追加"
git push origin main</code></pre>
<hr>
<h2><span id="toc17">⑥ パイプラインスタックのデプロイ（初回のみ）</span></h2>
<p>CDK Pipelines では<strong>パイプライン自体を最初に1回だけ手動でデプロイ</strong>します。以降はパイプラインが自動的に動作します。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-webapp-pipeline

.venv\Scripts\activate

cdk synth

cdk deploy my-WebappPipelineStack</code></pre>
<p>「Do you wish to deploy these changes?」に <code>y</code> を入力します。</p>
<blockquote>
<p><strong>所要時間</strong>: パイプラインスタック自体のデプロイは 3〜5 分。その後パイプラインが自動起動します。</p>
</blockquote>
<p>デプロイ完了後、CodePipeline が自動起動します:</p>
<table>
<thead>
<tr>
<th>ステージ</th>
<th>内容</th>
<th>所要時間</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>GitHub から最新コードを取得</td>
<td>〜1分</td>
</tr>
<tr>
<td><strong>Build (Synth)</strong></td>
<td><code>cdk synth</code> でテンプレート生成</td>
<td>〜3分</td>
</tr>
<tr>
<td><strong>UpdatePipeline</strong></td>
<td>パイプライン自己更新（セルフミューテーション）</td>
<td>〜2分</td>
</tr>
<tr>
<td><strong>Deploy > InfraDeploy</strong></td>
<td>CloudFormation で VPC/EC2/RDS/ALB/CodeDeploy 設定を構築</td>
<td>〜15分</td>
</tr>
<tr>
<td><strong>Deploy > BuildAndDeployApp</strong></td>
<td>Maven ビルド → S3 → CodeDeploy で WAR デプロイ</td>
<td>〜10分</td>
</tr>
</tbody>
</table>
<blockquote>
<p>RDS の起動に時間がかかるため、InfraDeploy は 10〜20 分かかることがあります。</p>
</blockquote>
<hr>
<h2><span id="toc18">⑦ パイプラインの実行状況を確認する</span></h2>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> を開く</li>
<li><code>my-cdk4-pipeline</code> を選択</li>
<li>各ステージが <strong>「成功」</strong> になるまで待つ</li>
</ol>
<h3><span id="toc19">CloudFormation スタックの命名規則</span></h3>
<p>CDK Pipelines でステージをデプロイすると、CloudFormation スタック名は以下の形式になります:</p>
<pre><code class="language-plaintext">{StageName}-{StackName}</code></pre>
<table>
<thead>
<tr>
<th>要素</th>
<th>このハンズオンでの値</th>
</tr>
</thead>
<tbody>
<tr>
<td>StageName</td>
<td><code>my-Deploy</code>（<code>pipeline_stack.py</code> でステージを追加した際の名前）</td>
</tr>
<tr>
<td>StackName</td>
<td><code>WebappStack</code>（<code>webapp_stage.py</code> の <code>WebappStack(self, &quot;WebappStack&quot;)</code> のID）</td>
</tr>
<tr>
<td>結果</td>
<td><strong><code>my-Deploy-WebappStack</code></strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p><code>my-WebappPipelineStack</code>（パイプライン）と <code>my-Deploy-WebappStack</code>（インフラ）の <strong>2つが別スタック</strong>として CloudFormation に表示されます。</p>
</blockquote>
<h3><span id="toc20">CloudFormation Outputs の確認</span></h3>
<p>InfraDeploy ステージが完了したら、インフラスタックの Outputs を確認します。</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs"</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>EC2PublicIP</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CodePipelineの全ステージが成功している画面](images/pipeline-success.jpg) --></p>
<hr>
<h2><span id="toc21">⑧ アプリへアクセスする</span></h2>
<p><code>BuildAndDeployApp</code> ステージが完了（全ステージが成功）したら、ALB 経由でアプリにアクセスします。</p>
<h3><span id="toc22">ALB の DNS 名を確認する</span></h3>
<p>Outputs の <code>ALBEndpoint</code> をブラウザで開きます。</p>
<blockquote>
<p><strong>重要</strong>: URL のパスに <code>/webapp/</code> が必要です。</p>
</blockquote>
<pre><code class="language-plaintext">http://&lt;ALBEndpoint&gt;/webapp/</code></pre>
<blockquote>
<p><code>http://&lt;ALBEndpoint&gt;/</code>（ルートパス）では Tomcat のデフォルト画面が表示されます。<br />WAR が <code>webapp.war</code> としてデプロイされるため、アプリは <code>/webapp/</code> 以下に配置されます。</p>
</blockquote>
<p>または CLI で ALBEndpoint を確認:</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs[?OutputKey=='ALBEndpoint'].OutputValue" ^
  --output text</code></pre>
<h3><span id="toc23">アプリの動作確認</span></h3>
<p>以下が表示されれば正常です:</p>
<ul>
<li>「<strong>Spring Boot + RDS Item Manager</strong>」というタイトルが表示される</li>
<li>アイテムの追加・削除ができる</li>
<li>ブラウザをリロードしてもデータが保持される（RDS への永続化が成功）</li>
</ul>
<p><!-- ![Spring Boot アプリのアイテム管理画面が表示されている様子](images/webapp-access.jpg) --></p>
<h3><span id="toc24">EC2 に SSH 接続する（オプション）</span></h3>
<pre><code class="language-cmd">ssh -i %USERPROFILE%\.ssh\my-cdk4-key.pem ec2-user@（EC2PublicIP）</code></pre>
<p>EC2 接続後にログ確認などを行う場合（以下は EC2 上での操作）:</p>
<pre><code class="language-bash">sudo systemctl status codedeploy-agent   # CodeDeploy エージェントの状態確認

sudo cat /opt/tomcat/logs/catalina.out   # Tomcat のログ確認

sudo ls -la /opt/tomcat/webapps/         # デプロイされた WAR を確認</code></pre>
<h3><span id="toc25">EC2 から RDS への接続確認（オプション）</span></h3>
<p>EC2 に SSH 接続後:</p>
<pre><code class="language-bash">sudo dnf install -y mariadb105

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

mysql -h &lt;RDSEndpoint&gt; -u admin -p${DB_PASSWORD} sampledb

mysql&gt; SHOW TABLES;
mysql&gt; exit</code></pre>
<hr>
<h2><span id="toc26">⑨ CI/CD を体験する（アプリ変更を push で自動反映）</span></h2>
<p>CDK Pipelines + CodeDeploy の醍醐味は「<strong>アプリのコードを push するだけで EC2 の Tomcat に自動デプロイされる</strong>」ことです。</p>
<h3><span id="toc27">アプリの HTML を変更する</span></h3>
<p><code>app/src/main/resources/templates/index.html</code> を開き、以下の行を変更します:</p>
<p>変更前:</p>
<pre><code class="language-html">&lt;h1&gt;Spring Boot + RDS Item Manager&lt;/h1&gt;</code></pre>
<p>変更後:</p>
<pre><code class="language-html">&lt;h1&gt;Spring Boot + RDS Item Manager v2&lt;/h1&gt;</code></pre>
<h3><span id="toc28">コミット＆プッシュ</span></h3>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add cdk-webapp-pipeline/app/src/main/resources/templates/index.html
git commit -m "feat: update webapp UI to v2"
git push</code></pre>
<h3><span id="toc29">パイプラインの自動起動を確認する</span></h3>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> → <code>my-cdk4-pipeline</code></li>
<li>push 後、数十秒以内にパイプラインが<strong>自動起動</strong>することを確認</li>
<li><code>BuildAndDeployApp</code> ステージが完了したらブラウザで <code>/webapp/</code> を更新する</li>
</ol>
<p>変更前後の見出しが切り替わっていれば、アプリの CI/CD が正常に動作しています。</p>
<p><!-- ![ブラウザに「v2」の見出しが表示されている様子](images/webapp-v2.jpg) --></p>
<blockquote>
<p><strong>補足</strong>: <code>InfraDeploy</code> ステージはインフラに変更がない場合は「変更なし」でスキップされ、高速に完了します。</p>
<p><strong>注意</strong>: CodePipeline は <code>main</code> ブランチへの全ての push でトリガーされます。<br /><code>.md</code> ファイルだけ変更した場合でもビルド・デプロイが走ります。本番環境ではブランチ戦略や CodePipeline V2 のトリガーフィルターで絞ることができます。</p>
</blockquote>
<hr>
<h2><span id="toc30">⑩ リソース削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p><strong>削除順序が重要です</strong>。パイプラインによってデプロイされたスタック（<code>my-Deploy-WebappStack</code>）を<strong>先に</strong>削除してから、パイプラインスタック（<code>my-WebappPipelineStack</code>）を削除します。</p>
</blockquote>
<h3><span id="toc31">1. アプリスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-Deploy-WebappStack ^
  --region ap-northeast-1

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

echo WebappStack 削除完了</code></pre>
<blockquote>
<p>RDS の削除に 10〜15 分かかります。<code>wait</code> コマンドが完了するまでそのまま待ってください。</p>
</blockquote>
<h3><span id="toc32">2. パイプラインスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name my-WebappPipelineStack ^
  --region ap-northeast-1

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

echo PipelineStack 削除完了</code></pre>
<h3><span id="toc33">3. SSM パラメータを削除する</span></h3>
<pre><code class="language-cmd">aws ssm delete-parameter ^
  --name "/my/cdk4/db-password" ^
  --region ap-northeast-1</code></pre>
<h3><span id="toc34">4. 仮想環境の終了</span></h3>
<pre><code class="language-cmd">deactivate</code></pre>
<h3><span id="toc35">5. キーペアの削除</span></h3>
<pre><code class="language-cmd">aws ec2 delete-key-pair ^
  --key-name my-cdk4-key ^
  --region ap-northeast-1

del %USERPROFILE%\.ssh\my-cdk4-key.pem</code></pre>
<h3><span id="toc36">6. GitHub との接続を削除する（オプション）</span></h3>
<p>次のハンズオンでも使う場合はそのままでよいです。</p>
<p><strong>CodePipeline → 設定 → 接続 → <code>my-github-connection</code> → 「削除」</strong></p>
<h3><span id="toc37">削除確認</span></h3>
<pre><code class="language-cmd">aws cloudformation list-stacks ^
  --region ap-northeast-1 ^
  --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE ^
  --query "StackSummaries[?contains(StackName, 'my-')].StackName"</code></pre>
<p>空のリスト <code>[]</code> が返れば削除完了です。</p>
<h3><span id="toc38">削除されるリソース一覧</span></h3>
<table>
<thead>
<tr>
<th>リソース</th>
<th>削除方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>ALB・EC2・RDS・VPC・SG・S3（アーティファクト）・CodeDeploy</td>
<td><code>delete-stack my-Deploy-WebappStack</code> で削除</td>
</tr>
<tr>
<td>CodePipeline・CodeBuild</td>
<td><code>delete-stack my-WebappPipelineStack</code> で削除</td>
</tr>
<tr>
<td>SSM パラメータ（DBパスワード）</td>
<td><code>aws ssm delete-parameter</code> で手動削除</td>
</tr>
<tr>
<td>キーペア</td>
<td><code>aws ec2 delete-key-pair</code> で手動削除</td>
</tr>
<tr>
<td>GitHub との接続（CodeConnections）</td>
<td>CodePipeline コンソールから手動削除</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc39">CDK Pipelines + CodeDeploy のポイント解説</span></h2>
<h3><span id="toc40">appspec.yml とライフサイクルフック</span></h3>
<pre><code class="language-yaml">version: 0.0
os: linux
files:
  - source: webapp.war
    destination: /opt/tomcat/webapps/
    overwrite: true
hooks:
  ApplicationStop:
    - location: scripts/stop_tomcat.sh    # Tomcat 停止
  BeforeInstall:
    - location: scripts/cleanup.sh         # 古い WAR・展開ディレクトリを削除
  AfterInstall:
    - location: scripts/start_tomcat.sh    # Tomcat 起動</code></pre>
<p>CodeDeploy はこの順序でフックを実行し、Tomcat の再起動を伴わずに WAR を差し替えます。</p>
<h3><span id="toc41">env_from_cfn_outputs の仕組み</span></h3>
<p>インフラスタック（<code>webapp_stack.py</code>）は CodeDeploy のリソース情報を <code>CfnOutput</code> で公開します:</p>
<pre><code class="language-python">self.deploy_app_name_output = CfnOutput(
    self, "DeployApplicationName",
    value=web.deploy_application.application_name,
)</code></pre>
<p>パイプライン（<code>pipeline_stack.py</code>）はこの出力をアプリデプロイステップの環境変数として受け取ります:</p>
<pre><code class="language-python"># pipeline_stack.py
build_and_deploy = CodeBuildStep(
    "BuildAndDeployApp",
    env_from_cfn_outputs={
        "DEPLOY_APP_NAME": stage.deploy_app_name_output,   # CloudFormation Output → 環境変数
    },
    commands=["aws deploy create-deployment --application-name $DEPLOY_APP_NAME ..."],
)</code></pre>
<p>これにより、パイプラインのコードにリソース名をハードコードせず、CloudFormation が実際に作成したリソース名を動的に参照できます。</p>
<h3><span id="toc42">post step の役割</span></h3>
<pre><code class="language-python">pipeline.add_stage(stage, post=[build_and_deploy])</code></pre>
<p><code>post</code> にステップを渡すことで、ステージ（InfraDeploy）の完了後に続けて <code>BuildAndDeployApp</code> が実行されます。これにより「インフラが確実に存在する状態でアプリをデプロイする」という順序が保証されます。</p>
<hr>
<h2><span id="toc43">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td>Source ステージが「失敗」</td>
<td>GitHub との接続が「保留中」のまま</td>
<td>CodePipeline → 設定 → 接続 → 承認して「利用可能」にする</td>
</tr>
<tr>
<td>BuildAndDeployApp が <code>mvn package</code> で失敗</td>
<td>Java ソースが GitHub に push されていない</td>
<td><code>git add cdk-webapp-pipeline/</code> で全ファイルを push してから再実行</td>
</tr>
<tr>
<td>CodeDeploy が失敗する</td>
<td>EC2 の UserData が完了していない</td>
<td>EC2 起動後 1〜2 分待ってから再デプロイ。<code>sudo systemctl status codedeploy-agent</code> で確認</td>
</tr>
<tr>
<td>ALB にアクセスすると 404 or 502</td>
<td>WAR デプロイ未完了 または Tomcat 起動中</td>
<td><code>BuildAndDeployApp</code> ステージが完了するまで待つ</td>
</tr>
<tr>
<td><code>/webapp/</code> で接続できない</td>
<td>URL パスが間違い</td>
<td>ルート URL（<code>/</code>）ではなく <code>/webapp/</code> にアクセスする</td>
</tr>
<tr>
<td>Tomcat の <code>catalina.out</code> に RDS 接続エラー</td>
<td><code>setenv.sh</code> の DB_HOST が未設定</td>
<td>EC2 に SSH して <code>cat /opt/tomcat/bin/setenv.sh</code> を確認する</td>
</tr>
<tr>
<td>パイプラインが自動起動しない</td>
<td>接続が「保留中」または branch 名が違う</td>
<td>接続を承認 / <code>cdk.json</code> の <code>github_branch</code> を確認</td>
</tr>
</tbody>
</table>
<p>CodeDeploy 失敗時のログ確認:</p>
<p><strong>AWS コンソール → CodeDeploy → アプリケーション → デプロイグループ → デプロイ履歴 → 失敗したデプロイ → 「ログの表示」</strong></p>
<hr>
<h2><span id="toc44">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①②</td>
<td>キーペア作成 + Python 仮想環境のセットアップ</td>
</tr>
<tr>
<td>③</td>
<td>CodeConnections で GitHub との接続を作成・承認（cdk-pipelines 実施済みなら再利用）</td>
</tr>
<tr>
<td>④</td>
<td><code>cdk.json</code> に <code>github_owner</code>・<code>github_repo</code>・<code>connection_arn</code>・<code>key_name</code> を設定</td>
</tr>
<tr>
<td>⑤</td>
<td><strong><code>cdk-webapp-pipeline/</code> 全体</strong>を GitHub に push（Java ソース・appspec.yml も含む）</td>
</tr>
<tr>
<td>⑥</td>
<td><code>cdk deploy my-WebappPipelineStack</code>（<strong>初回のみ手動</strong>）でパイプラインスタックをデプロイ</td>
</tr>
<tr>
<td>⑦</td>
<td>CodePipeline が自動起動 → Synth → Mutate → InfraDeploy（15〜20 分）→ BuildAndDeployApp（10 分）</td>
</tr>
<tr>
<td>⑧</td>
<td><code>http://&lt;ALBEndpoint&gt;/webapp/</code> でアプリにアクセスし、アイテムの追加・削除を確認</td>
</tr>
<tr>
<td>⑨</td>
<td>HTML を変更して push → パイプライン自動起動 → ブラウザで変更を確認（アプリ CI/CD 体験）</td>
</tr>
<tr>
<td>⑩</td>
<td><code>delete-stack my-Deploy-WebappStack</code> → <code>delete-stack my-WebappPipelineStack</code> の順で削除</td>
</tr>
</tbody>
</table>
<p>CDK Pipelines + CodeDeploy を組み合わせることで、「<strong>インフラもアプリも、コードが唯一の変更手段になる</strong>」GitOps の世界を体験できました。インフラ変更は CloudFormation が、アプリ変更は CodeDeploy が、それぞれ自動で EC2 に反映します。</p>
<p>コンソールで3層構成を視覚的に学びたい場合は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>、CDK の基本は <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン（cdk-alb-ec2-rds）</a>、カスタム Construct は <a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">CDK カスタム Construct ハンズオン</a>、インフラ CI/CD のみは <a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines ハンズオン</a> を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cdk-webapp-pipeline/">CDK Pipelines で Spring Boot を CI/CD 自動デプロイする【CodeDeploy で WAR ホットデプロイ】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cdk-webapp-pipeline/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CDK Pipelines でインフラの CI/CD を構築する【git push だけで ALB + EC2 + RDS が自動デプロイ】</title>
		<link>https://caymezon.com/aws-handson-cdk-pipelines/</link>
					<comments>https://caymezon.com/aws-handson-cdk-pipelines/#respond</comments>
		
		<dc:creator><![CDATA[caymezon]]></dc:creator>
		<pubDate>Sun, 26 Apr 2026 07:25:39 +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 Pipelines]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[CodeBuild]]></category>
		<category><![CDATA[CodeConnections]]></category>
		<category><![CDATA[CodePipeline]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[GitOps]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[Stage]]></category>
		<category><![CDATA[セルフミューテーション]]></category>
		<category><![CDATA[ハンズオン]]></category>
		<category><![CDATA[初心者]]></category>
		<category><![CDATA[自動デプロイ]]></category>
		<guid isPermaLink="false">https://caymezon.com/?p=20401</guid>

					<description><![CDATA[<p>はじめに 「インフラの変更を反映するたびに cdk deploy を手動実行するのが面倒」「コードをプッシュするだけで AWS リソースが自動更新されるようにしたい」——これを実現するのが CDK Pipelines で [&#8230;]</p>
<p>The post <a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines でインフラの CI/CD を構築する【git push だけで ALB + EC2 + RDS が自動デプロイ】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2><span id="toc1">はじめに</span></h2>
<p>「インフラの変更を反映するたびに <code>cdk deploy</code> を手動実行するのが面倒」「コードをプッシュするだけで AWS リソースが自動更新されるようにしたい」——これを実現するのが <strong>CDK Pipelines</strong> です。</p>
<p>この記事では、<a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">CDK カスタム Construct ハンズオン</a>で構築した <strong>ALB + EC2(Tomcat) + RDS の3層構成</strong>を、<strong>CDK Pipelines（CodePipeline + CodeBuild）</strong> で CI/CD 化します。GitHub にコードをプッシュするだけで自動的にインフラがデプロイ・更新される仕組みを体験します。</p>
<pre><code class="language-plaintext">GitHub リポジトリ
  ↓ push（自動トリガー）
CodePipeline (my-cdk3-pipeline)
  ├── Source:  GitHub (CodeConnections 経由)
  ├── Build:   CodeBuild — cdk synth
  ├── Mutate:  パイプライン自身を自動更新（セルフミューテーション）
  └── Deploy:  CloudFormation — ThreeTierStack
                  ↓
         [ALB: my-cdk3-alb]
                  ↓
         [EC2: my-cdk3-ap-instance]  ← Tomcat
                  ↓
         [RDS: my-cdk3-rds-mysql]    ← MySQL 8.0</code></pre>
<p><strong>このハンズオンで体験できること（インフラの CI/CD）：</strong></p>
<ul>
<li><code>git push</code> するだけで VPC / EC2 / RDS / ALB などのインフラ構築・更新が自動実行される</li>
<li>SG ルール追加・インスタンスタイプ変更などのインフラ変更が push で自動反映される</li>
<li>パイプライン自体の定義変更も自動反映される（<strong>セルフミューテーション</strong>）</li>
</ul>
<p><strong>このハンズオンで体験できないこと（アプリの CI/CD）：</strong></p>
<blockquote>
<p>このハンズオンでは HTML を EC2 の <strong>UserData</strong> に埋め込んでいます。UserData は EC2 の<strong>初回起動時にのみ実行</strong>される仕組みのため、push しても既存 EC2 上のファイルは書き換えられません。<br />Java アプリ（WAR ファイル）を push で自動デプロイしたい場合は、次のハンズオン <code>cdk-webapp-pipeline</code>（CodeDeploy を使用）で体験します。</p>
</blockquote>
<hr>
<blockquote>
<p><strong>この記事は <a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">CDK カスタム Construct ハンズオン（cdk-custom-constructs）</a> の発展記事です。</strong><br />CDK の基本（Bootstrap・synth・deploy）と カスタム Construct を体験済みの方向けです。コード詳細は <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-pipelines">GitHub</a> を参照してください。</p>
</blockquote>
<hr>
<p><!-- ![CodePipelineの実行ステージがすべて「成功」になっている画面](images/pipeline-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>CDK Pipelines</strong></td>
<td>CDK アプリの CI/CD を定義する高レベル Construct ライブラリ。内部で CodePipeline + CodeBuild + CloudFormation を組み合わせる</td>
</tr>
<tr>
<td><strong>Stage</strong></td>
<td>パイプラインのデプロイ単位。複数スタックをひとまとめにしてパイプラインに追加できる</td>
</tr>
<tr>
<td><strong>セルフミューテーション</strong></td>
<td>パイプライン自身のコード（<code>pipeline_stack.py</code>）を変更して push するだけで、パイプラインが自動的に自分自身を更新する仕組み</td>
</tr>
<tr>
<td><strong>CodeConnections</strong></td>
<td>GitHub などの外部 SCM と AWS を接続するサービス（旧 CodeStar Connections）。ブラウザからの GitHub App 認証が必要</td>
</tr>
<tr>
<td><strong>Synth Step</strong></td>
<td>CodeBuild で <code>cdk synth</code> を実行し Cloud Assembly（CloudFormation テンプレート一式）を生成するパイプラインのステップ</td>
</tr>
<tr>
<td><strong>セルフミューテーション後のデプロイ</strong></td>
<td>Mutate ステージでパイプラインが更新された後、続けて Deploy ステージが実行される</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc3">cdk-custom-constructs との比較</span></h2>
<table>
<thead>
<tr>
<th>比較項目</th>
<th>cdk-custom-constructs</th>
<th>cdk-pipelines</th>
</tr>
</thead>
<tbody>
<tr>
<td>デプロイ方法</td>
<td><code>cdk deploy</code>（手動）</td>
<td><strong>GitHub push → 自動</strong></td>
</tr>
<tr>
<td>インフラ変更の反映</td>
<td>毎回 <code>cdk deploy</code> を手動実行</td>
<td><strong>push するだけで自動反映</strong></td>
</tr>
<tr>
<td>パイプライン管理</td>
<td>なし</td>
<td><strong>CodePipeline がデプロイを管理</strong></td>
</tr>
<tr>
<td>セルフミューテーション</td>
<td>なし</td>
<td><strong>パイプライン自体もコードで管理・自動更新</strong></td>
</tr>
<tr>
<td>初回デプロイ</td>
<td><code>cdk deploy</code></td>
<td><code>cdk deploy</code>（<strong>以降は push のみ</strong>）</td>
</tr>
<tr>
<td>アプリデプロイ</td>
<td>EC2 起動時（UserData）</td>
<td>EC2 起動時（UserData）※今回は同じ</td>
</tr>
</tbody>
</table>
<blockquote>
<p>インフラの構成は cdk-custom-constructs と完全に同等です。変わるのは<strong>デプロイの仕組み</strong>（手動 → 自動）です。</p>
</blockquote>
<hr>
<h2><span id="toc4">ファイル構成と役割</span></h2>
<pre><code class="language-plaintext">cdk-pipelines/
├── app.py                    ← CDK アプリ エントリポイント（PipelineStack を生成）
├── cdk.json                  ← CDK 設定・Context 変数（GitHub 接続情報を含む）
├── pipeline/
│   └── pipeline_stack.py     ← CodePipeline + Synth + Stage を定義するスタック
├── stages/
│   └── three_tier_stage.py   ← Stage（ThreeTierStack をデプロイ対象としてまとめる）
├── stacks/
│   └── three_tier_stack.py   ← 3層Web構成スタック（cdk-custom-constructs から流用）
└── components/
    └── three_tier_web.py     ← カスタム L3 Construct（cdk-custom-constructs から流用）</code></pre>
<table>
<thead>
<tr>
<th>ファイル</th>
<th>役割</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pipeline_stack.py</code></td>
<td>パイプライン本体。Source・Synth Step・Stage を定義する</td>
</tr>
<tr>
<td><code>three_tier_stage.py</code></td>
<td>Stage。<code>ThreeTierStack</code> を内包し、デプロイ対象としてパイプラインに渡す</td>
</tr>
<tr>
<td><code>three_tier_stack.py</code></td>
<td>3層Web構成スタック（cdk-custom-constructs と同じ構造）</td>
</tr>
<tr>
<td><code>three_tier_web.py</code></td>
<td>カスタム Construct。cdk-custom-constructs から流用</td>
</tr>
</tbody>
</table>
<p>詳細なコードは <a href="https://github.com/caymezon/aws-handson/tree/main/cdk-pipelines">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>CodePipeline</strong></td>
<td>CI/CD パイプライン管理</td>
<td>月1パイプラインまで無料枠あり</td>
</tr>
<tr>
<td><strong>CodeBuild</strong></td>
<td>cdk synth を実行するビルド環境</td>
<td>月100分まで無料枠あり</td>
</tr>
<tr>
<td><strong>SSM Parameter Store</strong></td>
<td>DBパスワードの安全な保管</td>
<td>スタンダード層は無料</td>
</tr>
<tr>
<td><strong>S3</strong></td>
<td>パイプラインのアーティファクト置き場</td>
<td>無料枠あり</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>各サービスの権限</td>
<td>無料</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>: ALBは無料枠がありません。ハンズオン後は必ずリソースを削除してください。</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>
<tr>
<td>Git</td>
<td><code>git --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>
<blockquote>
<p><strong>GitHub アカウントが必要です</strong>。<code>aws-learning-projects</code> リポジトリに push できる状態にしておいてください。</p>
</blockquote>
<hr>
<h2><span id="toc7">作業順序</span></h2>
<pre><code class="language-plaintext">⓪ 自分のIPアドレスを確認する
      ↓
① キーペアを作成する（AWS CLI で実施）
      ↓
② プロジェクトのセットアップ（Python 仮想環境）
      ↓
③ GitHub との接続を作成する（CodeConnections）
      ↓
④ cdk.json を設定する
      ↓
⑤ コードを GitHub にプッシュする
      ↓
⑥ パイプラインスタックをデプロイする（初回のみ手動）
      ↓
⑦ パイプラインの実行状況を確認する
      ↓
⑧ 動作確認（ALBアクセス・SSH・RDS接続）
      ↓
⑨ CI/CD を体験する（インフラ変更を push で自動反映）
      ↓
⑩ リソース削除</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-cdk3-key ^
  --query KeyMaterial ^
  --output text ^
  --region ap-northeast-1 &gt; %USERPROFILE%\.ssh\my-cdk3-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-pipelines

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 / destroy）はすべてこの仮想環境が有効な状態で実行する必要があります。<br />ターミナルを開き直した際は必ず最初に以下を実行してください。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-pipelines
.venv\Scripts\activate</code></pre>
</blockquote>
<hr>
<h2><span id="toc11">③ GitHub との接続を作成する（CodeConnections）</span></h2>
<p>CodePipeline が GitHub リポジトリを監視するために、<strong>GitHub との接続（CodeConnections）</strong> を作成します。接続の管理はブラウザから行います。</p>
<blockquote>
<p><strong>重要</strong>: AWS コンソールと GitHub の操作は<strong>同じブラウザ</strong>で行ってください。<br />別ブラウザで GitHub を操作すると、AWS にリダイレクトされた際にデフォルトリージョン（バージニア北部等）でログインされる場合があります。</p>
</blockquote>
<h3><span id="toc12">接続を作成する</span></h3>
<ol>
<li>AWS コンソール検索（<code>Alt+S</code>）で <code>CodePipeline</code> と入力 → <strong>CodePipeline</strong> を開く</li>
<li>左サイドバー → <strong>「設定」</strong> → <strong>「接続」</strong></li>
<li>リージョンが <strong>東京（ap-northeast-1）</strong> になっていることを確認する</li>
<li>「<strong>接続を作成</strong>」をクリック</li>
<li>プロバイダー: <strong>GitHub</strong> を選択</li>
<li>接続名: <code>my-github-connection</code>（任意）</li>
<li>「<strong>GitHub に接続する</strong>」をクリック</li>
</ol>
<h3><span id="toc13">GitHub App をインストールする</span></h3>
<p>クリック後、GitHub の OAuth 認証画面が開きます。</p>
<ol start="8">
<li>「<strong>Authorize</strong>」をクリック → AWS コンソールの「GitHub 接続設定」画面に自動で戻る</li>
<li>「<strong>新しいアプリをインストールする</strong>」をクリック → GitHub の「Install & Authorize」画面が開く</li>
<li><strong>「Only select repositories」</strong> を選択 → <code>aws-learning-projects</code> を追加</li>
<li>「<strong>Install & Authorize</strong>」をクリック（メール認証コードが求められる場合は入力）</li>
<li>AWS コンソールに自動でリダイレクトされる → 「<strong>接続</strong>」ボタンをクリック</li>
</ol>
<h3><span id="toc14">接続 ARN を確認する</span></h3>
<p>接続一覧から作成した接続の ARN をコピーします。</p>
<pre><code class="language-plaintext">arn:aws:codeconnections:ap-northeast-1:123456789012:connection/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</code></pre>
<blockquote>
<p><strong>重要</strong>: ステータスが <strong>「利用可能」</strong> になっていることを確認してから次に進んでください。<br />「保留中」のままの場合は接続を選択して「<strong>保留中の接続を更新する</strong>」をクリックします。</p>
</blockquote>
<p><!-- ![CodeConnectionsの接続一覧で「利用可能」になっている画面](images/connections-available.jpg) --></p>
<p><strong>控えておく情報</strong>: 接続 ARN（<code>arn:aws:codeconnections:...</code>）</p>
<hr>
<h2><span id="toc15">④ 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-cdk3-key",
    "tomcat_version": "10.1.28",
    "github_owner": "your-github-username",
    "github_repo": "aws-learning-projects",
    "github_branch": "main",
    "connection_arn": "arn:aws:codeconnections:ap-northeast-1:..."
  }
}</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-cdk3-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>key_name</code></td>
<td><code>my-cdk3-key</code></td>
<td>①で作成したキーペア名</td>
</tr>
<tr>
<td><code>github_owner</code></td>
<td>GitHub ユーザー名</td>
<td>リポジトリのオーナー名（または Org 名）</td>
</tr>
<tr>
<td><code>github_repo</code></td>
<td><code>aws-learning-projects</code></td>
<td>リポジトリ名</td>
</tr>
<tr>
<td><code>github_branch</code></td>
<td><code>main</code></td>
<td>監視するブランチ名</td>
</tr>
<tr>
<td><code>connection_arn</code></td>
<td>③で確認した ARN</td>
<td>CodeConnections の接続 ARN</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>cdk-custom-constructs（前回）と同時にデプロイしてもリソース名が競合しません</strong>。前回は <code>my-cdk2-xxx</code>、今回は <code>my-cdk3-xxx</code> というプレフィックスになります。</p>
</blockquote>
<hr>
<h2><span id="toc16">⑤ コードを GitHub にプッシュする</span></h2>
<p>パイプラインはリポジトリのコードを監視します。まず現在の変更（<code>cdk.json</code> の更新）を push しておきます。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add cdk-pipelines/cdk.json
git commit -m "chore: cdk-pipelines の cdk.json を設定"
git push origin main</code></pre>
<hr>
<h2><span id="toc17">⑥ パイプラインスタックのデプロイ（初回のみ）</span></h2>
<p>CDK Pipelines では<strong>パイプライン自体を最初に1回だけ手動でデプロイ</strong>します。以降はパイプラインが自動的に動作します。</p>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-pipelines

.venv\Scripts\activate

cdk deploy</code></pre>
<p>「Do you wish to deploy these changes?」に <code>y</code> を入力します。</p>
<blockquote>
<p><strong>所要時間</strong>: パイプラインスタック自体のデプロイは 3〜5 分</p>
</blockquote>
<p>デプロイ完了後、CodePipeline が自動起動します:</p>
<table>
<thead>
<tr>
<th>ステージ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>GitHub から最新コードを取得</td>
</tr>
<tr>
<td><strong>Build</strong></td>
<td>CodeBuild で <code>cdk synth</code> を実行→ Cloud Assembly を生成</td>
</tr>
<tr>
<td><strong>Mutate</strong></td>
<td>パイプライン自身を最新定義に更新（セルフミューテーション）</td>
</tr>
<tr>
<td><strong>Deploy</strong></td>
<td>CloudFormation で ThreeTierStack（ALB + EC2 + RDS）をデプロイ</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>所要時間</strong>: RDS の作成に 15〜20 分かかります。</p>
</blockquote>
<hr>
<h2><span id="toc18">⑦ パイプラインの実行状況を確認する</span></h2>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> を開く</li>
<li><code>my-cdk3-pipeline</code> を選択</li>
<li>各ステージが <strong>「成功」</strong> になるまで待つ</li>
</ol>
<blockquote>
<p><strong>実行履歴に「失敗」が表示されている場合</strong>:<br />パイプライン作成直後（GitHub App インストール前）に自動実行された結果が残っている場合があります。<br />確認すべき実行は「<strong>Webhook</strong>」トリガーの最新のもののみです。</p>
</blockquote>
<h3><span id="toc19">CloudFormation スタックの命名規則</span></h3>
<p>CDK Pipelines でステージをデプロイすると、CloudFormation スタック名は以下の形式になります:</p>
<pre><code class="language-plaintext">{StageName}-{StackName}</code></pre>
<table>
<thead>
<tr>
<th>要素</th>
<th>このハンズオンでの値</th>
</tr>
</thead>
<tbody>
<tr>
<td>StageName</td>
<td><code>Deploy</code>（<code>pipeline_stack.py</code> でステージを追加した際の名前）</td>
</tr>
<tr>
<td>StackName</td>
<td><code>ThreeTierStack</code>（<code>three_tier_stack.py</code> のクラス名）</td>
</tr>
<tr>
<td>結果</td>
<td><strong><code>Deploy-ThreeTierStack</code></strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p><code>CdkPipelinesStack</code>（パイプライン）と <code>Deploy-ThreeTierStack</code>（インフラ）の <strong>2つが別スタック</strong>として CloudFormation に表示されます。これは想定通りです。</p>
</blockquote>
<h3><span id="toc20">CloudFormation Outputs の確認</span></h3>
<p>Deploy ステージが完了したら、インフラスタックの Outputs を確認します。</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name Deploy-ThreeTierStack ^
  --region ap-northeast-1 ^
  --query "Stacks[0].Outputs"</code></pre>
<p><strong>控えておく情報</strong>: <code>ALBEndpoint</code>、<code>EC2PublicIP</code>、<code>RDSEndpoint</code></p>
<p><!-- ![CodePipelineのステージがすべて成功している画面](images/pipeline-success.jpg) --></p>
<hr>
<h2><span id="toc21">⑧ 動作確認</span></h2>
<h3><span id="toc22">8-1. ALB 経由でWebアクセス確認</span></h3>
<p>Outputs の <code>ALBEndpoint</code> をブラウザで開きます。</p>
<blockquote>
<p><strong>EC2 が作成されてから Tomcat が起動するまで 10〜15 分かかります</strong>（UserData で Java・Tomcat のインストールが実行されるため）。Deploy ステージ完了直後は 502 Bad Gateway が表示されることが多いです。しばらく待ってリトライしてください。</p>
</blockquote>
<p>以下が表示されれば正常です:</p>
<pre><code class="language-plaintext">AP Server - my-cdk3 3-Tier Web
Deployed via AWS CDK Pipelines (auto-deployed on git push).</code></pre>
<p><!-- ![ALB経由でTomcat画面が表示されている様子](images/alb-healthy-access.jpg) --></p>
<h3><span id="toc23">8-2. EC2 に SSH 接続する</span></h3>
<p>Outputs の <code>SSHCommand</code> を実行します。</p>
<pre><code class="language-cmd">ssh -i %USERPROFILE%\.ssh\my-cdk3-key.pem ec2-user@（EC2PublicIP）</code></pre>
<h3><span id="toc24">8-3. 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/cdk3/db-password \
  --query Parameter.Value \
  --output text \
  --region ap-northeast-1)

mysql -h &lt;RDSEndpoint&gt; -u admin -p${DB_PASSWORD} sampledb

mysql&gt; SHOW DATABASES;
mysql&gt; exit</code></pre>
<h3><span id="toc25">8-4. SSH 接続を切断する</span></h3>
<pre><code class="language-bash">exit</code></pre>
<hr>
<h2><span id="toc26">⑨ CI/CD を体験する（インフラ変更を push で自動反映）</span></h2>
<p>CDK Pipelines の醍醐味は「<strong>インフラのコードを push するだけで CloudFormation への変更が自動反映される</strong>」ことです。</p>
<blockquote>
<p><strong>注意</strong>: このハンズオンで自動デプロイできるのは<strong>インフラ（CloudFormation リソース）のみ</strong>です。<br />HTML や WAR などのアプリファイルは対象外です（次のハンズオン <code>cdk-webapp-pipeline</code> で体験します）。</p>
</blockquote>
<h3><span id="toc27">EC2 にタグを追加して CI/CD を確認する</span></h3>
<p>EC2 タグの追加は EC2 の置き換えなし（in-place 更新）で行われるため、Tomcat が止まらず 2〜3 分でパイプラインが完了します。EC2 コンソールのタグタブで変更を目視確認できます。</p>
<p><code>stacks/three_tier_stack.py</code> を開き、以下を追加します:</p>
<pre><code class="language-python">from aws_cdk import Tags

Tags.of(web.instance).add("DeployedBy", "CDK Pipelines v2")</code></pre>
<h3><span id="toc28">push する</span></h3>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects
git add cdk-pipelines/stacks/three_tier_stack.py
git commit -m "test: EC2 タグ追加で CDK Pipelines CI/CD を確認"
git push origin main</code></pre>
<h3><span id="toc29">パイプラインの自動起動を確認する</span></h3>
<ol>
<li>AWS コンソール → <strong>CodePipeline</strong> → <code>my-cdk3-pipeline</code></li>
<li>push 後、数十秒以内にパイプラインが<strong>自動起動</strong>することを確認</li>
<li>Deploy ステージが完了するまで待つ（2〜3 分）</li>
</ol>
<h3><span id="toc30">タグが反映されたことを確認する</span></h3>
<p><strong>EC2 コンソール</strong> → インスタンス → <code>my-cdk3-ap-instance</code> → <strong>「タグ」タブ</strong></p>
<p><code>DeployedBy = CDK Pipelines v2</code> が追加されていれば、インフラの CI/CD が正常に動作しています。</p>
<p><!-- ![EC2タグに「DeployedBy = CDK Pipelines v2」が追加されている画面](images/ec2-tag-cicd.jpg) --></p>
<hr>
<h2><span id="toc31">⑩ リソース削除</span></h2>
<p><strong>課金を止めるために、ハンズオン完了後は必ず削除します。</strong></p>
<blockquote>
<p><strong>削除順序が重要です</strong>。パイプラインによってデプロイされたスタック（<code>Deploy-ThreeTierStack</code>）を<strong>先に</strong>削除してから、パイプラインスタック（<code>CdkPipelinesStack</code>）を削除します。</p>
</blockquote>
<h3><span id="toc32">1. パイプラインで作成されたスタックを削除する</span></h3>
<pre><code class="language-cmd">aws cloudformation delete-stack ^
  --stack-name Deploy-ThreeTierStack ^
  --region ap-northeast-1</code></pre>
<p>削除完了（RDS の削除に 10〜15 分かかります）を確認:</p>
<pre><code class="language-cmd">aws cloudformation describe-stacks ^
  --stack-name Deploy-ThreeTierStack ^
  --region ap-northeast-1</code></pre>
<p>「does not exist」エラーが出れば削除完了です。</p>
<h3><span id="toc33">2. パイプラインスタック自体を削除する</span></h3>
<pre><code class="language-cmd">cd C:\my-aws\aws-learning-projects\cdk-pipelines

.venv\Scripts\activate

cdk destroy</code></pre>
<p>「Are you sure you want to delete?」に <code>y</code> を入力します。</p>
<blockquote>
<p><code>--app is required</code> エラーが出る場合は <code>cdk-pipelines</code> ディレクトリで実行していません。<br />必ず <code>cd C:\my-aws\aws-learning-projects\cdk-pipelines</code> を先に実行してください。</p>
</blockquote>
<h3><span id="toc34">3. アーティファクト S3 バケットを削除する（必要な場合）</span></h3>
<p>CDK Pipelines が自動生成するアーティファクトバケットは手動削除が必要な場合があります。</p>
<pre><code class="language-cmd">aws s3 ls | findstr pipelineartifacts</code></pre>
<p>バケットが残っている場合:</p>
<pre><code class="language-cmd">aws s3 rb s3://&lt;バケット名&gt; --force</code></pre>
<h3><span id="toc35">4. 仮想環境の終了</span></h3>
<pre><code class="language-cmd">deactivate</code></pre>
<h3><span id="toc36">5. キーペアの削除（手動）</span></h3>
<pre><code class="language-cmd">aws ec2 delete-key-pair ^
  --key-name my-cdk3-key ^
  --region ap-northeast-1

del %USERPROFILE%\.ssh\my-cdk3-key.pem</code></pre>
<h3><span id="toc37">6. GitHub との接続を削除する（手動）</span></h3>
<p><strong>CodePipeline → 設定 → 接続 → <code>my-github-connection</code> → 「削除」</strong></p>
<h3><span id="toc38">削除されるリソース一覧</span></h3>
<table>
<thead>
<tr>
<th>リソース</th>
<th>削除方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>ALB・EC2・RDS・VPC・SG・SSM</td>
<td><code>delete-stack Deploy-ThreeTierStack</code> で削除</td>
</tr>
<tr>
<td>CodePipeline</td>
<td><code>cdk destroy</code> で削除</td>
</tr>
<tr>
<td>CodeBuild プロジェクト</td>
<td><code>cdk destroy</code> で削除</td>
</tr>
<tr>
<td>アーティファクト S3 バケット</td>
<td>手動削除が必要な場合あり</td>
</tr>
<tr>
<td>IAM ロール（パイプライン用）</td>
<td><code>cdk destroy</code> で削除</td>
</tr>
<tr>
<td>GitHub との接続（CodeConnections）</td>
<td>CodePipeline コンソールから手動削除</td>
</tr>
</tbody>
</table>
<hr>
<h2><span id="toc39">CDK Pipelines のポイント解説</span></h2>
<h3><span id="toc40">なぜ cdk deploy は初回だけ必要なのか</span></h3>
<p>パイプラインが存在しない状態では GitHub push を検知できません。初回の <code>cdk deploy</code> でパイプライン自体を作成する必要があります。以降はパイプラインが自分自身を更新（<strong>セルフミューテーション</strong>）するため、再度 <code>cdk deploy</code> を手動実行する必要はありません。</p>
<h3><span id="toc41">Stage クラスの役割</span></h3>
<pre><code class="language-python"># stages/three_tier_stage.py
class ThreeTierStage(cdk.Stage):
    def __init__(self, ...):
        super().__init__(...)
        ThreeTierStack(self, "ThreeTierStack")  # ← Stage に含めるスタック</code></pre>
<p>Stage に複数スタックを含めると、パイプラインが順序制御してデプロイします:</p>
<pre><code class="language-python">class MultiStackStage(cdk.Stage):
    def __init__(self, ...):
        super().__init__(...)
        NetworkStack(self, "NetworkStack")  # ← 先にデプロイ
        AppStack(self, "AppStack")          # ← Network の後にデプロイ</code></pre>
<h3><span id="toc42">モノレポでの primary_output_directory</span></h3>
<p>このリポジトリは複数ハンズオンが共存するモノレポ構成のため、<code>cdk synth</code> をサブディレクトリで実行する設定が必要です:</p>
<pre><code class="language-python">CodeBuildStep(
    "Synth",
    commands=[
        "cd cdk-pipelines",        # ← サブディレクトリに移動
        "pip install -r requirements.txt",
        "npx cdk synth",
    ],
    primary_output_directory="cdk-pipelines/cdk.out",  # ← cdk.out の場所を指定
)</code></pre>
<hr>
<h2><span id="toc43">トラブルシューティング</span></h2>
<table>
<thead>
<tr>
<th>症状</th>
<th>原因</th>
<th>対処</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>connection_arn</code> が見つからないエラー</td>
<td>cdk.json に ARN を設定していない</td>
<td>③で確認した接続 ARN を cdk.json に設定</td>
</tr>
<tr>
<td>Source ステージが「失敗」</td>
<td>GitHub との接続が「保留中」のまま</td>
<td>CodePipeline → 設定 → 接続 → 承認して「利用可能」にする</td>
</tr>
<tr>
<td>Build ステージが失敗</td>
<td><code>github_owner</code>・<code>github_repo</code> が不正</td>
<td>cdk.json の値を確認</td>
</tr>
<tr>
<td>Deploy ステージが失敗</td>
<td>Bootstrap 未実施</td>
<td><code>cdk bootstrap</code> を実行してから再 push</td>
</tr>
<tr>
<td>ALB にアクセスすると 502</td>
<td>UserData でのインストール実行中</td>
<td>10〜15 分待ってリトライ。EC2 に SSH して <code>sudo tail -f /var/log/user-data.log</code> で進捗確認</td>
</tr>
<tr>
<td>パイプラインが自動起動しない</td>
<td>接続が「保留中」または branch 名が違う</td>
<td>接続を承認 / <code>github_branch</code> を確認</td>
</tr>
<tr>
<td><code>cdk destroy</code> で <code>--app is required</code></td>
<td>cdk-pipelines ディレクトリ以外で実行</td>
<td><code>cd C:\my-aws\aws-learning-projects\cdk-pipelines</code> を先に実行</td>
</tr>
</tbody>
</table>
<p>パイプライン失敗時のログ確認（CodeBuild）:</p>
<ol>
<li>AWS コンソール → CodePipeline → <code>my-cdk3-pipeline</code></li>
<li>失敗したステージの「詳細」→「ログを表示」をクリック</li>
<li>CloudWatch Logs でエラーを確認</li>
</ol>
<hr>
<h2><span id="toc44">まとめ</span></h2>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>①②</td>
<td>キーペア作成 + Python 仮想環境のセットアップ</td>
</tr>
<tr>
<td>③</td>
<td>CodeConnections で GitHub との接続を作成・承認（「利用可能」を確認）</td>
</tr>
<tr>
<td>④</td>
<td><code>cdk.json</code> に <code>github_owner</code>・<code>github_repo</code>・<code>connection_arn</code> を設定</td>
</tr>
<tr>
<td>⑤</td>
<td>変更を GitHub に push してリポジトリに反映</td>
</tr>
<tr>
<td>⑥</td>
<td><code>cdk deploy</code>（<strong>初回のみ手動</strong>）でパイプラインスタックをデプロイ</td>
</tr>
<tr>
<td>⑦</td>
<td>CodePipeline が自動起動 → Synth → Mutate → Deploy（RDS 待ち 15〜20 分）</td>
</tr>
<tr>
<td>⑧</td>
<td>ALB DNS 名でアクセス → EC2 → RDS への接続テスト</td>
</tr>
<tr>
<td>⑨</td>
<td>EC2 タグ追加を push → パイプライン自動起動 → タグ反映を確認（CI/CD 体験）</td>
</tr>
<tr>
<td>⑩</td>
<td><code>delete-stack Deploy-ThreeTierStack</code> → <code>cdk destroy</code> の順で削除</td>
</tr>
</tbody>
</table>
<p>CDK Pipelines の最大のメリットは「<strong>インフラのコードが唯一の変更手段になる</strong>」点です。誰かが AWS コンソールで手動変更しても、次の push でコードの状態に戻されます。これにより環境の一貫性を保てます。</p>
<p>今回はインフラの CI/CD のみを体験しました。アプリ（Java WAR ファイル）の自動デプロイを体験したい場合は、次のハンズオン <code>cdk-webapp-pipeline</code>（CodeDeploy を使用）で体験できます。</p>
<p>コンソールで3層構成を視覚的に学びたい場合は <a href="https://caymezon.com/aws-handson-console-alb-ec2-rds/">AWSコンソール版ハンズオン</a>、CDK の基本は <a href="https://caymezon.com/aws-handson-cdk-alb-ec2-rds/">CDK版ハンズオン（cdk-alb-ec2-rds）</a>、カスタム Construct は <a href="https://caymezon.com/aws-handson-cdk-custom-constructs/">CDK カスタム Construct ハンズオン</a> を参照してください。</p><p>The post <a href="https://caymezon.com/aws-handson-cdk-pipelines/">CDK Pipelines でインフラの CI/CD を構築する【git push だけで ALB + EC2 + RDS が自動デプロイ】</a> first appeared on <a href="https://caymezon.com">CayTech Lab</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://caymezon.com/aws-handson-cdk-pipelines/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で書いたインフラコードをdev環境とprod環境で使い回したい」「スタックがどんどん肥大化してきた」——これを解決するのが CDK カスタム Construct です。 この記事では、CDK版ハンズオン [&#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[<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 の YAML 460行をもっとシンプルに書けないか」——それを実現するのが AWS CDK（Cloud Development Kit） です。 この記事では、CloudForm [&#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[<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コンソール版ハンズオン（ALB + EC2 + RDS）では、Tomcatに静的HTMLを配置して3層構成を体験しました。 今回はそこからさらに一歩進め、Spring Boot + Maven で作 [&#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[<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://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 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://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/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>はじめに 前回のAWSコンソール版ハンズオン（Spring Boot Webアプリ）では、コンソールでインフラを手動構築してSCPでWARをデプロイしました。 この記事では、同じ構成を CloudFormation で自 [&#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[<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://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 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://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/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>
	</channel>
</rss>
