bash
-
+
# 1. インスタンステンプレート(VMの設計図)を作成
@@ -559,37 +559,41 @@ export default function CloudLoadBalancingGuide() {
{"gcloud compute instance-groups managed create lb-backend-group \\"}
{" --template=lb-backend-template --size=2 --zone=ZONE"}
{" "}
- # 3. ヘルスチェック用のファイアウォールルールを作成
+ # 3. MIG の named port を設定
+ {"gcloud compute instance-groups managed set-named-ports lb-backend-group \\"}
+ {" --named-ports=http:80 --zone=ZONE"}
+ {" "}
+ # 4. ヘルスチェック用のファイアウォールルールを作成
{"gcloud compute firewall-rules create fw-allow-health-check \\"}
{" --network=default --action=allow --direction=ingress \\"}
{" --source-ranges=130.211.0.0/22,35.191.0.0/16 \\"}
{" --target-tags=allow-health-check --rules=tcp:80"}
{" "}
- # 4. グローバル静的外部IPを予約
+ # 5. グローバル静的外部IPを予約
{"gcloud compute addresses create lb-ipv4-1 --ip-version=IPV4 --global"}
{" "}
- # 5. ヘルスチェックを作成
+ # 6. ヘルスチェックを作成
{"gcloud compute health-checks create http http-basic-check --port 80"}
{" "}
- # 6. バックエンドサービスを作成
+ # 7. バックエンドサービスを作成
{"gcloud compute backend-services create web-backend-service \\"}
{" --protocol=HTTP --port-name=http \\"}
{" --health-checks=http-basic-check --global"}
{" "}
- # 7. MIGをバックエンドサービスに追加
+ # 8. MIGをバックエンドサービスに追加
{"gcloud compute backend-services add-backend web-backend-service \\"}
{" --instance-group=lb-backend-group \\"}
{" --instance-group-zone=ZONE --global"}
{" "}
- # 8. URLマップを作成
+ # 9. URLマップを作成
{"gcloud compute url-maps create web-map-http \\"}
{" --default-service web-backend-service"}
{" "}
- # 9. ターゲットHTTPプロキシを作成
+ # 10. ターゲットHTTPプロキシを作成
{"gcloud compute target-http-proxies create http-lb-proxy \\"}
{" --url-map web-map-http"}
{" "}
- # 10. グローバル転送ルールを作成
+ # 11. グローバル転送ルールを作成
{"gcloud compute forwarding-rules create http-content-rule \\"}
{" --address=lb-ipv4-1 --global \\"}
{" --target-http-proxy=http-lb-proxy --ports=80"}
diff --git a/app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/NavBar.tsx b/app/gcl/hands-on/cloud-load-balancing-guide/NavBar.tsx
similarity index 100%
rename from app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/NavBar.tsx
rename to app/gcl/hands-on/cloud-load-balancing-guide/NavBar.tsx
diff --git a/app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/constants.ts b/app/gcl/hands-on/cloud-load-balancing-guide/constants.ts
similarity index 97%
rename from app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/constants.ts
rename to app/gcl/hands-on/cloud-load-balancing-guide/constants.ts
index 3ae9f9bc0..825f01dc6 100644
--- a/app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/constants.ts
+++ b/app/gcl/hands-on/cloud-load-balancing-guide/constants.ts
@@ -1,6 +1,6 @@
export const REVISION_DATE = '2026年6月版';
-export const DIAGRAMS: Record = {
+export const DIAGRAMS = {
'diag-setup': `flowchart TD
A[Start Lab をクリック] --> B[一時credentialsで Google Cloud コンソールにサインイン]
B --> C[Cloud Shell をアクティブ化]
@@ -45,7 +45,8 @@ export const DIAGRAMS: Record = {
Q2 -->|いいえ TLS終端したい| ProxyNLB[プロキシ ネットワークLB L4]
ALB --> Q3{公開範囲は?}
PNLB --> Q3
+ ProxyNLB --> Q3
Q3 -->|インターネット向け| Ext[外部LB]
Q3 -->|VPC内のみ| Int[内部LB]`,
-};
+} as const;
export type DiagramId = keyof typeof DIAGRAMS;
diff --git a/app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/page.module.css b/app/gcl/hands-on/cloud-load-balancing-guide/page.module.css
similarity index 88%
rename from app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/page.module.css
rename to app/gcl/hands-on/cloud-load-balancing-guide/page.module.css
index f6b00b97a..af4d0ab36 100644
--- a/app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/page.module.css
+++ b/app/gcl/hands-on/cloud-load-balancing-guide/page.module.css
@@ -48,10 +48,10 @@
height: calc(100vh - var(--header-h, 60px) - var(--disclaimer-height, 0px));
width: 280px;
flex-shrink: 0;
- z-index: 40;
+ z-index: 100;
overflow-y: auto;
padding: 32px 18px 32px 28px;
- border-right: 1px solid rgba(66, 133, 244, 0.2);
+ border-right: 1px solid var(--color-border);
background: var(--color-theme-ace-bg-page);
}
@@ -89,20 +89,20 @@
.container :global(.nav-item .num) {
font-family: var(--font-mono);
font-size: 0.72rem;
- color: #274460;
+ color: var(--color-guide-border-strong);
margin-right: 8px;
}
.container :global(.nav-item:hover) {
color: var(--color-foreground);
text-decoration: none;
- background: rgba(61, 142, 245, 0.06);
+ background: var(--color-accent-hover);
}
.container :global(.nav-item.active) {
color: var(--color-foreground);
border-left-color: var(--color-theme-ace-accent);
- background: rgba(0, 200, 255, 0.07);
+ background: var(--color-accent-active);
}
.container :global(.nav-item.active .num) {
@@ -119,7 +119,7 @@
/* ── ヒーロー ── */
.container :global(.hero) {
padding: 72px 0 56px;
- border-bottom: 1px solid rgba(66, 133, 244, 0.2);
+ border-bottom: 1px solid var(--color-border);
margin-bottom: 56px;
}
@@ -176,11 +176,11 @@
background:
radial-gradient(
circle at 20% 50%,
- rgba(0, 200, 255, 0.07),
+ color-mix(in srgb, var(--color-theme-ace-accent) 7%, transparent),
transparent 60%
),
- #080f1c;
- border: 1px solid rgba(66, 133, 244, 0.2);
+ var(--color-guide-deep-background);
+ border: 1px solid rgb(var(--color-google-blue-rgb) / 0.2);
border-radius: 16px;
padding: 18px;
position: relative;
@@ -211,19 +211,19 @@
}
.container :global(.vz-ingress) {
- fill: #0d1b2a;
+ fill: var(--color-guide-node-background);
stroke: var(--color-theme-ace-accent);
stroke-width: 1.6;
}
.container :global(.vz-backend) {
- fill: #0d1b2a;
- stroke: #274460;
+ fill: var(--color-guide-node-background);
+ stroke: var(--color-guide-border-strong);
stroke-width: 1.4;
}
.container :global(.vz-hc) {
- fill: #0d1b2a;
+ fill: var(--color-guide-node-background);
stroke: var(--color-google-green);
stroke-width: 1.4;
stroke-dasharray: 4 3;
@@ -231,7 +231,7 @@
.container :global(.vz-wire) {
fill: none;
- stroke: #274460;
+ stroke: var(--color-guide-border-strong);
stroke-width: 1.4;
}
@@ -288,7 +288,7 @@
gap: 14px;
margin-bottom: 10px;
padding-bottom: 14px;
- border-bottom: 1px solid rgba(66, 133, 244, 0.2);
+ border-bottom: 1px solid var(--color-border);
}
.container :global(.sec-num) {
@@ -355,7 +355,7 @@
.container :global(.table-wrap) {
overflow-x: auto;
margin: 18px 0;
- border: 1px solid rgba(66, 133, 244, 0.2);
+ border: 1px solid var(--color-border);
border-radius: 10px;
}
@@ -366,19 +366,19 @@
}
.container :global(thead th) {
- background: #112030;
+ background: var(--color-card-secondary);
color: var(--color-foreground);
font-family: var(--font-display);
font-weight: 600;
text-align: left;
padding: 12px 16px;
- border-bottom: 1px solid #274460;
+ border-bottom: 1px solid var(--color-guide-border-strong);
white-space: nowrap;
}
.container :global(tbody td) {
padding: 11px 16px;
- border-bottom: 1px solid rgba(66, 133, 244, 0.2);
+ border-bottom: 1px solid var(--color-border);
vertical-align: top;
}
@@ -387,7 +387,7 @@
}
.container :global(tbody tr:nth-child(even)) {
- background: rgba(17, 32, 48, 0.4);
+ background: color-mix(in srgb, var(--color-card-secondary) 40%, transparent);
}
.container :global(td code),
@@ -395,7 +395,7 @@
.container :global(li code) {
font-family: var(--font-mono);
font-size: 0.85em;
- background: #112030;
+ background: var(--color-card-secondary);
color: var(--color-theme-ace-accent);
padding: 1px 6px;
border-radius: 4px;
@@ -409,8 +409,8 @@
/* ── コードカード ── */
.container :global(.code-card) {
position: relative;
- background: #080f1c;
- border: 1px solid rgba(66, 133, 244, 0.2);
+ background: var(--color-guide-deep-background);
+ border: 1px solid var(--color-border);
border-radius: 10px;
margin: 16px 0;
overflow: hidden;
@@ -421,8 +421,8 @@
align-items: center;
justify-content: space-between;
padding: 8px 14px;
- background: #112030;
- border-bottom: 1px solid rgba(66, 133, 244, 0.2);
+ background: var(--color-card-secondary);
+ border-bottom: 1px solid var(--color-border);
}
.container :global(.code-lang) {
@@ -438,7 +438,7 @@
font-size: 0.72rem;
color: var(--color-muted-foreground);
background: transparent;
- border: 1px solid #274460;
+ border: 1px solid var(--color-guide-border-strong);
border-radius: 5px;
padding: 4px 10px;
cursor: pointer;
@@ -490,7 +490,7 @@
padding: 16px 18px 16px 20px;
margin: 18px 0;
border-left: 3px solid var(--color-google-blue);
- background: rgba(61, 142, 245, 0.06);
+ background: var(--color-accent-hover);
}
.container :global(.callout .c-title) {
@@ -511,22 +511,22 @@
.container :global(.callout.tip) {
border-left-color: var(--color-theme-ace-accent);
- background: rgba(0, 200, 255, 0.06);
+ background: var(--color-accent-hover);
}
.container :global(.callout.warn) {
border-left-color: var(--color-google-yellow);
- background: rgba(251, 188, 4, 0.07);
+ background: color-mix(in srgb, var(--color-google-yellow) 7%, transparent);
}
.container :global(.callout.danger) {
border-left-color: var(--color-google-red);
- background: rgba(234, 67, 53, 0.07);
+ background: color-mix(in srgb, var(--color-google-red) 7%, transparent);
}
.container :global(.callout.sec) {
border-left-color: var(--color-google-green);
- background: rgba(52, 168, 83, 0.07);
+ background: color-mix(in srgb, var(--color-google-green) 7%, transparent);
}
.container :global(.src) {
@@ -545,8 +545,8 @@
.container :global(.diagram) {
margin: 22px 0;
padding: 18px;
- background: #080f1c;
- border: 1px solid rgba(66, 133, 244, 0.2);
+ background: var(--color-guide-deep-background);
+ border: 1px solid var(--color-border);
border-radius: 12px;
}
@@ -575,7 +575,7 @@
}
.container :global(footer) {
- border-top: 1px solid rgba(66, 133, 244, 0.2);
+ border-top: 1px solid var(--color-border);
padding: 32px 0 0;
margin-top: 24px;
font-size: 0.84rem;
@@ -605,7 +605,7 @@
display: inline-block;
width: 18px;
height: 18px;
- border: 2px solid #274460;
+ border: 2px solid var(--color-guide-border-strong);
border-radius: 4px;
margin-right: 12px;
position: relative;
@@ -683,7 +683,7 @@
position: static;
height: auto;
border-right: none;
- border-bottom: 1px solid rgba(66, 133, 244, 0.2);
+ border-bottom: 1px solid var(--color-border);
padding: 18px 20px;
width: 100%;
}
@@ -691,8 +691,8 @@
display: block;
width: 100%;
text-align: left;
- background: #112030;
- border: 1px solid rgba(66, 133, 244, 0.2);
+ background: var(--color-card-secondary);
+ border: 1px solid var(--color-border);
color: var(--color-foreground);
font-family: var(--font-display);
font-weight: 600;
diff --git a/app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/page.tsx b/app/gcl/hands-on/cloud-load-balancing-guide/page.tsx
similarity index 100%
rename from app/gcl/associate-cloud-engineer/cloud-load-balancing-guide/page.tsx
rename to app/gcl/hands-on/cloud-load-balancing-guide/page.tsx
diff --git a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/DevelopYourGcpNetworkGuide.tsx b/app/gcl/hands-on/develop-your-gcp-network/DevelopYourGcpNetworkGuide.tsx
similarity index 94%
rename from app/gcl/associate-cloud-engineer/develop-your-gcp-network/DevelopYourGcpNetworkGuide.tsx
rename to app/gcl/hands-on/develop-your-gcp-network/DevelopYourGcpNetworkGuide.tsx
index f98ef2205..871b4cc7e 100644
--- a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/DevelopYourGcpNetworkGuide.tsx
+++ b/app/gcl/hands-on/develop-your-gcp-network/DevelopYourGcpNetworkGuide.tsx
@@ -53,7 +53,7 @@ function HtmlCodeBlock({ lang, html }: { lang: string; html: string }) {
marginLeft: '12px',
background: 'transparent',
border: 'none',
- color: 'var(--n-cyan, #5fd3f0)',
+ color: 'var(--color-theme-ace-accent)',
cursor: 'pointer',
fontSize: '11px',
fontFamily: 'var(--font-mono, monospace)',
@@ -89,7 +89,7 @@ function Diagram({ id, label }: { id: DiagramId; label: string }) {
);
@@ -130,28 +130,28 @@ export default function DevelopYourGcpNetworkGuide() {
padding: '12px clamp(16px, 4vw, 40px)',
background: 'rgba(10, 15, 28, 0.82)',
backdropFilter: 'blur(14px)',
- borderBottom: '1px solid var(--n-line-soft)',
+ borderBottom: '1px solid var(--color-border)',
}}>
- gcp-network :~/learn$ traceroute infra
+ gcp-network :~/learn$ traceroute infra
6 hops · 初学者向け · 最終更新 2026-06
@@ -164,16 +164,16 @@ export default function DevelopYourGcpNetworkGuide() {
クラウドの足回りを、一筆書き で理解する。
BigQuery のクエリから Cloud SQL への移行、VPC 設計、監視、Kubernetes のデプロイ戦略まで。
- Google Cloud の network とインフラを 6 つの hop に分け、
+ Google Cloud の network とインフラを 6 つの hop に分け、
初学者が手を動かしながら最短ルートで通過できるように再構成した実践ガイドです。
- SQL / BigQuery
- Cloud SQL
- VPC ネットワーク
- Cloud Monitoring
- GKE / Kubernetes
+ SQL / BigQuery
+ Cloud SQL
+ VPC ネットワーク
+ Cloud Monitoring
+ GKE / Kubernetes
@@ -1093,14 +1093,20 @@ resource.labels.instance_id = "INSTANCE_ID" # マルチ NIC Bastion Host を作成(dev / prod 両 VPC に接続)
gcloud compute instances create griffin-bastion \\
--zone =us-east1-b --machine-type =e2-medium \\
- --network-interface =network=griffin-dev-vpc,subnet=griffin-dev-mgmt \\
- --network-interface =network=griffin-prod-vpc,subnet=griffin-prod-mgmt
+ --tags =iap-ssh \\
+ --network-interface =network=griffin-dev-vpc,subnet=griffin-dev-mgmt,no-address \\
+ --network-interface =network=griffin-prod-vpc,subnet=griffin-prod-mgmt,no-address
-# SSH 許可の FW ルールを作成
+# 外部 IP は付与せず、IAP TCP forwarding の送信元だけに SSH を許可
gcloud compute firewall-rules create griffin-dev-allow-ssh \\
- --network =griffin-dev-vpc --allow =tcp:22 --source-ranges =0.0.0.0/0
+ --network =griffin-dev-vpc --allow =tcp:22 \\
+ --source-ranges =35.235.240.0/20 --target-tags =iap-ssh
gcloud compute firewall-rules create griffin-prod-allow-ssh \\
- --network =griffin-prod-vpc --allow =tcp:22 --source-ranges =0.0.0.0/0`}
+ --network =griffin-prod-vpc --allow =tcp:22 \\
+ --source-ranges =35.235.240.0/20 --target-tags =iap-ssh
+
+# インターネットから直接 SSH せず、IAP トンネルを使用
+gcloud compute ssh griffin-bastion --zone =us-east1-b --tunnel-through-iap `}
/>
Task 4 — Cloud SQL と WordPress DB
@@ -1115,10 +1121,10 @@ resource.labels.instance_id = "INSTANCE_ID"
-- MySQL プロンプト内で実行
+ html={`-- MySQL プロンプト内で実行(値は固定せず、接続元も許可ホストに限定)
CREATE DATABASE wordpress;
-CREATE USER "wp_user" @"%" IDENTIFIED BY "stormwind_rules" ;
-GRANT ALL PRIVILEGES ON wordpress.* TO "wp_user" @"%" ;
+CREATE USER "wp_user" @"<AUTHORIZED_DB_HOST>" IDENTIFIED BY "<SECRET_MANAGER_VALUE>" ;
+GRANT ALL PRIVILEGES ON wordpress.* TO "wp_user" @"<AUTHORIZED_DB_HOST>" ;
FLUSH PRIVILEGES ;`}
/>
@@ -1128,18 +1134,37 @@ resource.labels.instance_id = "INSTANCE_ID" # GKE クラスターの作成
gcloud container clusters create griffin-dev \\
--zone =us-east1-b --machine-type =e2-standard-4 --num-nodes =2 \\
- --network =griffin-dev-vpc --subnetwork =griffin-dev-wp
+ --network =griffin-dev-vpc --subnetwork =griffin-dev-wp \\
+ --workload-pool =$GOOGLE_CLOUD_PROJECT.svc.id.goog --enable-secret-manager
+
+# DB パスワードは Secret Manager に登録(実値は安全な入力元から渡す)
+gcloud secrets create wordpress-db-password --replication-policy =automatic
+gcloud secrets versions add wordpress-db-password --data-file =<PASSWORD_FILE>
-# WordPress 用シークレットとボリュームの設定
+# WordPress 用マニフェストを取得
gsutil cp -r gs://spls/gsp321/wp-k8s .
cd wp-k8s
-# wp-env.yaml を編集して username: wp_user / password: stormwind_rules を設定
-kubectl create -f wp-env.yaml
-# Cloud SQL Proxy 用のサービスアカウントキーを作成
-gcloud iam service-accounts keys create key.json \\
- --iam-account =cloud-sql-proxy@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
-kubectl create secret generic cloudsql-instance-credentials --from-file key.json`}
+# Kubernetes SA と Google Cloud SA を Workload Identity で関連付け
+kubectl create namespace wordpress
+kubectl create serviceaccount wordpress-ksa --namespace =wordpress
+gcloud iam service-accounts add-iam-policy-binding \\
+ cloud-sql-proxy@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \\
+ --role =roles/iam.workloadIdentityUser \\
+ --member ="serviceAccount:$GOOGLE_CLOUD_PROJECT.svc.id.goog[wordpress/wordpress-ksa]"
+kubectl annotate serviceaccount wordpress-ksa --namespace =wordpress \\
+ iam.gke.io/gcp-service-account=cloud-sql-proxy@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
+
+# Google Cloud SA には Cloud SQL 接続と対象シークレット参照だけを許可
+gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \\
+ --member ="serviceAccount:cloud-sql-proxy@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \\
+ --role =roles/cloudsql.client
+gcloud secrets add-iam-policy-binding wordpress-db-password \\
+ --member ="serviceAccount:cloud-sql-proxy@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com" \\
+ --role =roles/secretmanager.secretAccessor
+
+# マニフェストでは serviceAccountName: wordpress-ksa を指定し、
+# Secret Manager add-on と Workload Identity で認証する(SA キーファイルは作成しない) `}
/>
Task 7 — WordPress Deployment の作成
@@ -1410,7 +1435,7 @@ resource.labels.instance_id = "INSTANCE_ID"
diff --git a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/NavBar.tsx b/app/gcl/hands-on/develop-your-gcp-network/NavBar.tsx
similarity index 88%
rename from app/gcl/associate-cloud-engineer/develop-your-gcp-network/NavBar.tsx
rename to app/gcl/hands-on/develop-your-gcp-network/NavBar.tsx
index b64827f89..944d1ecc0 100644
--- a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/NavBar.tsx
+++ b/app/gcl/hands-on/develop-your-gcp-network/NavBar.tsx
@@ -23,7 +23,7 @@ const HOPS = [
* Renders the page's side navigation and highlights the section currently in view.
*
* The navigation links are generated from the predefined hop list and update their active
- * state as the matching section enters the viewport.
+ * state and location indicator as the matching section enters the viewport.
*/
export default function NavBar() {
const hopRefs = useRef>(new Map());
@@ -35,9 +35,15 @@ export default function NavBar() {
const hops = hopRefs.current;
const activate = (id: string) => {
- hops.forEach((el) => el.classList.remove('active'));
+ hops.forEach((el) => {
+ el.classList.remove('active');
+ el.removeAttribute('aria-current');
+ });
const target = hops.get(id);
- if (target) target.classList.add('active');
+ if (target) {
+ target.classList.add('active');
+ target.setAttribute('aria-current', 'location');
+ }
};
const obs = new IntersectionObserver(
diff --git a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/constants.ts b/app/gcl/hands-on/develop-your-gcp-network/constants.ts
similarity index 97%
rename from app/gcl/associate-cloud-engineer/develop-your-gcp-network/constants.ts
rename to app/gcl/hands-on/develop-your-gcp-network/constants.ts
index 44bc78e14..40a49e9c8 100644
--- a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/constants.ts
+++ b/app/gcl/hands-on/develop-your-gcp-network/constants.ts
@@ -84,10 +84,8 @@ export const DIAGRAMS = {
nic2 <--> mynet["mynetwork"]`,
'diag-observability': `flowchart TD
- A["Compute Engine VM (lamp-1-vm)"] -->|メトリクス収集| B["Cloud Monitoring Agent (google-cloud-ops-agent)"]
- A -->|ログ収集| C["Cloud Logging Agent"]
+ A["Compute Engine VM (lamp-1-vm)"] -->|メトリクスとログを収集| B["Google Cloud Ops Agent"]
B --> D["Cloud Monitoring"]
- C --> D
D --> E["ダッシュボード"]
D --> F["アラートポリシー"]
D --> G["アップタイムチェック"]
diff --git a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/page.css b/app/gcl/hands-on/develop-your-gcp-network/page.css
similarity index 70%
rename from app/gcl/associate-cloud-engineer/develop-your-gcp-network/page.css
rename to app/gcl/hands-on/develop-your-gcp-network/page.css
index 39bf02ebb..ea3218742 100644
--- a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/page.css
+++ b/app/gcl/hands-on/develop-your-gcp-network/page.css
@@ -4,29 +4,6 @@
Night Ops Console / Blueprint テーマ
============================================================= */
-/* ---- Design tokens (page-local literals) ---- */
-.gcp-network-page {
- --n-ink: var(--color-background);
- --n-ink-2: #0d1426;
- --n-panel: var(--color-card);
- --n-panel-2: #16223b;
- --n-line: #233252;
- --n-line-soft: #1a2740;
- --n-grid: rgba(95, 211, 240, 0.035);
-
- --n-cyan: #5fd3f0;
- --n-blue: var(--color-google-blue, #4285f4);
- --n-amber: var(--color-google-yellow, #fbbc04);
- --n-green: var(--color-google-green, #34a853);
- --n-rose: var(--color-google-red, #ea4335);
- --n-violet: #a98bf0;
- --n-faint: #5f6f93;
-
- --n-text: var(--color-foreground);
- --n-muted: var(--color-muted-foreground);
- --n-radius: var(--radius-lg, 14px);
-}
-
/* ---- Global resets for page ---- */
.gcp-network-page,
.gcp-network-page * {
@@ -37,14 +14,15 @@
font-family: var(--font-body, system-ui, -apple-system, sans-serif);
font-size: 16px;
line-height: 1.75;
- color: var(--n-text);
- background: var(--n-ink);
+ color: var(--color-foreground);
+ background: var(--color-background);
min-height: 100vh;
}
/* ---- Layout ---- */
.gcp-network-page .shell {
- max-width: 1200px;
+ width: 100%;
+ max-width: 1400px;
margin-inline: auto;
padding: 0 clamp(16px, 4vw, 40px);
}
@@ -74,8 +52,8 @@
position: absolute;
inset: 0;
background-image:
- linear-gradient(var(--n-grid) 1px, transparent 1px),
- linear-gradient(90deg, var(--n-grid) 1px, transparent 1px);
+ linear-gradient(var(--color-surface-subtle) 1px, transparent 1px),
+ linear-gradient(90deg, var(--color-surface-subtle) 1px, transparent 1px);
background-size: 32px 32px;
mask-image: radial-gradient(120% 80% at 50% 0%, #000 30%, transparent 78%);
pointer-events: none;
@@ -92,12 +70,12 @@
font-size: 12.5px;
letter-spacing: 0.16em;
text-transform: uppercase;
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
display: inline-flex;
align-items: center;
gap: 10px;
padding: 6px 12px;
- border: 1px solid var(--n-line);
+ border: 1px solid var(--color-guide-border-strong);
border-radius: 999px;
background: rgba(95, 211, 240, 0.05);
}
@@ -107,8 +85,8 @@
width: 6px;
height: 6px;
border-radius: 50%;
- background: var(--n-cyan);
- box-shadow: 0 0 8px var(--n-cyan);
+ background: var(--color-theme-ace-accent);
+ box-shadow: 0 0 8px var(--color-theme-ace-accent);
}
.gcp-network-page h1.title {
@@ -122,7 +100,7 @@
}
.gcp-network-page h1.title .accent {
- background: linear-gradient(100deg, var(--n-cyan), var(--n-blue) 55%, var(--n-violet));
+ background: linear-gradient(100deg, var(--color-theme-ace-accent), var(--color-google-blue) 55%, var(--color-tip));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
@@ -132,7 +110,7 @@
margin: 22px 0 0;
max-width: 640px;
font-size: clamp(16px, 2.1vw, 19px);
- color: var(--n-muted);
+ color: var(--color-muted-foreground);
}
.gcp-network-page .hero-meta {
@@ -145,11 +123,11 @@
.gcp-network-page .chip {
font-family: var(--font-mono, monospace);
font-size: 12px;
- color: var(--n-text);
+ color: var(--color-foreground);
padding: 7px 12px;
- border: 1px solid var(--n-line);
+ border: 1px solid var(--color-guide-border-strong);
border-radius: 8px;
- background: var(--n-panel);
+ background: var(--color-card);
display: inline-flex;
align-items: center;
gap: 8px;
@@ -168,9 +146,9 @@
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1px;
- background: var(--n-line-soft);
- border: 1px solid var(--n-line-soft);
- border-radius: var(--n-radius);
+ background: var(--color-border);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius-lg);
overflow: hidden;
}
@@ -181,7 +159,7 @@
}
.gcp-network-page .stat {
- background: var(--n-ink-2);
+ background: var(--color-guide-node-background);
padding: 22px 20px;
}
@@ -194,13 +172,13 @@
}
.gcp-network-page .stat .n span {
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
}
.gcp-network-page .stat .l {
margin-top: 8px;
font-size: 12.5px;
- color: var(--n-muted);
+ color: var(--color-muted-foreground);
}
/* ---- Side rail ---- */
@@ -225,7 +203,7 @@
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
- color: var(--n-faint);
+ color: var(--color-guide-meta);
margin: 0 0 16px 14px;
}
@@ -233,17 +211,17 @@
position: relative;
display: block;
padding: 9px 0 9px 30px;
- color: var(--n-muted);
- border-left: 2px solid var(--n-line);
+ color: var(--color-muted-foreground);
+ border-left: 2px solid var(--color-guide-border-strong);
margin-left: 14px;
transition: color 0.2s, border-color 0.2s;
text-decoration: none;
}
.gcp-network-page .hop:hover {
- color: var(--n-text);
+ color: var(--color-foreground);
text-decoration: none;
- border-color: var(--n-blue);
+ border-color: var(--color-google-blue);
}
.gcp-network-page .hop::before {
@@ -254,31 +232,31 @@
width: 9px;
height: 9px;
border-radius: 50%;
- background: var(--n-ink);
- border: 2px solid var(--n-line);
+ background: var(--color-background);
+ border: 2px solid var(--color-guide-border-strong);
transition: all 0.2s;
}
.gcp-network-page .hop.active {
color: #fff;
- border-color: var(--n-cyan);
+ border-color: var(--color-theme-ace-accent);
}
.gcp-network-page .hop.active::before {
- background: var(--n-cyan);
- border-color: var(--n-cyan);
- box-shadow: 0 0 10px var(--n-cyan);
+ background: var(--color-theme-ace-accent);
+ border-color: var(--color-theme-ace-accent);
+ box-shadow: 0 0 10px var(--color-theme-ace-accent);
}
.gcp-network-page .hop .h-no {
font-family: var(--font-mono, monospace);
font-size: 11px;
- color: var(--n-faint);
+ color: var(--color-guide-meta);
display: block;
}
.gcp-network-page .hop.active .h-no {
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
}
.gcp-network-page .hop .h-name {
@@ -308,13 +286,13 @@
.gcp-network-page .part-addr {
font-family: var(--font-mono, monospace);
font-size: 12.5px;
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
letter-spacing: 0.04em;
white-space: nowrap;
}
.gcp-network-page .part-addr .slash {
- color: var(--n-faint);
+ color: var(--color-guide-meta);
}
.gcp-network-page h2.part-title {
@@ -328,7 +306,7 @@
}
.gcp-network-page .part-sub {
- color: var(--n-muted);
+ color: var(--color-muted-foreground);
margin: 12px 0 0;
max-width: 720px;
}
@@ -337,14 +315,14 @@
height: 1px;
border: 0;
margin: 26px 0 0;
- background: linear-gradient(90deg, var(--n-cyan), var(--n-line) 40%, transparent);
+ background: linear-gradient(90deg, var(--color-theme-ace-accent), var(--color-guide-border-strong) 40%, transparent);
}
.gcp-network-page h3 {
font-family: var(--font-display, system-ui);
font-weight: 600;
font-size: 20px;
- color: var(--n-text);
+ color: var(--color-foreground);
margin: 44px 0 4px;
display: flex;
align-items: center;
@@ -354,8 +332,8 @@
.gcp-network-page h3 .num {
font-family: var(--font-mono, monospace);
font-size: 13px;
- color: var(--n-blue);
- border: 1px solid var(--n-line);
+ color: var(--color-google-blue);
+ border: 1px solid var(--color-guide-border-strong);
border-radius: 6px;
padding: 2px 8px;
flex: none;
@@ -365,7 +343,7 @@
font-family: var(--font-display, system-ui);
font-weight: 600;
font-size: 16px;
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
margin: 28px 0 10px;
}
@@ -375,15 +353,15 @@
.gcp-network-page .term {
font-family: var(--font-mono, monospace);
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
font-size: 0.92em;
}
/* ---- Cards / panels ---- */
.gcp-network-page .panel {
- background: var(--n-panel);
- border: 1px solid var(--n-line);
- border-radius: var(--n-radius);
+ background: var(--color-card);
+ border: 1px solid var(--color-guide-border-strong);
+ border-radius: var(--radius-lg);
padding: 22px 24px;
margin: 22px 0;
}
@@ -392,8 +370,8 @@
.gcp-network-page .table-scroll {
overflow-x: auto;
margin: 20px 0;
- border-radius: var(--n-radius);
- border: 1px solid var(--n-line);
+ border-radius: var(--radius-lg);
+ border: 1px solid var(--color-guide-border-strong);
}
.gcp-network-page table {
@@ -404,23 +382,23 @@
}
.gcp-network-page thead th {
- background: var(--n-ink-2);
+ background: var(--color-guide-node-background);
text-align: left;
font-family: var(--font-mono, monospace);
font-weight: 500;
font-size: 12px;
letter-spacing: 0.04em;
text-transform: uppercase;
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
padding: 13px 16px;
- border-bottom: 1px solid var(--n-line);
+ border-bottom: 1px solid var(--color-guide-border-strong);
white-space: nowrap;
}
.gcp-network-page tbody td {
padding: 13px 16px;
- border-bottom: 1px solid var(--n-line-soft);
- color: var(--n-text);
+ border-bottom: 1px solid var(--color-border);
+ color: var(--color-foreground);
vertical-align: top;
}
@@ -442,19 +420,19 @@
.gcp-network-page li code {
font-family: var(--font-mono, monospace);
font-size: 0.86em;
- background: var(--n-ink-2);
- color: var(--n-cyan);
+ background: var(--color-guide-node-background);
+ color: var(--color-theme-ace-accent);
padding: 2px 6px;
border-radius: 5px;
- border: 1px solid var(--n-line-soft);
+ border: 1px solid var(--color-border);
}
/* ---- Code blocks ---- */
.gcp-network-page .code {
position: relative;
- background: var(--n-ink-2);
- border: 1px solid var(--n-line);
- border-radius: var(--n-radius);
+ background: var(--color-guide-node-background);
+ border: 1px solid var(--color-guide-border-strong);
+ border-radius: var(--radius-lg);
margin: 20px 0;
overflow: hidden;
}
@@ -464,7 +442,7 @@
align-items: center;
gap: 8px;
padding: 10px 16px;
- border-bottom: 1px solid var(--n-line-soft);
+ border-bottom: 1px solid var(--color-border);
background: rgba(255, 255, 255, 0.015);
}
@@ -473,7 +451,7 @@
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
- color: var(--n-faint);
+ color: var(--color-guide-meta);
margin-left: auto;
}
@@ -499,7 +477,7 @@
color: #cdd9f0;
white-space: pre;
scrollbar-width: thin;
- scrollbar-color: var(--n-line) transparent;
+ scrollbar-color: var(--color-guide-border-strong) transparent;
}
.gcp-network-page .code pre::-webkit-scrollbar {
@@ -507,26 +485,26 @@
}
.gcp-network-page .code pre::-webkit-scrollbar-thumb {
- background: var(--n-line);
+ background: var(--color-guide-border-strong);
border-radius: 2px;
}
/* Syntax highlight classes */
.gcp-network-page .code pre .c {
- color: var(--n-faint);
+ color: var(--color-guide-meta);
font-style: italic;
}
.gcp-network-page .code pre .k {
- color: var(--n-violet);
+ color: var(--color-tip);
}
.gcp-network-page .code pre .s {
- color: var(--n-green);
+ color: var(--color-google-green);
}
.gcp-network-page .code pre .f {
- color: var(--n-amber);
+ color: var(--color-google-yellow);
}
.gcp-network-page .code pre .o {
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
}
/* ---- Callouts ---- */
@@ -535,8 +513,8 @@
margin: 22px 0;
padding: 18px 20px 18px 22px;
border-radius: 12px;
- border: 1px solid var(--n-line);
- background: var(--n-panel);
+ border: 1px solid var(--color-guide-border-strong);
+ background: var(--color-card);
}
.gcp-network-page .callout::before {
@@ -566,46 +544,47 @@
.gcp-network-page .callout p {
margin: 4px 0 0;
font-size: 14.5px;
- color: var(--n-text);
+ color: var(--color-foreground);
}
.gcp-network-page .callout.best::before {
- background: var(--n-amber);
+ background: var(--color-google-yellow);
}
.gcp-network-page .callout.best .tag {
- color: var(--n-amber);
+ color: var(--color-google-yellow);
}
.gcp-network-page .callout.warn::before {
- background: var(--n-rose);
+ background: var(--color-google-red);
}
.gcp-network-page .callout.warn .tag {
- color: var(--n-rose);
+ color: var(--color-google-red);
}
.gcp-network-page .callout.note::before {
- background: var(--n-cyan);
+ background: var(--color-theme-ace-accent);
}
.gcp-network-page .callout.note .tag {
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
}
.gcp-network-page .callout.tip::before {
- background: var(--n-green);
+ background: var(--color-google-green);
}
.gcp-network-page .callout.tip .tag {
- color: var(--n-green);
+ color: var(--color-google-green);
}
/* ---- Mermaid frame ---- */
.gcp-network-page .diagram {
margin: 24px 0;
background:
- linear-gradient(var(--n-grid) 1px, transparent 1px),
- linear-gradient(90deg, var(--n-grid) 1px, transparent 1px),
- var(--n-ink-2);
+ linear-gradient(var(--color-surface-subtle) 1px, transparent 1px),
+ linear-gradient(90deg, var(--color-surface-subtle) 1px, transparent 1px),
+ var(--color-guide-node-background);
background-size: 26px 26px, 26px 26px, auto;
- border: 1px solid var(--n-line);
- border-radius: var(--n-radius);
- padding: 14px 10px;
+ border: 1px solid var(--color-guide-border-strong);
+ border-radius: var(--radius-lg);
+ padding: 18px 16px;
overflow-x: auto;
+ scrollbar-width: thin;
}
.gcp-network-page .diagram .cap {
@@ -613,12 +592,30 @@
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
- color: var(--n-faint);
- padding: 6px 12px 12px;
+ color: var(--color-guide-meta);
+ padding: 6px 12px 14px;
+ text-align: center;
}
.gcp-network-page .mermaid-wrap {
- text-align: center;
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ min-width: min-content;
+}
+
+.gcp-network-page .mermaid-wrap > div {
+ background: transparent !important;
+ border: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ width: 100%;
+}
+
+.gcp-network-page .mermaid-wrap svg {
+ display: block;
+ margin: 0 auto;
+ height: auto !important;
}
/* ---- Clean list ---- */
@@ -632,7 +629,7 @@
position: relative;
padding-left: 26px;
margin: 9px 0;
- color: var(--n-text);
+ color: var(--color-foreground);
}
.gcp-network-page ul.clean li::before {
@@ -643,7 +640,7 @@
width: 7px;
height: 7px;
border-radius: 2px;
- background: var(--n-cyan);
+ background: var(--color-theme-ace-accent);
transform: rotate(45deg);
}
@@ -657,7 +654,7 @@
font-size: 12px;
letter-spacing: 0.1em;
text-transform: uppercase;
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
margin: 24px 0 10px;
}
@@ -673,37 +670,37 @@
align-items: flex-start;
gap: 12px;
padding: 12px 16px;
- background: var(--n-panel);
- border: 1px solid var(--n-line);
+ background: var(--color-card);
+ border: 1px solid var(--color-guide-border-strong);
border-radius: 10px;
cursor: pointer;
font-size: 14.5px;
- color: var(--n-text);
+ color: var(--color-foreground);
transition: border-color 0.2s;
}
.gcp-network-page .checklist label:hover {
- border-color: var(--n-blue);
+ border-color: var(--color-google-blue);
}
.gcp-network-page .checklist input {
margin-top: 4px;
- accent-color: var(--n-green);
+ accent-color: var(--color-google-green);
width: 16px;
height: 16px;
flex: none;
}
.gcp-network-page .checklist input:checked + span {
- color: var(--n-faint);
+ color: var(--color-guide-meta);
text-decoration: line-through;
}
/* ---- Footer ---- */
.gcp-network-page .page-footer {
- border-top: 1px solid var(--n-line-soft);
+ border-top: 1px solid var(--color-border);
padding: 40px 0 60px;
- color: var(--n-faint);
+ color: var(--color-guide-meta);
font-size: 13px;
}
@@ -713,7 +710,7 @@
/* ---- Links ---- */
.gcp-network-page a {
- color: var(--n-cyan);
+ color: var(--color-theme-ace-accent);
text-decoration: none;
}
@@ -724,13 +721,13 @@
/* ---- Selection ---- */
.gcp-network-page ::selection {
- background: var(--n-cyan);
+ background: var(--color-theme-ace-accent);
color: #06121b;
}
/* ---- Focus visible ---- */
.gcp-network-page :focus-visible {
- outline: 2px solid var(--n-cyan);
+ outline: 2px solid var(--color-theme-ace-accent);
outline-offset: 3px;
border-radius: 4px;
}
diff --git a/app/gcl/associate-cloud-engineer/develop-your-gcp-network/page.tsx b/app/gcl/hands-on/develop-your-gcp-network/page.tsx
similarity index 100%
rename from app/gcl/associate-cloud-engineer/develop-your-gcp-network/page.tsx
rename to app/gcl/hands-on/develop-your-gcp-network/page.tsx
diff --git a/app/gcl/hands-on/gcp-security-fundamentals-guide/GcpSecurityFundamentalsGuide.tsx b/app/gcl/hands-on/gcp-security-fundamentals-guide/GcpSecurityFundamentalsGuide.tsx
new file mode 100644
index 000000000..c08b3819d
--- /dev/null
+++ b/app/gcl/hands-on/gcp-security-fundamentals-guide/GcpSecurityFundamentalsGuide.tsx
@@ -0,0 +1,1293 @@
+'use client';
+
+import React from 'react';
+import { MermaidDiagram } from '@/components/MermaidDiagram';
+import { DIAGRAMS } from './constants';
+import { NavBar } from './NavBar';
+
+/**
+ * Renders a Mermaid diagram and its caption when the specified diagram exists.
+ *
+ * @param id - The diagram identifier used to select the chart.
+ * @param label - The accessible label and caption displayed for the diagram.
+ * @returns The rendered diagram with its caption, or `null` when no chart exists for `id`.
+ */
+function Diagram({ id, label }: { id: keyof typeof DIAGRAMS; label: string }) {
+ const chart = DIAGRAMS[id];
+ if (!chart) return null;
+ return (
+
+ );
+}
+
+/**
+ * Renders a comprehensive Google Cloud security fundamentals guide covering IAM, networking, encryption, and private GKE design.
+ */
+export function GcpSecurityFundamentalsGuide() {
+ return (
+
+
+
+ Google Cloud Skill Boost 準拠教材
+ Google Cloud セキュリティ基礎 完全ガイド
+
+ IAM / カスタムロール / サービスアカウント / VPC Peering / IAP / Cloud KMS / Private GKE
+
+
+ Google Cloud を触り始めたばかりのエンジニア向けに、7つのハンズオンラボを「なぜその設定が必要なのか」という視点で再構成しました。
+ 全体を貫くのは 最小権限の原則(Principle of Least Privilege) ただ一つです。
+
+
+
+
対象読者
+
Google Cloud を触り始めたエンジニア
+
+
+
前提知識
+
コンソール基本操作・gcloud の雛形が読める程度
+
+
+
到達目標
+
IAM・ネットワーク・暗号化を横断した安全設計ができる
+
+
+
+
+
+
+ 0. この教材の全体像
+
+ このガイドは、Google Cloud の「Implement Cloud Security Fundamentals」系スキルバッジで扱う7つのハンズオンラボを、単なる手順書ではなく「なぜその設定が必要なのか」 という観点で再構成したものです。全体を貫く思想はただ一つ、最小権限の原則 です。各章はこの原則を異なるレイヤー(誰が/何に対して/どの経路で)に適用したものだと考えると、バラバラに見える7つのラボが1本の線でつながります。
+
+
+
+
+
+
Reading Tip
+
+ 各章の冒頭に「到達目標レベル」を記載しています。K1=記憶している、K2=理由を説明できる、K3=自分の要件に合わせて設計できる、という3段階です。
+
+
+
+ 目次
+
+
+
+ {/* ================= CHAPTER 1 ================= */}
+
+
+ 01
+
IAM基礎 — 誰が・何に・何をできるか
+
+ 到達目標レベル: K1 記憶 → K2 理解
+
+ Cloud IAMの根本設計と、なぜ基本ロールを避けるべきかを理解する。
+
+
+
+
Definition
+
+ Cloud IAM(Identity and Access Management)は、Google Cloud上のあらゆる操作に対して「誰が(Principal)」「何に(Resource)」「何を(Role = 権限の集合)」できるかを一元管理する仕組みです。ユーザーに直接パーミッションを渡すのではなく、ロールという権限の束を経由して 付与する設計になっている点が最大の特徴です。
+
+
+
+
+
なぜこの設計なのか
+
+ もしパーミッションを1つずつユーザーへ割り当てる方式だったら、数百人の組織では「誰が何をできるか」を追跡することが事実上不可能になります。ロールという中間層を挟むことで、職務ベースでロールをグループに割り当てられる、新機能追加時にGoogle側が事前定義ロールを自動更新してくれる、監査時に1箇所を確認すればよい、といったメリットが生まれます。
+
+
+
+ 基本ロール(Primitive Roles)の一覧
+
+ IAM導入以前から存在する4つの基本ロールは、今でもラボでよく登場します。実務では基本的に使用を避けるべき ロールですが、仕組みを理解するために整理します。
+
+
+
+
+
+ ロール名
+ ロールID
+ できること
+
+
+
+
+ ブラウザ
+ roles/browser
+ フォルダ・組織階層の閲覧のみ。プロジェクト内のリソース自体は見えない
+
+
+ 閲覧者 (Viewer)
+ roles/viewer
+ 状態を変更しない読み取り専用操作(既存リソースの閲覧)
+
+
+ 編集者 (Editor)
+ roles/editor
+ Viewerの全権限 + リソースの作成・変更・削除
+
+
+ オーナー (Owner)
+ roles/owner
+ Editorの全権限 + 権限管理(IAMポリシー変更)+ 課金設定
+
+
+
+
+
+
+
なぜOwner/Editor/Viewerを避けるべきか
+
+ これらは数千もの権限をサービス横断でまとめて付与してしまいます。「Cloud Storageのファイルを見せたいだけ」の相手にViewerを渡すと、BigQueryやCompute Engineの情報まで見えてしまいます。本番環境では事前定義ロール(例: roles/storage.objectViewer)またはカスタムロールを使うのが定石です。
+
+
+
+ 権限が伝播する仕組み
+
+ IAMのポリシーはリソース階層(組織 → フォルダ → プロジェクト → 個々のリソース)に沿って継承 されます。上位で付与したロールは下位のすべての子リソースに効きます。
+
+
+
+
+ ハンズオンで確認する2つの挙動
+
+ ラボでは2つのユーザー(Owner権限のUser1、Viewer権限のUser2)を使って以下を体験します。
+
+
+
+ Viewerロールを持つユーザーはIAMページの「アクセス権を付与」ボタン自体が押せない — resourcemanager.projects.setIamPolicy 権限がないため。権限管理を行うにはOwnerまたはそれに準ずるIAM関連ロールが必要。
+
+
+ プロジェクトロールを剥奪しても、リソース個別のロールが残っていればアクセスは可能 — プロジェクトのViewerロールを削除しても、Cloud Storageバケットに個別に roles/storage.objectViewer を付与しておけば、そのバケットだけは引き続き閲覧できます。
+
+
+
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ 事前定義ロール(例: roles/storage.objectViewer)を使う
+ 必要な範囲(バケット単位・データセット単位)にロールを絞る
+ 権限変更後は反映まで最大80秒程度かかることを見込んで検証する
+ 定期的にIAMポリシーの棚卸しを行う
+
+
+
+
❌ 避けるべき
+
+ 基本ロール(Owner/Editor/Viewer)を安易に付与する
+ プロジェクト全体にまとめてロールを付与する
+ 変更直後にエラーだと即座に「壊れた」と判断する
+ 一度付与した権限を放置する
+
+
+
+
+
+ {/* ================= CHAPTER 2 ================= */}
+
+
+ 02
+
IAMカスタムロール — 権限を自分でデザインする
+
+ 到達目標レベル: K2 理解 → K3 適用
+
+ 権限の命名規則を理解し、自分で最小限のロールを設計・運用できるようになる。
+
+
+
+
Definition
+
+ カスタムロールとは、Google が用意した事前定義ロールでは粒度が合わない場合に、自分で権限(Permission)を1つずつ選んで束ねて作るロール です。組織レベルまたはプロジェクトレベルで作成でき、Googleによる自動更新の対象にはなりません(自分でメンテナンスする必要があります)。
+
+
+
+ 権限の命名規則を理解する
+
+ Cloud IAMの権限はすべて <サービス>.<リソース>.<動詞> という統一フォーマットに従います。多くの場合、1つの権限が1つのREST APIメソッドに対応しています。
+
+
+
permission format
+
+ # compute.instances.list → Compute Engine の instances リソースを一覧表示できる
+ # compute.instances.stop → Compute Engine の instances リソースを停止できる
+ # storage.buckets.get → Cloud Storage の bucket 情報を取得できる
+ # pubsub.topics.publish → Pub/Sub の topic にメッセージを発行できる
+
+
+
+ 事前定義ロール vs カスタムロールの比較
+
+
+
+
+ 観点
+ 事前定義ロール
+ カスタムロール
+
+
+
+
+ 管理主体
+ Google
+ 自分(プロジェクト/組織の管理者)
+
+
+ 新機能追加時の更新
+ 自動
+ 手動でメンテナンスが必要
+
+
+ 粒度
+ サービス単位で比較的大きい
+ 権限を1つずつ自由に選択できる
+
+
+ 付与できる階層
+ 全階層
+ 組織レベル or プロジェクトレベル(フォルダ不可)
+
+
+
+
+
+
+
プロジェクトレベルの制約
+
+ プロジェクトレベルのカスタムロールには、組織やフォルダでしか意味を持たない権限を含めることはできません。IAMの権限は階層を下方向にしか継承されないためです。
+
+
+
+ カスタムロールを作る2つの方法
+ 方法A: YAMLファイルによる定義
+
+
yaml
+
+ title: "Role Editor"
+ description: "App Versionsへの編集アクセス"
+ stage: "ALPHA"
+ includedPermissions:
+ - appengine.versions.create
+ - appengine.versions.delete
+
+
+
+
bash
+
+ gcloud iam roles create editor \
+ --project $DEVSHELL_PROJECT_ID \
+ --file role-definition.yaml
+
+
+
+ 方法B: フラグによる直接指定
+
+
bash
+
+ gcloud iam roles create viewer \
+ --project $DEVSHELL_PROJECT_ID \
+ --title "Role Viewer" \
+ --description "カスタムロールの説明" \
+ --permissions compute.instances.get,compute.instances.list \
+ --stage ALPHA
+
+
+
+ etag による楽観的ロック
+
+ 複数の管理者が同時にロールを更新しようとすると、片方の変更が意図せず上書きされる恐れがあります。Cloud IAMはこれを防ぐために etag というバージョン識別子を使います。
+
+
+
+
+ カスタムロールのライフサイクル
+
+
+
+
無効化と削除の違い
+
+ DISABLED にしても既存のポリシーバインディングは残ったまま(効果が無くなるだけ)です。誤って権限が広がりすぎたロールを一時停止したい場合は、削除より先に無効化を検討すると安全です。
+
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ 「本当に必要な権限」だけをリストアップしてから作成する
+ 更新前に必ず describe して最新のetagを確認する
+ 説明文に「どの事前定義ロールを参考にしたか」を書いておく
+ 廃止時は DEPRECATED にして移行先を案内する
+
+
+
+
❌ 避けるべき
+
+ とりあえず広めの権限を付与して後で絞ろうとする
+ ローカルにキャッシュした古い定義を使い回して更新する
+ 説明が空欄のまま放置する
+ 突然削除して依存しているユーザーを詰まらせる
+
+
+
+
+
+ {/* ================= CHAPTER 3 ================= */}
+
+
+ 03
+
サービスアカウント — 人間ではないIDの管理
+
+ 到達目標レベル: K2 理解 → K3 適用
+
+ 「ID」と「リソース」という2つの立場でサービスアカウントを扱えるようになる。
+
+
+
+
Definition
+
+ サービスアカウントとは、人間ではなくアプリケーションやVMのためのGoogleアカウント です。APIを呼び出す際、エンドユーザーを介さずに「アプリそのもの」として認証・認可を行うために使われます。一意なメールアドレス形式の識別子を持ちます。
+
+
+
+
+
なぜ人間のアカウントを使い回してはいけないのか
+
+ 個人アカウントの認証情報をVMやバッチ処理が使っていると、退職・異動で処理が止まる、個人アカウントの過剰な権限がそのままアプリに渡る、監査ログで「誰が」「何の目的で」実行したのかが曖昧になる、といった問題が生まれます。サービスアカウントを使うことでアプリの識別・権限・監査を人間のライフサイクルと分離できます。
+
+
+
+ サービスアカウントの種類
+
+
+
+
+ 種類
+ 例
+ 説明
+
+
+
+
+ Compute Engine デフォルトSA
+ PROJECT_NUMBER-compute@developer.gserviceaccount.com
+ Compute Engine APIを有効化すると自動作成
+
+
+ App Engine デフォルトSA
+ PROJECT_ID@appspot.gserviceaccount.com
+ App Engineアプリを含むプロジェクトに自動作成
+
+
+ ユーザー管理SA
+ 任意の名前@PROJECT_ID.iam.gserviceaccount.com
+ 開発者が明示的に作成
+
+
+ Google管理SA(サービスエージェント)
+ PROJECT_NUMBER@cloudservices.gserviceaccount.com
+ Google内部処理用。デフォルトでEditorロールを持つため変更・削除は非推奨
+
+
+
+
+
+ 「ID」としての利用と「リソース」としての利用
+
+ サービスアカウントは2つの立場 で登場する点が初学者にとって混乱しやすいポイントです。
+
+
+
+
+
+ この2つを分けて設計することで、「VMを起動できる人」と「VMが実際に持つ権限」を独立してコントロールできます。
+
+
+ 実例: BigQueryにアクセスするサービスアカウント
+
+ bigquery-qwiklab という名前でサービスアカウントを作成
+ このサービスアカウントに、必要な BigQuery Data Viewer と BigQuery User の IAM ロールを付与
+ Compute Engine インスタンスの ID として、この専用サービスアカウントをアタッチ
+ VM の OAuth アクセススコープは、サービスアカウントとは別に cloud-platform を設定(実際の許可範囲は IAM ロールで制限)
+ VM内のPythonコードは Application Default Credentials で認証情報を取得し、ユーザー介在なしにBigQueryへクエリを実行する
+
+
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ ワークロードごとに専用のサービスアカウントを作成する
+ SAには必要最小限のロールだけを付与する
+ 90日以上認証実績がないSAは無効化・削除を検討する
+ 誰がそのSAを「使える」か明示的に管理する
+
+
+
+
❌ 避けるべき
+
+ すべてのVM/アプリでデフォルトSA(強い権限)を使い回す
+ とりあえずEditorやOwnerを付与する
+ 使われなくなったSAを放置する
+ SAの鍵をローカルにダウンロードして共有する
+
+
+
+
+
+ {/* ================= CHAPTER 4 ================= */}
+
+
+ 04
+
VPC Network Peering — プロジェクトをまたぐ内部通信
+
+ 到達目標レベル: K2 理解 → K3 適用
+
+ 双方向合意という設計思想を理解し、疎通確認までを組み立てられるようになる。
+
+
+
+
Definition
+
+ VPC Network Peeringは、2つのVPCネットワーク(同一プロジェクト内・別プロジェクト・別組織のいずれでも可)を、内部IPアドレスだけで直接接続する 仕組みです。ゲートウェイやVPN機器を経由せず、まるで同じネットワーク内にいるかのように通信できます。
+
+
+
+ なぜVPNや外部IPより優れているのか
+
+
+
+
+ 観点
+ 外部IP経由の通信
+ VPN
+ VPC Peering
+
+
+
+
+ レイテンシ
+ 高い(インターネット経由)
+ 中程度(暗号化オーバーヘッド)
+ 低い(同一ネットワーク内相当)
+
+
+ セキュリティ
+ サービスがインターネットに露出
+ 内部化されるが構成が複雑
+ 内部化され露出面がない
+
+
+ コスト
+ 通常の帯域課金
+ トンネル維持コストが発生
+ 内部IP通信のため有利
+
+
+
+
+
+ 双方向の設定が必要という重要な特性
+
+ VPC Peeringで最もつまずきやすいポイントは、片側だけの設定では有効化されない ことです。
+
+
+
+
+
+
なぜこの設計なのか
+
+ ピア接続の作成は、相手のVPCに対するIAMロールを一切付与しません。つまり「あなたのネットワークに繋ぎたい」という一方的な申請にすぎず、相手側の管理者が同意(自分の側にも同じ接続を作成)することで初めて成立します。他人のネットワークに勝手に接続できてしまう事態を防ぐための安全設計です。
+
+
+
+ 疎通確認までの流れ
+
+ project-A に network-a(サブネット 10.0.0.0/16)を作成し、VM vm-a を配置
+ project-B に network-b(サブネット 10.8.0.0/16)を作成し、VM vm-b を配置
+ SSH/ICMPを許可するファイアウォールルールをそれぞれ作成
+ 双方向のピア接続(peer-ab / peer-ba)を作成し ACTIVE になったことを確認
+ vm-b から vm-a の内部IPへ ping を実行し、パケットロス0%であることを確認
+
+
+
+
bash
+
+ # project-A側
+ gcloud compute networks subnets create network-a-subnet --network network-a \
+ --range 10.0.0.0/16 --region "REGION_1"
+
+ # project-B側
+ gcloud compute networks subnets create network-b-subnet --network network-b \
+ --range 10.8.0.0/16 --region "REGION_2"
+
+
+
+
+
サブネットのIP範囲重複に注意
+
+ ピア接続先とサブネットの範囲が重複していると、ピアリング自体が失敗します。設計段階でIPアドレス計画を必ず立てましょう。
+
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ ピアリング前にIPアドレス範囲の重複がないか確認する
+ 両側で名前(peer-ab / peer-ba)を対応付けて管理する
+ 必要な通信だけをファイアウォールルールで許可する
+ 重要サービスの接続には consensus モードを検討する
+
+
+
+
❌ 避けるべき
+
+ とりあえずデフォルトのCIDRで作成して後から気づく
+ 名前をランダムにして後から追跡できなくする
+ ピアリング=無制限アクセスだと誤解して全許可にする
+ 誰でも片側から一方的に削除できる状態を放置する
+
+
+
+
+
+ {/* ================= CHAPTER 5 ================= */}
+
+
+ 05
+
Identity-Aware Proxy (IAP) — アプリ層のゼロトラスト
+
+ 到達目標レベル: K2 理解 → K3 適用
+
+ ヘッダーなりすましのリスクとJWT暗号検証の重要性を理解する。
+
+
+
+
Definition
+
+ IAP(Identity-Aware Proxy)は、HTTPSでアクセスするアプリケーションの手前に立ち、ネットワークレベルのファイアウォールではなく、アプリケーションレベルの認証・認可 でアクセス制御を行うGoogle Cloudのサービスです。VPNを使わずにゼロトラストアクセスを実現します。
+
+
+
+
+
なぜVPNではなくIAPなのか
+
+ 従来型のVPNは「一度ネットワークに入れば内部は信頼される」という前提に立っています。しかしVPN経由で侵入されると内部のあらゆるリソースへ横展開されるリスクを抱えています。IAPはリクエストごと・ユーザーごとに認証と認可を行う ため、境界ではなくID(誰がアクセスしているか)を信頼の基準にします。
+
+
+
+ 認証・認可・ヘッダー伝播の流れ
+
+
+
+
python
+
+ user_email = request.headers.get('X-Goog-Authenticated-User-Email')
+ user_id = request.headers.get('X-Goog-Authenticated-User-ID')
+
+
+
+ 「なりすまし」の危険性とJWT暗号検証
+
+ ここが本チャプターの一番重要なポイントです。IAPが無効化・バイパスされた場合、アプリはそれを検知できません。
+ IAPがオフの状態で同じヘッダー名を偽装したリクエストを送ると、アプリはそれを正規のIAP経由のリクエストだと誤認してしまいます。
+
+
+
+
+
+ 対策として X-Goog-IAP-JWT-Assertion ヘッダーに含まれる暗号署名付きのアサーション をアプリ側で検証する方法があります。この署名はGoogleの秘密鍵でしか生成できないため、IAPを経由していないリクエストは検証に失敗します。
+
+
+
+
python
+
+ from google.auth.transport.requests import Request
+ from google.oauth2 import id_token
+
+ IAP_PUBLIC_KEY_URL = 'https://www.gstatic.com/iap/verify/public_key'
+ IAP_ISSUER = 'https://cloud.google.com/iap'
+
+ def user():
+ assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
+ if assertion is None:
+ return None, None
+ info = id_token.verify_token(
+ assertion,
+ Request(),
+ audience=expected_audience,
+ certs_url=IAP_PUBLIC_KEY_URL,
+ )
+ if info.get('iss') != IAP_ISSUER:
+ raise ValueError('Invalid IAP JWT issuer')
+ return info['email'], info['sub']
+
+
+
+ 素のヘッダー参照 vs JWT暗号検証の比較
+
+
+
+
+ 観点
+ ヘッダーをそのまま参照
+ JWTを暗号検証
+
+
+
+
+ 実装の手間
+ 低い(ヘッダーを読むだけ)
+ やや高い(公開鍵取得・署名検証が必要)
+
+
+ IAPがオフ/迂回された場合
+ ❌ 偽装ヘッダーをそのまま信用
+ ✅ 署名検証に失敗し検知できる
+
+
+ 推奨される用途
+ リスクの低い社内ツール試作段階
+ 機密性の高い本番アプリケーション
+
+
+
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ 機密性の高いアプリではJWTアサーションを検証する
+ IAP有効化配下のバックエンドも直接アクセス経路を遮断する
+ IAP-secured Web App User は必要な範囲だけに絞る
+ Cloud Runは run.app 直接URLを無効化しLB経由を強制する
+
+
+
+
❌ 避けるべき
+
+ ヘッダーの値を無条件に信頼する
+ IAP設定後もバックエンドへの直接アクセス経路を放置する
+ 組織全体に大盤振る舞いする
+ デフォルトURLが誰でも叩ける状態を放置する
+
+
+
+
+
+ {/* ================= CHAPTER 6 ================= */}
+
+
+ 06
+
Cloud KMS — 鍵管理と暗号化
+
+ 到達目標レベル: K2 理解 → K3 適用
+ 鍵の管理者と利用者を分離する設計思想を理解する。
+
+
+
Definition
+
+ Cloud KMS(Key Management Service)は、Google Cloud上のサービスや自作アプリケーションで使う暗号鍵をクラウド上で一元的に生成・管理・利用するサービス です。KeyRing(鍵のグループ)とCryptoKey(実際の鍵)という2階層のリソースモデルを持ちます。
+
+
+
+ リソース階層
+
+
+
+ KeyRing は特定のロケーションに鍵をグループ化する入れ物。環境別(test/staging/prod)やデータの機密度別に分けるのが一般的で、一度作成すると削除できません (コストは発生しません)。CryptoKey は実際に暗号化/復号に使われる鍵で、複数の Key Version を持ち、ローテーションのたびに新バージョンが追加されます。
+
+
+ 暗号化・復号のワークフロー
+
+
+
+
bash
+
+ # 平文をbase64エンコード
+ PLAINTEXT=$(cat 1.txt | base64 -w0)
+
+ # encryptエンドポイントを呼び出し、結果からciphertextだけ抽出
+ curl -v "https://cloudkms.googleapis.com/v1/projects/$DEVSHELL_PROJECT_ID/locations/global/keyRings/$KEYRING_NAME/cryptoKeys/$CRYPTOKEY_NAME:encrypt" \
+ {` -d "{\\"plaintext\\":\\"$PLAINTEXT\\"}" \\`}
+ -H "Authorization:Bearer $(gcloud auth application-default print-access-token)" \
+ -H "Content-Type:application/json" \
+ | jq .ciphertext -r > 1.encrypted
+
+
+
+
+
重要な性質
+
+ Cloud KMSは確率的暗号化を採用しているため、同じ平文を同じ鍵で2回暗号化しても異なる暗号文になります 。暗号文からパターンを推測されるリスクを下げるための仕様です。
+
+
+
+ 権限分離: 「鍵の管理」と「鍵の使用」は別の権限
+
+ KMSを理解する上で最も重要な設計思想が、鍵の管理者(admin)と鍵の利用者(encrypter/decrypter)を役割として分離できる ことです。
+
+
+
+
+
+
+ ロール
+ 権限ID
+ できること
+
+
+
+
+ Cloud KMS 管理者
+ roles/cloudkms.admin
+ KeyRingの作成、CryptoKeyの作成・無効化・破棄などの管理操作
+
+
+ 暗号化・復号 実行者
+ roles/cloudkms.cryptoKeyEncrypterDecrypter
+ encrypt/decrypt APIのみ呼び出せる(鍵そのものの管理は不可)
+
+
+
+
+
+
+
+
+
bash
+
+ # 鍵管理権限を付与
+ gcloud kms keyrings add-iam-policy-binding $KEYRING_NAME \
+ --location global --member user:$USER_EMAIL \
+ --role roles/cloudkms.admin
+
+ # 暗号化/復号の実行権限を別途付与
+ gcloud kms keyrings add-iam-policy-binding $KEYRING_NAME \
+ --location global --member user:$USER_EMAIL \
+ --role roles/cloudkms.cryptoKeyEncrypterDecrypter
+
+
+
+ 鍵のライフサイクルと監査
+
+ KeyRing は削除不可 です。CryptoKey は配下の全キーバージョンを削除した後に、キーバージョンは DESTROYED、IMPORT_FAILED、GENERATION_FAILED のいずれかの状態で、必要な権限を持つ場合に削除できます。通常の無効化は「キーバージョンの破棄(destroy)」で行います。
+ 鍵のローテーションは既存の暗号化データを自動で再暗号化しません 。鍵の漏洩が疑われる場合は、データの再暗号化・IAMアクセスの取り消し・キーバージョンの破棄をセットで行う必要があります。
+ Cloud Audit Logs(Admin Activity / Data Access)で「誰が・いつ・どの鍵に対して何をしたか」を追跡できます。
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ 鍵の管理権限とアプリの暗号化実行権限を別ロールで分離する
+ 環境やデータの機密度別にKeyRingを分ける
+ 鍵侵害の疑いがあればアクセス取消+再暗号化+鍵破棄をセットで行う
+ Cloud Audit Logsで鍵の利用状況を定期的に確認する
+
+
+
+
❌ 避けるべき
+
+ 同じSAに管理権限と実行権限の両方を渡す
+ 全環境の鍵を1つのKeyRingに雑多にまとめる
+ ローテーションだけすれば安全だと誤解する
+ 監査ログを見ずに「設定したから安全」で放置する
+
+
+
+
+
+ {/* ================= CHAPTER 7 ================= */}
+
+
+ 07
+
Private GKE クラスタ — Kubernetesのネットワーク隔離
+
+ 到達目標レベル: K2 理解 → K3 適用
+
+ 露出面を段階的に絞り込むという考え方でクラスタネットワークを設計できるようになる。
+
+
+
+
Definition
+
+ Private GKEクラスタとは、コントロールプレーン(マスター)をパブリックインターネットから到達不可能にした GKEクラスタです。ノードにはプライベートIPアドレスのみが割り当てられ、ノードとコントロールプレーン間の通信はVPC Peeringを通じて行われます。
+
+
+
+
+
なぜノードに外部IPを持たせないのか
+
+ 通常のKubernetesクラスタでは、ノードが外部IPを持つとインターネットから直接ノードのポートへアクセスされるリスクが生まれます。プライベートノードにすることで攻撃対象領域(Attack Surface)をVPC内部に限定 できます。
+
+
+
+
+
+ Master Authorized Networks(承認済みネットワーク)
+
+ プライベートクラスタでも、管理者はどこかからkubectlでクラスタを操作する必要があります。そこで使うのがMaster Authorized Networks で、「このCIDR範囲からのみコントロールプレーンへのアクセスを許可する」という許可リストです。
+
+
+
+
bash
+
+ gcloud container clusters create private-cluster \
+ --enable-private-nodes \
+ --master-ipv4-cidr 172.16.0.16/28 \
+ --enable-ip-alias \
+ --create-subnetwork "" \
+ --machine-type e2-medium
+
+ # 特定の外部IP(例: 自分のVMのNAT IP)だけを許可
+ gcloud container clusters update private-cluster \
+ --enable-master-authorized-networks \
+ --master-authorized-networks <MY_EXTERNAL_RANGE>/32
+
+
+
+ パブリックエンドポイントを完全に無効化する
+
+ さらにセキュリティレベルを上げたい場合、コントロールプレーンのパブリックエンドポイント自体を無効化(--enable-private-endpoint) できます。この設定を行うと、VPC外部からは一切コントロールプレーンに到達できなくなり、同じVPC内の踏み台ホスト(bastion/jumphost)経由でのみ操作可能になります。
+
+
+
+
+
+
--internal-ip フラグを忘れずに
+
+ パブリックエンドポイントを無効化したクラスタでは、gcloud container clusters get-credentials の際に --internal-ip を付けないと、存在しないパブリックIPへ接続しようとして失敗します。
+
+
+
+ カスタムサブネットとセカンダリレンジ
+
+
bash
+
+ gcloud compute networks subnets create my-subnet \
+ --network default \
+ --range 10.0.4.0/22 \
+ --enable-private-ip-google-access \
+ --region=$REGION \
+ --secondary-range my-svc-range=10.0.32.0/20,my-pod-range=10.4.0.0/14
+
+
+
+ --enable-private-ip-google-access を有効にすることで、外部IPを持たないノードでもGoogle API群に到達できるようになります。
+
+
+ ベストプラクティス
+
+
+
✅ 推奨
+
+ 本番クラスタは --enable-private-nodes を基本設定にする
+ 機密性の高いワークロードは --enable-private-endpoint も検討する
+ Master Authorized Networksは /32 など狭い範囲で許可する
+ IPアドレス計画を事前に立て他のVPC/オンプレと重複させない
+
+
+
+
❌ 避けるべき
+
+ ノードに外部IPを持たせたまま本番運用する
+ パブリックエンドポイントを無条件に許可し続ける
+ 0.0.0.0/0 のような広すぎる範囲を許可する
+ セカンダリレンジを行き当たりばったりで割り当てる
+
+
+
+
+
+ {/* ================= CHAPTER 8 ================= */}
+
+
+ 08
+
総合演習 — 全レイヤーを統合したセキュアなクラスタ設計
+
+ 到達目標レベル: K3 適用
+
+ 7つの要素を統合し、実際のチャレンジラボ形式の設計課題を解けるようになる。
+
+
+
+ ここまでの7つの要素(IAM基礎/カスタムロール/サービスアカウント/VPC Peering/IAP/KMS/Private GKE)を統合すると、実際の「チャレンジラボ」で問われるような設計課題を解けるようになります。題材は、架空の企業「Jooli Inc.」のOrcaチームが、開発チーム向けに安全なGKEクラスタを構築するというシナリオです。
+
+
+ 要件の整理
+
+
+
+
+ #
+ 要件
+ 対応するChapter
+
+
+
+
+ 1
+ クラスタは専用の最小権限サービスアカウントを使う
+ Chapter 3(サービスアカウント)
+
+
+ 2
+ Storage操作用にカスタムロールを作成し、SAへバインドする
+ Chapter 2(カスタムロール)
+
+
+ 3
+ Privateクラスタとし、パブリックエンドポイントも無効化する
+ Chapter 7(Private GKE)
+
+
+ 4
+ Master Authorized Networksに管理用jumphostのIPだけを登録する
+ Chapter 7(Private GKE)
+
+
+ 5
+ クラスタは指定のカスタムサブネットに配置する
+ Chapter 7 / VPC設計
+
+
+ 6
+ jumphostからkubectlでの疎通を確認する
+ Chapter 4 + Chapter 7
+
+
+
+
+
+ 統合アーキテクチャ図
+
+
+ 手順の骨格(gcloudコマンド抜粋)
+
+
bash
+
+ # 1. カスタムロールの作成(Storage操作に限定)
+ gcloud iam roles create orca_custom_role \
+ --project $DEVSHELL_PROJECT_ID \
+ --title "Custom Security Role" \
+ --permissions storage.buckets.get,storage.objects.get,storage.objects.list,storage.objects.update,storage.objects.create
+
+ # 2. 専用サービスアカウントの作成
+ gcloud iam service-accounts create orca-sa --display-name "orca-cluster-sa"
+
+ # 3. 組込みロール + カスタムロールをバインド(例)
+ gcloud projects add-iam-policy-binding $DEVSHELL_PROJECT_ID \
+ --member="serviceAccount:orca-sa@$DEVSHELL_PROJECT_ID.iam.gserviceaccount.com" \
+ --role="roles/monitoring.viewer"
+
+ # 4. Privateクラスタの作成(パブリックエンドポイントも無効化)
+ gcloud container clusters create orca-cluster-name \
+ --service-account=orca-sa@$DEVSHELL_PROJECT_ID.iam.gserviceaccount.com \
+ --subnetwork=orca-build-subnet \
+ --enable-private-nodes \
+ --enable-private-endpoint \
+ --enable-master-authorized-networks \
+ --enable-ip-alias \
+ --zone=ZONE
+
+ # 5. jumphostの内部IPをMaster Authorized Networksに追加
+ gcloud container clusters update orca-cluster-name \
+ --enable-master-authorized-networks \
+ --master-authorized-networks=<JUMPHOST_INTERNAL_IP>/32 \
+ --zone=ZONE
+
+ # 6. jumphostから internal-ip 経由で認証情報を取得
+ gcloud container clusters get-credentials orca-cluster-name \
+ --internal-ip --zone=ZONE --project=$DEVSHELL_PROJECT_ID
+
+ # 7. 疎通テスト用の簡易アプリをデプロイ
+ kubectl create deployment hello-server --image=gcr.io/google-samples/hello-app:1.0
+
+
+
+ このシナリオが示す設計原則
+
+
+
Principle 01
+
権限は役割ごとに最小の粒度で分離する
+
+
+
Principle 02
+
ネットワークの露出面を段階的に絞り込む
+
+
+
Principle 03
+
管理経路自体も専用の踏み台に限定する
+
+
+
Principle 04
+
すべての操作をCloud Audit Logsで追跡可能にする
+
+
+
+ この4つはそれぞれ Chapter 1〜3(IAM)、Chapter 4〜5(ネットワーク露出)、Chapter 7(クラスタ隔離)で学んだ内容の応用そのものです。
+
+
+
+ {/* ================= SUMMARY ================= */}
+
+
+ +
+
ベストプラクティス総まとめ表
+
+
+
+
+
+ レイヤー
+ 原則
+ 具体的な実践
+
+
+
+
+ IAM全般
+ 最小権限の原則
+ 基本ロールを避け、事前定義ロール/カスタムロールで必要な権限だけを付与する
+
+
+ カスタムロール
+ 変更管理の徹底
+ etagで競合を防ぎ、廃止時はDEPRECATEDで移行猶予を設ける
+
+
+ サービスアカウント
+ ID/リソースの分離設計
+ ワークロードごとに専用SAを作り「誰が使えるか」と「何ができるか」を別々に管理する
+
+
+ VPC Peering
+ 双方向合意の原則
+ 両側が明示的に接続を作成した場合のみ有効化される設計を活用し、IP設計を事前に行う
+
+
+ IAP
+ ゼロトラスト
+ ネットワーク境界ではなくIDを信頼の基準にし、機密データはJWT署名検証まで行う
+
+
+ Cloud KMS
+ 権限分離
+ 鍵の管理者と利用者のロールを分け、侵害時は「取り消し+再暗号化+鍵破棄」をセットで行う
+
+
+ Private GKE
+ 露出面の最小化
+ ノードのプライベート化、パブリックエンドポイント無効化、承認済みネットワークの/32運用を組み合わせる
+
+
+ 全体
+ 監査可能性
+ Cloud Audit Logsで「誰が・いつ・何を」変更したかを常に追跡できる状態を保つ
+
+
+
+
+
+
+ {/* ================= REFERENCES ================= */}
+
+
+ 📚
+
参考文献 / 公式ドキュメント一覧
+
+
+ 各章の内容の根拠となる Google Cloud 公式ドキュメントです(検索により最新性を確認済み)。
+
+
+
+
IAM基礎・カスタムロール・サービスアカウント
+
+
+
+
+
VPC Network Peering
+
+
+
+
+
Identity-Aware Proxy (IAP)
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/gcl/hands-on/gcp-security-fundamentals-guide/NavBar.tsx b/app/gcl/hands-on/gcp-security-fundamentals-guide/NavBar.tsx
new file mode 100644
index 000000000..2ea640615
--- /dev/null
+++ b/app/gcl/hands-on/gcp-security-fundamentals-guide/NavBar.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import React from 'react';
+
+/**
+ * Renders the navigation bar for the GCP Security Fundamentals guide.
+ *
+ * @returns The guide's navigation bar with links to its chapters and references
+ */
+export function NavBar() {
+ return (
+
+
+
GCP SECURITY FUNDAMENTALS
+
+
+
+ );
+}
diff --git a/app/gcl/hands-on/gcp-security-fundamentals-guide/constants.ts b/app/gcl/hands-on/gcp-security-fundamentals-guide/constants.ts
new file mode 100644
index 000000000..ae40d278c
--- /dev/null
+++ b/app/gcl/hands-on/gcp-security-fundamentals-guide/constants.ts
@@ -0,0 +1,188 @@
+export const DIAGRAMS = {
+ 'diag-0': `flowchart TB
+subgraph L1["レイヤー1: 誰がアクセスできるか(IAM)"]
+direction LR
+A1["基本ロール Owner/Editor/Viewer"] --> A2["カスタムロール 必要な権限だけを束ねる"] --> A3["サービスアカウント 人間以外のID"]
+end
+subgraph L2["レイヤー2: どの経路でアクセスできるか(ネットワーク)"]
+direction LR
+B1["VPC Peering プロジェクト間の内部通信"] --> B2["IAP アプリ層の認証プロキシ"] --> B3["Private GKE クラスタの外部露出を遮断"]
+end
+subgraph L3["レイヤー3: データそのものを守る(暗号化)"]
+direction LR
+C1["Cloud KMS 鍵の生成と権限分離"] --> C2["暗号化データの保存 Cloud Storage"]
+end
+L1 --> L2 --> L3`,
+
+ 'diag-1': `flowchart TD
+Org["組織"] -->|継承| Folder["フォルダ"]
+Folder -->|継承| Proj["プロジェクト"]
+Proj -->|継承| Res1["Cloud Storage バケット"]
+Proj -->|継承| Res2["Compute Engine インスタンス"]
+Proj -->|継承| Res3["BigQuery データセット"]
+Owner["Owner ロールを プロジェクトに付与"] -.->|自動的に配下すべてに適用| Res1
+Owner -.->|自動的に配下すべてに適用| Res2
+Owner -.->|自動的に配下すべてに適用| Res3`,
+
+ 'diag-2': `sequenceDiagram
+actor U2 as User2 (Viewer剥奪後)
+participant Console as Cloud Console
+participant Bucket as Cloud Storage バケット
+U2->>Console: プロジェクトのリソース一覧を見ようとする
+Console-->>U2: Permission Denied(プロジェクトViewerがない)
+U2->>Bucket: バケットに直接アクセス(gcloud storage ls)
+Note over Bucket: roles/storage.objectViewer が個別に付与されている
+Bucket-->>U2: ファイル一覧を取得できる`,
+
+ 'diag-3': `sequenceDiagram
+participant Admin as 管理者
+participant IAM as Cloud IAM
+Admin->>IAM: describe でロール定義を取得(etag AAA)
+Admin->>Admin: ローカルで権限を追加編集
+Admin->>IAM: update でetag AAA を添えて送信
+alt etagが一致
+ IAM-->>Admin: 更新成功、新しいetag BBB が発行される
+else 他の変更が先に入りetagが不一致
+ IAM-->>Admin: 更新拒否(競合を検出)
+end`,
+
+ 'diag-4': `flowchart LR
+A["作成 iam roles create"] --> B["更新 iam roles update"]
+B --> C["無効化 stage DISABLED"]
+C --> D["削除 iam roles delete"]
+D -->|7日以内なら| E["復元 iam roles undelete"]
+D -->|7日経過| F["完全削除プロセス 最大30日、計37日で完全消滅"]
+F --> G["37日後、同じRole IDが 再利用可能になる"]`,
+
+ 'diag-5': `flowchart TB
+subgraph Case1["ケース1: サービスアカウントを「ID」として扱う"]
+VM["Compute Engine VM"] -->|"このVMとして動作する"| SA1["サービスアカウント"]
+SA1 -->|"IAMロールを付与"| R1["Cloud Storage / BigQuery"]
+end
+subgraph Case2["ケース2: サービスアカウントを「リソース」として扱う"]
+U["人間のユーザー"] -->|"serviceAccountUser を付与"| SA2["サービスアカウント"]
+SA2 -->|"このSAとしてVMを起動できる"| VM2["VMインスタンス"]
+end`,
+
+ 'diag-6': `sequenceDiagram
+participant App as Pythonアプリ(VM上)
+participant Meta as VMメタデータサーバー
+participant BQ as BigQuery API
+App->>Meta: サービスアカウントの認証情報を要求
+Meta-->>App: 一時的なアクセストークンを返却
+App->>BQ: トークンを添えてクエリを実行
+Note over BQ: bigquery-qwiklab SA が必要なロールを持つことを確認
+BQ-->>App: クエリ結果を返却`,
+
+ 'diag-7': `sequenceDiagram
+participant A as project-A (network-a)
+participant B as project-B (network-b)
+A->>A: ピア接続 "peer-ab" を作成(相手先 project-B / network-b)
+Note over A: 状態 = INACTIVE(Waiting for peer network to connect)
+B->>B: ピア接続 "peer-ba" を作成(相手先 project-A / network-a)
+Note over A,B: 双方の設定が揃った瞬間に状態 = ACTIVE
+A-->>B: ルートが自動的に交換される
+B-->>A: 内部IPでの相互通信が可能になる`,
+
+ 'diag-8': `sequenceDiagram
+actor User as ユーザー
+participant IAP as Identity-Aware Proxy
+participant GIS as Google Identity Service
+participant App as Cloud Run アプリ
+User->>IAP: アプリのURLへHTTPSリクエスト
+IAP->>GIS: サインインを要求
+GIS-->>User: Googleログイン画面を表示
+User->>GIS: 認証情報を入力
+GIS-->>IAP: 認証済みIDを返却
+IAP->>IAP: IAM許可ポリシーを確認(IAP-secured Web App User)
+alt 権限あり
+ IAP->>App: リクエスト転送 + 認証ユーザー情報のヘッダーを付与
+ App-->>User: ユーザー情報を含むレスポンス
+else 権限なし
+ IAP-->>User: アクセス拒否画面
+end`,
+
+ 'diag-9': `flowchart LR
+subgraph Danger["⚠ 危険な状態: IAPがオフ、ヘッダーだけを信頼している"]
+Attacker["攻撃者"] -->|"認証ヘッダーを偽装"| App1["アプリ"]
+App1 -->|"ヘッダーをそのまま信用"| Result1["なりすまし成功"]
+end
+subgraph Safe["✅ 安全な状態: 署名付きJWTを検証している"]
+Attacker2["攻撃者"] -->|"JWTアサーションを偽装しようとする"| App2["アプリ"]
+App2 -->|"Googleの公開鍵で署名を検証"| Result2["署名が一致しない"]
+Result2 --> Result3["拒否される(なりすまし不可)"]
+end`,
+
+ 'diag-10': `flowchart TB
+PRJ["プロジェクト"] --> KR["KeyRing: labkey (location global)"]
+KR --> CK["CryptoKey: qwiklab (purpose encryption)"]
+CK --> V1["Key Version 1"]
+CK --> V2["Key Version 2 (ローテーション後)"]`,
+
+ 'diag-11': `flowchart LR
+P["平文データ (1.txt)"] -->|"base64エンコード"| B64["Base64文字列"]
+B64 -->|"encrypt API を呼び出し"| KMS["Cloud KMS CryptoKey qwiklab"]
+KMS -->|"ciphertext を返却"| ENC["暗号化データ (1.encrypted)"]
+ENC -->|"アップロード"| GCS["Cloud Storage バケット"]
+ENC -.->|"decrypt API を呼び出し(検証用)"| KMS
+KMS -.->|"plaintext を返却"| P`,
+
+ 'diag-12': `flowchart TB
+Admin["鍵管理者 roles/cloudkms.admin"] -->|"KeyRing/CryptoKeyの作成・破棄"| KR["KeyRing / CryptoKey"]
+App["アプリ用SA roles/cloudkms.cryptoKeyEncrypterDecrypter"] -->|"encrypt / decrypt APIのみ呼び出し可"| KR
+App -.->|"鍵の削除・無効化はできない"| KR`,
+
+ 'diag-13': `flowchart TB
+subgraph VPC["自分のVPCネットワーク"]
+subgraph Subnet["ノード用サブネット"]
+Nodes["GKEノード群(内部IPのみ)"]
+end
+PodRange["Podセカンダリレンジ 例:10.40.0.0/14"]
+SvcRange["Serviceセカンダリレンジ 例:10.0.16.0/20"]
+end
+subgraph GoogleVPC["Google管理VPC(ピア接続)"]
+CP["コントロールプレーン 例:172.16.0.16/28"]
+end
+Nodes <-->|"VPC Peering(自動構成)"| CP
+Allowed["許可された外部IP(master-authorized-networks)"] -.->|"認可された通信のみ通す"| CP
+Internet(("インターネット")) -.->|"直接アクセス不可"| Nodes
+Internet -.->|"直接アクセス不可(デフォルト)"| CP`,
+
+ 'diag-14': `flowchart LR
+subgraph WithPublic["--enable-private-endpoint なし"]
+Admin1["管理者(社外)"] -->|"承認済みIPなら到達可"| CP1["コントロールプレーン(パブリックIPあり)"]
+end
+subgraph WithoutPublic["--enable-private-endpoint あり(より安全)"]
+Admin2["管理者(社外)"] -.->|"到達不可"| CP2["コントロールプレーン(パブリックIPなし)"]
+Jump["踏み台ホスト(同一VPC内)"] -->|"内部IP経由でのみ到達可"| CP2
+Admin2 -->|"まずVPN/IAPで踏み台に接続"| Jump
+end`,
+
+ 'diag-15': `flowchart TB
+subgraph IAMLayer["① IAMレイヤー: 最小権限の設計"]
+SA["専用サービスアカウント"]
+CR["カスタムロール: storage操作4権限"]
+BR1["roles/monitoring.viewer"]
+BR2["roles/monitoring.metricWriter"]
+BR3["roles/logging.logWriter"]
+SA --> CR
+SA --> BR1
+SA --> BR2
+SA --> BR3
+end
+subgraph NetworkLayer["② ネットワークレイヤー: 隔離設計"]
+VPC2["Orca Build VPC"]
+Subnet2["orca-build-subnet"]
+MgmtSubnet["orca-mgmt-subnet"]
+Jump["orca-jumphost"]
+VPC2 --> Subnet2
+MgmtSubnet --> Jump
+end
+subgraph ClusterLayer["③ クラスタレイヤー: Private GKE"]
+PrivCluster["Cluster private-nodes / private-endpoint / authorized-networks"]
+end
+SA -->|"サービスアカウントとして指定"| PrivCluster
+Subnet2 -->|"デプロイ先ネットワーク"| PrivCluster
+Jump -->|"internal-ip 経由 kubectl 接続"| PrivCluster
+PrivCluster -->|"jumphostの内部IPを/32で登録"| Jump`
+} as const;
diff --git a/app/gcl/hands-on/gcp-security-fundamentals-guide/page.css b/app/gcl/hands-on/gcp-security-fundamentals-guide/page.css
new file mode 100644
index 000000000..93baeaaa3
--- /dev/null
+++ b/app/gcl/hands-on/gcp-security-fundamentals-guide/page.css
@@ -0,0 +1,671 @@
+/* TODO: Replace these page-scoped tokens with globals.css theme tokens such as
+ --color-background when the guide theme is consolidated. */
+.gcp-security-page {
+ --bg: #030712;
+ --surface: #0b1220;
+ --surface-2: #0f172a;
+ --border: rgba(148, 163, 184, 0.16);
+ --border-strong: rgba(148, 163, 184, 0.3);
+ --text: #e2e8f0;
+ --text-muted: #8b98ad;
+ --cyan: #22d3ee;
+ --violet: #a78bfa;
+ --pink: #f472b6;
+ --amber: #fbbf24;
+ --green: #34d399;
+ --red: #f87171;
+
+ width: 100%;
+ min-height: 100vh;
+ background:
+ radial-gradient(ellipse 900px 500px at 12% -10%, rgba(34, 211, 238, 0.1), transparent),
+ radial-gradient(ellipse 900px 600px at 100% 0%, rgba(167, 139, 250, 0.09), transparent),
+ var(--bg);
+ color: var(--text);
+ font-family: var(--font-body);
+ line-height: 1.75;
+ font-weight: 400;
+ -webkit-font-smoothing: antialiased;
+}
+
+.gcp-security-page h1,
+.gcp-security-page h2,
+.gcp-security-page h3,
+.gcp-security-page h4 {
+ font-family: var(--font-display);
+ color: #f8fafc;
+ letter-spacing: 0.01em;
+ line-height: 1.3;
+}
+
+.gcp-security-page a {
+ color: var(--cyan);
+ text-decoration: none;
+}
+
+.gcp-security-page a:hover {
+ text-decoration: underline;
+}
+
+.gcp-security-page p {
+ color: var(--text);
+}
+
+.gcp-security-page code {
+ font-family: var(--font-mono);
+}
+
+/* ---------- Nav ---------- */
+.gcp-security-page .nav {
+ position: sticky;
+ top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px));
+ z-index: 40;
+ backdrop-filter: blur(14px);
+ background: rgba(3, 7, 18, 0.82);
+ border-bottom: 1px solid var(--border);
+ width: 100%;
+}
+
+.gcp-security-page .nav-inner {
+ width: 100%;
+ max-width: 100%;
+ margin: 0 auto;
+ padding: 14px clamp(16px, 4vw, 40px);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 20px;
+}
+
+.gcp-security-page .nav-brand {
+ font-family: var(--font-mono);
+ font-size: 12.5px;
+ letter-spacing: 0.14em;
+ color: var(--cyan);
+ text-transform: uppercase;
+ white-space: nowrap;
+}
+
+.gcp-security-page .nav-links {
+ display: flex;
+ gap: 18px;
+ flex-wrap: wrap;
+ font-size: 12.5px;
+ font-family: var(--font-mono);
+}
+
+.gcp-security-page .nav-links a {
+ color: var(--text-muted);
+}
+
+.gcp-security-page .nav-links a:hover {
+ color: var(--cyan);
+ text-decoration: none;
+}
+
+@media (max-width: 900px) {
+ .gcp-security-page .nav-links {
+ display: none;
+ }
+}
+
+/* ---------- Hero (full-width background, centered content) ---------- */
+.gcp-security-page .hero {
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 64px clamp(16px, 4vw, 40px) 40px;
+}
+
+.gcp-security-page .hero .eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-family: var(--font-mono);
+ font-size: 12px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--violet);
+ border: 1px solid var(--border-strong);
+ border-radius: 999px;
+ padding: 6px 14px;
+ margin-bottom: 26px;
+}
+
+.gcp-security-page .hero .eyebrow::before {
+ content: '';
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--cyan);
+ box-shadow: 0 0 12px var(--cyan);
+}
+
+.gcp-security-page .hero h1 {
+ font-size: clamp(32px, 5vw, 54px);
+ font-weight: 800;
+ margin: 0 0 14px;
+ background: linear-gradient(120deg, #f8fafc 20%, var(--cyan) 60%, var(--violet) 100%);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.gcp-security-page .hero .sub {
+ font-family: var(--font-display);
+ font-size: clamp(16px, 2vw, 21px);
+ color: var(--text-muted);
+ font-weight: 600;
+ margin: 0 0 30px;
+}
+
+.gcp-security-page .hero-meta {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 16px;
+ margin-top: 34px;
+}
+
+.gcp-security-page .hero-meta .card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 14px;
+ padding: 18px 20px;
+}
+
+.gcp-security-page .hero-meta .card .label {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--cyan);
+ margin-bottom: 6px;
+}
+
+.gcp-security-page .hero-meta .card .value {
+ font-size: 14.5px;
+ color: var(--text);
+}
+
+/* ---------- Layout (centered readable content) ---------- */
+.gcp-security-page .wrap {
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 clamp(16px, 4vw, 40px) 60px;
+}
+
+.gcp-security-page section.chapter {
+ margin-top: 64px;
+ padding-top: 20px;
+ border-top: 1px solid var(--border);
+ scroll-margin-top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px) + 20px);
+}
+
+.gcp-security-page .chapter-head {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ flex-wrap: wrap;
+ margin-bottom: 6px;
+}
+
+.gcp-security-page .chapter-num {
+ font-family: var(--font-mono);
+ font-size: 13px;
+ color: var(--bg);
+ background: var(--cyan);
+ border-radius: 8px;
+ padding: 4px 10px;
+ font-weight: 700;
+}
+
+.gcp-security-page .chapter-head h2 {
+ font-size: clamp(22px, 3vw, 30px);
+ margin: 0;
+}
+
+.gcp-security-page .k-badge {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.06em;
+ border: 1px solid var(--border-strong);
+ border-radius: 999px;
+ padding: 4px 12px;
+ color: var(--amber);
+ background: rgba(251, 191, 36, 0.06);
+}
+
+.gcp-security-page .chapter-lead {
+ color: var(--text-muted);
+ font-size: 15px;
+ margin: 10px 0 26px;
+}
+
+.gcp-security-page h3.section-h {
+ font-size: 19px;
+ margin: 38px 0 14px;
+ padding-left: 14px;
+ border-left: 3px solid var(--violet);
+ color: #f1f5f9;
+}
+
+/* ---------- Callouts ---------- */
+.gcp-security-page .callout {
+ border-radius: 14px;
+ padding: 20px 22px;
+ margin: 22px 0;
+ border: 1px solid var(--border);
+ background: var(--surface);
+ position: relative;
+}
+
+.gcp-security-page .callout .tag {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ margin-bottom: 8px;
+ display: block;
+}
+
+.gcp-security-page .callout.def {
+ border-left: 3px solid var(--cyan);
+}
+
+.gcp-security-page .callout.def .tag {
+ color: var(--cyan);
+}
+
+.gcp-security-page .callout.why {
+ border-left: 3px solid var(--violet);
+ background: rgba(167, 139, 250, 0.05);
+}
+
+.gcp-security-page .callout.why .tag {
+ color: var(--violet);
+}
+
+.gcp-security-page .callout.warn {
+ border-left: 3px solid var(--amber);
+ background: rgba(251, 191, 36, 0.06);
+}
+
+.gcp-security-page .callout.warn .tag {
+ color: var(--amber);
+}
+
+.gcp-security-page .callout.tip {
+ border-left: 3px solid var(--green);
+ background: rgba(52, 211, 153, 0.05);
+}
+
+.gcp-security-page .callout.tip .tag {
+ color: var(--green);
+}
+
+.gcp-security-page .callout p:last-child {
+ margin-bottom: 0;
+}
+
+/* ---------- Tables ---------- */
+.gcp-security-page .table-wrap {
+ overflow-x: auto;
+ margin: 20px 0;
+ border: 1px solid var(--border);
+ border-radius: 14px;
+ width: 100%;
+}
+
+.gcp-security-page table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 14px;
+ min-width: 520px;
+}
+
+.gcp-security-page thead th {
+ text-align: left;
+ font-family: var(--font-mono);
+ font-size: 11.5px;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: var(--cyan);
+ background: rgba(34, 211, 238, 0.06);
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--border);
+ white-space: nowrap;
+}
+
+.gcp-security-page tbody td {
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--border);
+ color: var(--text);
+ vertical-align: top;
+}
+
+.gcp-security-page tbody tr:last-child td {
+ border-bottom: none;
+}
+
+.gcp-security-page tbody tr:hover {
+ background: rgba(148, 163, 184, 0.04);
+}
+
+/* ---------- Code ---------- */
+.gcp-security-page .code-block {
+ background: #080d19;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ padding: 18px 20px;
+ overflow-x: auto;
+ margin: 18px 0;
+ position: relative;
+ width: 100%;
+}
+
+.gcp-security-page .code-block .lang {
+ position: absolute;
+ top: 10px;
+ right: 16px;
+ font-family: var(--font-mono);
+ font-size: 10.5px;
+ color: var(--text-muted);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.gcp-security-page .code-block pre {
+ margin: 0;
+ font-family: var(--font-mono);
+ font-size: 13px;
+ color: #a5f3fc;
+ line-height: 1.7;
+}
+
+.gcp-security-page .code-line {
+ white-space: pre;
+}
+
+.gcp-security-page .code-block .cm {
+ color: #64748b;
+}
+
+/* ---------- Diagram & 1rem Text Scaling (.claude/skills/fix-mermaid 準拠) ----------
+ 鉄則: preserveNaturalScale=true の場合は 表示SVG幅 = viewBox幅。
+ 親 .diagram-wrap に overflow-x:auto を置き、内側 .mermaid は幅を min-content にして
+ SVG が自然幅(viewBox幅px)より縮まらないようにする。
+ max-width:100% は .mermaid の外側(.diagram-wrap)が担い、SVG 自体には maxWidth は
+ applySvgFixups が width:${w}px + maxWidth:100% で付与済みのため CSS から重複指定しない。
+ ---------------------------------------------------------------- */
+.gcp-security-page .diagram-wrap {
+ background: var(--surface-2);
+ border: 1px solid var(--border);
+ border-radius: 16px;
+ padding: 24px;
+ margin: 24px 0;
+ width: 100%;
+ /* スクロールコンテナ: SVG が画面幅より広い場合は横スクロールで自然サイズを維持 */
+ overflow-x: auto;
+ scrollbar-width: thin;
+ scrollbar-color: var(--cyan) transparent;
+}
+
+.gcp-security-page .diagram-caption {
+ font-family: var(--font-mono);
+ font-size: 11.5px;
+ color: var(--text-muted);
+ margin-top: 14px;
+ text-align: center;
+ letter-spacing: 0.03em;
+}
+
+/* .mermaid は内側コンテナ。min-content にして SVG の自然幅を縮めない。
+ 幅が viewBox 幅より小さいと max-width:100% が SVG を縮小して文字が 1rem 未満になる。 */
+.gcp-security-page .mermaid {
+ display: flex;
+ justify-content: center;
+ /* min-content で内側の SVG の自然幅(px)を確保 */
+ width: min-content;
+ min-width: 100%;
+}
+
+/* SVG 自体のサイズは applySvgFixups が style で注入するため、CSS では layout 責務のみ */
+.gcp-security-page .mermaid svg {
+ display: block;
+ height: auto !important;
+}
+
+/* ---------- Compare grid (good/bad) ---------- */
+.gcp-security-page .compare-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+ gap: 16px;
+ margin: 20px 0;
+}
+
+.gcp-security-page .compare-col {
+ border-radius: 14px;
+ padding: 18px 20px;
+ border: 1px solid var(--border);
+}
+
+.gcp-security-page .compare-col.good {
+ background: rgba(52, 211, 153, 0.05);
+ border-color: rgba(52, 211, 153, 0.3);
+}
+
+.gcp-security-page .compare-col.bad {
+ background: rgba(248, 113, 113, 0.05);
+ border-color: rgba(248, 113, 113, 0.3);
+}
+
+.gcp-security-page .compare-col h4 {
+ font-family: var(--font-mono);
+ font-size: 12.5px;
+ letter-spacing: 0.06em;
+ margin: 0 0 12px;
+ text-transform: uppercase;
+}
+
+.gcp-security-page .compare-col.good h4 {
+ color: var(--green);
+}
+
+.gcp-security-page .compare-col.bad h4 {
+ color: var(--red);
+}
+
+.gcp-security-page .compare-col ul {
+ margin: 0;
+ padding-left: 20px;
+}
+
+.gcp-security-page .compare-col li {
+ margin-bottom: 8px;
+ font-size: 14px;
+ color: var(--text);
+}
+
+/* ---------- Step list ---------- */
+.gcp-security-page .step-list {
+ list-style: none;
+ margin: 20px 0;
+ padding: 0;
+ counter-reset: step;
+}
+
+.gcp-security-page .step-list li {
+ position: relative;
+ padding: 6px 0 6px 42px;
+ margin-bottom: 10px;
+ font-size: 14.5px;
+ counter-increment: step;
+}
+
+.gcp-security-page .step-list li::before {
+ content: counter(step);
+ position: absolute;
+ left: 0;
+ top: 4px;
+ width: 28px;
+ height: 28px;
+ border-radius: 8px;
+ background: linear-gradient(135deg, var(--cyan), var(--violet));
+ color: #04121c;
+ font-family: var(--font-mono);
+ font-weight: 700;
+ font-size: 13px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* ---------- Toc grid ---------- */
+.gcp-security-page .toc-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 16px;
+ margin: 20px 0;
+}
+
+.gcp-security-page .toc-card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 14px;
+ padding: 18px 20px;
+ text-decoration: none;
+ color: var(--text);
+ transition: transform 0.2s, border-color 0.2s;
+}
+
+.gcp-security-page .toc-card:hover {
+ transform: translateY(-2px);
+ border-color: var(--cyan);
+ text-decoration: none;
+}
+
+.gcp-security-page .toc-card .n {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.1em;
+ color: var(--cyan);
+ margin-bottom: 6px;
+}
+
+.gcp-security-page .toc-card .t {
+ font-family: var(--font-display);
+ font-size: 15px;
+ font-weight: 700;
+ color: #f1f5f9;
+ margin-bottom: 6px;
+}
+
+.gcp-security-page .toc-card .d {
+ font-size: 13px;
+ color: var(--text-muted);
+ line-height: 1.5;
+}
+
+/* ---------- References Section ---------- */
+.gcp-security-page .refs-group {
+ margin: 28px 0;
+ padding: 0;
+}
+
+.gcp-security-page .refs-group-label {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--cyan);
+ padding: 5px 12px;
+ border-left: 3px solid var(--cyan);
+ background: rgba(34, 211, 238, 0.05);
+ border-radius: 0 6px 6px 0;
+ margin-bottom: 10px;
+ display: inline-block;
+}
+
+.gcp-security-page .refs-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+.gcp-security-page .ref-item {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding: 12px 18px;
+ border-bottom: 1px solid var(--border);
+ transition: background 0.15s;
+}
+
+.gcp-security-page .ref-item:last-child {
+ border-bottom: none;
+}
+
+.gcp-security-page .ref-item:hover {
+ background: rgba(34, 211, 238, 0.04);
+}
+
+.gcp-security-page .ref-title {
+ font-size: 13.5px;
+ font-weight: 600;
+ color: var(--text);
+ white-space: nowrap;
+ min-width: 240px;
+ flex-shrink: 0;
+}
+
+.gcp-security-page .ref-link {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ color: var(--cyan);
+ text-decoration: none;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ min-width: 0;
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ opacity: 0.85;
+ transition: opacity 0.15s;
+}
+
+.gcp-security-page .ref-link::before {
+ content: '↗';
+ font-size: 11px;
+ flex-shrink: 0;
+ opacity: 0.6;
+}
+
+.gcp-security-page .ref-link:hover {
+ opacity: 1;
+ text-decoration: underline;
+}
+
+/* モバイル対応: 縦並びに変換 */
+@media (max-width: 640px) {
+ .gcp-security-page .ref-item {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 6px;
+ }
+
+ .gcp-security-page .ref-title {
+ min-width: unset;
+ white-space: normal;
+ }
+
+ .gcp-security-page .ref-link {
+ white-space: normal;
+ word-break: break-all;
+ }
+}
diff --git a/app/gcl/hands-on/gcp-security-fundamentals-guide/page.tsx b/app/gcl/hands-on/gcp-security-fundamentals-guide/page.tsx
new file mode 100644
index 000000000..344a32266
--- /dev/null
+++ b/app/gcl/hands-on/gcp-security-fundamentals-guide/page.tsx
@@ -0,0 +1,12 @@
+import type { Metadata } from 'next';
+import { GcpSecurityFundamentalsGuide } from './GcpSecurityFundamentalsGuide';
+import './page.css';
+
+export const metadata: Metadata = {
+ title: 'Google Cloud セキュリティ基礎 完全ガイド | Associate Cloud Engineer ハンズオン',
+ description: 'IAM, カスタムロール, サービスアカウント, VPC Peering, IAP, Cloud KMS, Private GKE を活用した最小権限の原則に基づく Google Cloud セキュリティ設計の完全ガイド。',
+};
+
+export default function Page() {
+ return ;
+}
diff --git a/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/IapTcpForwardingGuide.tsx b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/IapTcpForwardingGuide.tsx
new file mode 100644
index 000000000..0155de853
--- /dev/null
+++ b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/IapTcpForwardingGuide.tsx
@@ -0,0 +1,906 @@
+'use client';
+
+import { MermaidDiagram } from '@/components/MermaidDiagram';
+import NavBar from './NavBar';
+import { DIAGRAMS } from './constants';
+import './page.css';
+
+/**
+ * Renders a guide to securely accessing VMs without external IP addresses through IAP TCP forwarding.
+ *
+ * @returns The rendered IAP TCP forwarding best-practices guide.
+ */
+export default function IapTcpForwardingGuide() {
+ return (
+
+
+
+
+
+
+ GOOGLE CLOUD · SECURITY
+
+ IAP(Identity-Aware Proxy)TCP フォワーディング ベストプラクティスガイド
+
+
+ 外部IPなしのVMへ安全に接続する ― 初学者向けステップバイステップ解説
+
+
+
+ 対象読者: Google Cloud
+ を学び始めた初学者〜VMの踏み台構成を見直したいエンジニア
+
+
+ 前提知識:
+ コンソールの基本操作、VPC・ファイアウォールの初歩
+
+
+
+
+ {/* 1. このガイドについて */}
+
+ 1. このガイドについて
+
+ このガイドは、「外部IPアドレスを持たないVM(Linux/Windows)に、Identity-Aware
+ Proxy(IAP)のTCPフォワーディング機能を使って安全にSSH/RDP接続する」というハンズオン内容を題材に、
+ 実務で使うベストプラクティス の観点から解説し直したものです。
+
+
+ ハンズオンそのものは学習用に単純化されているため、本ガイドでは各ステップについて
+
+
+ ハンズオンで行っている操作の意味
+
+ 本番環境で採用すべきベストプラクティス(ハンズオンとの差分がある場合は明記)
+
+ 公式ドキュメントなど根拠となる出典URL
+
+ の3点をセットで示します。
+
+
+
+ {/* 2. IAPとは何か、なぜ使うのか */}
+
+ 2. IAPとは何か、なぜ使うのか
+
+ 2.1 課題: 「踏み台サーバー」と外部IPのリスク
+
+
+ 従来型のインフラでは、社内ネットワークの外からVMに接続するために、以下のいずれかが必要でした。
+
+
+ VMに外部IPアドレスを割り当てて直接SSH/RDPを開放する
+
+ 踏み台(Bastion)サーバーを外部公開し、そこを経由して内部VMへ接続する
+
+
+
+ どちらの方式も、インターネットに露出するポートが常に存在する という点で攻撃対象領域(アタックサーフェス)を広げてしまいます。
+
+
+ 2.2 IAPというアプローチ
+
+ IAP TCP フォワーディングは、Google が提唱する
+ BeyondCorp(ゼロトラストネットワーク)
+ の考え方に基づく機能です。VM側にはポートを外部公開せず、代わりに次の流れでアクセスを仲介します。
+
+
+
+ クライアント(gcloud CLI・ブラウザ・IAP
+ Desktopなど)がIAPに対してHTTPSでトンネル確立を要求する
+
+
+ IAPは要求元のIDに対して
+ IAMポリシー (「誰が」「どのVMに」アクセスできるか)を判定する
+
+
+ 許可された場合のみ、HTTPSでラップされたTCPトラフィック(SSHやRDP)をVMの内部IPへ中継する
+
+
+
+ つまり「ネットワークの位置(社内かどうか)」ではなく「IDと権限 」でアクセス可否を決めるのがIAPの本質です。この仕組みにより、VMは外部IPアドレスを一切持たずに運用できます。
+
+
+
+ 参考:{' '}
+
+ TCP forwarding overview(Google Cloud公式)
+
+
+
+
+
+
+ {/* 3. 全体アーキテクチャ */}
+
+ 3. 全体アーキテクチャ
+ ハンズオンで構築する構成は次の通りです。
+
+
+
+
+
+ linux-iap と windows-iap は
+ 外部IPアドレスを持たない デモ用インスタンスです。
+
+
+ windows-connectivity は、学習のために外部IPを持たせた検証専用VMで、ここから gcloud やIAP Desktopを操作してIAP経由の接続を確認します。
+
+
+ 実際の本番運用では、この「踏み台的に使うクライアント」自体も管理者の手元PC(会社支給端末など)に置き換えることができます。
+
+
+
+
+
+ {/* 4. 作業の全体像 */}
+
+ 4. 作業の全体像
+
+
+
+
+ #
+ タスク
+ 目的
+
+
+
+
+ 1
+ IAP TCP forwarding APIの有効化
+ プロジェクトでIAPのTCP機能を使えるようにする
+
+
+ 2
+ VMインスタンスの作成
+ 外部IPなしのVM2台+検証用VM1台を用意する
+
+
+ 3
+ 接続不可であることの確認
+ 「外部IPがないと直接は繋がらない」ことを体感する
+
+
+ 4
+ ファイアウォールルールの作成
+ IAPの送信元IP範囲からの通信のみを許可する
+
+
+ 5
+ IAM権限の付与
+ 「誰が」IAPトンネルを使えるかを最小権限で設定する
+
+
+ 6
+ IAP Desktopでの接続
+ GUIツールでSSH/RDP接続を体験する
+
+
+ 7
+ gcloud CLIでのトンネリング
+ CLIでSSHトンネル・RDPトンネルを手動で張る
+
+
+
+
+
+
+
+ {/* 5. ステップ別解説 */}
+
+ 5. ステップ別解説
+
+
+ 5.1 Task 1: IAP TCP forwarding APIの有効化
+
+
+ 操作 : ナビゲーションメニュー → 「APIとサービス」→「ライブラリ」→「Cloud Identity-Aware Proxy API」を検索して有効化する。
+
+
+ ポイント : IAPは「HTTPS経由でIAP対応アプリを保護する機能」と「TCPフォワーディングでVMに接続する機能」の2つの側面がありますが、どちらも同じ Cloud Identity-Aware Proxy API の有効化が前提になります。API自体の有効化は課金を発生させるものではなく、以降の設定(ファイアウォール・IAM)が本体です。
+
+
+
+ 参考:{' '}
+
+ Identity-Aware Proxy ドキュメント(Google Cloud公式)
+
+
+
+
+
+
+ 5.2 Task 2: VMインスタンスの作成(ベストプラクティス注記)
+
+ ハンズオンでは3台のVMを作成します。
+
+ linux-iap: Linux、外部IPなし
+ windows-iap: Windows Server、外部IPなし
+
+ windows-connectivity: 検証用、外部IPあり、フルアクセスのアクセススコープ
+
+
+ 本番運用でのベストプラクティス :
+
+
+
+
+ 項目
+ ハンズオンの設定
+ 本番でのベストプラクティス
+
+
+
+
+ 外部IP
+ 業務VMは「なし」に設定
+
+ 同様に「なし」を徹底し、必要な外向き通信はCloud NATで代替する
+
+
+
+ サービスアカウントのアクセススコープ
+ 検証VMのみ「Cloud APIへのフルアクセス」を許可
+
+ 本番VMでは用途に応じた最小限のスコープ 、または専用サービスアカウント+IAMロールの組み合わせを使う
+
+
+
+ ネットワークタグ
+ 特に設定なし
+
+ ファイアウォールルールの対象を絞るため、役割ごとにネットワークタグを付与しておく
+
+
+
+
+
+
+ 「フルアクセス」のアクセススコープはハンズオンを単純化するためのものであり、本番環境では権限の過剰付与(Over-Privilege)につながるため推奨されません。
+
+
+
+
+ 5.3 Task 3: 接続不可であることの確認
+
+
+ 外部IPを持たない linux-iap にSSHボタンで接続しようとするとエラーになり、windows-iap へのRDPも同様に失敗します。これは設計通りの動作 です。
+
+
+ 学びのポイント : SSH/RDPボタン自体はクリックできる状態でも、実際には「外部IPアドレスがないため接続できません」というメッセージが表示されます。これは、外部IPの有無とコンソールUIの見た目が必ずしも一致しないため、VM詳細ページでボタンにカーソルを合わせて明示的に確認する習慣が重要であることを示しています。
+
+
+
+
+ 5.4 Task 4: ファイアウォールルールの作成(重要な差分あり)
+
+ ハンズオンの設定はこちらです。
+
+
+
+
+ 項目
+ 設定値
+
+
+
+
+ 名前
+ allow-ingress-from-iap
+
+
+ トラフィックの方向
+ Ingress
+
+
+ ターゲット
+ ネットワーク内のすべてのインスタンス
+
+
+ ソースフィルタ
+ IPv4範囲
+
+
+ ソースIPv4範囲
+ 35.235.240.0/20
+
+
+ プロトコルとポート
+ TCP 22(SSH)、3389(RDP)
+
+
+
+
+
+ 35.235.240.0/20 は IAPがTCPフォワーディングに使用する固定のIPアドレス範囲 であり、この範囲以外からの通信を許可しても、IAP経由の接続は成立しません。IPv6環境の場合は 2600:2d00:1:7::/64 を使用します。
+
+
+
+ 参考:{' '}
+
+ Use IAP for TCP forwarding(Google Cloud公式)
+
+
+
+
+ ベストプラクティスとの差分 : ハンズオンでは学習を簡単にするため「ネットワーク内のすべてのインスタンス」をターゲットにしていますが、これはGoogle自身が「多くの場合、避けるべき選択肢」と明言している設定です。理由は、対象を絞らないと、本来意図していない他のVMまでこのルールの影響範囲に入ってしまうためです。
+
+
+
+
+
+
+
+
+ 方式
+ 特徴
+ 向いているケース
+
+
+
+
+ すべてのインスタンス
+ 設定は簡単だが、意図しないVMにも適用されるリスクがある
+ 検証・学習環境のみ
+
+
+ ターゲットタグ
+ ワークロードの役割ごとにグルーピングしやすい
+ 多くの本番環境での標準的な選択
+
+
+ ターゲットサービスアカウント
+
+ タグより厳格。編集権限だけでなく該当サービスアカウントの使用権限も必要になるため改ざんされにくい
+
+ ワークロードIDベースでアクセス制御したい環境
+
+
+
+
+
+ 参考:
+
+
+
+
+
+ 5.5 Task 5: IAM権限の付与(最小権限の原則)
+
+
+ ハンズオンでは、「セキュリティ」→「Identity-Aware Proxy」の「SSH and TCP Resources」タブから、VM単位で windows-connectivity のサービスアカウントと学習用アカウントに roles/iap.tunnelResourceAccessor(IAP-Secured Tunnel User)ロールを付与します。
+
+
+
+
+
+ このハンズオンが既に良い点 : roles/iap.tunnelResourceAccessor をプロジェクト全体ではなくVM単位 で付与している点は、最小権限の原則(Principle of Least Privilege)に沿ったベストプラクティスです。
+
+
+ さらに踏み込んだベストプラクティス : 本番環境では、VM単位の付与に加えて IAM条件(IAM Conditions) を使い、特定のポート番号のみに限定したり、コントラクター向けに有効期限付きでアクセスを許可したりすることが推奨されます。
+
+
+
+
+
+ CLIで同等の設定を行う場合の代表的なコマンドは次の通りです(値は環境に合わせて置き換えてください)。
+
+
+ gcloud compute instances add-iam-policy-binding INSTANCE_NAME \
+ --zone=ZONE \
+ --member="user:EMAIL" \
+ --role="roles/iap.tunnelResourceAccessor"
+
+ さらにポートを絞り込む場合は --condition を付与します。
+
+ gcloud compute instances add-iam-policy-binding INSTANCE_NAME \
+ --zone=ZONE \
+ --member="group:EMAIL" \
+ --role="roles/iap.tunnelResourceAccessor" \
+ --condition="expression=destination.port==22,title=ssh-only"
+
+
+ 参考:
+
+
+
+
+ 5.6 Task 6: IAP Desktopでの接続
+
+ IAP Desktopは、GoogleのSolutions Architectsチームが開発するオープンソースのWindowsアプリケーションで、IAP TCPフォワーディングを使ってSSH/RDP接続をGUIで管理できます(Googleの公式サポート対象製品ではない点に注意してください)。
+
+ 接続までの流れ :
+
+ windows-connectivity インスタンスへRDP接続する
+
+ デスクトップ上のIAP Desktopを起動し、Googleアカウントでサインインする
+
+ 接続先プロジェクトを追加する
+
+ 対象VM(windows-iap)をダブルクリックし、初回接続時は「Generate new credentials」で認証情報を生成する
+
+
+
+ IAP Desktop自体も内部的にはIAP TCPフォワーディングを利用しているため、Task 4のファイアウォールルールとTask 5のIAMロールが正しく設定されていることが前提 になります。
+
+
+ 参考:
+
+
+
+
+
+ 5.7 Task 7: gcloud CLIによるSSH/RDPトンネリング
+
+
+ SSH接続の場合 は、gcloud compute ssh コマンドが自動的にIAP経由のトンネルを検知して利用します。
+
+
+ gcloud compute ssh linux-iap --zone=ZONE
+
+
+ RDP接続の場合 は、RDPプロトコル自体がgcloudに組み込まれていないため、手動でローカルにトンネルを張り 、Windowsのリモートデスクトップ接続アプリからそのトンネル(localhost:ポート番号)へ接続します。
+
+
+ gcloud compute start-iap-tunnel windows-iap 3389 \
+ --local-host-port=localhost:0 \
+ --zone=ZONE
+
+
+ --local-host-port=localhost:0 はローカルの空きポートを自動的に割り当てる指定です。表示された Listening on port [XXXX] のポート番号を、リモートデスクトップ接続アプリの接続先に localhost:XXXX として入力します。
+
+
+
+
+
+
+ 参考:{' '}
+
+ gcloud compute start-iap-tunnel リファレンス(Google Cloud公式)
+
+
+
+
+
+
+ {/* 6. 本番環境への適用時のベストプラクティスまとめ */}
+
+
+ 6. 本番環境への適用時のベストプラクティスまとめ
+
+
+
+
+
+ {/* 7. トラブルシューティング */}
+
+ 7. トラブルシューティング
+
+
+
+
+
+
+
+ 症状
+ 主な原因
+ 対処
+
+
+
+
+ SSH/RDPボタンを押しても接続できない
+ 外部IPがない状態でIAP未設定
+ Task 4・Task 5の設定を確認
+
+
+ ファイアウォールルールはあるのに繋がらない
+ 送信元範囲やポート番号の記載ミス
+ 35.235.240.0/20 とポート22/3389を再確認
+
+
+ 「Permission denied」エラー
+ IAMロールが未付与、または付与先の粒度が誤っている
+
+ 該当プリンシパルに対象VM単位で roles/iap.tunnelResourceAccessor を付与
+
+
+
+ 社内ネットワークからだけ繋がらない
+ 社内プロキシがIAP用ドメインをブロックしている
+ IAP for TCP用ドメインを社内プロキシの許可リストに追加
+
+
+
+
+
+
+ 参考:{' '}
+
+ Use IAP for TCP forwarding(社内プロキシに関する注意事項)
+
+
+
+
+
+
+ {/* 8. まとめ */}
+
+ 8. まとめ
+
+
+ IAP TCPフォワーディングは、VMに外部IPを持たせずに「IDと権限」でSSH/RDPアクセスを制御する、ゼロトラストに基づいた仕組みである
+
+
+ 最低限必要なのは「①API有効化」「②IAPのIP範囲からのファイアウォール許可」「③IAM roles/iap.tunnelResourceAccessor の付与」の3点
+
+
+ ハンズオンの設定をそのまま本番へ持ち込むと、ファイアウォールのターゲットが広すぎる (すべてのインスタンス)という点が主な改善ポイントになる
+
+
+ IAM側は既にVM単位で権限を絞る良い設計になっており、さらにIAM Conditionsでポートや期限を絞ることで、より安全な運用に近づけられる
+
+
+
+
+
+ {/* 9. 参考文献 */}
+
+
+
+ 本ガイドは公開されているハンズオン教材の内容を基に、Google Cloud公式ドキュメント等を出典として再構成したものです。各セクション内のリンク先で最新情報をご確認ください。
+
+
+
+
+ );
+}
diff --git a/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/NavBar.tsx b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/NavBar.tsx
new file mode 100644
index 000000000..dfe7cc927
--- /dev/null
+++ b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/NavBar.tsx
@@ -0,0 +1,105 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { NAV_ITEMS } from './constants';
+
+/**
+ * Renders a responsive sidebar navigation with scroll-aware active section highlighting.
+ */
+export default function NavBar() {
+ const [isOpen, setIsOpen] = useState(false);
+ const [activeId, setActiveId] = useState('');
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+
+ const headingElements: Element[] = [];
+ NAV_ITEMS.forEach((item) => {
+ const el = document.getElementById(item.id);
+ if (el) headingElements.push(el);
+ item.subItems?.forEach((sub) => {
+ const subEl = document.getElementById(sub.id);
+ if (subEl) headingElements.push(subEl);
+ });
+ });
+
+ if (!headingElements.length || typeof IntersectionObserver === 'undefined') return;
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setActiveId(entry.target.id);
+ }
+ });
+ },
+ { root: null, rootMargin: '-15% 0px -70% 0px', threshold: 0 }
+ );
+
+ headingElements.forEach((el) => observer.observe(el));
+
+ return () => observer.disconnect();
+ }, []);
+
+ const toggleSidebar = () => setIsOpen((prev) => !prev);
+ const closeSidebar = () => setIsOpen(false);
+
+ return (
+ <>
+
+ ☰
+
+
+
+ >
+ );
+}
diff --git a/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/constants.ts b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/constants.ts
new file mode 100644
index 000000000..51812b6b4
--- /dev/null
+++ b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/constants.ts
@@ -0,0 +1,111 @@
+export interface NavItem {
+ id: string;
+ label: string;
+ subItems?: { id: string; label: string }[];
+}
+
+export const NAV_ITEMS: NavItem[] = [
+ { id: '1-このガイドについて', label: '1. このガイドについて' },
+ {
+ id: '2-iapとは何かなぜ使うのか',
+ label: '2. IAPとは何か、なぜ使うのか',
+ subItems: [
+ { id: '21-課題-踏み台サーバーと外部ipのリスク', label: '2.1 課題: 「踏み台サーバー」と外部IPのリスク' },
+ { id: '22-iapというアプローチ', label: '2.2 IAPというアプローチ' },
+ ],
+ },
+ { id: '3-全体アーキテクチャ', label: '3. 全体アーキテクチャ' },
+ { id: '4-作業の全体像', label: '4. 作業の全体像' },
+ {
+ id: '5-ステップ別解説',
+ label: '5. ステップ別解説',
+ subItems: [
+ { id: '51-task-1-iap-tcp-forwarding-apiの有効化', label: '5.1 Task 1: APIの有効化' },
+ { id: '52-task-2-vmインスタンスの作成ベストプラクティス注記', label: '5.2 Task 2: VMインスタンスの作成' },
+ { id: '53-task-3-接続不可であることの確認', label: '5.3 Task 3: 接続不可であることの確認' },
+ { id: '54-task-4-ファイアウォールルールの作成重要な差分あり', label: '5.4 Task 4: ファイアウォールルールの作成' },
+ { id: '55-task-5-iam権限の付与最小権限の原則', label: '5.5 Task 5: IAM権限の付与' },
+ { id: '56-task-6-iap-desktopでの接続', label: '5.6 Task 6: IAP Desktopでの接続' },
+ { id: '57-task-7-gcloud-cliによるsshrdpトンネリング', label: '5.7 Task 7: gcloud CLIによるトンネリング' },
+ ],
+ },
+ { id: '6-本番環境への適用時のベストプラクティスまとめ', label: '6. 本番環境ベストプラクティスまとめ' },
+ { id: '7-トラブルシューティング', label: '7. トラブルシューティング' },
+ { id: '8-まとめ', label: '8. まとめ' },
+ { id: '9-参考文献', label: '9. 参考文献' },
+];
+
+export const DIAGRAMS = {
+ architecture: `flowchart LR
+ subgraph Client["管理者のクライアント"]
+ A["gcloud CLI / ブラウザSSH / RDPクライアント"]
+ end
+
+ subgraph GCP["Google Cloud プロジェクト"]
+ IAP["Identity-Aware Proxy TCPフォワーディング"]
+ subgraph VPC["VPCネットワーク"]
+ FW["ファイアウォールルール 送信元: 35.235.240.0/20 ポート: TCP 22 / 3389 を許可"]
+ L["linux-iap (外部IPなし)"]
+ W["windows-iap (外部IPなし)"]
+ C["windows-connectivity (外部IPあり・検証用の踏み台)"]
+ end
+ end
+
+ A -->|"HTTPSトンネルを確立"| IAP
+ IAP -->|"IAMポリシーで可否判定"| FW
+ FW --> L
+ FW --> W
+ C -.->|"IAP Desktop / gcloud を実行"| IAP`,
+
+ firewall: `flowchart TD
+ Start["VMへのアクセス要件を確認"] --> Source["送信元: 35.235.240.0/20 を許可"]
+ Source --> Target{"ターゲットの絞り込み方法"}
+ Target -->|"避けるべき"| All["すべてのインスタンス"]
+ Target -->|"推奨"| Tag["特定のターゲットタグ"]
+ Target -->|"より厳密に管理したい場合"| SA["特定のサービスアカウント"]`,
+
+ iamSequence: `sequenceDiagram
+ participant U as 管理者
+ participant G as gcloud CLI
+ participant IAP as IAP TCPフォワーディング
+ participant IAM as IAMポリシー
+ participant VM as linux-iap(内部IPのみ)
+
+ U->>G: gcloud compute ssh linux-iap
+ G->>IAP: HTTPSトンネル確立を要求
+ IAP->>IAM: roles/iap.tunnelResourceAccessor を持つか確認
+ IAM-->>IAP: 許可 または 拒否 を返す
+ alt 許可された場合
+ IAP->>VM: 内部IP宛にSSHトラフィックを転送
+ VM-->>IAP: SSHセッション応答
+ IAP-->>G: トンネル経由で応答を転送
+ G-->>U: ターミナルセッションが開始
+ else 拒否された場合
+ IAP-->>G: 403 Permission Denied
+ G-->>U: エラーを表示
+ end`,
+
+ iamDesign: `flowchart TD
+ A["プリンシパルを決定"] --> B{"付与範囲"}
+ B -->|"避けるべき"| C["プロジェクト全体 (全VMにアクセス可)"]
+ B -->|"推奨(ハンズオンの方式)"| D["VM単位で iap.tunnelResourceAccessor を付与"]
+ D --> E{"さらに絞り込むか"}
+ E -->|"推奨: IAM Conditionsを利用"| F["ポート番号・有効期限などで アクセス範囲を制限"]
+ E -->|"利用しない"| G["VM単位の権限のみ"]`,
+
+ rdpTunnel: `flowchart LR
+ A["gcloud compute start-iap-tunnel windows-iap 3389"] --> B["ローカルに listeningポートが開く"]
+ B --> C["RDPクライアントで localhost:ポート番号 に接続"]
+ C --> D["IAPがHTTPS経由で windows-iapの3389番へ中継"]`,
+
+ troubleshooting: `flowchart TD
+ Fail["SSH/RDP接続に失敗する"] --> C1{"ファイアウォールルールは 存在するか"}
+ C1 -->|"いいえ"| Fix1["35.235.240.0/20 からの TCP 22/3389 を許可するルールを作成"]
+ C1 -->|"はい"| C2{"送信元範囲・ポート番号は 正しいか"}
+ C2 -->|"いいえ"| Fix2["ポート番号とIP範囲の 設定を見直す"]
+ C2 -->|"はい"| C3{"IAMロールは 付与されているか"}
+ C3 -->|"いいえ"| Fix3["対象VMまたはプロジェクトに roles/iap.tunnelResourceAccessor を付与"]
+ C3 -->|"はい"| C4{"社内プロキシ経由の アクセスか"}
+ C4 -->|"はい"| Fix4["IAP for TCPのドメインを 社内ネットワークで許可リストに追加"]
+ C4 -->|"いいえ"| C5["Cloud Loggingで AuthorizeUserの監査ログを確認"]`,
+};
diff --git a/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.css b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.css
new file mode 100644
index 000000000..91ea650e3
--- /dev/null
+++ b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.css
@@ -0,0 +1,437 @@
+/* ---------- IAP TCP Forwarding Guide Styles ---------- */
+
+.iap-guide-page {
+ background:
+ radial-gradient(1200px 800px at 85% -10%, var(--color-guide-accent-glow), transparent 60%),
+ var(--color-guide-background);
+ color: var(--color-guide-foreground);
+ line-height: 1.85;
+ font-size: 16px;
+ min-height: 100vh;
+}
+
+.iap-guide-page a {
+ color: var(--color-guide-accent);
+ text-decoration: none;
+}
+
+.iap-guide-page a:hover {
+ text-decoration: underline;
+}
+
+/* ---------- Layout ---------- */
+.iap-guide-page .layout {
+ display: flex;
+ align-items: flex-start;
+ min-height: 100vh;
+}
+
+.iap-guide-page .sidebar {
+ position: sticky;
+ top: var(--fixed-offset, 0px);
+ width: 300px;
+ flex: 0 0 300px;
+ height: calc(100vh - var(--fixed-offset, 0px));
+ overflow-y: auto;
+ background: var(--color-guide-surface-1);
+ border-right: 1px solid var(--color-guide-border-soft);
+ padding: 28px 20px 60px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--color-guide-accent-soft) transparent;
+ z-index: 30;
+}
+
+.iap-guide-page .sidebar::-webkit-scrollbar {
+ width: 6px;
+}
+
+.iap-guide-page .sidebar::-webkit-scrollbar-thumb {
+ background: var(--color-guide-accent-soft);
+ border-radius: 4px;
+}
+
+.iap-guide-page .brand {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 22px;
+ padding-bottom: 18px;
+ border-bottom: 1px solid var(--color-guide-border-soft);
+}
+
+.iap-guide-page .brand-badge {
+ width: 34px;
+ height: 34px;
+ border-radius: 9px;
+ background: linear-gradient(135deg, var(--color-guide-accent), var(--color-guide-accent-deep));
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--color-guide-on-accent);
+ flex-shrink: 0;
+}
+
+.iap-guide-page .brand-text {
+ font-size: 13px;
+ color: var(--color-guide-foreground-soft);
+ line-height: 1.4;
+}
+
+.iap-guide-page .brand-text strong {
+ color: var(--color-guide-foreground);
+ display: block;
+ font-size: 14px;
+}
+
+.iap-guide-page .nav-list,
+.iap-guide-page .nav-sublist {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.iap-guide-page .nav-item {
+ margin-bottom: 2px;
+}
+
+.iap-guide-page .nav-link {
+ display: block;
+ padding: 9px 12px;
+ border-radius: 8px;
+ font-size: 13.5px;
+ color: var(--color-guide-foreground-soft);
+ border-left: 2px solid transparent;
+ transition:
+ background 0.15s ease,
+ color 0.15s ease,
+ border-color 0.15s ease;
+}
+
+.iap-guide-page .nav-link:hover {
+ background: var(--color-guide-surface-2);
+ color: var(--color-guide-foreground);
+ text-decoration: none;
+}
+
+.iap-guide-page .nav-sublist {
+ margin-left: 10px;
+ border-left: 1px solid var(--color-guide-border-soft);
+ padding-left: 4px;
+}
+
+.iap-guide-page .nav-sublink {
+ font-size: 12.5px;
+ padding: 6px 12px;
+ color: var(--color-guide-foreground-faint);
+}
+
+.iap-guide-page .nav-link.active {
+ background: var(--color-guide-accent-glow);
+ color: var(--color-guide-accent);
+ border-left-color: var(--color-guide-accent);
+ font-weight: 600;
+}
+
+.iap-guide-page .nav-sublink.active {
+ color: var(--color-guide-accent);
+ font-weight: 600;
+ border-left-color: var(--color-guide-accent);
+}
+
+.iap-guide-page .sidebar-toggle {
+ display: none;
+ position: fixed;
+ top: calc(var(--fixed-offset, 0px) + 14px);
+ left: 14px;
+ z-index: 50;
+ width: 42px;
+ height: 42px;
+ border-radius: 10px;
+ background: var(--color-guide-surface-2);
+ border: 1px solid var(--color-guide-border);
+ color: var(--color-guide-foreground);
+ font-size: 18px;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+}
+
+.iap-guide-page main {
+ flex: 1 1 auto;
+ min-width: 0;
+ width: 100%;
+ max-width: 1280px;
+ margin-inline: auto;
+ padding: 40px 48px 100px;
+}
+
+.iap-guide-page .page-header {
+ margin-bottom: 40px;
+ padding-bottom: 28px;
+ border-bottom: 1px solid var(--color-guide-border-soft);
+}
+
+.iap-guide-page .eyebrow {
+ display: inline-block;
+ font-size: 12px;
+ letter-spacing: 0.08em;
+ color: var(--color-guide-accent);
+ background: var(--color-guide-accent-glow);
+ border: 1px solid var(--color-guide-accent-soft);
+ padding: 4px 12px;
+ border-radius: 999px;
+ margin-bottom: 16px;
+}
+
+.iap-guide-page .page-header h1 {
+ font-size: 30px;
+ line-height: 1.5;
+ margin: 0 0 14px;
+ color: #fff;
+ letter-spacing: 0.01em;
+}
+
+.iap-guide-page .page-header .subtitle {
+ font-size: 15.5px;
+ color: var(--color-guide-foreground-soft);
+ margin: 0;
+}
+
+.iap-guide-page .meta-line {
+ margin-top: 18px;
+ font-size: 13.5px;
+ color: var(--color-guide-foreground-faint);
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px 22px;
+}
+
+.iap-guide-page .meta-line b {
+ color: var(--color-guide-foreground-soft);
+ font-weight: 600;
+}
+
+/* ---------- Content typography ---------- */
+.iap-guide-page .content-section {
+ margin-bottom: 32px;
+}
+
+.iap-guide-page h2 {
+ font-size: 23px;
+ color: #fff;
+ margin: 56px 0 20px;
+ padding-top: 4px;
+ scroll-margin-top: var(--topnav-height, 80px);
+ display: flex;
+ align-items: baseline;
+ gap: 10px;
+}
+
+.iap-guide-page .content-section:first-of-type h2 {
+ margin-top: 8px;
+}
+
+.iap-guide-page h3 {
+ font-size: 18px;
+ color: var(--color-guide-accent);
+ margin: 34px 0 14px;
+ scroll-margin-top: var(--topnav-height, 80px);
+}
+
+.iap-guide-page p {
+ color: var(--color-guide-foreground-soft);
+ margin: 14px 0;
+}
+
+.iap-guide-page strong {
+ color: var(--color-guide-foreground);
+ font-weight: 700;
+}
+
+.iap-guide-page ul,
+.iap-guide-page ol {
+ color: var(--color-guide-foreground-soft);
+ padding-left: 1.5em;
+ margin: 14px 0;
+}
+
+.iap-guide-page li {
+ margin: 6px 0;
+}
+
+.iap-guide-page li::marker {
+ color: var(--color-guide-accent);
+}
+
+.iap-guide-page hr {
+ border: none;
+ border-top: 1px solid var(--color-guide-border-soft);
+ margin: 40px 0;
+}
+
+.iap-guide-page code {
+ background: var(--color-guide-code-background);
+ color: var(--color-guide-code-keyword);
+ padding: 2px 6px;
+ border-radius: 5px;
+ font-size: 0.88em;
+ font-family: var(--font-mono, monospace);
+}
+
+.iap-guide-page .codeblock {
+ background: var(--color-guide-code-background);
+ border: 1px solid var(--color-guide-border-soft);
+ border-radius: 10px;
+ padding: 18px 20px;
+ overflow-x: auto;
+ margin: 20px 0;
+ color: var(--color-guide-code-foreground);
+ font-size: 13.5px;
+ line-height: 1.7;
+ font-family: var(--font-mono, monospace);
+}
+
+.iap-guide-page .code-line {
+ white-space: pre;
+}
+
+.iap-guide-page blockquote {
+ margin: 20px 0;
+ padding: 14px 20px;
+ background: var(--color-guide-surface-2);
+ border-left: 3px solid var(--color-guide-accent);
+ border-radius: 0 8px 8px 0;
+ color: var(--color-guide-foreground-soft);
+ font-size: 14.5px;
+}
+
+.iap-guide-page blockquote p {
+ margin: 4px 0;
+ color: var(--color-guide-foreground-soft);
+}
+
+.iap-guide-page blockquote a {
+ font-weight: 600;
+}
+
+.iap-guide-page .table-scroll {
+ overflow-x: auto;
+ margin: 22px 0;
+ border-radius: 10px;
+ border: 1px solid var(--color-guide-border-soft);
+ -webkit-overflow-scrolling: touch;
+}
+
+.iap-guide-page table {
+ width: 100%;
+ min-width: 560px;
+ border-collapse: collapse;
+ margin: 0;
+ font-size: 14px;
+}
+
+.iap-guide-page thead th {
+ background: var(--color-guide-surface-2);
+ color: var(--color-guide-accent);
+ text-align: left;
+ padding: 12px 16px;
+ font-weight: 600;
+ border-bottom: 1px solid var(--color-guide-border);
+ overflow-wrap: break-word;
+}
+
+.iap-guide-page tbody td {
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--color-guide-border-soft);
+ color: var(--color-guide-foreground-soft);
+ vertical-align: top;
+ overflow-wrap: break-word;
+}
+
+.iap-guide-page tbody tr:last-child td {
+ border-bottom: none;
+}
+
+.iap-guide-page tbody tr:hover td {
+ background: rgba(124, 158, 255, 0.045);
+}
+
+/* ---------- Reference grid ---------- */
+.iap-guide-page .ref-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px 16px;
+ list-style: none;
+ padding: 0;
+ margin: 20px 0;
+}
+
+.iap-guide-page .ref-card {
+ background: var(--color-guide-surface-1);
+ border: 1px solid var(--color-guide-border-soft);
+ border-radius: 10px;
+ padding: 13px 16px;
+ font-size: 13.5px;
+ transition:
+ border-color 0.15s ease,
+ background 0.15s ease;
+}
+
+.iap-guide-page .ref-card:hover {
+ border-color: var(--color-guide-accent-soft);
+ background: var(--color-guide-surface-2);
+}
+
+.iap-guide-page .ref-card a {
+ color: var(--color-guide-foreground);
+}
+
+.iap-guide-page .ref-card a::after {
+ content: ' ↗';
+ color: var(--color-guide-accent);
+}
+
+.iap-guide-page .ref-card a:hover {
+ color: var(--color-guide-accent);
+}
+
+.iap-guide-page footer.page-footer {
+ margin-top: 60px;
+ padding-top: 24px;
+ border-top: 1px solid var(--color-guide-border-soft);
+ color: var(--color-guide-foreground-faint);
+ font-size: 12.5px;
+}
+
+/* ---------- Responsive ---------- */
+@media (max-width: 900px) {
+ .iap-guide-page .sidebar {
+ position: fixed;
+ left: 0;
+ top: var(--fixed-offset, 0px);
+ height: calc(100vh - var(--fixed-offset, 0px));
+ transform: translateX(-105%);
+ transition: transform 0.25s ease;
+ box-shadow: 8px 0 30px rgba(0, 0, 0, 0.4);
+ }
+ .iap-guide-page .sidebar.open {
+ transform: translateX(0);
+ }
+ .iap-guide-page .sidebar-toggle {
+ display: flex;
+ }
+ .iap-guide-page main {
+ padding: 60px 20px 80px;
+ }
+ .iap-guide-page .ref-grid {
+ grid-template-columns: 1fr;
+ }
+ .iap-guide-page h2 {
+ font-size: 20px;
+ }
+ .iap-guide-page .page-header h1 {
+ font-size: 24px;
+ }
+}
diff --git a/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.tsx b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.tsx
new file mode 100644
index 000000000..44e64dba3
--- /dev/null
+++ b/app/gcl/hands-on/iap-tcp-forwarding-best-practices-guide/page.tsx
@@ -0,0 +1,15 @@
+import type { Metadata } from 'next';
+import IapTcpForwardingGuide from './IapTcpForwardingGuide';
+
+export const metadata: Metadata = {
+ title: 'IAP(Identity-Aware Proxy)TCP フォワーディング ベストプラクティスガイド',
+ description:
+ '外部IPなしのVMへ安全にSSH/RDP接続するIAP TCPフォワーディングの実践ベストプラクティスガイド。ファイアウォール・IAM・gcloudトンネリング設定までステップバイステップ解説。',
+};
+
+/**
+ * Renders the IAP TCP Forwarding Best Practices Study Guide page.
+ */
+export default function Page() {
+ return ;
+}
diff --git a/app/gcl/hands-on/layout.tsx b/app/gcl/hands-on/layout.tsx
new file mode 100644
index 000000000..c46225edf
--- /dev/null
+++ b/app/gcl/hands-on/layout.tsx
@@ -0,0 +1,21 @@
+import { notFound } from 'next/navigation';
+import { HANDS_ON_ENABLED } from '@/lib/featureFlags';
+
+/**
+ * Renders the hands-on route when the feature is enabled.
+ *
+ * When the feature is disabled, the route is treated as not found.
+ *
+ * @returns The supplied child content when hands-on features are enabled.
+ */
+export default function HandsOnLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ if (!HANDS_ON_ENABLED) {
+ notFound();
+ }
+
+ return children;
+}
diff --git a/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/NavBar.tsx b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/NavBar.tsx
new file mode 100644
index 000000000..1e43e3ba1
--- /dev/null
+++ b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/NavBar.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { NAV_ITEMS } from './constants';
+
+/**
+ * Renders a section navigation bar with links that indicate the currently visible section.
+ */
+export default function NavBar() {
+ const [activeId, setActiveId] = useState('overview');
+
+ useEffect(() => {
+ if (typeof IntersectionObserver === 'undefined') return;
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setActiveId(entry.target.id);
+ }
+ });
+ },
+ {
+ rootMargin: '-20% 0px -60% 0px',
+ threshold: 0,
+ }
+ );
+
+ NAV_ITEMS.forEach((item) => {
+ const el = document.getElementById(item.id);
+ if (el) observer.observe(el);
+ });
+
+ return () => observer.disconnect();
+ }, []);
+
+ return (
+
+
+
GCP // App Dev Guide
+
+
+
+ );
+}
diff --git a/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/SetUpAnAppDevEnvironmentGuide.tsx b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/SetUpAnAppDevEnvironmentGuide.tsx
new file mode 100644
index 000000000..24bb92877
--- /dev/null
+++ b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/SetUpAnAppDevEnvironmentGuide.tsx
@@ -0,0 +1,1435 @@
+import { MermaidDiagram } from '@/components/MermaidDiagram';
+import { DIAGRAMS } from './constants';
+import NavBar from './NavBar';
+
+/**
+ * Renders a Mermaid diagram for a registered diagram identifier.
+ *
+ * @param id - The identifier of the diagram to render
+ * @param label - The accessible label for the diagram
+ * @returns The rendered diagram, or `null` when the identifier is not registered
+ */
+function Diagram({ id, label }: { id: string; label: string }) {
+ const chart = DIAGRAMS[id];
+ if (!chart) return null;
+ return (
+
+
+
+ );
+}
+
+/**
+ * Renders the complete Google Cloud application development environment guide.
+ */
+export default function SetUpAnAppDevEnvironmentGuide() {
+ return (
+
+
+
+
+ {/* ===================== HERO ===================== */}
+
+
+ GOOGLE CLOUD SKILLS BOOST — COMPLETE GUIDE
+
+ Google Cloud アプリ開発環境構築 完全ガイド
+
+ Cloud Storage・IAM・Cloud Monitoring・Cloud Run functions・Pub/Sub
+ を、初学者がステップバイステップで理解できるように再構成。すべての章に「なぜそうするのか」と公式ドキュメントの一次情報源を併記しています。
+
+
+
+
+
+
+ 対象ラボ
+
+ Cloud Storage(Console/CLI)、IAM Qwik Start、Cloud Monitoring
+ LAMP、Cloud Run functions(Console/Pub/Sub
+ トリガー)、Pub/Sub(Console/CLI/Python)、Challenge
+ Lab(GSP315「Memories」)
+
+
+
+ 対象読者
+ Google Cloud 初学者 〜 ジュニアクラウドエンジニア
+
+
+ 扱う技術要素
+
+ Cloud Storage / IAM /{' '}
+ Cloud Monitoring・Logging /{' '}
+ Cloud Run functions / Eventarc /{' '}
+ Pub/Sub
+
+
+
+ 最終更新
+ 2026-07-01
+
+
+
+
+
+ 目次
+
+
+
+ Tip
+ 各章は単独でも読めますが、これらのサービスは実際には疎結合に連携します。特に第7章の
+ Challenge Lab では、Cloud Storage・Pub/Sub・Cloud Run functions・IAM
+ が1つのイベント駆動パイプラインとして統合される様子を確認できます。
+
+
+
+ {/* ===================== ARCHITECTURE ===================== */}
+
+
+ CH.01
+
全体アーキテクチャとラーニングパス
+
+
+ このコースで扱う5つのコアサービスは、次のような依存関係で学習すると理解が深まります。
+
+
+
+
+
Fig 1.1 — 5サービスの依存関係と学習パス
+
+
+ なぜこの順序で学ぶのか
+
+
+
+
+ サービス
+ 役割
+ このコースでの位置づけ
+
+
+
+
+ Cloud Storage
+ 非構造化データ(画像・ファイル)の格納
+ すべてのイベントの起点になるデータレイク
+
+
+ IAM
+ 「誰が」「何に」「どこまで」アクセスできるかの制御
+ 全サービス共通の横断的なセキュリティレイヤー
+
+
+ Cloud Monitoring
+ システムの健全性の可視化とアラート
+ 運用フェーズで異常を検知する仕組み
+
+
+ Cloud Run functions
+ イベントをトリガーに実行される軽量処理
+ Storage や Pub/Sub のイベントに反応する「のり」の役割
+
+
+ Pub/Sub
+ サービス間の非同期メッセージング
+ 疎結合なマイクロサービス連携を実現する基盤
+
+
+
+
+
+
+ {/* ===================== CLOUD STORAGE ===================== */}
+
+
+ CH.02
+
Cloud Storage — オブジェクトストレージの基礎
+
+
+ 定義
+
+ Cloud Storage
+ は、世界規模でオブジェクト(ファイル)を格納・取得できるマネージド型のオブジェクトストレージサービスです。Web
+ コンテンツの配信、アーカイブ/災害対策用のデータ保管、大容量データの配布など、幅広い用途に使えます。
+
+
+ 理由(なぜバケットという概念があるのか)
+
+ Cloud Storage
+ にデータを置くには、必ずバケット という入れ物を経由します。バケットはディレクトリのようにネストできず、フラットな名前空間の中でオブジェクトのキーとして「フォルダ風の階層」を疑似的に表現します。この設計により、Google
+ は水平スケーラビリティと高い耐久性を両立させています。
+
+
+ バケット命名規則(重要)
+
+ バケット名は Cloud Storage
+ の単一のグローバル名前空間 を共有するため、プロジェクトをまたいで世界中で一意である必要があります。バケット名は小文字の英字、数字、ダッシュ(-)、アンダースコア(_)、ドット(.)のみを使用でき、スペースは使用できません。
+
+
+
+
+
+
+ ルール
+ 内容
+
+
+
+
+ 使用可能文字
+
+ 小文字英数字、-、_、.
+ のみ
+
+
+
+ 開始/終了文字
+ 数字または英字で開始・終了する必要がある
+
+
+ 文字数
+
+ 3〜63文字(ドットを含む場合は最大222文字、各セグメントは63文字まで)
+
+
+
+ IPアドレス形式
+
+ ドット区切りの10進数表記(例: 192.168.5.4)は不可
+
+
+
+ 禁止プレフィックス
+ goog から始まる名前は不可
+
+
+ 禁止文字列
+ "google" や "g00gle" のような紛らわしい表記も使用不可
+
+
+ 一意性
+
+ グローバルに一意な名前空間を共有するため、既存の名前とは重複できない
+
+
+
+
+
+
+
+ Warning
+ バケット名は公開情報として誰でも見ることができるため、ユーザーID・メールアドレス・プロジェクト名・個人を特定できる情報(PII)をバケット名に含めるべきではありません。プロジェクトIDをそのままバケット名に使うラボの手順は学習用としては簡便ですが、本番環境では推測されにくいランダムな接尾辞を付けるのが推奨されます。
+
+
+
+
+
✅ 良い例
+ 命名:
mycompany-prod-images-x7k2p
+ アクセス制御: 用途ごとに最小権限のロールを付与
+ 削除運用: 不要になったら空にして保持を検討
+
+
+
❌ 悪い例
+ 命名:
mysecretproject-bucket
+ アクセス制御: プロジェクト全体に
allUsers で公開
+ 削除運用: すぐに削除して名前を再利用可能にする
+
+
+
+ コンソールでのバケット作成フロー
+
+
+
Fig 2.1 — コンソールでのバケット作成フロー
+
+
+ CLI でのベストプラクティス
+
+
# バケット作成(gcloud storage は gsutil の後継コマンド)
+
gcloud storage buckets create gs://<YOUR-BUCKET-NAME> \
+
--location=REGION \
+
--default-storage-class=STANDARD
+
+
# オブジェクトのアップロード
+
gcloud storage cp ada.jpg gs://YOUR-BUCKET-NAME
+
+
# フォルダ構造を模したコピー
+
gcloud storage cp gs://YOUR-BUCKET-NAME/ada.jpg gs://YOUR-BUCKET-NAME/image-folder/
+
+
# 一覧表示(詳細付き)
+
gcloud storage ls -l gs://YOUR-BUCKET-NAME
+
+
+
+ なぜ gcloud storage を使うのか :旧来の
+ gsutil コマンドと同等の操作ができますが、gcloud CLI
+ に統合されたことで認証・出力フォーマットの一貫性が高まっています。ラボの一部では
+ gsutil も登場しますが、現在は
+ gcloud storage 系のコマンドが推奨されます。
+
+
+ 公開アクセスとオブジェクト権限のベストプラクティス
+
+ ラボでは allUsers に
+ Storage Object Viewer
+ ロールを付与してオブジェクトを公開しますが、これは学習目的の例 であり、本番運用では以下の原則を守る必要があります。
+
+
+
+ オブジェクトを公開読み取り可能にする権限を使う際は、本当にそのオブジェクトを公開する意図があるかを必ず確認すること。一度「公開」されたデータはインターネット上のどこかにコピーされる可能性があり、実質的に読み取り制御を取り戻すことは不可能になる。
+
+
+ 個々のユーザーを大量に列挙するより、グループを使う方が望ましい。スケールしやすく、大量のオブジェクトに対するアクセス制御を一括で効率的に更新できる。
+
+
+ 均一バケットレベルアクセス(Uniform bucket-level
+ access)を有効にし、オブジェクト単位の ACL 管理よりも IAM
+ による一元管理を優先する。
+
+
+
+
+
+
Fig 2.2 — 公開アクセス設計の意思決定フロー
+
+
+
+ {/* ===================== IAM ===================== */}
+
+
+ CH.03
+
IAM — アクセス制御の基礎
+
+
+ 定義
+
+ Identity and Access
+ Management(IAM)は、「誰が(Identity)」「どのリソースに」「何ができるか(Role)」を一元管理する仕組みです。IAM
+ ポリシーは、プリンシパル(ユーザー・グループ・サービスアカウント)にロール(権限の集合)を紐付けることで機能します。
+
+
+ 基本ロール(Basic Roles)の理解
+
+ レガシーな基本ロールは
+ Owner(roles/owner)、Editor(roles/editor)、Viewer(roles/viewer)の3つです。プリンシパルに基本ロールを付与すると、そのロールに含まれるすべての権限が付与されます。
+
+
+
+
+
Fig 3.1 — 基本ロールの包含関係
+
+
+
+
+
+
+ ロール
+ できること
+
+
+
+
+ roles/viewer
+ リソースの閲覧のみ(状態を変更する操作は不可)
+
+
+ roles/editor
+ Viewer の全権限 + 既存リソースの変更
+
+
+ roles/owner
+ Editor の全権限 + プロジェクトの権限管理・課金設定
+
+
+
+
+
+
+ Caution
+ 基本ロール(Owner・Editor・Viewer)はすべての Google Cloud
+ サービスにまたがる膨大な数の権限を含みます。本番環境では、代替手段がない場合を除き基本ロールを付与すべきではなく、必要最小限の事前定義ロールまたはカスタムロールを使用することが推奨されます。これは最小権限の原則(Principle of Least Privilege) と呼ばれ、IAM設計の最重要指針です。
+
+
+ 事前定義ロールへの移行(本番運用のベストプラクティス)
+
+ ラボでは学習を簡単にするために基本ロールを使いますが、実運用では下表のようにサービス固有の事前定義ロールに置き換えるべきです。
+
+
+
+
+
+
+ シナリオ
+ ❌ ラボでの簡易設定
+ ✅ 本番運用でのベストプラクティス
+
+
+
+
+ Cloud Storage の読み取り専用アクセス
+ roles/viewer(プロジェクト全体)
+ roles/storage.objectViewer(バケット単位)
+
+
+ Pub/Sub へのメッセージ発行
+ roles/editor
+ roles/pubsub.publisher(トピック単位)
+
+
+ Cloud Run functions のデプロイ
+ roles/owner
+
+ roles/cloudfunctions.developer +{' '}
+ roles/iam.serviceAccountUser
+
+
+
+
+
+
+ 権限の伝播と反映時間
+
+ IAM
+ ポリシーの変更は即座にではなく、システム全体に伝播するまで時間がかかることがあります。ラボの手順内でも「最大80秒程度かかる」という注記がありますが、これは
+ Google のグローバルに分散したメタデータレイヤーの整合性モデルに起因します。
+
+
+
+
+
Fig 3.2 — IAM権限変更の伝播シーケンス
+
+
+ 権限を絞り込むための実践フロー
+
+ プリンシパルに付与すべき事前定義ロールを見つけるには、まず本番環境では基本ロールを候補から除外し、サービスエージェント用のロール(名前が
+ "Service Agent"
+ で終わるもの)も除外した上で、必要な権限を含む最も限定的な事前定義ロールを選びます。
+
+
+
+
+
Fig 3.3 — 最小権限ロール選定フロー
+
+
+
+ {/* ===================== MONITORING ===================== */}
+
+
+ CH.04
+
Cloud Monitoring — 可観測性の基礎
+
+
+ 定義
+
+ Cloud Monitoring は、Google
+ Cloud・AWS・オンプレミスのアプリケーションからメトリクス・イベント・メタデータを収集し、ダッシュボード・アラートを通じてシステムの健全性を可視化するサービスです。Cloud
+ Logging と密に統合されており、両者を合わせて「Google Cloud
+ Observability」と呼びます。
+
+
+ なぜエージェントが必要なのか
+
+ Compute Engine の VM は、ハイパーバイザー経由で CPU
+ 使用率やネットワークトラフィックなど一部のメトリクスを自動的に収集できますが、ディスク
+ I/O の詳細やアプリケーション固有のログ・メトリクスを取得するには
+ Ops Agent のインストールが必要です。Ops Agent は Compute Engine
+ インスタンス上でログとメトリクスを収集し、ログは Cloud Logging へ、メトリクスは
+ Cloud Monitoring へ送信します。
+
+
+
+
+
+ Fig 4.1 — Ops Agent を介したテレメトリー収集の流れ
+
+
+
+ インストール手順(ベストプラクティス比較)
+
+
+
+
+ 方法
+ 適したシーン
+
+
+
+
+ VM作成時にチェックボックスで自動インストール
+ 新規VM、少数台のシンプルな運用
+
+
+ インストールスクリプトを SSH 内で実行
+ 既存VMへの後付け、ラボでの学習
+
+
+ VM Extension Manager ポリシー
+ フリート全体への一括導入・自動アップグレード
+
+
+
+
+
+
+
# Ops Agent のインストール(SSH ターミナル内)
+
curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
+
sudo bash add-google-cloud-ops-agent-repo.sh --also-install
+
+
# インストール状態の確認
+
sudo systemctl status google-cloud-ops-agent"*"
+
+
+
+ Note
+ デフォルトでは Ops Agent は Compute Engine
+ のデフォルトサービスアカウントを使用し、そのサービスアカウントにはログとメトリクスの書き込みに必要な
+ Logs Writer(roles/logging.logWriter)と Monitoring Metric Writer
+ のロールが付与されています。本番環境では最小権限の専用サービスアカウントを VM
+ にアタッチすることが推奨されます(第3章参照)。
+
+
+ アップタイムチェックとアラートポリシーの設計
+
+
+
✅ 良い例
+ チェック頻度: サービスの SLA に応じて調整(1〜5分間隔)
+ 通知先: オンコール担当者のチャンネル(Slack/PagerDuty)
+ しきい値: 過去のベースラインを踏まえて設定
+
+
+
❌ 悪い例
+ チェック頻度: 常に最小間隔にしてコストを無駄にする
+ 通知先: 個人のメールアドレスのみで属人化
+ しきい値: 根拠のない値を仮置きしたまま放置
+
+
+
+
+
+
+ Fig 4.2 — アップタイムチェック〜アラート設計フロー
+
+
+
+ ダッシュボード設計の考え方
+
+ ラボでは CPU Load と Received Packets
+ の2つのウィジェットを持つカスタムダッシュボードを作成します。実運用では、以下の「4大シグナル(Four
+ Golden Signals)」を意識して設計すると効果的です。
+
+
+
+
+
+
Traffic
+
Received/Sent Packets
+
+
+
+
Saturation
+
CPU load・ディスク使用率
+
+
+
+
+ {/* ===================== CLOUD RUN FUNCTIONS ===================== */}
+
+
+ CH.05
+
Cloud Run functions — イベント駆動サーバーレス
+
+
+ 定義
+
+ Cloud Run function(旧称 Cloud
+ Functions)は、HTTPリクエストやメッセージング、ファイルアップロードなどの「イベント」に応答して実行される単一目的のコードです。常時起動するサーバーが不要なため、突発的・断続的なワークロードに向いています。
+
+
+ トリガーの2種類
+
+ Cloud Run functions のイベント駆動トリガーは、Google Cloud
+ プロジェクト内のイベントに反応します。これに対し HTTP トリガーは HTTP(S)
+ リクエストに反応します。イベント駆動の関数をトリガーするには、CloudEvents 仕様の
+ Google 実装である Eventarc を使う必要があります。
+
+
+
+
+
+ Fig 5.1 — HTTPトリガーとイベント駆動トリガーの分岐
+
+
+
+
+ Note
+ Pub/Sub トリガーと Cloud Storage トリガーは、いずれも Eventarc
+ トリガーの一種として実装されています。つまり「Pub/Sub
+ トリガー」というラボのタスクは、内部的には Eventarc が Pub/Sub
+ のイベントをフィルタリングして関数に配信する仕組みになっています。
+
+
+ コンソールでのデプロイフロー
+
+
+
Fig 5.2 — コンソールでの関数デプロイフロー
+
+
+ CLI でのデプロイと Pub/Sub トリガー
+
+
# 関数のデプロイ(Pub/Sub トリガー、第2世代)
+
gcloud functions deploy nodejs-pubsub-function \
+
--gen2 \
+
--runtime=nodejs22 \
+
--region=REGION \
+
--source=. \
+
--entry-point=helloPubSub \
+
--trigger-topic cf-demo \
+
--stage-bucket PROJECT_ID-bucket \
+
--service-account cloudfunctionsa@PROJECT_ID.iam.gserviceaccount.com
+
+
# デプロイ状態の確認
+
gcloud functions describe nodejs-pubsub-function --region=REGION
+
+
# トピックにメッセージを発行してテスト
+
gcloud pubsub topics publish cf-demo --message="Cloud Function Gen2"
+
+
# ログの確認
+
gcloud functions logs read nodejs-pubsub-function --region=REGION
+
+
+ Cloud Storage トリガー作成のベストプラクティス
+
+ 第7章の Challenge Lab で使う Cloud Storage トリガーは、次のように Eventarc の
+ google.cloud.storage.object.v1.finalized
+ イベントをフィルタリングして構築します。
+
+
+
+
gcloud eventarc triggers create TRIGGER_NAME \
+
--location=REGION \
+
--destination-run-service=SERVICE_NAME \
+
--destination-run-region=REGION \
+
--event-filters="type=google.cloud.storage.object.v1.finalized" \
+
--event-filters="bucket=BUCKET_NAME" \
+
--service-account=SERVICE_ACCOUNT_EMAIL
+
+
+
+ Eventarc
+ トリガーの作成後、すぐに稼働するわけではなく、トリガーが完全に機能するまで最大2分 ほどかかることがあります。ラボの手順で「サムネイル画像がすぐに反映されない」場合の多くは、この伝播待ちが原因です。
+
+
+ サービスアカウントとロールの整合性
+
+ Cloud Storage の直接イベントに対するトリガーを作成する前に、Cloud Storage
+ のサービスエージェントに Pub/Sub
+ パブリッシャーのロール(roles/pubsub.publisher)を付与する必要があります。これは、Cloud
+ Storage の変更イベントが内部的に Pub/Sub 経由で Eventarc
+ に配信される仕組みになっているためです。
+
+
+
+
+
Fig 5.3 — イベント配信とサービスアカウント権限構造
+
+
+
+
+
✅ 良い例
+ サービスアカウント: 関数専用の最小権限SAを作成・アタッチ
+ 権限管理: 必要なロール(
eventReceiver,
invoker)のみを明示付与
+ 認証設定: 要件に応じて IAM 認証を必須化
+
+
+
❌ 悪い例
+ サービスアカウント: Compute Engine のデフォルトSAを使い回す
+ 権限管理: エラーのたびに権限を過剰に付与して回避
+ 認証設定: 常に
--allow-unauthenticated で公開
+
+
+
+
+ {/* ===================== PUB/SUB ===================== */}
+
+
+ CH.06
+
Pub/Sub — 非同期メッセージング
+
+
+ 定義
+
+ Pub/Sub
+ は、メッセージの送信者(Publisher)と受信者(Subscriber)を分離した非同期・スケーラブルなメッセージングサービスです。レイテンシは通常100ミリ秒程度で、ストリーミング分析やデータ統合パイプラインでのデータのロード・配信によく使われます。
+
+
+ 基本コンセプト
+
+
+
Fig 6.1 — Publish/Subscribe の基本構造
+
+
+
+ Publisher(Producer
+ とも呼ばれる)はメッセージを作成し、指定したトピックに対してメッセージングサービスに送信(Publish)します。Subscription
+ は特定のトピックのメッセージを受信する意思を表す名前付きのエンティティで、Subscriber(Consumer
+ とも呼ばれる)は指定した Subscription からメッセージを受信します。
+
+
+ なぜ「先にサブスクリプションを作る」のか
+
+ サブスクリプションが接続されていないトピックに発行を開始すると、そのメッセージは保持されず、後から接続されたサブスクリプションに配信することはできません。ラボの手順で「トピックを作成
+ → サブスクリプションを作成 →
+ メッセージを発行」という順序が徹底されているのは、このためです。
+
+
+
+
+
+ Fig 6.2 — サブスクリプション作成タイミングの重要性
+
+
+
+ コンソール・CLI・Python の3つのアプローチ比較
+
+
+
+
+ アプローチ
+ 主なコマンド/操作
+ 向いている用途
+
+
+
+
+ コンソール
+ Pub/Sub > Topics > Create topic
+ 学習・GUIでの動作確認
+
+
+ gcloud CLI
+
+ gcloud pubsub topics create /{' '}
+ gcloud pubsub subscriptions pull
+
+ スクリプト化・自動化・CI/CD
+
+
+ Python クライアントライブラリ
+
+ publisher.py /{' '}
+ subscriber.py(公式サンプル)
+
+ アプリケーションへの組み込み
+
+
+
+
+
+
+
# トピック作成
+
gcloud pubsub topics create myTopic
+
+
# サブスクリプション作成(Pull型)
+
gcloud pubsub subscriptions create --topic myTopic mySubscription
+
+
# メッセージ発行
+
gcloud pubsub topics publish myTopic --message "Hello World"
+
+
# メッセージのPull(自動ACK)
+
gcloud pubsub subscriptions pull mySubscription --auto-ack
+
+
# 複数メッセージをまとめてPull
+
gcloud pubsub subscriptions pull mySubscription --limit=3
+
+
+
+ Tip
+ --auto-ack を付けずに Pull
+ すると、メッセージは確認応答(ACK)されないまま残り続け、確認応答期限が過ぎると再配信されます。ラボで「同じメッセージが1つずつしか出てこない」という挙動は、pull コマンドがデフォルトで1件しか返さない仕様によるものです。
+
+
+ Publish / Subscribe のベストプラクティス
+
+ Pub/Sub client library でメッセージを発行する際は、リクエストごとに新しい
+ Publisher クライアントを作るのではなく、同じ Publisher
+ クライアントを再利用する方が効率的です。新しい Publisher
+ クライアントを作成した後の最初の発行リクエストは、認証済み接続を確立するのに時間がかかるためです。
+
+
+ 発行側でメッセージに順序キー(ordering
+ key)を付けて同一リージョンに送信している場合、Subscriber
+ 側でもそのSubscriptionに対して順序付き配信を有効にすることで、メッセージを順序どおりに受信できます。
+
+
+
+
+
✅ 良い例
+ クライアント管理: Publisher/Subscriberクライアントを使い回す
+ メッセージ順序: 必要な場合のみ ordering key を使用
+ 重複耐性: アプリケーション側で重複配信に耐えられる設計
+
+
+
❌ 悪い例
+ クライアント管理: リクエストのたびに新規クライアントを生成
+ メッセージ順序: 全メッセージに不要な順序制御を強制しスループット低下
+ 重複耐性: 重複が来ない前提でロジックを書く
+
+
+
+ 信頼性設計(マルチゾーン/マルチリージョン)
+
+ Pub/Sub
+ はゾーン間レプリケーションを組み込みで備えており、サービス自体の単一ゾーン障害への対処は不要ですが、クライアント側やネットワークの障害に対する耐性を持たせるには、リージョン内の複数ゾーンで十分なキャパシティを持つ
+ Publisher と Subscriber を運用することがベストプラクティスです。
+
+
+
+ {/* ===================== CHALLENGE LAB ===================== */}
+
+
+ CH.07
+
総合演習:Challenge Lab(GSP315)"Memories" サムネイル生成システム
+
+
+ シナリオ
+
+ 新設された "Memories"
+ チーム向けに、写真をアップロードすると自動でサムネイルを生成するパイプラインを構築します。これは第2〜6章で学んだ全サービスの統合演習です。
+
+
+ 統合アーキテクチャ
+
+
+
Fig 7.1 — Challenge Lab 統合アーキテクチャ
+
+
+ タスクごとの実装ポイント
+
+
+
+
TASK 1
+
+ バケット作成 —{' '}
+ gcloud storage buckets create gs://<Bucket Name> --location=REGION。指定された REGION/ZONE
+ に必ず合わせて作成することが採点上のポイントです。標準サイズ(e2-micro/e2-medium)とリージョン指定はコスト管理の観点からも重要です。
+
+
+
+
TASK 2
+
+ Pub/Sub トピック作成 —{' '}
+ gcloud pubsub topics create <Topic Name>。このトピックは、サムネイル生成完了後に Cloud Run function
+ から通知を発行するために使われます(第6章参照)。
+
+
+
+
TASK 3
+
+ Cloud Run function(サムネイル生成) — Entry
+ point:関数名(イベントを処理する関数)。Trigger:Cloud Storage(第5章の
+ Eventarc トリガーと同じ仕組み)。ランタイム:Node.js
+ 22、第2世代(Execution environment)。
+
+
+
+
TASK 4
+
+ 前任エンジニアのアクセス除去 —
+ 第3章で学んだ「最小権限の原則」の実践。退職・異動したメンバーのアクセスを速やかに取り消すことは、セキュリティ運用の基本です。
+
+
+
+
+
+
{`// タスク3: コード内の要点`}
+
functions.cloudEvent('', async cloudEvent => {
+
const event = cloudEvent.data;
+
const fileName = event.name;
+
const bucketName = event.bucket;
+
{`// ファイル名にすでに "64x64_thumbnail" が含まれていないかチェックする`}
+
{`// → これは無限ループ(サムネイルからさらにサムネイルを作る)を防ぐガード`}
+
if (fileName.search("64x64_thumbnail") === -1) {
+
{`// sharpでリサイズしてサムネイルを生成`}
+
{`// 生成後、Pub/Subトピックに完了メッセージを発行`}
+
}
+
});
+
+
+
+ Important
+ この「すでにサムネイルかどうかをファイル名でチェックする」ロジックは、イベント駆動アーキテクチャで頻出する無限ループ防止パターン です。サムネイル生成が新しいオブジェクトを同じバケットに書き込むと、それ自体が新たな
+ object.finalized
+ イベントを発火させてしまうため、処理対象を判定するガード条件が不可欠です。
+
+
+
+
# タスク4: Username 2(Viewerロール)からアクセスを除去
+
gcloud projects remove-iam-policy-binding PROJECT_ID \
+
--member="user:PREVIOUS_ENGINEER_EMAIL" \
+
--role="roles/viewer"
+
+
+ 必要な IAM ロールの整理
+
+ gcloud eventarc triggers create --service-account
+ で指定する Eventarc トリガー用サービスアカウントに
+ roles/run.invoker(呼び出し許可)と
+ roles/eventarc.eventReceiver(イベント受信許可)を付与します。Eventarc
+ トリガーの作成者・デプロイヤーには、指定するトリガー用サービスアカウントに対する
+ roles/iam.serviceAccountUser(サービスアカウント利用許可)を付与します。
+ 関数コードが使用する Cloud Run function 実行時サービスアカウントとは分けて管理し、実行時 SA
+ にはコードが必要とする権限だけを付与します。Cloud
+ Storage のサービスアカウントには
+ roles/pubsub.publisher
+ を付与して、オブジェクトがアップロードされた際にイベントを発行できるようにする必要があります。
+
+
+
+
+
+
+ プリンシパル
+ 付与するロール
+ 目的
+
+
+
+
+ Eventarc トリガー用 SA
+ roles/eventarc.eventReceiver
+ Eventarc からイベントを受信
+
+
+ Eventarc トリガー用 SA
+ roles/run.invoker
+ 関数(サービス)を呼び出し可能にする
+
+
+ Eventarc トリガー作成者・デプロイヤー
+ roles/iam.serviceAccountUser
+ --service-accountでトリガー用 SA を関連付け
+
+
+ Cloud Storage サービスエージェント
+ roles/pubsub.publisher
+ オブジェクトイベントをEventarcに転送
+
+
+ Cloud Run function 実行時 SA
+ roles/pubsub.publisher(トピック単位)
+ 処理完了メッセージを発行
+
+
+
+
+
+
+ {/* ===================== PRACTICES TABLE ===================== */}
+
+
+ CH.08
+
サービス横断ベストプラクティス早見表
+
+
+
+
+
+
+ カテゴリ
+ ベストプラクティス
+ 該当章
+
+
+
+
+ コスト
+
+ リージョン・ゾーンを指定し、不要なマルチリージョン設定を避ける
+
+ CH.02, CH.07
+
+
+ コスト
+
+ VM サイズは要件に応じて e2-micro/e2-medium を選択
+
+ CH.04, CH.07
+
+
+ セキュリティ
+ 基本ロール(Owner/Editor/Viewer)は本番で極力使わない
+ CH.03
+
+
+ セキュリティ
+ 公開アクセスは範囲を最小限に、意図を明確にしてから設定
+ CH.02
+
+
+ セキュリティ
+ 用途ごとに専用サービスアカウントを作成する
+ CH.05, CH.07
+
+
+ 可用性
+ アップタイムチェックとアラートで異常を早期検知
+ CH.04
+
+
+ 可用性
+ Pub/Sub Publisher/Subscriber をマルチゾーンで運用
+ CH.06
+
+
+ 開発効率
+ Publisher/Subscriber クライアントを再利用する
+ CH.06
+
+
+ 開発効率
+ イベント駆動関数には無限ループ防止のガード条件を入れる
+ CH.05, CH.07
+
+
+ 運用
+ 退職・異動したメンバーのIAMロールを速やかに除去する
+ CH.03, CH.07
+
+
+
+
+
+
+ {/* ===================== TROUBLESHOOTING ===================== */}
+
+
+ CH.09
+
よくあるエラーとトラブルシューティング
+
+
+
+
+
+
+ 症状
+ 原因
+ 対処
+
+
+
+
+ バケット作成時に 409 Conflict
+ バケット名がグローバルに重複している
+ より一意性の高い名前(ランダムなサフィックス付き)に変更
+
+
+ IAM 権限変更後もアクセスが変わらない
+ ポリシーの伝播待ち(最大80秒程度)
+ 数分待って再試行、または再ログイン
+
+
+ Cloud Run function の Eventarc トリガーが発火しない
+ トリガー作成直後で伝播が完了していない、または権限不足
+
+ 最大2分待つ、roles/pubsub.publisher 等の権限を確認
+
+
+
+
+ AccessDeniedException(Pub/Sub 経由の Storage
+ 操作)
+
+ サービスエージェントへのロール付与が反映されていない
+ 1分程度待って再実行
+
+
+ Pub/Sub の pull で0件しか返らない
+
+ サブスクリプションが未接続の状態でメッセージを発行した、または既にACK済み
+
+ 先にサブスクリプションを作成してから発行する運用に変更
+
+
+ Ops Agent のステータスが "Not detected"
+ サービスアカウントの権限不足、またはエージェント未起動
+
+ roles/logging.logWriter/Monitoring
+ 関連ロールを確認しエージェントを再起動
+
+
+
+
+
+
+
+ {/* ===================== REFERENCES ===================== */}
+
+
+ CH.10
+
参考ソース一覧
+
+
+ 各章の内容を裏付ける一次情報源です。ラボの簡易設定を本番環境にそのまま持ち込まないよう、必ず最新のドキュメントを確認してください。
+
+
+ Cloud Storage
+
+
+ IAM
+
+
+ Cloud Monitoring
+
+
+ Cloud Run functions / Eventarc
+
+
+ Pub/Sub
+
+
+ Challenge Lab(GSP315)関連の実装参考
+
+
+
+
+
+ このガイドの使い方 —
+ 各章末の公式ドキュメントURLは、ラボの手順だけでは触れられていない「なぜ」の部分を裏付ける一次情報源です。実際にプロジェクトへ適用する際は、必ず最新のドキュメントを確認し、ラボの簡易設定(基本ロールの多用、
+ allUsers
+ への公開など)をそのまま本番環境に持ち込まないよう注意してください。
+ Generated 2026-07-01 · Google Cloud App Dev Environment Guide
+
+
+ );
+}
diff --git a/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/constants.ts b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/constants.ts
new file mode 100644
index 000000000..f7fb066be
--- /dev/null
+++ b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/constants.ts
@@ -0,0 +1,166 @@
+/**
+ * Google Cloud アプリ開発環境構築 完全ガイドの Mermaid 図定義集
+ */
+export const DIAGRAMS: Record = {
+ 'diag-arch-path': `flowchart TD
+ A[Cloud Storage データの置き場所] --> E[IAM 誰が何にアクセスできるか]
+ E --> B[Cloud Monitoring システムの状態を見る]
+ B --> C[Cloud Run functions イベントに反応する処理]
+ C --> D[Pub/Sub 非同期メッセージ連携]
+ D --> F[Challenge Lab GSP315: Memories]
+ A -.オブジェクト追加イベント.-> C
+ C -.メッセージ発行.-> D
+ E -.アクセス制御を全レイヤーに適用.-> A
+ E -.アクセス制御を全レイヤーに適用.-> C
+ E -.アクセス制御を全レイヤーに適用.-> D
+ style A fill:#4285F4,color:#fff,stroke:#4285F4
+ style E fill:#f87171,color:#1a0505,stroke:#f87171
+ style B fill:#fbbf24,color:#1a1204,stroke:#fbbf24
+ style C fill:#00ffa3,color:#04180f,stroke:#00ffa3
+ style D fill:#a78bfa,color:#12081f,stroke:#a78bfa
+ style F fill:#22d3ee,color:#04141a,stroke:#22d3ee`,
+
+ 'diag-storage-console': `flowchart TD
+ A[Navigation Menu] --> B["Cloud Storage > Buckets"] --> C["+ Create"]
+ C --> D[バケット名を入力] --> E["Location Type: Region を選択"]
+ E --> F["Storage Class: Standard"] --> G["Access Control: Uniform"]
+ G --> H["Enforce public access prevention を確認"] --> I[Create]
+ style A fill:#0f172a,color:#e6edf3,stroke:#4285F4
+ style I fill:#00ffa3,color:#04180f,stroke:#00ffa3`,
+
+ 'diag-storage-public': `flowchart TD
+ A[バケット作成] --> B{公開する必要があるか?}
+ B -->|Yes| C["Uniform access + IAM で allUsers に Storage Object Viewer のみ付与"]
+ B -->|No| D[Enforce public access prevention を有効化]
+ C --> E[公開範囲を最小オブジェクト単位に限定]
+ D --> F[プロジェクト内の権限のあるユーザーのみアクセス]
+ style B fill:#0f172a,color:#e6edf3,stroke:#fbbf24
+ style C fill:#00ffa3,color:#04180f,stroke:#00ffa3
+ style D fill:#22d3ee,color:#04141a,stroke:#22d3ee`,
+
+ 'diag-iam-basic': `flowchart TD
+ Owner["Owner (課金設定・権限管理も可能)"] --> Editor["Editor (リソースの変更が可能)"]
+ Editor --> Viewer["Viewer (読み取り専用)"]
+ style Owner fill:#f87171,color:#1a0505,stroke:#f87171
+ style Editor fill:#fbbf24,color:#1a1204,stroke:#fbbf24
+ style Viewer fill:#00ffa3,color:#04180f,stroke:#00ffa3`,
+
+ 'diag-iam-sequence': `sequenceDiagram
+ participant Admin as 管理者(Owner)
+ participant IAMPolicy as IAMポリシーストア
+ participant User as 対象ユーザー
+ participant Resource as Cloud Storage
+ Admin->>IAMPolicy: ロールを付与/剥奪
+ IAMPolicy-->>IAMPolicy: グローバルに伝播(最大80秒程度)
+ User->>Resource: リソースへアクセス試行
+ Resource-->>User: 伝播完了後に反映された権限で応答`,
+
+ 'diag-iam-flow': `flowchart TD
+ A[必要なタスクを洗い出す] --> B[基本ロールを候補から除外]
+ B --> C[サービスエージェント専用ロールを除外]
+ C --> D[タスクに対応する事前定義ロールを検索]
+ D --> E{要件を満たす 事前定義ロールがあるか?}
+ E -->|Yes| F[そのロールを付与]
+ E -->|No| G[カスタムロールを作成]
+ style E fill:#0f172a,color:#e6edf3,stroke:#fbbf24
+ style F fill:#00ffa3,color:#04180f,stroke:#00ffa3
+ style G fill:#a78bfa,color:#12081f,stroke:#a78bfa`,
+
+ 'diag-monitoring-agent': `flowchart LR
+ VM[Compute Engine VM] -->|システムメトリクス ハイパーバイザー経由・エージェント不要| CM[Cloud Monitoring]
+ VM -->|Ops Agent導入| OA[Ops Agent]
+ OA -->|詳細メトリクス| CM
+ OA -->|アプリケーションログ| CL[Cloud Logging]
+ CM --> DB[ダッシュボード]
+ CM --> AL[アラートポリシー]
+ CM --> UC[アップタイムチェック]
+ AL -->|通知| EM["メール / Slack / PagerDuty"]
+ style VM fill:#0f172a,color:#e6edf3,stroke:#4285F4
+ style CM fill:#fbbf24,color:#1a1204,stroke:#fbbf24
+ style CL fill:#22d3ee,color:#04141a,stroke:#22d3ee`,
+
+ 'diag-monitoring-alert': `flowchart TD
+ A[Uptime Check作成] --> B[Protocol: HTTP選択]
+ B --> C[対象VMの外部IPを指定]
+ C --> D[Check Frequency設定]
+ D --> E[Response Validationのデフォルト確認]
+ E --> F[通知チャンネル設定]
+ F --> G[Alerting Policy作成]
+ G --> H[しきい値/Retest windowを設定]
+ H --> I[運用開始・ダッシュボードで可視化]
+ style I fill:#00ffa3,color:#04180f,stroke:#00ffa3`,
+
+ 'diag-functions-triggers': `flowchart TD
+ Trigger{トリガー種別} -->|HTTPトリガー| HTTP["HTTP(S)リクエスト run.app URL に直接アクセス"]
+ Trigger -->|イベント駆動トリガー| Eventarc[Eventarc経由]
+ Eventarc --> GCS["Cloud Storageイベント object.finalized 等"]
+ Eventarc --> PubSub[Pub/Subメッセージ受信]
+ Eventarc --> Firestore[Firestoreドキュメント変更]
+ HTTP --> Func[Cloud Run function 実行]
+ GCS --> Func
+ PubSub --> Func
+ Firestore --> Func
+ style Eventarc fill:#a78bfa,color:#12081f,stroke:#a78bfa
+ style Func fill:#00ffa3,color:#04180f,stroke:#00ffa3`,
+
+ 'diag-functions-deploy': `flowchart TD
+ A["Cloud Run > Services"] --> B["WRITE A FUNCTION"] --> C[サービス名・リージョン設定]
+ C --> D["認証: Allow public access または要認証を選択"] --> E["Execution Environment: 第2世代 を選択"]
+ E --> F[Revision Scaling設定] --> G[ソースコード編集]
+ G --> H["SAVE and REDEPLOY"] --> I["TESTでイベントを模擬送信"] --> J["Observability > Logs で確認"]
+ style H fill:#00ffa3,color:#04180f,stroke:#00ffa3`,
+
+ 'diag-functions-sa': `flowchart LR
+ GCS["Cloud Storage サービスエージェント"] -->|roles/pubsub.publisher| PS[内部Pub/Subトピック]
+ PS --> EA[Eventarc]
+ EA -->|roles/run.invoker| CRF[Cloud Run function]
+ EA -->|roles/eventarc.eventReceiver| SA[実行用サービスアカウント]
+ style EA fill:#a78bfa,color:#12081f,stroke:#a78bfa`,
+
+ 'diag-pubsub-basic': `flowchart LR
+ Pub1[Publisher A] -->|メッセージ発行| Topic["Topic 共有された名前付きチャンネル"]
+ Pub2[Publisher B] -->|メッセージ発行| Topic
+ Topic --> Sub1[Subscription 1]
+ Topic --> Sub2[Subscription 2]
+ Sub1 --> Con1[Subscriberアプリ1]
+ Sub2 --> Con2[Subscriberアプリ2]
+ style Topic fill:#a78bfa,color:#12081f,stroke:#a78bfa`,
+
+ 'diag-pubsub-timing': `flowchart TD
+ A[トピック作成] --> B{サブスクリプションは接続済みか?}
+ B -->|No| C["メッセージ発行しても 後から作成したSubscriptionには届かない"]
+ B -->|Yes| D["発行したメッセージが 正しく保持・配信される"]
+ C --> E[先にサブスクリプションを作成]
+ E --> D
+ style C fill:#f87171,color:#1a0505,stroke:#f87171
+ style D fill:#00ffa3,color:#04180f,stroke:#00ffa3`,
+
+ 'diag-challenge-arch': `flowchart TD
+ User[ユーザー] -->|画像アップロード| Bucket["Cloud Storage Bucket Name"]
+ Bucket -->|object.finalizedイベント| Eventarc["Eventarc Cloud Storageトリガー"]
+ Eventarc --> Func["Cloud Run function Node.js 22 / 第2世代"]
+ Func -->|sharpでリサイズ| Thumb[64x64サムネイルを生成]
+ Thumb -->|同一バケットに保存| Bucket
+ Func -->|完了通知| Topic["Pub/Sub Topic Topic Name"]
+ IAM[IAM] -.アクセス制御.-> Bucket
+ IAM -.アクセス制御.-> Func
+ IAM -.アクセス制御.-> Topic
+ style Bucket fill:#4285F4,color:#fff,stroke:#4285F4
+ style Func fill:#00ffa3,color:#04180f,stroke:#00ffa3
+ style Topic fill:#a78bfa,color:#12081f,stroke:#a78bfa
+ style IAM fill:#f87171,color:#1a0505,stroke:#f87171`,
+};
+
+export const NAV_ITEMS = [
+ { id: 'overview', label: '概要' },
+ { id: 'architecture', label: '全体像' },
+ { id: 'storage', label: 'Storage' },
+ { id: 'iam', label: 'IAM' },
+ { id: 'monitoring', label: 'Monitoring' },
+ { id: 'functions', label: 'Functions' },
+ { id: 'pubsub', label: 'Pub/Sub' },
+ { id: 'challenge', label: 'Challenge Lab' },
+ { id: 'practices', label: '早見表' },
+ { id: 'troubleshoot', label: 'トラブル対応' },
+ { id: 'refs', label: '参考ソース' },
+] as const;
diff --git a/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/page.css b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/page.css
new file mode 100644
index 000000000..85fcef8a5
--- /dev/null
+++ b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/page.css
@@ -0,0 +1,675 @@
+/* GCP App Dev Environment Complete Guide - Page Specific CSS */
+
+.app-dev-environment-page {
+ background: var(--color-background);
+ color: var(--color-foreground);
+ font-family: var(--font-body);
+ line-height: 1.75;
+ position: relative;
+ overflow-x: hidden;
+ padding-bottom: 60px;
+}
+
+.app-dev-environment-page a {
+ color: var(--color-theme-ace-accent);
+ text-decoration: none;
+}
+
+.app-dev-environment-page a:hover {
+ text-decoration: underline;
+}
+
+.app-dev-environment-page h1,
+.app-dev-environment-page h2,
+.app-dev-environment-page h3,
+.app-dev-environment-page h4 {
+ font-family: var(--font-display);
+ font-weight: 800;
+ letter-spacing: 0.01em;
+ color: #fff;
+ margin: 0 0 0.5em;
+}
+
+.app-dev-environment-page code {
+ font-family: var(--font-mono);
+}
+
+/* ---------- STICKY SUB-NAVBAR ---------- */
+.app-dev-environment-page .sub-navbar {
+ position: sticky;
+ top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px));
+ z-index: 100;
+ background: rgba(3, 7, 18, 0.92);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid var(--color-border);
+ overflow-x: auto;
+ white-space: nowrap;
+ scrollbar-width: thin;
+}
+
+.app-dev-environment-page .sub-navbar::-webkit-scrollbar {
+ height: 4px;
+}
+
+.app-dev-environment-page .sub-navbar::-webkit-scrollbar-thumb {
+ background: var(--color-theme-ace-accent);
+}
+
+.app-dev-environment-page .nav-inner {
+ display: flex;
+ align-items: center;
+ gap: 22px;
+ padding: 12px 24px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.app-dev-environment-page .nav-brand {
+ font-family: var(--font-mono);
+ font-weight: 700;
+ color: var(--color-google-green);
+ font-size: 0.82rem;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ flex-shrink: 0;
+ padding-right: 14px;
+ border-right: 1px solid var(--color-border);
+}
+
+.app-dev-environment-page .nav-links {
+ display: flex;
+ gap: 16px;
+}
+
+.app-dev-environment-page .nav-links a {
+ font-family: var(--font-mono);
+ font-size: 0.76rem;
+ color: var(--color-muted-foreground);
+ letter-spacing: 0.03em;
+ transition: color 0.2s;
+ padding: 4px 8px;
+ border-radius: 4px;
+}
+
+.app-dev-environment-page .nav-links a:hover,
+.app-dev-environment-page .nav-links a.active {
+ color: var(--color-theme-ace-accent);
+ background: rgba(34, 211, 238, 0.08);
+ text-decoration: none;
+}
+
+/* ---------- LAYOUT ---------- */
+.app-dev-environment-page .wrap {
+ max-width: 1120px;
+ margin: 0 auto;
+ padding: 0 28px;
+ position: relative;
+ z-index: 2;
+}
+
+.app-dev-environment-page section {
+ padding: 72px 0;
+ border-bottom: 1px solid var(--color-border);
+ scroll-margin-top: calc(var(--header-h, 60px) + var(--disclaimer-height, 0px) + 60px);
+}
+
+.app-dev-environment-page section:last-of-type {
+ border-bottom: none;
+}
+
+/* ---------- HERO ---------- */
+.app-dev-environment-page .hero {
+ padding: 64px 0 48px;
+ text-align: left;
+ position: relative;
+}
+
+.app-dev-environment-page .hero-eyebrow {
+ font-family: var(--font-mono);
+ color: var(--color-google-green);
+ font-size: 0.78rem;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ display: flex;
+ align-items: center;
+}
+
+.app-dev-environment-page .hero h1 {
+ font-size: clamp(2.1rem, 5vw, 3.2rem);
+ line-height: 1.15;
+ margin-top: 14px;
+ background: linear-gradient(
+ 120deg,
+ #fff 30%,
+ var(--color-theme-ace-accent) 65%,
+ var(--color-google-green) 100%
+ );
+ -webkit-background-clip: text;
+ background-clip: text;
+ color: transparent;
+}
+
+.app-dev-environment-page .hero-sub {
+ font-size: 1.05rem;
+ color: var(--color-muted-foreground);
+ max-width: 720px;
+ margin-top: 18px;
+}
+
+.app-dev-environment-page .hero-meta-table {
+ margin-top: 36px;
+}
+
+.app-dev-environment-page .pulse-dot {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--color-google-green);
+ margin-right: 8px;
+ box-shadow: 0 0 8px var(--color-google-green);
+}
+
+/* ---------- TOC ---------- */
+.app-dev-environment-page .toc-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12px;
+ margin-top: 20px;
+}
+
+.app-dev-environment-page .toc-item {
+ background: var(--color-guide-surface-1);
+ border: 1px solid var(--color-border);
+ border-radius: 10px;
+ padding: 16px 18px;
+ transition: 0.25s;
+ display: flex;
+ gap: 12px;
+ align-items: flex-start;
+}
+
+.app-dev-environment-page .toc-item:hover {
+ border-color: var(--color-theme-ace-accent);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(34, 211, 238, 0.12);
+}
+
+.app-dev-environment-page .toc-num {
+ font-family: var(--font-mono);
+ color: var(--color-theme-ace-accent);
+ font-weight: 700;
+ font-size: 0.85rem;
+}
+
+.app-dev-environment-page .toc-item a {
+ color: var(--color-foreground);
+ font-size: 0.88rem;
+ font-weight: 500;
+}
+
+/* ---------- CHAPTER HEADERS ---------- */
+.app-dev-environment-page .chapter-head {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-bottom: 8px;
+}
+
+.app-dev-environment-page .chapter-num {
+ font-family: var(--font-mono);
+ font-weight: 700;
+ font-size: 0.78rem;
+ color: var(--color-background);
+ background: var(--color-google-green);
+ padding: 6px 12px;
+ border-radius: 6px;
+ letter-spacing: 0.05em;
+ flex-shrink: 0;
+}
+
+.app-dev-environment-page .chapter-head h2 {
+ margin: 0;
+ font-size: clamp(1.4rem, 3vw, 2rem);
+}
+
+.app-dev-environment-page .chapter-desc {
+ color: var(--color-muted-foreground);
+ font-size: 0.95rem;
+ margin-top: 6px;
+ max-width: 760px;
+}
+
+.app-dev-environment-page h3.subhead {
+ font-size: 1.08rem;
+ color: var(--color-theme-ace-accent);
+ margin-top: 40px;
+ font-family: var(--font-mono);
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ border-left: 3px solid var(--color-theme-ace-accent);
+ padding-left: 12px;
+}
+
+/* ---------- TABLES ---------- */
+.app-dev-environment-page .table-wrap {
+ overflow-x: auto;
+ margin: 20px 0;
+ border: 1px solid var(--color-border);
+ border-radius: 10px;
+ background: var(--color-guide-surface-1);
+}
+
+.app-dev-environment-page table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.88rem;
+ min-width: 480px;
+}
+
+.app-dev-environment-page thead th {
+ background: var(--color-guide-surface-2);
+ color: var(--color-google-green);
+ font-family: var(--font-mono);
+ text-align: left;
+ padding: 12px 16px;
+ font-size: 0.76rem;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ border-bottom: 1px solid var(--color-guide-border-strong);
+}
+
+.app-dev-environment-page tbody td {
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--color-border);
+ color: var(--color-foreground);
+ vertical-align: top;
+}
+
+.app-dev-environment-page tbody tr:last-child td {
+ border-bottom: none;
+}
+
+.app-dev-environment-page tbody tr:hover {
+ background: rgba(34, 211, 238, 0.04);
+}
+
+.app-dev-environment-page td code,
+.app-dev-environment-page th code {
+ color: var(--color-theme-ace-accent);
+ background: rgba(34, 211, 238, 0.08);
+ padding: 1px 6px;
+ border-radius: 4px;
+ font-size: 0.86em;
+}
+
+/* ---------- CODE BLOCK ---------- */
+.app-dev-environment-page .code-block {
+ background: #050a14;
+ border: 1px solid var(--color-border);
+ border-radius: 10px;
+ padding: 20px 22px;
+ margin: 20px 0;
+ overflow-x: auto;
+ position: relative;
+}
+
+.app-dev-environment-page .code-block::before {
+ content: '● ● ●';
+ color: var(--color-guide-meta);
+ font-size: 0.7rem;
+ letter-spacing: 3px;
+ display: block;
+ margin-bottom: 12px;
+}
+
+.app-dev-environment-page .code-block pre {
+ margin: 0;
+ font-family: var(--font-mono);
+ font-size: 0.84rem;
+ line-height: 1.7;
+ color: #c9e8ff;
+ white-space: pre;
+}
+
+.app-dev-environment-page .code-line {
+ white-space: pre;
+}
+
+.app-dev-environment-page .code-comment {
+ color: var(--color-guide-meta);
+}
+
+.app-dev-environment-page .code-green {
+ color: var(--color-google-green);
+}
+
+/* ---------- CALLOUTS / ALERTS ---------- */
+.app-dev-environment-page .callout {
+ border-left: 4px solid var(--color-theme-ace-accent);
+ background: rgba(34, 211, 238, 0.06);
+ border-radius: 0 10px 10px 0;
+ padding: 16px 20px;
+ margin: 22px 0;
+ font-size: 0.92rem;
+}
+
+.app-dev-environment-page .callout-label {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ font-weight: 700;
+ display: block;
+ margin-bottom: 6px;
+}
+
+.app-dev-environment-page .callout.info {
+ border-color: var(--color-theme-ace-accent);
+ background: rgba(34, 211, 238, 0.06);
+}
+
+.app-dev-environment-page .callout.info .callout-label {
+ color: var(--color-theme-ace-accent);
+}
+
+.app-dev-environment-page .callout.warning {
+ border-color: var(--color-google-yellow);
+ background: rgba(251, 191, 36, 0.06);
+}
+
+.app-dev-environment-page .callout.warning .callout-label {
+ color: var(--color-google-yellow);
+}
+
+.app-dev-environment-page .callout.danger {
+ border-color: var(--color-google-red);
+ background: rgba(248, 113, 113, 0.07);
+}
+
+.app-dev-environment-page .callout.danger .callout-label {
+ color: var(--color-google-red);
+}
+
+.app-dev-environment-page .callout.tip {
+ border-color: var(--color-google-green);
+ background: rgba(0, 255, 163, 0.06);
+}
+
+.app-dev-environment-page .callout.tip .callout-label {
+ color: var(--color-google-green);
+}
+
+/* ---------- GOOD / BAD ---------- */
+.app-dev-environment-page .gb-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 16px;
+ margin: 22px 0;
+}
+
+.app-dev-environment-page .gb-card {
+ border-radius: 10px;
+ padding: 18px 20px;
+ border: 1px solid var(--color-border);
+}
+
+.app-dev-environment-page .gb-card.good {
+ background: rgba(0, 255, 163, 0.05);
+ border-color: rgba(0, 255, 163, 0.3);
+}
+
+.app-dev-environment-page .gb-card.bad {
+ background: rgba(248, 113, 113, 0.05);
+ border-color: rgba(248, 113, 113, 0.3);
+}
+
+.app-dev-environment-page .gb-title {
+ font-family: var(--font-mono);
+ font-weight: 700;
+ font-size: 0.8rem;
+ letter-spacing: 0.05em;
+ margin-bottom: 8px;
+}
+
+.app-dev-environment-page .gb-card.good .gb-title {
+ color: var(--color-google-green);
+}
+
+.app-dev-environment-page .gb-card.bad .gb-title {
+ color: var(--color-google-red);
+}
+
+/* ---------- MERMAID WRAP ---------- */
+.app-dev-environment-page .diagram-wrap {
+ background: var(--color-guide-surface-1);
+ border: 1px solid var(--color-border);
+ border-radius: 12px;
+ padding: 24px;
+ margin: 24px 0;
+ overflow-x: auto;
+ scrollbar-width: thin;
+}
+
+.app-dev-environment-page .diagram-caption {
+ font-family: var(--font-mono);
+ font-size: 0.76rem;
+ color: var(--color-guide-meta);
+ text-align: center;
+ margin-top: 14px;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+}
+
+.app-dev-environment-page .mermaid-wrap {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ min-width: min-content;
+}
+
+.app-dev-environment-page .mermaid-wrap .mermaid-target {
+ background: transparent !important;
+ border: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ width: 100%;
+}
+
+.app-dev-environment-page .mermaid-wrap svg {
+ display: block;
+ margin: 0 auto;
+ height: auto !important;
+}
+
+/* ---------- METRIC GRID ---------- */
+.app-dev-environment-page .metric-grid {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 14px;
+ margin: 24px 0;
+}
+
+.app-dev-environment-page .metric-card {
+ background: var(--color-guide-surface-1);
+ border: 1px solid var(--color-border);
+ border-radius: 10px;
+ padding: 18px;
+ text-align: center;
+ transition: 0.25s;
+}
+
+.app-dev-environment-page .metric-card:hover {
+ transform: translateY(-3px);
+ border-color: var(--color-google-green);
+}
+
+.app-dev-environment-page .metric-val {
+ font-family: var(--font-display);
+ font-size: 1.6rem;
+ color: var(--color-google-green);
+}
+
+.app-dev-environment-page .metric-label {
+ font-size: 0.72rem;
+ color: var(--color-muted-foreground);
+ font-family: var(--font-mono);
+ margin-top: 4px;
+}
+
+/* ---------- REF GRID ---------- */
+.app-dev-environment-page .ref-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+ margin-top: 18px;
+}
+
+.app-dev-environment-page .ref-card {
+ background: var(--color-guide-surface-1);
+ border: 1px solid var(--color-border);
+ border-radius: 10px;
+ padding: 16px 18px;
+ transition: 0.25s;
+}
+
+.app-dev-environment-page .ref-card:hover {
+ border-color: var(--color-tip);
+ transform: translateX(4px);
+}
+
+.app-dev-environment-page .ref-cat {
+ font-family: var(--font-mono);
+ font-size: 0.68rem;
+ color: var(--color-tip);
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.app-dev-environment-page .ref-title {
+ font-size: 0.9rem;
+ margin: 6px 0 4px;
+ color: var(--color-foreground);
+ font-weight: 500;
+}
+
+.app-dev-environment-page .ref-url {
+ font-family: var(--font-mono);
+ font-size: 0.72rem;
+ color: var(--color-theme-ace-accent);
+ word-break: break-all;
+}
+
+/* ---------- ARCH LAYERS ---------- */
+.app-dev-environment-page .arch-layers {
+ margin: 22px 0;
+ border-left: 2px solid var(--color-guide-border-strong);
+ padding-left: 0;
+}
+
+.app-dev-environment-page .arch-row {
+ display: flex;
+ gap: 16px;
+ align-items: flex-start;
+ padding: 14px 0 14px 20px;
+ border-left: 3px solid var(--color-google-blue);
+ margin-left: -2px;
+ position: relative;
+}
+
+.app-dev-environment-page .arch-row + .arch-row {
+ border-top: 1px dashed var(--color-border);
+}
+
+.app-dev-environment-page .arch-row .arch-tag {
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ color: #fff;
+ background: var(--color-google-blue);
+ padding: 3px 10px;
+ border-radius: 5px;
+ flex-shrink: 0;
+ margin-top: 2px;
+ white-space: nowrap;
+}
+
+.app-dev-environment-page .arch-row.storage {
+ border-color: var(--color-google-blue);
+}
+
+.app-dev-environment-page .arch-row.storage .arch-tag {
+ background: var(--color-google-blue);
+}
+
+.app-dev-environment-page .arch-row.iam {
+ border-color: var(--color-google-red);
+}
+
+.app-dev-environment-page .arch-row.iam .arch-tag {
+ background: var(--color-google-red);
+ color: #1a0505;
+}
+
+.app-dev-environment-page .arch-row.monitor {
+ border-color: var(--color-google-yellow);
+}
+
+.app-dev-environment-page .arch-row.monitor .arch-tag {
+ background: var(--color-google-yellow);
+ color: #1a1204;
+}
+
+.app-dev-environment-page .arch-row.func {
+ border-color: var(--color-google-green);
+}
+
+.app-dev-environment-page .arch-row.func .arch-tag {
+ background: var(--color-google-green);
+ color: #04180f;
+}
+
+.app-dev-environment-page .arch-row.pubsub {
+ border-color: var(--color-tip);
+}
+
+.app-dev-environment-page .arch-row.pubsub .arch-tag {
+ background: var(--color-tip);
+ color: #12081f;
+}
+
+.app-dev-environment-page .arch-row p {
+ margin: 0;
+ font-size: 0.9rem;
+ color: var(--color-muted-foreground);
+}
+
+/* ---------- FOOTER ---------- */
+.app-dev-environment-page footer {
+ padding: 50px 0 30px;
+ text-align: center;
+ color: var(--color-guide-meta);
+ font-family: var(--font-mono);
+ font-size: 0.76rem;
+}
+
+/* ---------- RESPONSIVE ---------- */
+@media (max-width: 900px) {
+ .app-dev-environment-page .toc-grid {
+ grid-template-columns: 1fr;
+ }
+ .app-dev-environment-page .metric-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ .app-dev-environment-page .ref-grid {
+ grid-template-columns: 1fr;
+ }
+ .app-dev-environment-page .gb-grid {
+ grid-template-columns: 1fr;
+ }
+ .app-dev-environment-page .hero {
+ padding: 48px 0 36px;
+ }
+ .app-dev-environment-page section {
+ padding: 52px 0;
+ }
+}
diff --git a/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/page.tsx b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/page.tsx
new file mode 100644
index 000000000..197fbd824
--- /dev/null
+++ b/app/gcl/hands-on/set-up-an-app-dev-environment-on-google-cloud/page.tsx
@@ -0,0 +1,16 @@
+import type { Metadata } from 'next';
+import SetUpAnAppDevEnvironmentGuide from './SetUpAnAppDevEnvironmentGuide';
+import './page.css';
+
+export const metadata: Metadata = {
+ title: 'Google Cloud アプリ開発環境構築 完全ガイド — Storage・IAM・Monitoring・Functions・Pub/Sub',
+ description:
+ 'Cloud Storage・IAM・Cloud Monitoring・Cloud Run functions・Pub/Sub をゼロから理解する初学者向け完全ガイド。Challenge Lab (GSP315) や一次情報ドキュメントまでステップバイステップで解説。',
+};
+
+/**
+ * Renders the Google Cloud app dev environment complete study guide page.
+ */
+export default function Page() {
+ return ;
+}
diff --git a/app/globals.css b/app/globals.css
index f2609dcb8..a36ad718c 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -45,6 +45,37 @@
--color-theme-ace-bg-page: #050a14;
--color-theme-ace-accent: #00c8ff;
+ /* Shared dark-guide surfaces */
+ --color-guide-background: #07111e;
+ --color-guide-surface-1: #0b1729;
+ --color-guide-surface-2: #0f1e34;
+ --color-guide-deep-background: #080f1c;
+ --color-guide-node-background: #0d1b2a;
+ --color-guide-border: #1c2e4a;
+ --color-guide-border-soft: #16253d;
+ --color-guide-border-strong: #274460;
+ --color-guide-foreground: #e7edf7;
+ --color-guide-foreground-soft: #a9b8d3;
+ --color-guide-foreground-faint: #6f83a5;
+ --color-guide-accent: #7c9eff;
+ --color-guide-accent-deep: #4a6fd6;
+ --color-guide-accent-soft: #34456e;
+ --color-guide-accent-glow: rgba(124, 158, 255, 0.18);
+ --color-guide-on-accent: #051023;
+ --color-guide-positive: #56d6a8;
+ --color-guide-warning: #f2b866;
+ --color-guide-code-background: #0a1526;
+ --color-guide-code-keyword: #ffd9a0;
+ --color-guide-code-foreground: #d7e1f5;
+ --color-guide-heading: #a7bfff;
+ --color-guide-table-background: #122238;
+ --color-guide-interactive-background: rgba(124, 158, 255, 0.15);
+ --color-guide-practice: #5fd4a8;
+ --color-guide-practice-background: rgba(95, 212, 168, 0.1);
+ --color-guide-practice-border: rgba(95, 212, 168, 0.3);
+ --color-guide-meta: #5f7291;
+ --color-guide-hero-start: #ffffff;
+
--color-theme-cdl-bg: hsl(160 50% 45% / 0.25);
--color-theme-cdl-fg: hsl(160 70% 65%);
@@ -195,6 +226,11 @@ body {
color: var(--color-theme-cisco-fg);
}
+@utility icon-theme-hands-on {
+ background: var(--color-theme-ace-bg);
+ color: var(--color-theme-ace-fg);
+}
+
.code-line {
white-space: pre;
font-family: var(--font-mono);
diff --git a/app/navigation.ts b/app/navigation.ts
index 3c96b1702..7c64b9213 100644
--- a/app/navigation.ts
+++ b/app/navigation.ts
@@ -38,6 +38,7 @@ type NavExamInput = {
domains: ReadonlyArray<{ label: string; href: string }>;
provider?: Provider;
status?: NavExam['status'];
+ overviewLabel?: string;
};
const PROVIDER_LABEL: Record = {
@@ -49,12 +50,12 @@ const PROVIDER_LABEL: Record = {
const PROVIDER_ORDER: readonly Provider[] = ['GCP', 'AWS', 'Cisco'];
/**
- * Convert exam inputs into a provider-grouped navigation tree.
+ * Converts exam inputs into a navigation tree grouped by provider.
*
- * Groups each input exam under its provider (defaults to `'GCP'` when `provider` is missing) and transforms exams into `NavExam` entries whose `items` start with a `'概要'` leaf followed by domain leaves (excluding any domain whose `href` equals the exam `href`).
+ * Exams without a provider are grouped under `GCP`. Each exam includes an overview link followed by domain links, excluding domains that use the exam's overview URL. Overview labels use `overviewLabel` when provided and `'概要'` otherwise.
*
- * @param exams - Array of exam inputs to convert into navigation groups
- * @returns An array of `NavGroup` objects ordered by provider display order; each group includes `provider`, its display `label`, and the group's `exams`
+ * @param exams - Exam inputs to convert into navigation groups
+ * @returns Provider groups containing transformed exams in display order
*/
export function toNavTree(exams: ReadonlyArray): NavGroup[] {
if (exams.length === 0) return [];
@@ -72,7 +73,7 @@ export function toNavTree(exams: ReadonlyArray): NavGroup[] {
// exam.href(概要)と同一 href の domain がある場合は重複を除去する。
// 例: PCNE では domains[0] が exam.href と一致するため React key の衝突を防ぐ。
items: [
- { label: '概要', href: exam.href },
+ { label: exam.overviewLabel ?? '概要', href: exam.href },
...exam.domains
.filter((d) => d.href !== exam.href)
.map((d) => ({ label: d.label, href: d.href })),
diff --git a/AWS-Certified-Solutions-Architect-Associate-Domain1.html b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain1.html
similarity index 100%
rename from AWS-Certified-Solutions-Architect-Associate-Domain1.html
rename to archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain1.html
diff --git a/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain2.html b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain2.html
new file mode 100644
index 000000000..ce75b82d7
--- /dev/null
+++ b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain2.html
@@ -0,0 +1,3000 @@
+
+
+
+
+
+ AWS SAA-C03 ドメイン2: 回復力のあるアーキテクチャの設計 完全ガイド
+
+
+
+
+
+
+
+
+
+
+
+
+ AWS Certified Solutions Architect – Associate (SAA-C03)
+
+
+ ドメイン2: 回復力のあるアーキテクチャの設計 完全ガイド(初級者向け)
+
+
+ 出題比率26%、SAA-C03で2番目に大きなドメインを、公式試験ガイドのタスクステートメントに沿ってステップバイステップで解説します。
+
+
+
+
+ 本ガイドは AWS 公式試験ガイド
+
SAA-C03 Exam Guide
+ および
+
Domain 2 詳細ページ
+ に基づき、出題範囲の各知識・スキル項目をステップバイステップで解説します。フローチャートは
+ Mermaid、図解・比較表は Markdown 記法で統一し、ASCII 図は使用していません。
+
+ 0. このガイドについて
+ 0.1 SAA-C03 試験全体における位置づけ
+
+ SAA-C03 試験は 4
+ つのドメインで構成されており、ドメイン2「回復力のあるアーキテクチャの設計」は出題比率
+ 26% と、4 ドメインの中で2番目に大きな比重を占めます。
+
+
+
+
+ 0.2 ドメイン2の2つのタスク
+
+ AWS公式試験ガイドでは、ドメイン2は以下の2つのタスクステートメントに分かれています。
+
+
+
+
+
+ 本ガイドはこの2タスクの構成に沿って、初級者でも理解できるようステップバイステップで解説していきます。
+
+
+ 1. タスク2.1: スケーラブルで疎結合なアーキテクチャの設計
+
+ 1.1 マルチティア(多層)アーキテクチャの基本
+
+ なぜ必要か :
+ 1台のサーバーにすべての処理(画面表示・業務ロジック・データ保存)を詰め込むと、負荷が増えたときにサーバー全体を止めてスケールアップするしかなく、可用性もスケーラビリティも低くなります。マルチティアアーキテクチャは、役割ごとにサーバー群(ティア)を分離することで、各層を独立してスケール・保守できるようにする設計です。
+
+
+
+
+ 各層の役割
+
+
+
+
+ ティア
+ 役割
+ 代表サービス
+
+
+
+
+ プレゼンテーション層
+ ユーザーからのリクエストを受け取り画面を返す
+ CloudFront, ALB, EC2, S3(静的ホスティング)
+
+
+ アプリケーション層
+ ビジネスロジックの実行
+ EC2, ECS/EKS, Lambda, API Gateway
+
+
+ データ層
+ データの永続化・キャッシュ
+ RDS, DynamoDB, ElastiCache, EFS
+
+
+
+
+
+
ベストプラクティス
+
+
+ 各ティアはセキュリティグループ/サブネットで分離し、最小権限で通信させる(Webティアのみインターネット向け、データ層はプライベートサブネットに配置)。
+
+
+ 層と層の間は疎結合にし、片方の層の障害・スケーリングがもう片方に直接影響しないようにする(詳細は次節)。
+
+
+ 各ティアを独立した Auto Scaling
+ グループ/サービスとして構成し、負荷特性に応じて個別にスケールできるようにする。
+
+
+
+
+
+ 1.2 疎結合アーキテクチャとメッセージング(SQS / SNS / EventBridge)
+
+
+ 密結合の問題点 :
+ あるコンポーネントが別のコンポーネントを直接同期呼び出しする設計(密結合)では、呼び出し先が遅い・落ちているとリクエスト全体が失敗し、障害が連鎖的に広がります(カスケード障害)。疎結合 では、コンポーネント間にキューやイベントバスのような緩衝材を挟み、互いの可用性やスケーリング状況に依存しない設計にします。
+
+
+
+
+ 主要サービスの役割
+
+
+
+
+ サービス
+ モデル
+ 特徴
+
+
+
+
+ Amazon SQS
+ キュー(Point-to-Point)
+
+ メッセージを一時的に保持。Producer と Consumer
+ の処理速度差を吸収するバッファ。可視性タイムアウト・DLQ・遅延キューをサポート
+
+
+
+ Amazon SNS
+ Pub/Sub(Publish/Subscribe)
+
+ 1つのメッセージを複数のサブスクライバーに同時配信(ファンアウト)
+
+
+
+ Amazon EventBridge
+ イベントバス
+
+ 複数の AWS サービスや SaaS
+ からのイベントをルールに基づき多数のターゲットにルーティング。スキーマレジストリも提供
+
+
+
+
+
+
+
ベストプラクティス
+
+
+ SQS の可視性タイムアウトは、Consumer
+ の平均処理時間より長く設定する(処理中に他のワーカーが同じメッセージを取得しないようにする)。
+
+
+ 一定回数処理に失敗したメッセージは
+ デッドレターキュー(DLQ)
+ に退避させ、失敗したメッセージでキューが詰まる(ポイズンメッセージ問題)のを防ぐ。
+
+
+ 1つの発行元から複数の購読者に同時配信したい場合は SNS + SQS
+ のファンアウトパターンを使う。
+
+
+ 複数のイベントソース・多様なターゲットへの複雑なルーティングが必要な場合は
+ EventBridge を検討する。
+
+
+ 疎結合にすることで、各コンポーネントを個別に Auto Scaling
+ でき、あるコンポーネントの障害が他に伝播しにくくなる。
+
+
+
+
+
+ 1.3 API の作成・公開・管理(Amazon API Gateway)
+
+
+ API Gateway は、バックエンド(Lambda、EC2、他の AWS
+ サービス、オンプレミス等)への「フロントドア」として、REST/HTTP/WebSocket API
+ を作成・公開・保護・監視するためのフルマネージドサービスです。
+
+
+
+
+ REST API と HTTP API の比較
+
+
+
+
+ 項目
+ REST API
+ HTTP API
+
+
+
+
+ 機能セット
+
+ フル機能(APIキー、リクエストバリデーション、WAF統合、キャッシュ等)
+
+ 軽量・低コスト(プロキシ機能中心)
+
+
+ コスト
+ 相対的に高い
+ REST APIより低コスト
+
+
+ 用途
+ 高度なAPI管理機能が必要な場合
+ シンプルなプロキシ/低レイテンシが目的の場合
+
+
+
+
+
+
ベストプラクティス
+
+
+ スロットリング(レート制限・バーストリミット)を設定し、バックエンドをトラフィック急増から保護する。
+
+
+ レスポンスキャッシュを有効化し、頻繁に呼ばれる同一リクエストへのバックエンド負荷を減らす。
+
+
+ IAM、Lambda オーソライザー、Amazon Cognito
+ ユーザープールなどで認証・認可を必ず実装する。
+
+
+ Amazon CloudWatch
+ と統合してAPI呼び出し数・レイテンシ・エラー率を監視する。
+
+
+
+
+
+ 1.4 水平スケーリングと垂直スケーリング(Amazon EC2 Auto Scaling)
+
+
+ 垂直スケーリング(スケールアップ/ダウン) :
+ インスタンスタイプを大きく(または小さく)することでリソースを増減させる方式。単純だが上限があり、変更時にダウンタイムが生じやすい。
+
+
+ 水平スケーリング(スケールアウト/イン) :
+ インスタンスの「台数」を増減させる方式。台数を分散させることで単一障害点を減らしつつ需要に追従できる、クラウドネイティブなスケーリング手法です。
+
+
+
+
+ Auto Scaling の主要スケーリングポリシー
+
+
+
+
+ ポリシー種別
+ 説明
+
+
+
+
+ ターゲット追跡スケーリング
+
+ CPU使用率など特定メトリクスを目標値に維持するよう自動調整(推奨・最も簡単)
+
+
+
+ ステップスケーリング
+ アラームの逸脱幅に応じて段階的にスケール量を変える
+
+
+ シンプルスケーリング
+ 単一の増減幅でスケール(旧世代の方式)
+
+
+ スケジュールスケーリング
+
+ 予測できる負荷パターン(毎朝9時など)に合わせて事前にスケール
+
+
+
+
+
+
+
ベストプラクティス
+
+
+ 複数の Availability Zone にまたがって Auto Scaling
+ グループを構成し、AZ障害時にも自動復旧できるようにする。
+
+
+ ヘルスチェック(EC2 +
+ ELB)を有効にし、不健全なインスタンスを自動的に入れ替える。
+
+
+ ウォームプールやライフサイクルフックを使い、起動時間の長いアプリケーションでも迅速にスケールできるようにする。
+
+
+ 垂直スケーリングは根本的な上限があるため、可用性・回復性の観点では水平スケーリングを優先する設計が推奨される。
+
+
+
+
+
+ 1.5 ロードバランシングの概念(ALB / NLB / GWLB)
+
+
+ Elastic Load Balancing (ELB)
+ は複数のターゲットにトラフィックを分散し、単一障害点を排除しながら可用性を高めるサービスです。用途に応じて3種類のロードバランサーを使い分けます。
+
+
+
+
+
+
+
+
+ 種別
+ レイヤー
+ 主なユースケース
+
+
+
+
+ Application Load Balancer (ALB)
+ L7
+
+ Webアプリ、マイクロサービス、コンテナ、パス/ホストベースルーティング
+
+
+
+ Network Load Balancer (NLB)
+ L4
+
+ 超高スループット、低レイテンシ、TCP/UDPベースのアプリ、固定IP要件
+
+
+
+ Gateway Load Balancer (GWLB)
+ L3/GENEVE
+
+ ファイアウォールやIDS/IPSなどセキュリティアプライアンスへの透過的なトラフィック転送
+
+
+
+
+
+
+
ベストプラクティス
+
+
+ ロードバランサー自体は複数AZにまたがる形でデプロイし、クロスゾーン負荷分散を有効化する。
+
+
+ ALB / NLB
+ のヘルスチェックを適切な間隔・しきい値で設定し、不健全なターゲットに即座にトラフィックを送らないようにする。
+
+
+ ロードバランサーを Auto Scaling
+ グループと組み合わせることで、需要に応じたスケーラブルかつ高可用なフロントエンドを構築する。
+
+
+
+
+
+ 1.6 キャッシング戦略(Amazon CloudFront / ElastiCache / DAX)
+
+
+ キャッシュは「よく使われるデータ」をオリジンより近い場所・高速な媒体に一時保存することで、レイテンシ削減とバックエンド負荷の軽減を同時に実現する、パフォーマンスと回復性の両面で重要な技術です。
+
+
+
+
+
+
+
+
+ サービス
+ キャッシュ対象
+ 特徴
+
+
+
+
+ Amazon CloudFront
+ 静的/動的コンテンツ、API応答
+
+ 世界中のエッジロケーションでユーザーに最も近い場所から配信、オリジン負荷を大幅軽減
+
+
+
+ Amazon ElastiCache (Redis/Memcached)
+ DBクエリ結果、セッション情報等
+ インメモリで数ミリ秒未満の応答、アプリ層とDB層の間に設置
+
+
+ DynamoDB Accelerator (DAX)
+ DynamoDBの読み取り結果
+ マイクロ秒単位の応答、DynamoDB API互換でコード変更が少ない
+
+
+
+
+
+
ベストプラクティス
+
+
+ 静的アセット(画像・CSS・JS)は CloudFront
+ で積極的にキャッシュし、TTL(有効期限)を適切に設定する。
+
+
+ セッション情報など「ステートフルなデータ」はアプリケーションサーバーではなく
+ ElastiCache
+ のような外部ストアに保持し、アプリ層をステートレスにする(1.9節参照)。
+
+
+ キャッシュ更新(無効化)戦略を設計段階で決めておく(Cache-Aside、Write-Through等)。
+
+
+ キャッシュはパフォーマンス向上だけでなく、バックエンドDBへの負荷集中を防ぎ、結果的にDB層の可用性・回復性向上にも寄与する。
+
+
+
+
+
+ 1.7 サーバーレス技術とコンピューティングオプションの選択
+
+
+ サーバーレスとは、サーバーのプロビジョニングやパッチ適用、スケーリング管理を AWS
+ 側に任せ、開発者がコードとビジネスロジックに集中できるモデルです。回復性の観点では、サーバー管理を排除することで人為的ミスによる障害要因を減らせる利点があります。
+
+
+
+
+ Lambda と Fargate の使い分け
+
+
+
+
+ 観点
+ AWS Lambda
+ AWS Fargate
+
+
+
+
+ 適した処理
+ イベント駆動・短時間実行のタスク
+ 長時間実行、複雑な複数サービス構成
+
+
+ 起動単位
+ 関数(リクエストごとに環境をプロビジョニング)
+ タスク/Pod単位でコンテナを起動
+
+
+ 実行時間の制約
+ 最大15分
+ 制約なし(長時間稼働可能)
+
+
+ スケーリング
+ リクエスト数に応じ自動・瞬時
+ ECS/EKSのオートスケーリング設定に依存
+
+
+
+
+
+
ベストプラクティス
+
+
+ 単純なイベント処理(画像リサイズ、APIバックエンドの一部処理等)は Lambda
+ を検討する。
+
+
+ 常駐が必要な複雑なアプリケーションやマイクロサービスは
+ Fargate(サーバーレスコンテナ)を検討し、EC2インスタンスの管理負担を減らす。
+
+
+ サーバーレス化によって「単一のEC2インスタンス障害」という単一障害点を根本的に排除できる点が、回復性向上の観点で重要。
+
+
+
+
+
+ 1.8 コンテナの移行とオーケストレーション(Amazon ECS / Amazon EKS)
+
+
+ コンテナは、アプリケーションとその依存関係をパッケージ化し、環境に依存せず一貫して動作させる技術です。AWS
+ ではコンテナの実行・管理(オーケストレーション)のために ECS と EKS
+ の2つのマネージドサービスを提供しています。
+
+
+
+
+
+
ベストプラクティス
+
+
+ タスク/Podは複数のAZに分散配置し、単一AZ障害時にもサービスを継続できるようにする。
+
+
+ コンテナは「イミュータブル(不変)」に扱い、変更が必要な場合は新しいイメージをビルドしてデプロイする(1.20節/2.4節で詳述)。
+
+
+ ローリングアップデートやBlue/Greenデプロイと組み合わせ、更新時のダウンタイムを回避する。
+
+
+ 既存のオンプレミスアプリケーションをコンテナ化して移行する場合は AWS
+ App2Container のようなツールでの移行も選択肢となる。
+
+
+
+
+
+ 1.9 マイクロサービス設計原則:ステートレス vs ステートフル
+
+
+ ステートフルなアプリケーション は、セッション情報やユーザーの状態をサーバー自身のメモリ/ディスクに保持します。この場合、そのユーザーは常に「同じサーバー」に接続し続ける必要があり(スティッキーセッション)、そのサーバーが落ちると状態を失い、スケールアウトも困難になります。
+
+
+ ステートレスなアプリケーション は、状態を一切保持せず、外部の共有ストア(ElastiCache、DynamoDB等)に状態を移すことで、どのサーバーに接続してもリクエストを処理できる ようにする設計です。これは水平スケーリング・自己修復(Auto
+ Scaling による自動置き換え)を実現するための基盤となる考え方です。
+
+
+
+
+
+
ベストプラクティス
+
+
+ セッション状態は ElastiCache(Redis)や DynamoDB
+ のような外部の耐久性あるストアに保存する。
+
+
+ アプリケーションサーバーのローカルディスクに重要なデータを保存しない(インスタンス終了とともに失われるため)。
+
+
+ ステートレス設計により、任意のサーバーが障害を起こしても Auto Scaling
+ が別のサーバーに置き換えるだけで、ユーザー体験への影響を最小化できる。
+
+
+
+
+
+ 1.10 イベント駆動アーキテクチャとワークフローオーケストレーション(AWS Step
+ Functions)
+
+
+ 複数のサービスが連携する処理では、イベント駆動(Choreography)
+ と
+ オーケストレーション(Orchestration)
+ という2つの制御スタイルがあります。
+
+
+
+ Choreography(振り付け型) :
+ 各サービスがイベントを発行し合い、中央の管理者なしに連鎖的に処理が進む(EventBridge等を利用)。疎結合だが、全体のフローの見通しは悪くなりがち。
+
+
+ Orchestration(オーケストレーション型) :
+ 中央のワークフローエンジン(AWS Step
+ Functions)が各ステップの実行順序・リトライ・エラー処理を明示的に管理する。
+
+
+
+
+
+
+
ベストプラクティス
+
+
+ 複数ステップにまたがる長時間処理・複雑な条件分岐・エラーハンドリングが必要な場合は
+ Step Functions によるオーケストレーションを検討する。
+
+
+ Step Functions の
+ Standard ワークフロー (長時間実行・厳密な実行回数保証が必要な場合)と
+ Express ワークフロー (高頻度・短時間のイベント処理に最適化)を用途に応じて使い分ける。
+
+
+ Step Functions は AWS X-Ray
+ と統合してワークフロー全体のトレースを可視化できる(2.8節参照)。
+
+
+
+
+
+ 1.11 ストレージタイプの選択(オブジェクト/ブロック/ファイルストレージ)
+
+
+
+
+
+
+
+
+ 種別
+ サービス例
+ スコープ
+ 主な用途
+
+
+
+
+ オブジェクトストレージ
+ Amazon S3
+ リージョン内で自動的に複数AZへ複製
+ 静的コンテンツ、バックアップ、データレイク、ログ保管
+
+
+ ブロックストレージ
+ Amazon EBS
+ 単一AZ、単一インスタンスに接続(Multi-Attach可)
+ データベースのデータボリューム、OSブートボリューム
+
+
+ ファイルストレージ
+ Amazon EFS
+ リージョン内で複数AZ、複数インスタンス共有
+ 複数サーバーで共有するファイル領域、コンテンツ管理システム
+
+
+
+
+
+
ベストプラクティス
+
+
+ 大量の非構造化データ(画像・動画・ログ・バックアップ)は S3
+ に保存し、ストレージクラス(Standard, IA,
+ Glacier等)でコストと耐久性のバランスを取る。
+
+
+ データベースなど高いIOPSと低レイテンシが必要なワークロードは
+ EBS(Provisioned IOPS等)を使用する。
+
+
+ 複数のコンテナ/インスタンスから同じファイルに同時アクセスする必要がある場合は
+ EFS を選択する。
+
+
+
+
+ 1.12 リードレプリカによる読み取りスケーリング
+
+ Amazon RDS の
+ リードレプリカ
+ は、プライマリDBインスタンスの変更を非同期でコピーした「読み取り専用」のインスタンスです。読み取りが多いワークロードで、読み取りトラフィックをレプリカに逃がすことでプライマリの負荷を軽減し、スケーラビリティを高めます(同一リージョン内・クロスリージョンの両方が可能)。
+
+
+
+
+
+
ベストプラクティス
+
+
+ リードレプリカはあくまで「読み取りスケーリング」が主目的であり、レプリケーションが非同期のため、障害復旧の主手段としては
+ Multi-AZ 配置(2.2節参照)と役割を分けて考える。
+
+
+ クロスリージョンのリードレプリカは、読み取り性能向上に加えてディザスタリカバリの補助(プライマリへ昇格させる)としても活用できる。
+
+
+ レプリカ数が増えるとレプリケーションラグが発生しうるため、アプリケーション側で結果整合性を許容できるかを設計時に検討する。
+
+
+
+
+
+ 2. タスク2.2: 高可用性・フォールトトレラントなアーキテクチャの設計
+
+
+ 2.1 AWSグローバルインフラストラクチャ(リージョン・アベイラビリティゾーン)
+
+ 高可用性設計の出発点は、AWSの物理的なインフラ構造を理解することです。
+
+
+
+ 重要な設計原則
+
+
+ 各リージョンは最低3つの、物理的に離れた(が低レイテンシで接続された)AZで構成されており、1つのAZで火災・洪水・電源障害が起きても他のAZは影響を受けない設計。
+
+
+ リージョンは互いに完全に独立しているため、単一リージョンの大規模障害から保護するにはマルチリージョン設計(2.2節)が必要になる。
+
+
+ ワークロードを単一AZに閉じずに複数AZへ分散配置することが、高可用性設計の最も基本的かつ重要な手段。
+
+
+
+
ベストプラクティス
+
+
+ 本番ワークロードは最低2つ、理想的には3つ以上のAZにまたがって配置する。
+
+
+ Auto Scaling グループ、RDS
+ Multi-AZ、複数AZにまたがるELBなど、マルチAZをネイティブサポートするサービスを積極的に利用する。
+
+
+
+
+
+ 2.2 障害復旧(ディザスタリカバリ)戦略と RPO / RTO
+
+
+ RPO(目標復旧時点: Recovery Point Objective) :
+ 障害発生時に許容できる「データ損失の最大時間」。例えば
+ RPO=1時間なら、直近1時間分のデータ損失までは許容される。
+
+
+ RTO(目標復旧時間: Recovery Time Objective) :
+ 障害発生からサービスを復旧させるまでに許容できる「最大時間」。
+
+
+ AWSでは、コストとRTO/RPOのトレードオフに応じて主に4つのDR戦略が定義されています。
+
+
+
+
+ 各戦略の解説
+
+
+
+
+ 戦略
+ 概要
+ 平常時のセカンダリリージョンの状態
+
+
+
+
+ Backup & Restore
+
+ データを定期的にバックアップし、障害時に別リージョンでリソースを新規作成して復元
+
+ リソース稼働なし(最も低コスト)
+
+
+ Pilot Light
+
+ 中核となるデータベース等は常時レプリケーションしておくが、アプリケーション層は最小限(起動していない、または最小サイズ)
+
+ DBのみ起動、その他は停止
+
+
+ Warm Standby
+
+ 縮小版ながら本番と同じ構成のスタックを常時稼働させておき、障害時にスケールアップして切り替え
+
+ フルスタックが縮小規模で常時稼働
+
+
+ Multi-Site Active-Active
+
+ 複数リージョンで同時にフル本番トラフィックを処理し、片方が落ちてももう片方が即座に引き継ぐ
+
+ フル規模で常時稼働・トラフィック処理中
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ベストプラクティス
+
+
+ ビジネス要件(許容できるダウンタイム・データ損失量)から逆算してRTO/RPOを定義し、それに見合った戦略を選ぶ(過剰な戦略はコスト増、過小な戦略はビジネスリスク増)。
+
+
+ DR戦略は定期的に
+ 実際にフェイルオーバーの訓練(ゲームデー等)
+ を行い、机上の設計だけで終わらせない。
+
+
+ Backup & Restore
+ ではバックアップの保存先(S3クロスリージョンレプリケーション等)と復元手順の自動化(CloudFormation/Terraform)が重要。
+
+
+
+
+
+ 2.3 フェイルオーバー戦略(Amazon Route 53 ルーティングポリシー)
+
+
+ Amazon Route 53
+ は、ヘルスチェックと組み合わせることで、障害が起きたエンドポイントから自動的に正常なエンドポイントへトラフィックを切り替える(フェイルオーバー)ことができるDNSサービスです。
+
+
+
+
+ 代表的なルーティングポリシー
+
+
+
+
+ ポリシー
+ 説明
+
+
+
+
+ シンプル
+ 単一リソースへの基本的なルーティング
+
+
+ 加重(Weighted)
+
+ 指定した比率でトラフィックを複数リソースに振り分け(Blue/Greenやカナリアに活用)
+
+
+
+ レイテンシベース
+
+ ユーザーから見て最もレイテンシが低いリージョンにルーティング
+
+
+
+ フェイルオーバー
+ ヘルスチェック結果に基づきプライマリ/セカンダリを自動切替
+
+
+ 地理位置情報
+
+ ユーザーの地理的位置に基づきルーティング(コンプライアンス要件等)
+
+
+
+ 複数値回答
+ 複数の正常なIPをランダムに返す(簡易的な負荷分散)
+
+
+
+
+
+
ベストプラクティス
+
+
+ ヘルスチェックはアプリケーションの実際の状態(単なるTCP疎通ではなく、DB接続を含むエンドポイント応答等)を反映するよう設計する。
+
+
+ フェイルオーバールーティングと、2.2節のDR戦略(Pilot Light / Warm
+ Standby /
+ Active-Active)を組み合わせて、実際に切替が発動する仕組みを構築する。
+
+
+ TTL(Time To
+ Live)を適切に短く設定し、フェイルオーバー発生時にDNS変更が速やかにクライアントへ反映されるようにする。
+
+
+
+
+
+ 2.4 分散設計パターンとイミュータブルインフラストラクチャ
+
+
+ イミュータブル(不変)インフラストラクチャ とは、本番稼働中のサーバーに対して直接パッチ適用や設定変更を行わず、変更が必要な場合は新しいインフラを構築してデプロイし、検証後にトラフィックを切り替えるという設計モデルです。これにより「設定ドリフト(環境ごとの差異の蓄積)」を防ぎ、デプロイの信頼性を高めます。
+
+
+
+
+
+
ベストプラクティス
+
+
+ AWS CodeDeploy や AWS Elastic Beanstalk の Blue/Green
+ デプロイ機能を活用し、切替とロールバックを自動化する。
+
+
+ カナリアデプロイ(一部トラフィックのみ新バージョンに向ける)と組み合わせ、影響範囲を限定しながら段階的に展開する。
+
+
+ イミュータブルなデプロイは「デプロイは成功するか、何も変わらないか(部分的な中途半端な状態にならない)」という信頼性を提供する。
+
+
+
+
+
+ 2.5 プロキシ概念によるデータベース回復性の向上(Amazon RDS Proxy)
+
+
+ アプリケーションが大量の同時接続をデータベースに直接張ると、DB側の接続数上限を圧迫し、特にLambdaのようにスケール時に接続数が急増するアーキテクチャでは問題が顕在化しやすくなります。Amazon RDS Proxy
+ はアプリケーションとRDS/Auroraの間に立つ完全マネージドのコネクションプーラーで、接続を効率的にプール・再利用し、DBフェイルオーバー時の切替も高速化します。
+
+
+
+
+
+
ベストプラクティス
+
+
+ Lambda など接続数が急増しやすいサーバーレスアーキテクチャでは RDS Proxy
+ の利用を検討し、DBの「too many connections」エラーを防ぐ。
+
+
+ RDS Proxy
+ はフェイルオーバー時の接続切替を高速化するため、アプリケーション側での複雑な再接続ロジックの実装が不要になり、可用性向上に寄与する。
+
+ IAM認証と組み合わせることでDB認証情報の管理も簡素化できる。
+
+
+
+ 2.6 ストレージの耐久性とレプリケーション設計
+
+ 「耐久性(Durability)」と「可用性(Availability)」は似て非なる概念です。耐久性 は「データが失われない確率」、可用性 は「必要なときにアクセスできる確率」を指します。
+
+
+
+
+
+
ベストプラクティス
+
+
+ Amazon S3
+ は標準(Standard)ストレージクラスで、最低3つのAZにまたがってオブジェクトを冗長化し、単一AZの喪失を想定した耐久性設計になっている(S3
+ One Zone-IAは単一AZのみのためコストは下がるが耐久性は劣る)。
+
+
+ リージョン全体の障害に備える場合は、S3のクロスリージョンレプリケーション(CRR)でデータを別リージョンにも複製する。
+
+
+ 誤削除・ランサムウェア対策として、S3のバージョニングとMFA Delete、Object
+ Lock(WORM)を組み合わせる。
+
+
+ EBSボリュームは単一AZに紐づくため、EBSスナップショットをS3(リージョンサービス)に定期取得することで、AZ障害からのデータ保護を行う。
+
+
+
+
+
+ 2.7 サービスクォータとスロットリングを考慮した設計
+
+
+ サービスクォータ(旧称: 制限/limits) は、AWSアカウントで作成・利用できるリソースの上限値です。スロットリング は、APIリクエストの「頻度」が一定を超えた場合にリクエストを拒否・遅延させる仕組みです。この2つを理解せずに設計すると、スケールした瞬間に予期しないエラーで障害が発生することがあります。
+
+
+
+
+
+
ベストプラクティス
+
+
+ 固定的なクォータ(例: Lambdaのペイロードサイズ上限、API
+ Gatewayのスロットルバーストレート)は変更できないため、アーキテクチャ側で制約を吸収する設計にする。
+
+
+ DR用のセカンダリリージョンでも本番と同等のクォータが確保されているかを事前に確認する(フェイルオーバー時にクォータ不足で復旧できないという事態を防ぐ)。
+
+
+ スロットリングエラーに対してはアプリケーション側で指数バックオフ・ジッターを用いたリトライを実装する。
+
+
+
+
+
+ 2.8 ワークロードの可視性(AWS X-Ray による分散トレーシング)
+
+
+ マイクロサービス化・疎結合化が進むほど、「どのリクエストが、どのサービスで、なぜ遅い/失敗したのか」を追跡することが難しくなります。AWS X-Ray
+ は分散システム全体をエンドツーエンドでトレースし、サービスマップとして可視化することで、ボトルネックや障害箇所の特定を容易にします。
+
+
+
+
+
+
ベストプラクティス
+
+
+ Lambda、ECS、EC2、API Gateway など主要コンポーネントに X-Ray
+ SDK/エージェントを組み込み、アプリケーション全体を通したトレーシングを有効化する。
+
+
+ 個々のサービスのログ・メトリクスだけでなく、リクエスト単位の「横断的な」可視性を持つことで、疎結合アーキテクチャにおける障害切り分け時間を短縮できる。
+
+
+ Step Functions
+ のワークフローとも統合し、ステートマシン全体の実行状況を追跡できる。
+
+
+
+
+
+ 2.9 レガシー・クラウド非対応アプリケーションの信頼性向上
+
+
+ すべてのアプリケーションが最初からクラウドネイティブに設計されているわけではありません。既存のモノリシックなアプリケーションや、リファクタリングが困難なレガシーシステムでも、以下のようなAWSサービスを組み合わせることで、大きくコードを変えずに回復性を向上できます。
+
+
+
+
+
+ 課題
+ 対応するAWSの仕組み
+
+
+
+
+ アプリケーションがスケールしない
+ Application Load Balancer + Auto Scaling グループ配下に配置
+
+
+ DBの直接接続数が多くフェイルオーバーに弱い
+ Amazon RDS Proxy を挟んでコネクションプーリング(2.5節)
+
+
+ インフラ管理の負担が大きい
+ AWS Elastic Beanstalk でプラットフォーム管理を任せる
+
+
+ コンテナ化を進めたいが移行作業が大変
+ AWS App2Container 等の移行支援ツールを活用
+
+
+ 単一AZでしか稼働していない
+ Multi-AZ配置・EFSでの共有ストレージ化
+
+
+
+
+
+
ベストプラクティス
+
+
+ 一足飛びに全面的な作り直し(リアーキテクト)を狙うのではなく、まずロードバランサー配下への移動・Multi-AZ化・RDS
+ Proxyの導入など「変更コストが低く効果が高い」対策から段階的に適用する。
+
+
+ Elastic Beanstalk
+ のようなマネージドプラットフォームを使うことで、パッチ適用やスケーリングの設定をAWSに任せつつ、既存コードをほぼそのまま活用できる。
+
+
+
+
+ 3. まとめ:出題頻出ポイント チェックリスト
+
+
+
+
+ #
+ チェック項目
+ 関連キーワード
+
+
+
+
+ 1
+
+ 疎結合の実現手段(SQS/SNS/EventBridge)の使い分けを説明できる
+
+ Point-to-Point, Pub/Sub, ファンアウト, DLQ
+
+
+ 2
+ ステートレス設計の意味とセッションの外部化を説明できる
+ ElastiCache, DynamoDB, スティッキーセッション
+
+
+ 3
+ ALB/NLB/GWLBの違いとユースケースを判別できる
+ L7/L4/L3, パスベースルーティング, 固定IP
+
+
+ 4
+
+ 水平/垂直スケーリングの違いとAuto Scalingのポリシーを説明できる
+
+ ターゲット追跡, ステップスケーリング
+
+
+ 5
+
+ S3/EBS/EFSの違い(オブジェクト/ブロック/ファイル)を判別できる
+
+ 11 9's耐久性, 単一AZ, マルチAZ共有
+
+
+ 6
+ RTO/RPOの定義と4つのDR戦略の違いを説明できる
+ Backup&Restore, Pilot Light, Warm Standby, Active-Active
+
+
+ 7
+
+ Route
+ 53のルーティングポリシー、特にフェイルオーバーの仕組みを説明できる
+
+ ヘルスチェック, 加重ルーティング
+
+
+ 8
+
+ イミュータブルインフラ・Blue/Greenデプロイの利点を説明できる
+
+ 設定ドリフト, カナリアリリース
+
+
+ 9
+
+ RDS
+ Proxyの目的(コネクションプーリング、フェイルオーバー高速化)を説明できる
+
+ Lambda + RDS接続数問題
+
+
+ 10
+
+ サービスクォータとスロットリングの違い、DRリージョンでの考慮点を説明できる
+
+ Service Quotas, Trusted Advisor, 指数バックオフ
+
+
+ 11
+ X-Rayによる分散トレーシングの目的を説明できる
+ サービスマップ, ボトルネック特定
+
+
+ 12
+
+ リードレプリカとMulti-AZの目的の違い(読み取りスケーリング vs
+ 高可用性)を説明できる
+
+ 非同期レプリケーション, 同期レプリケーション
+
+
+
+
+ 4. 参考文献・出典一覧
+
+
+
+
+ AWS Well-Architected Framework(信頼性の柱)
+
+
+
+
+
+
+
+
+
グローバルインフラ・障害復旧(DR)戦略
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain3.html b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain3.html
new file mode 100644
index 000000000..3da96ff1f
--- /dev/null
+++ b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate-Domain3.html
@@ -0,0 +1,3566 @@
+
+
+
+
+
+ ドメイン3: 高性能アーキテクチャの設計 | AWS SAA-C03 学習ガイド
+
+
+
+
+
+
+
+
+
+
+ この章で学ぶこと
+
+ ドメイン3は「パフォーマンス」と「スケーラビリティ」を軸に、AWSの主要な5つの技術領域を横断します。試験では以下の5つのタスク(Task)に分かれて出題されます。
+
+
+
+ 出典:
+ Content Domain 3: Design High-Performing
+ Architectures(AWS公式)
+
+ ドメイン3の全体像
+
+
+
+
+
+ Task 3.1: 高性能・スケーラブルなストレージソリューション
+
+ 出題される知識・スキル項目(公式)
+
+ 知識: -
+ ビジネス要件を満たすハイブリッドストレージソリューション -
+ 適切なユースケースを伴うストレージサービス(例: Amazon S3、Amazon
+ EFS、Amazon EBS) - 関連する特性を持つストレージタイプ(例:
+ オブジェクト、ファイル、ブロック)
+
+
+ スキル: -
+ パフォーマンス要件を満たすストレージサービスと構成の決定 -
+ 将来のニーズに対応してスケールできるストレージサービスの決定
+
+
+ 出典:
+ Task 3.1(AWS公式Exam Guide)
+
+ 3.1.1 ストレージ3種類の基本特性
+
+ AWSのストレージは大きく
+ オブジェクト・ファイル・ブロック
+ の3タイプに分類されます。この分類を理解することが、どのサービスを選ぶべきかの土台になります。
+
+
+
+ 出典:
+ Amazon S3の概要
+ /
+ Amazon EFSとは
+ /
+ Amazon EBSの概要
+
+ ストレージ選定の判断フロー
+
+
+ 3.1.2 Amazon S3: ストレージクラスとライフサイクル管理
+
+
+ Amazon
+ S3は単一のバケット内でオブジェクトごとに異なるストレージクラスを混在 させることができます。パフォーマンス試験対策では「アクセス頻度」と「取得速度要件」の2軸でクラスを選ぶ考え方が重要です。
+
+
+
+ 出典:
+ Amazon S3 ストレージクラス(公式)
+ /
+ S3 Intelligent-Tiering
+ /
+ S3 Glacierストレージクラス
+
+ S3ライフサイクルポリシーによる自動階層化
+
+
+
+ ベストプラクティス:
+ アクセスパターンが読めない、または変動するワークロード(例:
+ 新規サービスのログデータ)には、手動でライフサイクルルールを設計するより先に
+ S3 Intelligent-Tiering
+ を検討する。監視・自動化の追加料金のみで、取得料金や早期削除料金が発生しない。
+
+
+ 3.1.3 Amazon EBS: ボリュームタイプの選択
+
+
+ 出典:
+ Amazon EBSボリュームタイプ(公式)
+
+
+
+
+ ベストプラクティス:
+ EC2インスタンスとEBSボリュームの間の帯域(EBS最適化)がボトルネックにならないよう、EBS最適化対応インスタンスタイプ を選ぶ。また、単一EC2インスタンス停止時にもデータを永続化したい場合はEBS(インスタンスストアは一時的でインスタンス停止/終了時にデータが消える点に注意)。
+
+
+
+ 3.1.4 Amazon EFS: 弾力性のある共有ファイルストレージ
+
+
+ Amazon
+ EFSはLinuxベースのワークロード向けにNFSプロトコルで複数のAZ・複数のインスタンスから同時マウントできるマネージド型ファイルストレージです。
+
+
+ パフォーマンスモード: -
+ General Purpose :
+ 低レイテンシ優先。大半のユースケースのデフォルト。 -
+ Max I/O :
+ 数百〜数千のクライアントからの高い並列アクセスが必要な場合(レイテンシは若干犠牲)。
+
+
+ スループットモード: -
+ Bursting Throughput :
+ ストレージ容量に比例してスループットがスケール(バーストクレジット方式)。
+ - Elastic Throughput :
+ ワークロードのI/Oパターンに応じて自動的にスループットをスケール(予測不能なワークロードに最適)。
+ - Provisioned Throughput :
+ 容量に依存せず必要なスループットを明示的に指定。
+
+
+ ストレージクラス(S3同様のライフサイクル管理): - EFS
+ Standard / EFS Standard-IA - EFS One Zone / EFS One
+ Zone-IA(単一AZでコスト削減)
+
+
+ 出典:
+ Amazon EFSのパフォーマンス
+ /
+ Amazon EFSストレージクラス
+
+
+
+
+ ベストプラクティス:
+ EFSは「複数のAZ・複数のEC2から同時に同じファイルへアクセスする」要件(例:
+ CMS、コンテンツリポジトリ、共有ホームディレクトリ)で採用する。単一インスタンス専有の高性能ブロックストレージが必要ならEBS、Windows(SMB)やHPC(Lustre)、NetApp
+ ONTAP互換が必要ならFSxファミリーを検討する。
+
+
+
+ 3.1.5 ハイブリッドストレージ: AWS Storage Gateway
+
+
+ オンプレミス環境とAWSのストレージをシームレスに統合するためのサービスです。試験では「オンプレミスのレガシーアプリをそのまま使いながらクラウドのストレージを活用したい」という要件で問われます。
+
+
+
+ 出典:
+ AWS Storage Gatewayとは(公式)
+
+
+
+
+ ベストプラクティス:
+ 「オンプレミスのファイルサーバーを段階的にクラウド移行したい」→
+ File Gateway 。「オンプレミスのブロックストレージ(iSCSI)をバックアップ/DR目的でクラウド化したい」→
+ Volume Gateway 。「既存のテープバックアップ運用を変えずにコストだけ削減したい」→
+ Tape Gateway 。大量データの一括移行なら
+ AWS Snow Family 、継続的な同期・転送が必要なら
+ AWS DataSync (Task 3.5で詳述)を使い分ける。
+
+
+
+
+
+
+ Task 3.2: 高性能で弾力性のあるコンピューティングソリューション
+
+ 出題される知識・スキル項目(公式)
+
+ 知識: -
+ 適切なユースケースを伴うAWSコンピューティングサービス(例: AWS
+ Batch、Amazon EMR、AWS Fargate) -
+ AWSのグローバルインフラストラクチャとエッジサービスがサポートする分散コンピューティングの概念
+ - キューイングとメッセージングの概念(例: パブリッシュ/サブスクライブ)
+ - 適切なユースケースを伴うスケーラビリティ機能(例: Amazon EC2 Auto
+ Scaling、AWS Auto Scaling) - サーバーレステクノロジーとパターン(例:
+ AWS Lambda、Fargate) - コンテナのオーケストレーション(例: Amazon
+ ECS、Amazon EKS)
+
+
+ スキル: -
+ コンポーネントが独立してスケールできるようにワークロードを疎結合化する -
+ スケーリングアクションを実行するための指標と条件の特定 -
+ ビジネス要件を満たす適切なコンピューティングオプションと機能の選択(例:
+ EC2インスタンスタイプ) -
+ ビジネス要件を満たす適切なリソースタイプとサイズの選択(例:
+ Lambdaメモリの量)
+
+
+ 出典:
+ Task 3.2(AWS公式Exam Guide)
+
+ 3.2.1 コンピューティングサービスの全体マップ
+
+
+ 3.2.2 EC2 Auto Scalingによる弾力性の実現
+
+ 高性能アーキテクチャの中核は「需要に応じて自動的にリソースを増減する」弾力性(Elasticity)です。EC2
+ Auto Scalingは 起動テンプレート と
+ Auto Scalingグループ(ASG) を使ってこれを実現します。
+
+
+ スケーリングポリシーの種類:
+
+
+ 出典:
+ Amazon EC2 Auto Scalingのスケーリングポリシー
+ /
+ AWS Auto Scalingとは
+
+
+
+ ベストプラクティス:
+ AWS Auto Scaling (複数サービス横断)とAmazon EC2 Auto Scaling (EC2専用)の違いに注意。前者はEC2・ECS・DynamoDB・Auroraなど複数リソースのスケーリングを一元管理するための上位サービスであり、後者はEC2のASGそのものを指す。試験では文脈でどちらを指しているか読み分ける。
+
+
+
+ 3.2.3 サーバーレスコンピューティング: AWS Lambda
+
+
+
+ 出典:
+ Lambda関数のメモリ設定
+ /
+ Lambdaの設定に関するトラブルシューティング
+
+
+
+ ベストプラクティス:
+ CPU集約的な処理で実行時間が長い場合、まずメモリを増やす ことを検討する(メモリ増加=CPU増加のため、処理が速くなり結果的にコストが変わらない、あるいは下がることがある)。15分の実行時間上限を超えるバッチ処理は、AWS Step Functions で複数のLambda関数をオーケストレーションするか、AWS Batch /Amazon EMR などLambda以外のコンピューティングオプションを検討する。
+
+
+
+ 3.2.4 コンテナオーケストレーション: ECS vs EKS vs Fargate
+
+
+ 「オーケストレーター(何が管理するか)」と「起動タイプ(どこで実行されるか)」は独立した軸として理解する。
+
+
+
+ 出典:
+ AWS Fargateとは
+ /
+ Amazon ECSとEKSの選択に関する考え方
+
+
+
+ ベストプラクティス:
+ 「サーバーのパッチ適用やキャパシティ管理から解放されたい」→
+ Fargate起動タイプ 。「既にKubernetesの知識・マニフェストが社内に蓄積されている、またはオンプレミス/他クラウドとの一貫性が必要」→
+ EKS 。「AWSに閉じたシンプルな構成にしたい」→
+ ECS 。
+
+
+
+ 3.2.5 ワークロードの疎結合化: キューイングとパブリッシュ/サブスクライブ
+
+
+ 高性能アーキテクチャでは、コンポーネント同士を疎結合にすることで、それぞれが独立してスケールできるようにします。代表的な2つのパターンです。
+
+
+
+
+
+ 出典:
+ Amazon SQSとは
+ /
+ Amazon SNSとは
+ /
+ Amazon EventBridgeとは
+
+
+
+ ベストプラクティス:
+ フロントエンドが受け付けた大量のリクエストをバックエンドの処理速度に関わらず受け止めたい場合、間にSQSキュー を挟んで疎結合化する。これにより、バックエンドの処理が一時的に遅れてもリクエストが失われず、バックエンド側は自分のペースでAuto
+ Scalingしながら処理できる(バッファリングによる負荷平準化)。
+
+
+
+
+
+ Task 3.3: 高性能なデータベースソリューション
+ 出題される知識・スキル項目(公式)
+
+ 知識: - AWSグローバルインフラストラクチャ(例:
+ アベイラビリティーゾーン、AWSリージョン) -
+ キャッシング戦略とサービス(例: Amazon ElastiCache) -
+ データアクセスパターン(例: 読み取り集約型と書き込み集約型の比較) -
+ データベースのキャパシティプランニング(例:
+ キャパシティユニット、インスタンスタイプ、プロビジョンドIOPS) -
+ データベース接続とプロキシ -
+ 適切なユースケースを伴うデータベースエンジン(例:
+ 異種間移行、同種間移行) - データベースレプリケーション(例:
+ 読み取りレプリカ) - データベースのタイプとサービス(例:
+ サーバーレス、リレーショナルと非リレーショナルの比較、インメモリ)
+
+
+ スキル: - ビジネス要件を満たす読み取りレプリカの構成 -
+ データベースアーキテクチャの設計 - 適切なデータベースエンジンの決定(例:
+ MySQLとPostgreSQLの比較) - 適切なデータベースタイプの決定(例: Amazon
+ Aurora、Amazon DynamoDB) - ビジネス要件を満たすキャッシングの統合
+
+
+ 出典:
+ Task 3.3(AWS公式Exam Guide)
+
+ 3.3.1 データベースタイプの選択フロー
+
+
+ 出典:
+ Amazon RDSとは
+ /
+ Amazon Auroraとは
+ /
+ Amazon DynamoDBとは
+
+
+ 3.3.2 Amazon RDS: マルチAZ配置と読み取りレプリカ
+
+
+ マルチAZ(高可用性) と
+ 読み取りレプリカ(性能スケーリング)
+ は目的が異なる点に注意が必要です。
+
+
+
+
+ 出典:
+ Amazon RDSのマルチAZ配置
+ /
+ Amazon RDS読み取りレプリカの操作
+
+
+
+ ベストプラクティス:
+ 「読み取りが集中してDBがボトルネックになっている」→
+ 読み取りレプリカ を追加してSELECTクエリを分散する。「DB障害時にダウンタイムを最小化したい」→
+ マルチAZ配置 で自動フェイルオーバーを構成する。両方を組み合わせる(マルチAZ+複数の読み取りレプリカ)のが一般的な高性能・高可用性構成。
+
+
+
+ 3.3.3 Amazon Aurora: クラウドネイティブなストレージアーキテクチャ
+
+
+ Auroraは、コンピュート層とストレージ層を分離し、ストレージを自動的に複数AZ・6つのコピーへ複製する独自アーキテクチャにより、RDS標準のMySQL/PostgreSQLより高いスループットと耐障害性を実現します。
+
+
+
+ Aurora特有の高性能機能: -
+ Aurora Serverless :
+ トラフィックに応じてコンピュート容量を自動でスケールアップ/ダウン(断続的・予測不能なワークロードに最適)
+ - Aurora Global Database :
+ 複数リージョンにまたがるレプリケーション(1秒未満のレプリケーションラグ)でグローバルな読み取り性能とDRを両立
+ - Auroraレプリカ :
+ 最大15台まで作成可能(標準MySQLは最大5台)で読み取りスケーリングの上限が高い
+
+
+ 出典:
+ Amazon Auroraのストレージと信頼性
+ /
+ Aurora Serverless v2
+ /
+ Aurora Global Database
+
+ 3.3.4 データベースエンジンの移行: 同種間 vs 異種間
+
+
+ 出典:
+ AWS Database Migration Serviceとは
+ /
+ AWS Schema Conversion Toolとは
+
+
+
+ ベストプラクティス:
+ 試験で「MySQL同士」のようにエンジンが同じ移行が問われたらDMSのみ で完結できると考える。「Oracle→Aurora
+ PostgreSQL」のようにエンジンが異なる移行では、まずSCT でスキーマ・ストアドプロシージャ等を変換し、その後DMS でデータそのものを移行する2段階アプローチになる。
+
+
+
+ 3.3.5 Amazon DynamoDB: キャパシティモードとアクセスパターン
+
+
+
+ データアクセスパターンの設計: -
+ 読み取り集約型(Read-heavy) : DynamoDB Accelerator (DAX)
+ によるマイクロ秒レベルのインメモリキャッシュ、または読み取りレプリカ/ElastiCacheの活用を検討
+ - 書き込み集約型(Write-heavy) :
+ パーティションキーの設計を分散させ「ホットパーティション」を避ける。オンデマンドモードやWCUの適切な設計が重要
+
+
+ 出典:
+ DynamoDBの読み取り/書き込みキャパシティモード
+ /
+ DynamoDB Accelerator (DAX)
+
+
+ 3.3.6 キャッシング戦略: Amazon ElastiCache
+
+
+ ElastiCacheは3つのエンジン(Valkey ・Redis OSS ・Memcached )から選択できるフルマネージド型インメモリデータストアです。AWSは新規構築のワークロードに対して、オープンソースでコスト効率の高い
+ Valkey を推奨しています(Redis
+ OSSからのコマンド・クライアント互換のドロップイン代替)。
+
+
+
+ 出典:
+ Amazon ElastiCache for Valkeyの発表
+ /
+ ElastiCacheのエンジン選択
+
+ 代表的なキャッシング戦略(Redis OSS/Valkey互換):
+
+
+
+
+ 出典:
+ キャッシング戦略のベストプラクティス
+
+
+
+ ベストプラクティス:
+ 読み取り集約型で、同じデータに何度もアクセスされるパターン(商品カタログ、セッション情報等)には遅延読み込み が適している。データの鮮度が極めて重要な場合(在庫数など)はライトスルー を検討するが、TTL(有効期限)を併用して古いデータの残留リスクを軽減する。
+
+
+
+ 3.3.7 データベース接続とプロキシ: Amazon RDS Proxy
+
+
+ サーバーレス(Lambda)やマイクロサービスのように大量の短命なコネクションを発生させるアーキテクチャでは、RDS/Auroraのコネクション数上限に達しやすいという課題があります。
+
+
+
+ 出典:
+ Amazon RDS Proxyとは
+
+
+
+ ベストプラクティス:
+ 「Lambda関数からRDSへの接続で"too many
+ connections"エラーが発生する」という試験の典型的シナリオにはRDS Proxy が正解になりやすい。RDS
+ Proxyはコネクションプーリングに加え、フェイルオーバー時の切り替え時間も短縮する。
+
+
+
+
+
+
+ Task 3.4: 高性能・スケーラブルなネットワークアーキテクチャ
+
+ 出題される知識・スキル項目(公式)
+
+ 知識: -
+ 適切なユースケースを伴うエッジネットワーキングサービス(例: Amazon
+ CloudFront、AWS Global Accelerator) -
+ ネットワークアーキテクチャの設計方法(例:
+ サブネット階層、ルーティング、IPアドレッシング) -
+ ロードバランシングの概念(例: Application Load Balancer) -
+ ネットワーク接続オプション(例: AWS VPN、AWS Direct Connect、AWS
+ PrivateLink)
+
+
+ スキル: -
+ さまざまなアーキテクチャ(グローバル、ハイブリッド、マルチティア等)向けのネットワークトポロジの作成
+ - 将来のニーズに対応してスケールできるネットワーク構成の決定 -
+ ビジネス要件を満たす適切なリソース配置の決定 -
+ 適切なロードバランシング戦略の選択
+
+
+ 出典:
+ Task 3.4(AWS公式Exam Guide)
+
+ 3.4.1 VPCのマルチティア・サブネット設計
+
+ 高性能・高可用性の基本形は「複数AZにまたがる、階層化されたサブネット設計 」です。
+
+
+
+ 出典:
+ VPCとサブネット
+ /
+ VPCのシナリオとサンプル構成
+
+
+ サブネット設計のポイント: -
+ パブリックサブネット :
+ インターネットゲートウェイへの経路を持つルートテーブルに関連付けられたサブネット(ALB、NATゲートウェイ、踏み台サーバー等)
+ - プライベートサブネット :
+ インターネットゲートウェイへの直接経路を持たないサブネット(アプリケーション層・データ層)。アウトバウンド通信が必要な場合はNATゲートウェイを経由
+ - CIDR設計 :
+ 将来の拡張を見越して、各サブネットに十分なIPアドレス余裕を持たせる(/24なら251個の使用可能IPアドレス、AWSは各サブネットの先頭4個+末尾1個を予約)
+
+
+
+ ベストプラクティス:
+ 最低でも2つのAZ にまたがるサブネット設計を行い、単一AZ障害でもサービスを継続できるようにする。データ層のサブネットにはインターネットゲートウェイへの経路を一切持たせない ことで、データベースへの直接的なインターネットアクセスを構造的に排除する(多層防御)。
+
+
+ 3.4.2 ロードバランシング戦略の選択
+
+
+
+ 出典:
+ Elastic Load Balancingの機能比較
+ /
+ Application Load Balancerとは
+ /
+ Network Load Balancerとは
+
+ ALBによるパスベース/ホストベースルーティング
+
+
+ 3.4.3 エッジネットワーキング: CloudFront と Global Accelerator
+
+
+
+ 出典:
+ Amazon CloudFrontとは
+ /
+ AWS Global Acceleratorとは
+
+
+
+
+ ベストプラクティス:
+ 「静的コンテンツの配信を高速化したい」「動画配信のキャッシュ効率を上げたい」→
+ CloudFront 。「TCP/UDPベースのゲームサーバー・IoTなどHTTP以外のプロトコルを高速化したい」「複数リージョン間で瞬時にフェイルオーバーしたい」→
+ Global Accelerator 。両者は併用可能(例:
+ CloudFrontで静的コンテンツ、Global
+ AcceleratorでAPIのTCP接続を最適化)。
+
+
+
+ 3.4.4 ハイブリッド接続: VPN・Direct Connect・PrivateLink
+
+
+
+
+ 出典:
+ AWS Site-to-Site VPNとは
+ /
+ AWS Direct Connectとは
+ /
+ AWS PrivateLinkとは
+
+
+
+ ベストプラクティス:
+ VPCピアリングやルートテーブルの複雑な管理を避けつつ、特定のサービス(自社のマイクロサービスや、SaaSベンダーが提供するサービス)にだけ安全にアクセスしたい場合はPrivateLink(インターフェースVPCエンドポイント) を使う。IPアドレス空間が重複していても問題なく接続できる点が、VPCピアリングに対する大きな利点。
+
+
+
+
+
+ Task 3.5: 高性能なデータ取り込み・変換ソリューション
+ 出題される知識・スキル項目(公式)
+
+ 知識: -
+ 適切なユースケースを伴うデータ分析・可視化サービス(例: Amazon
+ Athena、AWS Lake Formation、Amazon QuickSuite) -
+ データ取り込みパターン(例: 頻度) -
+ 適切なユースケースを伴うデータ転送サービス(例: AWS DataSync、AWS
+ Storage Gateway) - 適切なユースケースを伴うデータ変換サービス(例: AWS
+ Glue) - 取り込みアクセスポイントへのセキュアなアクセス -
+ ビジネス要件を満たすために必要なサイズと速度 -
+ 適切なユースケースを伴うストリーミングデータサービス(例: Amazon
+ Kinesis)
+
+
+ スキル: - データレイクの構築とセキュリティ確保 -
+ データストリーミングアーキテクチャの設計 -
+ データ転送ソリューションの設計 - 可視化戦略の実装 -
+ データ処理に適したコンピューティングオプションの選択(例: Amazon EMR) -
+ 取り込みに適した構成の選択 - フォーマット間のデータ変換(例:
+ .csvから.parquetへ)
+
+
+ 出典:
+ Task 3.5(AWS公式Exam Guide)
+
+
+
+ 補足(サービス名の変遷): Amazon
+ QuickSightは2025年10月に
+ Amazon Quick Suite
+ へと進化し、AIエージェント機能(Quick Research、Quick
+ Flows等)が追加されました。既存のQuickSightのダッシュボード・データセット・権限設定はそのまま引き継がれます。出典:
+ Amazon QuickSightからAmazon Quick
+ Suiteへの進化(AWS公式ブログ) 。同様に、Amazon Kinesis Data FirehoseはAmazon Data Firehose という名称に変更されています。
+
+
+ 3.5.1 データレイクアーキテクチャの全体像
+
+
+ 出典:
+ AWS Lake Formationとは
+ /
+ AWS Glueとは
+ /
+ Amazon Athenaとは
+
+
+
+
+ ベストプラクティス:
+ 複数の部門・チームがデータレイクにアクセスする場合、IAMポリシーだけで細かい権限(特定の列だけ、特定の行だけ)を管理するのは煩雑になりがちなので、Lake Formation の一元的な権限管理を使う。「S3に溜まったデータをすぐにSQLで分析したいが、DWHを構築するほどではない」という要件にはAthena が適している。
+
+
+
+ 3.5.2 ストリーミングデータの取り込み: Amazon Kinesis
+
+
+
+ 出典:
+ Amazon Kinesis Data Streamsとは
+ /
+ Amazon Data Firehoseとは
+
+
+
+
+ ベストプラクティス:
+ 「取り込んだストリームデータを複数の異なるアプリケーションが同時に処理する必要がある」→
+ Kinesis Data Streams (コンシューマーを複数アタッチ可能)。「単純にストリームデータをS3やRedshiftに流し込みたいだけで、運用の手間を減らしたい」→
+ Amazon Data Firehose 。
+
+
+ 3.5.3 バッチ vs ストリーミング: 取り込み頻度の設計
+
+
+
+ ベストプラクティス:
+ 不正取引検知やリアルタイムダッシュボードなど「今すぐの反応」が価値を持つユースケースはストリーミングを選ぶ。日次バッチのレポーティングなど、多少の遅延が許容できコスト効率を優先する場合はバッチ処理(Glue/EMRのスケジュール実行)を選ぶ。
+
+
+
+ 3.5.4 データ変換: AWS Glue ETLとフォーマット変換
+
+
+
+ 出典:
+ AWS Glueクローラとは
+ /
+ AWS GlueのETLプログラミング
+
+
+
+ ベストプラクティス:
+ Athenaでの分析コストとクエリ性能を最適化するために、CSV/JSONのような行指向フォーマットから
+ Parquet
+ のような列指向・圧縮フォーマットへ変換する(スキャンするデータ量が減り、クエリ料金・実行時間の両方を削減できる)。この変換はAWS Glue ETLジョブ で自動化するのが一般的なパターン。
+
+
+
+ 3.5.5 データ転送: DataSync と Storage Gateway の使い分け
+
+
+
+ 出典:
+ AWS DataSyncとは
+ /
+ AWS Storage Gatewayとは
+ /
+ AWS Snow Familyとは
+
+
+
+ セキュアな取り込みアクセスポイントの設計: -
+ 取り込み用のS3バケットへは
+ VPCエンドポイント(Gateway型/Interface型)
+ 経由でアクセスし、インターネットを経由させない -
+ IAMポリシーとS3バケットポリシーで、取り込み専用のロール/ユーザーに最小権限(PutObjectのみ等)を付与
+ -
+ Kinesisへのデータ投入元は、IAM認証 やVPCエンドポイント を使って未認可のクライアントからの投入を防ぐ
+
+
+ 出典:
+ Amazon S3向けVPCエンドポイント
+
+
+
+
+ 参考文献
+ 試験ガイド(公式)
+
+ Task 3.1: ストレージ関連
+
+ Task 3.2: コンピューティング関連
+
+ Task 3.3: データベース関連
+
+ Task 3.4: ネットワーク関連
+
+ Task 3.5: データ分析・取り込み関連
+
+
+
+ 本ガイドは2026年7月時点のAWS公式ドキュメントおよびAWS公式ブログの情報に基づいて作成しています。AWSのサービス仕様・料金・名称は変更される可能性があるため、実際の試験対策・設計判断の際は必ず最新の公式ドキュメントを参照してください。
+
+
+
+
+
+
+
+
+
diff --git a/AWS-Certified-Solutions-Architect-Associate.html b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate.html
similarity index 99%
rename from AWS-Certified-Solutions-Architect-Associate.html
rename to archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate.html
index 633d35217..f4da1fd9e 100644
--- a/AWS-Certified-Solutions-Architect-Associate.html
+++ b/archive/Aws/SAA/html/AWS-Certified-Solutions-Architect-Associate.html
@@ -3454,15 +3454,20 @@ 8. 参考文献・出典一覧
// 進捗バー(記事の読了位置)
const progressBar = document.getElementById('navProgressBar');
+ const article = document.querySelector('.article');
+ let progressFrame = null;
function updateProgress() {
- const article = document.querySelector('.article');
- const total = article.scrollHeight - window.innerHeight;
- const scrolled = Math.min(
- Math.max(window.scrollY - article.offsetTop + 200, 0),
- total,
- );
- const pct = total > 0 ? (scrolled / total) * 100 : 0;
- progressBar.style.width = pct + '%';
+ if (progressFrame !== null) return;
+ progressFrame = requestAnimationFrame(function () {
+ const total = article.scrollHeight - window.innerHeight;
+ const scrolled = Math.min(
+ Math.max(window.scrollY - article.offsetTop, 0),
+ total,
+ );
+ const pct = total > 0 ? (scrolled / total) * 100 : 0;
+ progressBar.style.width = pct + '%';
+ progressFrame = null;
+ });
}
window.addEventListener('scroll', updateProgress, { passive: true });
updateProgress();
diff --git a/AWS-Certified-Solutions-Architect-Associate-Domain1.md b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain1.md
similarity index 98%
rename from AWS-Certified-Solutions-Architect-Associate-Domain1.md
rename to archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain1.md
index 38e13d4e2..febab9d8d 100644
--- a/AWS-Certified-Solutions-Architect-Associate-Domain1.md
+++ b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain1.md
@@ -533,7 +533,7 @@ flowchart LR
| カスタマー管理キー(CMK) | 利用者が作成・管理 | ポリシー、ローテーション、有効/無効化を利用者が制御できる |
| インポートされたキー | 利用者が外部で生成し持ち込む | AWSによる自動ローテーション対象外(手動ローテーションが必要) |
-暗号化キーへのアクセスはIAMポリシーだけでなく**キーポリシー(リソースベースポリシー)**でも制御され、両方の共通部分が実効権限になります。
+暗号化キーへのアクセスは、**キーポリシー(リソースベースポリシー)**単独でも許可できます。IAMポリシーで権限を付与する場合は、同一アカウントのIAMへ認可を委任する記述がキーポリシーに必要です。これらに加えて、KMS Grantsも権限を付与する認可経路です。
出典: [AWS Key Management Service best practices](https://docs.aws.amazon.com/pdfs/prescriptive-guidance/latest/aws-kms-best-practices/aws-kms-best-practices.pdf)
@@ -555,9 +555,9 @@ flowchart TD
出典: [Rotate AWS KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html)
ポイント:
-- 自動ローテーションのデフォルトは365日(年1回)だが、カスタマーマネージドキーでは周期を設定可能で、キーIDやARNは変更されない(アプリケーション側の変更は不要)
+- カスタマーマネージドキーの自動ローテーションはデフォルトで無効。有効化した場合のデフォルト間隔は365日(年1回)で、周期を設定可能。対象は対称暗号化のAWS_KMSオリジンキーで、キーIDやARNは変更されない(アプリケーション側の変更は不要)
- 過去のキーマテリアルはすべて保持されるため、古いバージョンで暗号化されたデータも引き続き復号可能
-- インポートされたキーマテリアル(EXTERNAL origin)は自動ローテーションの対象外で、手動または オンデマンドローテーション機能を使う必要がある
+- インポートされたキーマテリアル(EXTERNAL origin)は自動ローテーションの対象外。ただし対称暗号化キーであればオンデマンドローテーションを利用できる
- AWS KMSはデータキーの過度な再利用を推奨しておらず、データキー自体は「ラッピングキー」であるCMKよりも高頻度で使い捨てられる設計になっている
出典: [Rotate AWS KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html)
diff --git a/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain2.md b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain2.md
new file mode 100644
index 000000000..722c1c2cb
--- /dev/null
+++ b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain2.md
@@ -0,0 +1,859 @@
+# AWS Certified Solutions Architect - Associate (SAA-C03)
+# ドメイン2: 回復力のあるアーキテクチャの設計 完全ガイド(初級者向け)
+
+> 本ガイドは AWS 公式試験ガイド [SAA-C03 Exam Guide](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03.html) および [Domain 2 詳細ページ](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain2.html) に基づき、出題範囲の各知識・スキル項目をステップバイステップで解説します。フローチャートは Mermaid、図解・比較表は Markdown 記法で統一し、ASCII 図は使用していません。
+
+---
+
+## 0. このガイドについて
+
+### 0.1 SAA-C03 試験全体における位置づけ
+
+SAA-C03 試験は 4 つのドメインで構成されており、ドメイン2「回復力のあるアーキテクチャの設計」は出題比率 **26%** と、4 ドメインの中で最大の比重を占めます。
+
+```mermaid
+pie showData
+ title SAA-C03 出題ドメイン別の比率
+ "ドメイン1: セキュアなアーキテクチャの設計 (30%)" : 30
+ "ドメイン2: 回復力のあるアーキテクチャの設計 (26%)" : 26
+ "ドメイン3: 高性能アーキテクチャの設計 (24%)" : 24
+ "ドメイン4: コスト最適化アーキテクチャの設計 (20%)" : 20
+```
+
+### 0.2 ドメイン2の2つのタスク
+
+AWS公式試験ガイドでは、ドメイン2は以下の2つのタスクステートメントに分かれています。
+
+```mermaid
+flowchart TD
+ D2["ドメイン2 回復力のあるアーキテクチャの設計 (26%)"]
+ D2 --> T21["タスク2.1 スケーラブルで疎結合な アーキテクチャの設計"]
+ D2 --> T22["タスク2.2 高可用性および/または フォールトトレラントな アーキテクチャの設計"]
+
+ T21 --> T21a["マルチティア設計"]
+ T21 --> T21b["疎結合・メッセージング"]
+ T21 --> T21c["スケーリング設計"]
+ T21 --> T21d["サーバーレス/コンテナ"]
+
+ T22 --> T22a["高可用性設計"]
+ T22 --> T22b["障害復旧(DR)戦略"]
+ T22 --> T22c["フォールトトレランス"]
+ T22 --> T22d["可視性・監視"]
+
+ style D2 fill:#1f3a5f,color:#fff
+ style T21 fill:#2c5480,color:#fff
+ style T22 fill:#2c5480,color:#fff
+```
+
+本ガイドはこの2タスクの構成に沿って、初級者でも理解できるようステップバイステップで解説していきます。
+
+---
+
+## 1. タスク2.1: スケーラブルで疎結合なアーキテクチャの設計
+
+### 1.1 マルチティア(多層)アーキテクチャの基本
+
+**なぜ必要か**: 1台のサーバーにすべての処理(画面表示・業務ロジック・データ保存)を詰め込むと、負荷が増えたときにサーバー全体を止めてスケールアップするしかなく、可用性もスケーラビリティも低くなります。マルチティアアーキテクチャは、役割ごとにサーバー群(ティア)を分離することで、各層を独立してスケール・保守できるようにする設計です。
+
+```mermaid
+flowchart LR
+ User(("ユーザー")) --> DNS["Amazon Route 53 (DNS)"]
+ DNS --> CF["Amazon CloudFront (CDN/エッジ層)"]
+ CF --> WebALB["Application Load Balancer (Webティア入口)"]
+
+ subgraph WebTier["Webティア(プレゼンテーション層)"]
+ Web1["EC2 / Fargate"]
+ Web2["EC2 / Fargate"]
+ end
+ WebALB --> Web1
+ WebALB --> Web2
+
+ subgraph AppTier["アプリケーションティア(ロジック層)"]
+ App1["EC2 / ECS / Lambda"]
+ App2["EC2 / ECS / Lambda"]
+ end
+ Web1 --> InternalALB["内部ロードバランサー"]
+ Web2 --> InternalALB
+ InternalALB --> App1
+ InternalALB --> App2
+
+ subgraph DataTier["データティア(永続化層)"]
+ Primary[("Amazon RDS プライマリ")]
+ Replica[("読み取り レプリカ")]
+ Cache[("Amazon ElastiCache")]
+ end
+ App1 --> Primary
+ App2 --> Primary
+ App1 --> Cache
+ Primary -.非同期レプリケーション.-> Replica
+
+ style WebTier fill:#1f3a5f,color:#fff
+ style AppTier fill:#1f3a5f,color:#fff
+ style DataTier fill:#1f3a5f,color:#fff
+```
+
+**各層の役割**
+
+| ティア | 役割 | 代表サービス |
+|---|---|---|
+| プレゼンテーション層 | ユーザーからのリクエストを受け取り画面を返す | CloudFront, ALB, EC2, S3(静的ホスティング) |
+| アプリケーション層 | ビジネスロジックの実行 | EC2, ECS/EKS, Lambda, API Gateway |
+| データ層 | データの永続化・キャッシュ | RDS, DynamoDB, ElastiCache, EFS |
+
+**ベストプラクティス**
+- 各ティアはセキュリティグループ/サブネットで分離し、最小権限で通信させる(Webティアのみインターネット向け、データ層はプライベートサブネットに配置)。
+- 層と層の間は疎結合にし、片方の層の障害・スケーリングがもう片方に直接影響しないようにする(詳細は次節)。
+- 各ティアを独立した Auto Scaling グループ/サービスとして構成し、負荷特性に応じて個別にスケールできるようにする。
+
+出典: [AWS Well-Architected Framework - Reliability Pillar](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html)
+
+---
+
+### 1.2 疎結合アーキテクチャとメッセージング(SQS / SNS / EventBridge)
+
+**密結合の問題点**: あるコンポーネントが別のコンポーネントを直接同期呼び出しする設計(密結合)では、呼び出し先が遅い・落ちているとリクエスト全体が失敗し、障害が連鎖的に広がります(カスケード障害)。**疎結合**では、コンポーネント間にキューやイベントバスのような緩衝材を挟み、互いの可用性やスケーリング状況に依存しない設計にします。
+
+```mermaid
+flowchart TD
+ Producer["注文サービス (Producer)"] -->|1. イベント発行| SNS["Amazon SNS トピック"]
+ SNS -->|2a. ファンアウト| SQS1["SQS キュー (在庫サービス用)"]
+ SNS -->|2b. ファンアウト| SQS2["SQS キュー (通知サービス用)"]
+ SNS -->|2c. ファンアウト| SQS3["SQS キュー (請求サービス用)"]
+
+ SQS1 --> C1["在庫更新 Lambda"]
+ SQS2 --> C2["メール通知 Lambda"]
+ SQS3 --> C3["請求処理 Lambda"]
+
+ SQS1 -.失敗を規定回数超過.-> DLQ1["デッドレターキュー(DLQ)"]
+
+ style SNS fill:#2c5480,color:#fff
+ style SQS1 fill:#1f3a5f,color:#fff
+ style SQS2 fill:#1f3a5f,color:#fff
+ style SQS3 fill:#1f3a5f,color:#fff
+ style DLQ1 fill:#7a2e2e,color:#fff
+```
+
+**主要サービスの役割**
+
+| サービス | モデル | 特徴 |
+|---|---|---|
+| Amazon SQS | キュー(Point-to-Point) | メッセージを一時的に保持。Producer と Consumer の処理速度差を吸収するバッファ。可視性タイムアウト・DLQ・遅延キューをサポート |
+| Amazon SNS | Pub/Sub(Publish/Subscribe) | 1つのメッセージを複数のサブスクライバーに同時配信(ファンアウト) |
+| Amazon EventBridge | イベントバス | 複数の AWS サービスや SaaS からのイベントをルールに基づき多数のターゲットにルーティング。スキーマレジストリも提供 |
+
+**ベストプラクティス**
+- SQS の可視性タイムアウトは、Consumer の最大処理時間に余裕を加えて設定する。長時間処理では `ChangeMessageVisibility` を使って処理中にタイムアウトを延長し、重複配信が発生しても安全に再処理できるよう Consumer を冪等に設計する。
+- 一定回数処理に失敗したメッセージは **デッドレターキュー(DLQ)** に退避させ、失敗したメッセージでキューが詰まる(ポイズンメッセージ問題)のを防ぐ。
+- 1つの発行元から複数の購読者に同時配信したい場合は SNS + SQS のファンアウトパターンを使う。
+- 複数のイベントソース・多様なターゲットへの複雑なルーティングが必要な場合は EventBridge を検討する。
+- 疎結合にすることで、各コンポーネントを個別に Auto Scaling でき、あるコンポーネントの障害が他に伝播しにくくなる。
+
+出典: [SNS or SQS or EventBridge 選択ガイド](https://docs.aws.amazon.com/decision-guides/latest/sns-or-sqs-or-eventbridge/sns-or-sqs-or-eventbridge.html) / [Publish-subscribe パターン (Prescriptive Guidance)](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/publish-subscribe.html)
+
+---
+
+### 1.3 API の作成・公開・管理(Amazon API Gateway)
+
+API Gateway は、バックエンド(Lambda、EC2、他の AWS サービス、オンプレミス等)への「フロントドア」として、REST/HTTP/WebSocket API を作成・公開・保護・監視するためのフルマネージドサービスです。
+
+```mermaid
+sequenceDiagram
+ participant C as クライアント
+ participant AG as Amazon API Gateway
+ participant Auth as Lambdaオーソライザー/Cognito
+ participant L as AWS Lambda
+ participant DB as DynamoDB/RDS
+
+ C->>AG: HTTPSリクエスト
+ AG->>Auth: トークン検証を依頼
+ Auth-->>AG: 認可結果(許可/拒否)
+ alt 認可OK
+ AG->>L: バックエンド統合呼び出し
+ L->>DB: データ読み書き
+ DB-->>L: 結果
+ L-->>AG: レスポンス
+ AG-->>C: HTTPSレスポンス
+ else 認可NG
+ AG-->>C: 401/403エラー
+ end
+```
+
+**REST API と HTTP API の比較**
+
+| 項目 | REST API | HTTP API |
+|---|---|---|
+| 機能セット | フル機能(APIキー、リクエストバリデーション、WAF統合、キャッシュ等) | 軽量・低コスト(プロキシ機能中心) |
+| コスト | 相対的に高い | REST APIより低コスト |
+| 用途 | 高度なAPI管理機能が必要な場合 | シンプルなプロキシ/低レイテンシが目的の場合 |
+
+**ベストプラクティス**
+- スロットリング(レート制限・バーストリミット)を設定し、バックエンドをトラフィック急増から保護する。
+- レスポンスキャッシュを有効化し、頻繁に呼ばれる同一リクエストへのバックエンド負荷を減らす。
+- IAM、Lambda オーソライザー、Amazon Cognito ユーザープールなどで認証・認可を必ず実装する。
+- Amazon CloudWatch と統合してAPI呼び出し数・レイテンシ・エラー率を監視する。
+
+出典: [Amazon API Gateway とは](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) / [REST APIとHTTP APIの選択](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html)
+
+---
+
+### 1.4 水平スケーリングと垂直スケーリング(Amazon EC2 Auto Scaling)
+
+**垂直スケーリング(スケールアップ/ダウン)**: インスタンスタイプを大きく(または小さく)することでリソースを増減させる方式。単純だが上限があり、変更時にダウンタイムが生じやすい。
+
+**水平スケーリング(スケールアウト/イン)**: インスタンスの「台数」を増減させる方式。台数を分散させることで単一障害点を減らしつつ需要に追従できる、クラウドネイティブなスケーリング手法です。
+
+```mermaid
+flowchart TD
+ CW["Amazon CloudWatch メトリクス監視 (CPU使用率等)"] -->|しきい値超過| Alarm["CloudWatch アラーム"]
+ Alarm -->|スケールアウト指示| ASG["EC2 Auto Scaling グループ"]
+ ASG -->|新規インスタンス起動| LT["起動テンプレート (AMI, インスタンスタイプ等)"]
+ LT --> NewInst["新しいEC2インスタンス"]
+ NewInst --> ALB["Application Load Balancer ヘルスチェック合格後に登録"]
+
+ Alarm2["CloudWatch アラーム (負荷低下を検知)"] -->|スケールイン指示| ASG
+ ASG -->|インスタンス終了| Term["需要に応じて インスタンスを終了"]
+
+ style ASG fill:#2c5480,color:#fff
+ style CW fill:#1f3a5f,color:#fff
+```
+
+**Auto Scaling の主要スケーリングポリシー**
+
+| ポリシー種別 | 説明 |
+|---|---|
+| ターゲット追跡スケーリング | CPU使用率など特定メトリクスを目標値に維持するよう自動調整(推奨・最も簡単) |
+| ステップスケーリング | アラームの逸脱幅に応じて段階的にスケール量を変える |
+| シンプルスケーリング | 単一の増減幅でスケール(旧世代の方式) |
+| スケジュールスケーリング | 予測できる負荷パターン(毎朝9時など)に合わせて事前にスケール |
+
+**ベストプラクティス**
+- 複数の Availability Zone にまたがって Auto Scaling グループを構成し、AZ障害時にも自動復旧できるようにする。
+- ヘルスチェック(EC2 + ELB)を有効にし、不健全なインスタンスを自動的に入れ替える。
+- ウォームプールやライフサイクルフックを使い、起動時間の長いアプリケーションでも迅速にスケールできるようにする。
+- 垂直スケーリングは根本的な上限があるため、可用性・回復性の観点では水平スケーリングを優先する設計が推奨される。
+
+出典: [Amazon EC2 Auto Scaling とは](https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html) / [スケーリングプランのベストプラクティス](https://docs.aws.amazon.com/autoscaling/plans/userguide/best-practices-for-scaling-plans.html)
+
+---
+
+### 1.5 ロードバランシングの概念(ALB / NLB / GWLB)
+
+Elastic Load Balancing (ELB) は複数のターゲットにトラフィックを分散し、単一障害点を排除しながら可用性を高めるサービスです。用途に応じて3種類のロードバランサーを使い分けます。
+
+```mermaid
+flowchart TD
+ Start["どのロードバランサーを選ぶか?"] --> Q1{"レイヤー7(HTTP/HTTPS)の 高度なルーティングが必要?"}
+ Q1 -->|はい| ALB["Application Load Balancer (ALB) パスベース/ホストベースルーティング WebSocket, gRPC対応"]
+ Q1 -->|いいえ| Q2{"超低レイテンシ・ 高スループット・固定IPが必要?"}
+ Q2 -->|はい| NLB["Network Load Balancer (NLB) レイヤー4(TCP/UDP) 数百万req/秒、静的IP対応"]
+ Q2 -->|いいえ| Q3{"サードパーティの仮想アプライアンス (IDS/IPS,FW等)を 透過的に経由させたい?"}
+ Q3 -->|はい| GWLB["Gateway Load Balancer (GWLB) GENEVEプロトコルで トラフィックを検査アプライアンスへ透過的に転送"]
+ Q3 -->|いいえ| ALB
+
+ style ALB fill:#2c5480,color:#fff
+ style NLB fill:#2c5480,color:#fff
+ style GWLB fill:#2c5480,color:#fff
+```
+
+| 種別 | レイヤー | 主なユースケース |
+|---|---|---|
+| Application Load Balancer (ALB) | L7 | Webアプリ、マイクロサービス、コンテナ、パス/ホストベースルーティング |
+| Network Load Balancer (NLB) | L4 | 超高スループット、低レイテンシ、TCP/UDPベースのアプリ、固定IP要件 |
+| Gateway Load Balancer (GWLB) | L3/GENEVE | ファイアウォールやIDS/IPSなどセキュリティアプライアンスへの透過的なトラフィック転送 |
+
+**ベストプラクティス**
+- ロードバランサー自体は複数AZにまたがる形でデプロイし、クロスゾーン負荷分散を有効化する。
+- ALB / NLB のヘルスチェックを適切な間隔・しきい値で設定し、不健全なターゲットに即座にトラフィックを送らないようにする。
+- ロードバランサーを Auto Scaling グループと組み合わせることで、需要に応じたスケーラブルかつ高可用なフロントエンドを構築する。
+
+出典: [EKS ベストプラクティス - ロードバランシング](https://docs.aws.amazon.com/eks/latest/best-practices/load-balancing.html)
+
+---
+
+### 1.6 キャッシング戦略(Amazon CloudFront / ElastiCache / DAX)
+
+キャッシュは「よく使われるデータ」をオリジンより近い場所・高速な媒体に一時保存することで、レイテンシ削減とバックエンド負荷の軽減を同時に実現する、パフォーマンスと回復性の両面で重要な技術です。
+
+```mermaid
+flowchart TD
+ User(("ユーザー")) --> Edge["Amazon CloudFront エッジロケーション (静的/動的コンテンツをキャッシュ)"]
+ Edge -->|キャッシュミス時のみ| Origin["オリジン (ALB / S3 / API Gateway)"]
+ Origin --> App["アプリケーション層"]
+ App -->|クエリ結果をキャッシュ| EC["Amazon ElastiCache (Redis/Memcached)"]
+ App -->|DynamoDB専用キャッシュ| DAX["DynamoDB Accelerator (DAX)"]
+ App --> DB[("データベース RDS / DynamoDB")]
+ EC -.キャッシュヒット.-> App
+ DAX -.マイクロ秒応答.-> App
+
+ style Edge fill:#2c5480,color:#fff
+ style EC fill:#1f3a5f,color:#fff
+ style DAX fill:#1f3a5f,color:#fff
+```
+
+| サービス | キャッシュ対象 | 特徴 |
+|---|---|---|
+| Amazon CloudFront | 静的/動的コンテンツ、API応答 | 世界中のエッジロケーションでユーザーに最も近い場所から配信、オリジン負荷を大幅軽減 |
+| Amazon ElastiCache (Redis/Memcached) | DBクエリ結果、セッション情報等 | インメモリで数ミリ秒未満の応答、アプリ層とDB層の間に設置 |
+| DynamoDB Accelerator (DAX) | DynamoDBの読み取り結果 | マイクロ秒単位の応答、DynamoDB API互換でコード変更が少ない |
+
+**ベストプラクティス**
+- 静的アセット(画像・CSS・JS)は CloudFront で積極的にキャッシュし、TTL(有効期限)を適切に設定する。
+- セッション情報など「ステートフルなデータ」はアプリケーションサーバーではなく ElastiCache のような外部ストアに保持し、アプリ層をステートレスにする(1.9節参照)。
+- キャッシュ更新(無効化)戦略を設計段階で決めておく(Cache-Aside、Write-Through等)。
+- キャッシュはパフォーマンス向上だけでなく、バックエンドDBへの負荷集中を防ぎ、結果的にDB層の可用性・回復性向上にも寄与する。
+
+出典: [AWS Caching Solutions](https://aws.amazon.com/caching/aws-caching/)
+
+---
+
+### 1.7 サーバーレス技術とコンピューティングオプションの選択
+
+サーバーレスとは、サーバーのプロビジョニングやパッチ適用、スケーリング管理を AWS 側に任せ、開発者がコードとビジネスロジックに集中できるモデルです。回復性の観点では、サーバー管理を排除することで人為的ミスによる障害要因を減らせる利点があります。
+
+```mermaid
+flowchart LR
+ subgraph Spectrum["管理責任の範囲(左:自分で管理 → 右:AWSが管理)"]
+ direction LR
+ EC2b["Amazon EC2 (OS・スケーリングを自分で管理)"] --> ECSEC2["Amazon ECS on EC2 (コンテナ管理はAWS、 ホストは自分で管理)"]
+ ECSEC2 --> Fargate["AWS Fargate (ECS/EKS用サーバーレスコンピューティング ホスト管理不要)"]
+ Fargate --> Lambda["AWS Lambda (関数単位の完全サーバーレス イベント駆動・自動スケール)"]
+ end
+
+ style Fargate fill:#2c5480,color:#fff
+ style Lambda fill:#2c5480,color:#fff
+```
+
+**Lambda と Fargate の使い分け**
+
+| 観点 | AWS Lambda | AWS Fargate |
+|---|---|---|
+| 適した処理 | イベント駆動・短時間実行のタスク | 長時間実行、複雑な複数サービス構成 |
+| 起動単位 | 関数(リクエストごとに環境をプロビジョニング) | タスク/Pod単位でコンテナを起動 |
+| 実行時間の制約 | 最大15分 | 制約なし(長時間稼働可能) |
+| スケーリング | リクエスト数に応じ自動・瞬時 | ECS/EKSのオートスケーリング設定に依存 |
+
+**ベストプラクティス**
+- 単純なイベント処理(画像リサイズ、APIバックエンドの一部処理等)は Lambda を検討する。
+- 常駐が必要な複雑なアプリケーションやマイクロサービスは Fargate(サーバーレスコンテナ)を検討し、EC2インスタンスの管理負担を減らす。
+- サーバーレス化によって「単一のEC2インスタンス障害」という単一障害点を根本的に排除できる点が、回復性向上の観点で重要。
+
+出典: [Fargate or Lambda 選択ガイド](https://docs.aws.amazon.com/decision-guides/latest/fargate-or-lambda/fargate-or-lambda.html) / [AWS Fargate](https://aws.amazon.com/fargate/)
+
+---
+
+### 1.8 コンテナの移行とオーケストレーション(Amazon ECS / Amazon EKS)
+
+コンテナは、アプリケーションとその依存関係をパッケージ化し、環境に依存せず一貫して動作させる技術です。AWS ではコンテナの実行・管理(オーケストレーション)のために ECS と EKS の2つのマネージドサービスを提供しています。
+
+```mermaid
+flowchart TD
+ Start["コンテナオーケストレーションの選択"] --> Q1{"すでにKubernetesの 知識・エコシステムに 投資している、または マルチクラウド前提か?"}
+ Q1 -->|はい| EKS["Amazon EKS (マネージドKubernetes)"]
+ Q1 -->|いいえ| ECS["Amazon ECS (AWSネイティブなオーケストレーション シンプルで学習コストが低い)"]
+
+ ECS --> Q2{"インフラ管理を 完全に排除したい?"}
+ EKS --> Q2
+ Q2 -->|はい| Fargate2["起動タイプ: AWS Fargate (サーバーレス)"]
+ Q2 -->|いいえ、コスト最適化や 特殊なインスタンス要件がある| EC2Type["起動タイプ: EC2 (自己管理のホスト)"]
+
+ style ECS fill:#2c5480,color:#fff
+ style EKS fill:#2c5480,color:#fff
+ style Fargate2 fill:#1f3a5f,color:#fff
+```
+
+**ベストプラクティス**
+- タスク/Podは複数のAZに分散配置し、単一AZ障害時にもサービスを継続できるようにする。
+- コンテナは「イミュータブル(不変)」に扱い、変更が必要な場合は新しいイメージをビルドしてデプロイする(1.20節/2.4節で詳述)。
+- ローリングアップデートやBlue/Greenデプロイと組み合わせ、更新時のダウンタイムを回避する。
+- 既存のオンプレミスアプリケーションをコンテナ化して移行する場合は AWS App2Container のようなツールでの移行も選択肢となる。
+
+出典: [Amazon ECS とは](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) / [AWSコンテナサービスの選択ガイド](https://docs.aws.amazon.com/decision-guides/latest/containers-on-aws-how-to-choose/choosing-aws-container-service.html)
+
+---
+
+### 1.9 マイクロサービス設計原則:ステートレス vs ステートフル
+
+**ステートフルなアプリケーション**は、セッション情報やユーザーの状態をサーバー自身のメモリ/ディスクに保持します。この場合、そのユーザーは常に「同じサーバー」に接続し続ける必要があり(スティッキーセッション)、そのサーバーが落ちると状態を失い、スケールアウトも困難になります。
+
+**ステートレスなアプリケーション**は、状態を一切保持せず、外部の共有ストア(ElastiCache、DynamoDB等)に状態を移すことで、**どのサーバーに接続してもリクエストを処理できる**ようにする設計です。これは水平スケーリング・自己修復(Auto Scaling による自動置き換え)を実現するための基盤となる考え方です。
+
+```mermaid
+flowchart LR
+ subgraph Stateful["❌ ステートフル設計(回復性が低い)"]
+ U1(("ユーザー")) -->|常に同じサーバーに固定| S1["サーバーA (セッションをメモリ保持)"]
+ end
+
+ subgraph Stateless["✅ ステートレス設計(回復性が高い)"]
+ U2(("ユーザー")) --> LB2["ロードバランサー"]
+ LB2 --> S2a["サーバーA"]
+ LB2 --> S2b["サーバーB"]
+ LB2 --> S2c["サーバーC (新規追加)"]
+ S2a --> Shared[("共有ストア ElastiCache / DynamoDB セッション情報")]
+ S2b --> Shared
+ S2c --> Shared
+ end
+
+ style Stateful fill:#7a2e2e,color:#fff
+ style Stateless fill:#1f3a5f,color:#fff
+```
+
+**ベストプラクティス**
+- セッション状態は ElastiCache(Redis)や DynamoDB のような外部の耐久性あるストアに保存する。
+- アプリケーションサーバーのローカルディスクに重要なデータを保存しない(インスタンス終了とともに失われるため)。
+- ステートレス設計により、任意のサーバーが障害を起こしても Auto Scaling が別のサーバーに置き換えるだけで、ユーザー体験への影響を最小化できる。
+
+出典: [AWS Well-Architected Framework - Reliability Pillar](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html)
+
+---
+
+### 1.10 イベント駆動アーキテクチャとワークフローオーケストレーション(AWS Step Functions)
+
+複数のサービスが連携する処理では、**イベント駆動(Choreography)** と **オーケストレーション(Orchestration)** という2つの制御スタイルがあります。
+
+- **Choreography(振り付け型)**: 各サービスがイベントを発行し合い、中央の管理者なしに連鎖的に処理が進む(EventBridge等を利用)。疎結合だが、全体のフローの見通しは悪くなりがち。
+- **Orchestration(オーケストレーション型)**: 中央のワークフローエンジン(AWS Step Functions)が各ステップの実行順序・リトライ・エラー処理を明示的に管理する。
+
+```mermaid
+stateDiagram-v2
+ [*] --> 注文受付
+ 注文受付 --> 在庫確認
+ 在庫確認 --> 決済処理: 在庫あり
+ 在庫確認 --> 注文キャンセル: 在庫なし
+ 決済処理 --> 出荷手配: 決済成功
+ 決済処理 --> 決済リトライ: 決済失敗
+ 決済リトライ --> 決済処理: 再試行
+ 決済リトライ --> 注文キャンセル: 規定回数失敗
+ 出荷手配 --> [*]
+ 注文キャンセル --> [*]
+```
+
+**ベストプラクティス**
+- 複数ステップにまたがる長時間処理・複雑な条件分岐・エラーハンドリングが必要な場合は Step Functions によるオーケストレーションを検討する。
+- Step Functions の **Standard ワークフロー**(長時間実行・厳密な実行回数保証が必要な場合)と **Express ワークフロー**(高頻度・短時間のイベント処理に最適化)を用途に応じて使い分ける。
+- Step Functions は AWS X-Ray と統合してワークフロー全体のトレースを可視化できる(2.8節参照)。
+
+出典: [AWS Step Functions と X-Ray の統合](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-xray-tracing.html)
+
+---
+
+### 1.11 ストレージタイプの選択(オブジェクト/ブロック/ファイルストレージ)
+
+```mermaid
+flowchart TD
+ Start["どのストレージを選ぶか?"] --> Q1{"OSのファイルシステムから 複数インスタンスで 同時共有アクセスしたい?"}
+ Q1 -->|はい| EFS["Amazon EFS (ファイルストレージ) NFSプロトコル、複数AZで自動レプリケーション"]
+ Q1 -->|いいえ| Q2{"単一のEC2インスタンスに 接続する低レイテンシ・ 高IOPSなディスクが必要?"}
+ Q2 -->|はい| EBS["Amazon EBS (ブロックストレージ) 単一AZ内、DBやOS用ボリューム"]
+ Q2 -->|いいえ| S3["Amazon S3 (オブジェクトストレージ) API経由、ほぼ無制限にスケール 99.999999999%(11 9's)の耐久性"]
+
+ style EFS fill:#2c5480,color:#fff
+ style EBS fill:#2c5480,color:#fff
+ style S3 fill:#2c5480,color:#fff
+```
+
+| 種別 | サービス例 | スコープ | 主な用途 |
+|---|---|---|---|
+| オブジェクトストレージ | Amazon S3 | リージョン内で自動的に複数AZへ複製 | 静的コンテンツ、バックアップ、データレイク、ログ保管 |
+| ブロックストレージ | Amazon EBS | 単一AZ、単一インスタンスに接続(Multi-Attach可) | データベースのデータボリューム、OSブートボリューム |
+| ファイルストレージ | Amazon EFS | リージョン内で複数AZ、複数インスタンス共有 | 複数サーバーで共有するファイル領域、コンテンツ管理システム |
+
+**ベストプラクティス**
+- 大量の非構造化データ(画像・動画・ログ・バックアップ)は S3 に保存し、ストレージクラス(Standard, IA, Glacier等)でコストと耐久性のバランスを取る。
+- データベースなど高いIOPSと低レイテンシが必要なワークロードは EBS(Provisioned IOPS等)を使用する。
+- 複数のコンテナ/インスタンスから同じファイルに同時アクセスする必要がある場合は EFS を選択する。
+
+出典: [Amazon S3 ストレージクラス概要](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html) / [Amazon EFS を選ぶべき時](https://aws.amazon.com/efs/when-to-choose-efs/)
+
+---
+
+### 1.12 リードレプリカによる読み取りスケーリング
+
+Amazon RDS の **リードレプリカ** は、プライマリDBインスタンスの変更を非同期でコピーした「読み取り専用」のインスタンスです。読み取りが多いワークロードで、読み取りトラフィックをレプリカに逃がすことでプライマリの負荷を軽減し、スケーラビリティを高めます(同一リージョン内・クロスリージョンの両方が可能)。
+
+```mermaid
+flowchart TD
+ App["アプリケーション"] -->|書き込み| Primary[("RDS プライマリ (読み書き両方)")]
+ App -->|読み取り| R1[("リードレプリカ1 (同一リージョン)")]
+ App -->|読み取り| R2[("リードレプリカ2 (クロスリージョン)")]
+ Primary -.非同期レプリケーション.-> R1
+ Primary -.非同期レプリケーション.-> R2
+
+ style Primary fill:#2c5480,color:#fff
+ style R1 fill:#1f3a5f,color:#fff
+ style R2 fill:#1f3a5f,color:#fff
+```
+
+**ベストプラクティス**
+- リードレプリカはあくまで「読み取りスケーリング」が主目的であり、レプリケーションが非同期のため、障害復旧の主手段としては Multi-AZ 配置(2.2節参照)と役割を分けて考える。
+- クロスリージョンのリードレプリカは、読み取り性能向上に加えてディザスタリカバリの補助(プライマリへ昇格させる)としても活用できる。
+- レプリカ数が増えるとレプリケーションラグが発生しうるため、アプリケーション側で結果整合性を許容できるかを設計時に検討する。
+
+出典: [Amazon RDS リードレプリカの利用](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html)
+
+---
+
+## 2. タスク2.2: 高可用性・フォールトトレラントなアーキテクチャの設計
+
+### 2.1 AWSグローバルインフラストラクチャ(リージョン・アベイラビリティゾーン)
+
+高可用性設計の出発点は、AWSの物理的なインフラ構造を理解することです。
+
+```mermaid
+flowchart TD
+ Global["AWSグローバルインフラストラクチャ"] --> R1["リージョン A (例: ap-northeast-1 東京)"]
+ Global --> R2["リージョン B (例: us-east-1 バージニア北部)"]
+
+ R1 --> AZ1["アベイラビリティゾーン 1a (独立したデータセンター群)"]
+ R1 --> AZ2["アベイラビリティゾーン 1c"]
+ R1 --> AZ3["アベイラビリティゾーン 1d"]
+
+ AZ1 --- Net["低レイテンシ・高帯域の 専用ファイバーで相互接続"]
+ AZ2 --- Net
+ AZ3 --- Net
+
+ Global --> Edge["エッジロケーション (CloudFront/Route 53用)"]
+
+ style R1 fill:#2c5480,color:#fff
+ style R2 fill:#2c5480,color:#fff
+ style AZ1 fill:#1f3a5f,color:#fff
+ style AZ2 fill:#1f3a5f,color:#fff
+ style AZ3 fill:#1f3a5f,color:#fff
+```
+
+**重要な設計原則**
+- 各リージョンは最低3つの、物理的に離れた(が低レイテンシで接続された)AZで構成されており、1つのAZで火災・洪水・電源障害が起きても他のAZは影響を受けない設計。
+- リージョンは互いに完全に独立しているため、単一リージョンの大規模障害から保護するにはマルチリージョン設計(2.2節)が必要になる。
+- ワークロードを単一AZに閉じずに複数AZへ分散配置することが、高可用性設計の最も基本的かつ重要な手段。
+
+**ベストプラクティス**
+- 本番ワークロードは最低2つ、理想的には3つ以上のAZにまたがって配置する。
+- Auto Scaling グループ、RDS Multi-AZ、複数AZにまたがるELBなど、マルチAZをネイティブサポートするサービスを積極的に利用する。
+
+出典: [AWS リージョンとアベイラビリティゾーン](https://docs.aws.amazon.com/global-infrastructure/latest/regions/aws-regions-availability-zones.html) / [AWS グローバルインフラストラクチャ](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
+
+---
+
+### 2.2 障害復旧(ディザスタリカバリ)戦略と RPO / RTO
+
+**RPO(目標復旧時点: Recovery Point Objective)**: 障害発生時に許容できる「データ損失の最大時間」。例えば RPO=1時間なら、直近1時間分のデータ損失までは許容される。
+
+**RTO(目標復旧時間: Recovery Time Objective)**: 障害発生からサービスを復旧させるまでに許容できる「最大時間」。
+
+AWSでは、コストとRTO/RPOのトレードオフに応じて主に4つのDR戦略が定義されています。
+
+```mermaid
+flowchart LR
+ subgraph Spectrum["コスト・複雑さが低い ← → 高い(RTO/RPOも短くなる)"]
+ direction LR
+ BR["1. Backup & Restore (バックアップと復元) RTO:数時間〜/RPO:数時間〜"] --> PL["2. Pilot Light (パイロットライト) RTO:数十分〜/RPO:数分〜"]
+ PL --> WS["3. Warm Standby (ウォームスタンバイ) RTO:数分/RPO:秒〜数分"]
+ WS --> MA["4. Multi-Site Active-Active (マルチサイト active-active) RTO:ほぼ0/RPO:ほぼ0"]
+ end
+
+ style BR fill:#1f3a5f,color:#fff
+ style PL fill:#2c5480,color:#fff
+ style WS fill:#2c5480,color:#fff
+ style MA fill:#7c9eff,color:#000
+```
+
+**各戦略の解説**
+
+| 戦略 | 概要 | 平常時のセカンダリリージョンの状態 |
+|---|---|---|
+| Backup & Restore | データを定期的にバックアップし、障害時に別リージョンでリソースを新規作成して復元 | リソース稼働なし(最も低コスト) |
+| Pilot Light | 中核となるデータベース等は常時レプリケーションしておくが、アプリケーション層は最小限(起動していない、または最小サイズ) | DBのみ起動、その他は停止 |
+| Warm Standby | 縮小版ながら本番と同じ構成のスタックを常時稼働させておき、障害時にスケールアップして切り替え | フルスタックが縮小規模で常時稼働 |
+| Multi-Site Active-Active | 複数リージョンで同時にフル本番トラフィックを処理し、片方が落ちてももう片方が即座に引き継ぐ | フル規模で常時稼働・トラフィック処理中 |
+
+```mermaid
+flowchart TD
+ subgraph PilotLight["Pilot Light 構成例"]
+ direction TB
+ PrimaryRegion["プライマリリージョン (フル稼働・全トラフィック処理)"]
+ SecondaryRegion["セカンダリリージョン (DBレプリカのみ常時稼働、 アプリ層はAMI/起動テンプレートを準備済みで停止中)"]
+ PrimaryRegion -.継続的なDBレプリケーション.-> SecondaryRegion
+ SecondaryRegion -.障害発生時: Auto Scalingでアプリ層を起動しRoute53を切替.-> Activate["フル本番環境に昇格"]
+ end
+ style PrimaryRegion fill:#2c5480,color:#fff
+ style SecondaryRegion fill:#7a2e2e,color:#fff
+```
+
+```mermaid
+flowchart TD
+ subgraph WarmStandby["Warm Standby 構成例"]
+ direction TB
+ PrimaryRegion2["プライマリリージョン (フル規模で全トラフィック処理)"]
+ SecondaryRegion2["セカンダリリージョン (縮小規模ながら全ティアが常時稼働)"]
+ PrimaryRegion2 -.継続的なレプリケーション.-> SecondaryRegion2
+ SecondaryRegion2 -.障害発生時: Auto Scalingでフル規模にスケールしRoute53を切替.-> Activate2["フル本番環境にスケールアップ"]
+ end
+ style PrimaryRegion2 fill:#2c5480,color:#fff
+ style SecondaryRegion2 fill:#7a2e2e,color:#fff
+```
+
+```mermaid
+flowchart TD
+ subgraph ActiveActive["Multi-Site Active-Active 構成例"]
+ direction LR
+ R53["Amazon Route 53 (レイテンシ/加重ルーティング)"]
+ RegionA["リージョンA (フル規模・常時トラフィック処理)"]
+ RegionB["リージョンB (フル規模・常時トラフィック処理)"]
+ R53 --> RegionA
+ R53 --> RegionB
+ RegionA <-.双方向レプリケーション.-> RegionB
+ end
+ style RegionA fill:#2c5480,color:#fff
+ style RegionB fill:#2c5480,color:#fff
+```
+
+**ベストプラクティス**
+- ビジネス要件(許容できるダウンタイム・データ損失量)から逆算してRTO/RPOを定義し、それに見合った戦略を選ぶ(過剰な戦略はコスト増、過小な戦略はビジネスリスク増)。
+- DR戦略は定期的に **実際にフェイルオーバーの訓練(ゲームデー等)** を行い、机上の設計だけで終わらせない。
+- Backup & Restore ではバックアップの保存先(S3クロスリージョンレプリケーション等)と復元手順の自動化(CloudFormation/Terraform)が重要。
+
+出典: [AWS ディザスタリカバリー戦略ホワイトペーパー](https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/disaster-recovery-options-in-the-cloud.html) / [Reliability Pillar - 障害復旧の計画](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_planning_for_recovery_disaster_recovery.html) / [DRアーキテクチャ Part III: Pilot Light & Warm Standby](https://aws.amazon.com/blogs/architecture/disaster-recovery-dr-architecture-on-aws-part-iii-pilot-light-and-warm-standby) / [DRアーキテクチャ Part IV: Multi-Site Active-Active](https://aws.amazon.com/blogs/architecture/disaster-recovery-dr-architecture-on-aws-part-iv-multi-site-active-active/)
+
+---
+
+### 2.3 フェイルオーバー戦略(Amazon Route 53 ルーティングポリシー)
+
+Amazon Route 53 は、ヘルスチェックと組み合わせることで、障害が起きたエンドポイントから自動的に正常なエンドポイントへトラフィックを切り替える(フェイルオーバー)ことができるDNSサービスです。
+
+```mermaid
+flowchart TD
+ User(("ユーザー")) --> R53["Amazon Route 53"]
+ R53 --> HC{"ヘルスチェック: プライマリエンドポイントは正常か?"}
+ HC -->|正常| Primary["プライマリエンドポイント (例: us-east-1のALB)"]
+ HC -->|異常を検知| Secondary["セカンダリエンドポイント (例: us-west-2のALB) へ自動的にルーティング"]
+
+ style Primary fill:#2c5480,color:#fff
+ style Secondary fill:#7a2e2e,color:#fff
+```
+
+**代表的なルーティングポリシー**
+
+| ポリシー | 説明 |
+|---|---|
+| シンプル | 単一リソースへの基本的なルーティング |
+| 加重(Weighted) | 指定した比率でトラフィックを複数リソースに振り分け(Blue/Greenやカナリアに活用) |
+| レイテンシベース | ユーザーから見て最もレイテンシが低いリージョンにルーティング |
+| フェイルオーバー | ヘルスチェック結果に基づきプライマリ/セカンダリを自動切替 |
+| 地理位置情報 | ユーザーの地理的位置に基づきルーティング(コンプライアンス要件等) |
+| 複数値回答 | 複数の正常なIPをランダムに返す(簡易的な負荷分散) |
+
+**ベストプラクティス**
+- ヘルスチェックはアプリケーションの実際の状態(単なるTCP疎通ではなく、DB接続を含むエンドポイント応答等)を反映するよう設計する。
+- フェイルオーバールーティングと、2.2節のDR戦略(Pilot Light / Warm Standby / Active-Active)を組み合わせて、実際に切替が発動する仕組みを構築する。
+- TTL(Time To Live)を適切に短く設定し、フェイルオーバー発生時にDNS変更が速やかにクライアントへ反映されるようにする。
+
+出典: [Route 53 DNSフェイルオーバーの仕組み](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover.html) / [DNSフェイルオーバーの構成パターン](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover-types.html)
+
+---
+
+### 2.4 分散設計パターンとイミュータブルインフラストラクチャ
+
+**イミュータブル(不変)インフラストラクチャ**とは、本番稼働中のサーバーに対して直接パッチ適用や設定変更を行わず、変更が必要な場合は新しいインフラを構築してデプロイし、検証後にトラフィックを切り替えるという設計モデルです。これにより「設定ドリフト(環境ごとの差異の蓄積)」を防ぎ、デプロイの信頼性を高めます。
+
+```mermaid
+flowchart TD
+ Start["新バージョンのデプロイが必要"] --> Build["新しいAMI/コンテナイメージを ビルド(Blue環境とは別に)"]
+ Build --> Green["Green環境 (新バージョン)を 新しいAuto Scalingグループで起動"]
+ Green --> Test["Green環境をテスト・検証"]
+ Test -->|問題なし| Switch["Route 53 の加重ルーティングまたは ELBの向き先を切り替え トラフィックをGreenへ"]
+ Test -->|問題あり| Rollback["Blue環境のまま維持 (Greenは破棄)"]
+ Switch --> Monitor["監視: 問題があれば 即座にBlueへロールバック"]
+ Monitor --> Decommission["問題なければ 旧Blue環境を終了"]
+
+ style Green fill:#2c5480,color:#fff
+ style Switch fill:#7c9eff,color:#000
+```
+
+**ベストプラクティス**
+- AWS CodeDeploy や AWS Elastic Beanstalk の Blue/Green デプロイ機能を活用し、切替とロールバックを自動化する。
+- カナリアデプロイ(一部トラフィックのみ新バージョンに向ける)と組み合わせ、影響範囲を限定しながら段階的に展開する。
+- イミュータブルなデプロイは「デプロイは成功するか、何も変わらないか(部分的な中途半端な状態にならない)」という信頼性を提供する。
+
+出典: [REL08-BP04 イミュータブルインフラストラクチャによるデプロイ](https://docs.aws.amazon.com/wellarchitected/latest/framework/rel_tracking_change_management_immutable_infrastructure.html)
+
+---
+
+### 2.5 プロキシ概念によるデータベース回復性の向上(Amazon RDS Proxy)
+
+アプリケーションが大量の同時接続をデータベースに直接張ると、DB側の接続数上限を圧迫し、特にLambdaのようにスケール時に接続数が急増するアーキテクチャでは問題が顕在化しやすくなります。**Amazon RDS Proxy** はアプリケーションとRDS/Auroraの間に立つ完全マネージドのコネクションプーラーで、接続を効率的にプール・再利用し、DBフェイルオーバー時の切替も高速化します。
+
+```mermaid
+flowchart LR
+ subgraph Clients["大量の同時クライアント"]
+ L1["Lambda 実行環境 x 100+"]
+ end
+ Clients -->|多数の短命な接続| Proxy["Amazon RDS Proxy (コネクションプーリング)"]
+ Proxy -->|少数の安定した接続| DB[("RDS / Aurora プライマリ")]
+ DB -.フェイルオーバー発生.-> Standby[("スタンバイ インスタンス")]
+ Proxy -.フェイルオーバーを自動検知し 透過的に新プライマリへ再接続.-> Standby
+
+ style Proxy fill:#2c5480,color:#fff
+```
+
+**ベストプラクティス**
+- Lambda など接続数が急増しやすいサーバーレスアーキテクチャでは RDS Proxy の利用を検討し、DBの「too many connections」エラーを防ぐ。
+- RDS Proxy はフェイルオーバー時の接続切替を高速化するため、アプリケーション側での複雑な再接続ロジックの実装が不要になり、可用性向上に寄与する。
+- IAM認証と組み合わせることでDB認証情報の管理も簡素化できる。
+
+出典: [Amazon RDS Proxy とは](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)
+
+---
+
+### 2.6 ストレージの耐久性とレプリケーション設計
+
+「耐久性(Durability)」と「可用性(Availability)」は似て非なる概念です。**耐久性**は「データが失われない確率」、**可用性**は「必要なときにアクセスできる確率」を指します。
+
+```mermaid
+flowchart TD
+ S3Obj["S3オブジェクト アップロード"] --> AZ1s["AZ 1に複製"]
+ S3Obj --> AZ2s["AZ 2に複製"]
+ S3Obj --> AZ3s["AZ 3に複製"]
+ AZ1s & AZ2s & AZ3s --> Result["99.999999999% (イレブンナイン)の年間耐久性 = 1000万個のオブジェクトを 1万年保管して平均1個を失う程度"]
+
+ style Result fill:#2c5480,color:#fff
+```
+
+**ベストプラクティス**
+- Amazon S3 は標準(Standard)ストレージクラスで、最低3つのAZにまたがってオブジェクトを冗長化し、単一AZの喪失を想定した耐久性設計になっている(S3 One Zone-IAは単一AZのみのためコストは下がるが耐久性は劣る)。
+- リージョン全体の障害に備える場合は、S3のクロスリージョンレプリケーション(CRR)でデータを別リージョンにも複製する。
+- 誤削除・ランサムウェア対策として、S3のバージョニングとMFA Delete、Object Lock(WORM)を組み合わせる。
+- EBSボリュームは単一AZに紐づくため、EBSスナップショットをS3(リージョンサービス)に定期取得することで、AZ障害からのデータ保護を行う。
+
+出典: [Amazon S3 のデータ保護(耐久性)](https://docs.aws.amazon.com/AmazonS3/latest/userguide/DataDurability.html)
+
+---
+
+### 2.7 サービスクォータとスロットリングを考慮した設計
+
+**サービスクォータ(旧称: 制限/limits)**は、AWSアカウントで作成・利用できるリソースの上限値です。**スロットリング**は、APIリクエストの「頻度」が一定を超えた場合にリクエストを拒否・遅延させる仕組みです。この2つを理解せずに設計すると、スケールした瞬間に予期しないエラーで障害が発生することがあります。
+
+```mermaid
+flowchart TD
+ Design["アーキテクチャ設計"] --> Check["主要サービスの デフォルトクォータを事前に確認"]
+ Check --> DR["DR用セカンダリリージョンの クォータも同様に確認・引き上げ申請"]
+ DR --> Monitor["AWS Service Quotas / Trusted Advisor でクォータ使用率を監視"]
+ Monitor --> Alarm["CloudWatchアラームで 80%到達時に通知"]
+ Alarm --> Increase["必要に応じて 事前にクォータ引き上げをリクエスト"]
+
+ style Check fill:#2c5480,color:#fff
+ style Monitor fill:#1f3a5f,color:#fff
+```
+
+**ベストプラクティス**
+- 固定的なクォータ(例: Lambdaのペイロードサイズ上限、API Gatewayのスロットルバーストレート)は変更できないため、アーキテクチャ側で制約を吸収する設計にする。
+- DR用のセカンダリリージョンでも本番と同等のクォータが確保されているかを事前に確認する(フェイルオーバー時にクォータ不足で復旧できないという事態を防ぐ)。
+- スロットリングエラーに対してはアプリケーション側で指数バックオフ・ジッターを用いたリトライを実装する。
+
+出典: [Service Quotas とは](https://docs.aws.amazon.com/servicequotas/latest/userguide/intro.html) / [Reliability Pillar - サービスクォータの管理](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_manage_service_limits_limits_considered.html)
+
+---
+
+### 2.8 ワークロードの可視性(AWS X-Ray による分散トレーシング)
+
+マイクロサービス化・疎結合化が進むほど、「どのリクエストが、どのサービスで、なぜ遅い/失敗したのか」を追跡することが難しくなります。**AWS X-Ray** は分散システム全体をエンドツーエンドでトレースし、サービスマップとして可視化することで、ボトルネックや障害箇所の特定を容易にします。
+
+```mermaid
+flowchart LR
+ Req["1つのユーザーリクエスト"] --> AG2["API Gateway"]
+ AG2 --> L2["Lambda"]
+ L2 --> DDB["DynamoDB"]
+ L2 --> S3b["S3"]
+ L2 --> Ext["外部API"]
+
+ AG2 -.トレースセグメント送信.-> XRay["AWS X-Ray"]
+ L2 -.トレースセグメント送信.-> XRay
+ DDB -.トレースセグメント送信.-> XRay
+ XRay --> Map["サービスマップとして可視化 (どこで遅延/エラーが 発生しているか一目瞭然)"]
+
+ style XRay fill:#2c5480,color:#fff
+ style Map fill:#7c9eff,color:#000
+```
+
+**ベストプラクティス**
+- Lambda、ECS、EC2、API Gateway など主要コンポーネントに X-Ray SDK/エージェントを組み込み、アプリケーション全体を通したトレーシングを有効化する。
+- 個々のサービスのログ・メトリクスだけでなく、リクエスト単位の「横断的な」可視性を持つことで、疎結合アーキテクチャにおける障害切り分け時間を短縮できる。
+- Step Functions のワークフローとも統合し、ステートマシン全体の実行状況を追跡できる。
+
+出典: [AWS X-Ray とは](https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html)
+
+---
+
+### 2.9 レガシー・クラウド非対応アプリケーションの信頼性向上
+
+すべてのアプリケーションが最初からクラウドネイティブに設計されているわけではありません。既存のモノリシックなアプリケーションや、リファクタリングが困難なレガシーシステムでも、以下のようなAWSサービスを組み合わせることで、大きくコードを変えずに回復性を向上できます。
+
+| 課題 | 対応するAWSの仕組み |
+|---|---|
+| アプリケーションがスケールしない | Application Load Balancer + Auto Scaling グループ配下に配置 |
+| DBの直接接続数が多くフェイルオーバーに弱い | Amazon RDS Proxy を挟んでコネクションプーリング(2.5節) |
+| インフラ管理の負担が大きい | AWS Elastic Beanstalk でプラットフォーム管理を任せる |
+| コンテナ化を進めたいが移行作業が大変 | AWS App2Container 等の移行支援ツールを活用 |
+| 単一AZでしか稼働していない | Multi-AZ配置・EFSでの共有ストレージ化 |
+
+**ベストプラクティス**
+- 一足飛びに全面的な作り直し(リアーキテクト)を狙うのではなく、まずロードバランサー配下への移動・Multi-AZ化・RDS Proxyの導入など「変更コストが低く効果が高い」対策から段階的に適用する。
+- Elastic Beanstalk のようなマネージドプラットフォームを使うことで、パッチ適用やスケーリングの設定をAWSに任せつつ、既存コードをほぼそのまま活用できる。
+
+出典: [AWS Well-Architected Framework - Reliability Pillar](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html)
+
+---
+
+## 3. まとめ:出題頻出ポイント チェックリスト
+
+| # | チェック項目 | 関連キーワード |
+|---|---|---|
+| 1 | 疎結合の実現手段(SQS/SNS/EventBridge)の使い分けを説明できる | Point-to-Point, Pub/Sub, ファンアウト, DLQ |
+| 2 | ステートレス設計の意味とセッションの外部化を説明できる | ElastiCache, DynamoDB, スティッキーセッション |
+| 3 | ALB/NLB/GWLBの違いとユースケースを判別できる | L7/L4/L3, パスベースルーティング, 固定IP |
+| 4 | 水平/垂直スケーリングの違いとAuto Scalingのポリシーを説明できる | ターゲット追跡, ステップスケーリング |
+| 5 | S3/EBS/EFSの違い(オブジェクト/ブロック/ファイル)を判別できる | 11 9's耐久性, 単一AZ, マルチAZ共有 |
+| 6 | RTO/RPOの定義と4つのDR戦略の違いを説明できる | Backup&Restore, Pilot Light, Warm Standby, Active-Active |
+| 7 | Route 53のルーティングポリシー、特にフェイルオーバーの仕組みを説明できる | ヘルスチェック, 加重ルーティング |
+| 8 | イミュータブルインフラ・Blue/Greenデプロイの利点を説明できる | 設定ドリフト, カナリアリリース |
+| 9 | RDS Proxyの目的(コネクションプーリング、フェイルオーバー高速化)を説明できる | Lambda + RDS接続数問題 |
+| 10 | サービスクォータとスロットリングの違い、DRリージョンでの考慮点を説明できる | Service Quotas, Trusted Advisor, 指数バックオフ |
+| 11 | X-Rayによる分散トレーシングの目的を説明できる | サービスマップ, ボトルネック特定 |
+| 12 | リードレプリカとMulti-AZの目的の違い(読み取りスケーリング vs 高可用性)を説明できる | 非同期レプリケーション, 同期レプリケーション |
+
+---
+
+## 4. 参考文献・出典一覧
+
+- [SAA-C03 Exam Guide(試験ガイド全体)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03.html)
+- [SAA-C03 Domain 2 詳細(タスクステートメント)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain2.html)
+- [AWS Well-Architected Framework - Reliability Pillar (Welcome)](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html)
+- [AWS Well-Architected Framework - Reliability (フレームワーク概要)](https://docs.aws.amazon.com/wellarchitected/latest/framework/reliability.html)
+- [SNS or SQS or EventBridge 選択ガイド](https://docs.aws.amazon.com/decision-guides/latest/sns-or-sqs-or-eventbridge/sns-or-sqs-or-eventbridge.html)
+- [Publish-Subscribe パターン (Prescriptive Guidance)](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/publish-subscribe.html)
+- [Amazon API Gateway とは](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html)
+- [REST APIとHTTP APIの選択](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html)
+- [Amazon EC2 Auto Scaling とは](https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html)
+- [スケーリングプランのベストプラクティス](https://docs.aws.amazon.com/autoscaling/plans/userguide/best-practices-for-scaling-plans.html)
+- [Amazon EKS ベストプラクティス - ロードバランシング](https://docs.aws.amazon.com/eks/latest/best-practices/load-balancing.html)
+- [AWS Caching Solutions(CloudFront/ElastiCache)](https://aws.amazon.com/caching/aws-caching/)
+- [Fargate or Lambda 選択ガイド](https://docs.aws.amazon.com/decision-guides/latest/fargate-or-lambda/fargate-or-lambda.html)
+- [AWS Fargate 製品ページ](https://aws.amazon.com/fargate/)
+- [Amazon ECS とは](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html)
+- [AWSコンテナサービスの選択ガイド](https://docs.aws.amazon.com/decision-guides/latest/containers-on-aws-how-to-choose/choosing-aws-container-service.html)
+- [AWS Step Functions と X-Ray の統合](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-xray-tracing.html)
+- [Amazon S3 ストレージクラス概要](https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html)
+- [Amazon EFS を選ぶべき時](https://aws.amazon.com/efs/when-to-choose-efs/)
+- [Amazon RDS リードレプリカの利用](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html)
+- [AWS リージョンとアベイラビリティゾーン](https://docs.aws.amazon.com/global-infrastructure/latest/regions/aws-regions-availability-zones.html)
+- [AWS グローバルインフラストラクチャ](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
+- [AWS ディザスタリカバリー戦略ホワイトペーパー](https://docs.aws.amazon.com/whitepapers/latest/disaster-recovery-workloads-on-aws/disaster-recovery-options-in-the-cloud.html)
+- [Reliability Pillar - 障害復旧の計画](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_planning_for_recovery_disaster_recovery.html)
+- [DRアーキテクチャブログ Part III: Pilot Light & Warm Standby](https://aws.amazon.com/blogs/architecture/disaster-recovery-dr-architecture-on-aws-part-iii-pilot-light-and-warm-standby)
+- [DRアーキテクチャブログ Part IV: Multi-Site Active-Active](https://aws.amazon.com/blogs/architecture/disaster-recovery-dr-architecture-on-aws-part-iv-multi-site-active-active/)
+- [Route 53 DNSフェイルオーバーの仕組み](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover.html)
+- [DNSフェイルオーバーの構成パターン](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover-types.html)
+- [REL08-BP04 イミュータブルインフラストラクチャによるデプロイ](https://docs.aws.amazon.com/wellarchitected/latest/framework/rel_tracking_change_management_immutable_infrastructure.html)
+- [Amazon RDS Proxy とは](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)
+- [Amazon S3 のデータ保護(耐久性)](https://docs.aws.amazon.com/AmazonS3/latest/userguide/DataDurability.html)
+- [Service Quotas とは](https://docs.aws.amazon.com/servicequotas/latest/userguide/intro.html)
+- [Reliability Pillar - サービスクォータの管理](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_manage_service_limits_limits_considered.html)
+- [AWS X-Ray とは](https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html)
diff --git a/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain3.md b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain3.md
new file mode 100644
index 000000000..1905a3692
--- /dev/null
+++ b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate-Domain3.md
@@ -0,0 +1,1137 @@
+# AWS Certified Solutions Architect - Associate (SAA-C03)
+# ドメイン3: 高性能アーキテクチャの設計(Design High-Performing Architectures)
+
+> **出題比率: 24%**(4ドメイン中2番目に高い比重)
+> 本ガイドは [AWS公式Exam Guide (SAA-C03) ドメイン3](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html) の公式タスクステートメントに基づき、初級者向けにステップバイステップで解説する技術文書です。
+
+---
+
+## この章で学ぶこと
+
+ドメイン3は「パフォーマンス」と「スケーラビリティ」を軸に、AWSの主要な5つの技術領域を横断します。試験では以下の5つのタスク(Task)に分かれて出題されます。
+
+| タスク番号 | タスク名(公式) | 日本語訳 |
+|---|---|---|
+| Task 3.1 | Determine high-performing and/or scalable storage solutions | 高性能かつ/またはスケーラブルなストレージソリューションの決定 |
+| Task 3.2 | Design high-performing and elastic compute solutions | 高性能で弾力性のあるコンピューティングソリューションの設計 |
+| Task 3.3 | Determine high-performing database solutions | 高性能なデータベースソリューションの決定 |
+| Task 3.4 | Determine high-performing and/or scalable network architectures | 高性能かつ/またはスケーラブルなネットワークアーキテクチャの決定 |
+| Task 3.5 | Determine high-performing data ingestion and transformation solutions | 高性能なデータ取り込み・変換ソリューションの決定 |
+
+出典: [Content Domain 3: Design High-Performing Architectures(AWS公式)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html)
+
+### ドメイン3の全体像
+
+```mermaid
+flowchart TD
+ D3["ドメイン3 高性能アーキテクチャの設計(24%)"]
+ D3 --> T1["Task 3.1 ストレージ"]
+ D3 --> T2["Task 3.2 コンピューティング"]
+ D3 --> T3["Task 3.3 データベース"]
+ D3 --> T4["Task 3.4 ネットワーク"]
+ D3 --> T5["Task 3.5 データ取り込み・変換"]
+
+ T1 --> T1a["S3 / EBS / EFS ハイブリッドストレージ"]
+ T2 --> T2a["EC2 / Lambda / Fargate ECS・EKS / Auto Scaling"]
+ T3 --> T3a["RDS / Aurora / DynamoDB ElastiCache / 読み取りレプリカ"]
+ T4 --> T4a["VPC設計 / ELB CloudFront / Global Accelerator"]
+ T5 --> T5a["Kinesis / Glue Lake Formation / Athena"]
+
+ style D3 fill:#2c5480,stroke:#7c9eff,stroke-width:2px,color:#eef4ff
+ style T1 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style T2 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style T3 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style T4 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style T5 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+---
+
+## 目次
+
+1. [Task 3.1: 高性能・スケーラブルなストレージソリューション](#task-31-高性能スケーラブルなストレージソリューション)
+2. [Task 3.2: 高性能で弾力性のあるコンピューティングソリューション](#task-32-高性能で弾力性のあるコンピューティングソリューション)
+3. [Task 3.3: 高性能なデータベースソリューション](#task-33-高性能なデータベースソリューション)
+4. [Task 3.4: 高性能・スケーラブルなネットワークアーキテクチャ](#task-34-高性能スケーラブルなネットワークアーキテクチャ)
+5. [Task 3.5: 高性能なデータ取り込み・変換ソリューション](#task-35-高性能なデータ取り込み変換ソリューション)
+6. [参考文献](#参考文献)
+
+---
+
+## Task 3.1: 高性能・スケーラブルなストレージソリューション
+
+### 出題される知識・スキル項目(公式)
+
+**知識:**
+- ビジネス要件を満たすハイブリッドストレージソリューション
+- 適切なユースケースを伴うストレージサービス(例: Amazon S3、Amazon EFS、Amazon EBS)
+- 関連する特性を持つストレージタイプ(例: オブジェクト、ファイル、ブロック)
+
+**スキル:**
+- パフォーマンス要件を満たすストレージサービスと構成の決定
+- 将来のニーズに対応してスケールできるストレージサービスの決定
+
+出典: [Task 3.1(AWS公式Exam Guide)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html#solutions-architect-associate-03-domain3-task1)
+
+### 3.1.1 ストレージ3種類の基本特性
+
+AWSのストレージは大きく **オブジェクト・ファイル・ブロック** の3タイプに分類されます。この分類を理解することが、どのサービスを選ぶべきかの土台になります。
+
+| 特性 | オブジェクトストレージ (Amazon S3) | ファイルストレージ (Amazon EFS / FSx) | ブロックストレージ (Amazon EBS / インスタンスストア) |
+|---|---|---|---|
+| データの単位 | オブジェクト(フラットな名前空間、メタデータ付き) | ファイル階層(ディレクトリ構造) | 固定サイズのブロック |
+| アクセス方法 | HTTP(S) API(REST) | NFS / SMBプロトコル | OSのファイルシステム経由 |
+| 同時アクセス | 多数のクライアントから並行アクセス可能 | 多数のEC2/オンプレミスから同時マウント可能 | 基本的に1つのEC2インスタンスに1対1でアタッチ(io2 Block Expressのマルチアタッチは例外) |
+| 典型的ユースケース | 静的ウェブサイト、バックアップ、データレイク、ログ、メディア配信 | 共有ホームディレクトリ、CMS、コンテンツ管理、ビッグデータ解析の共有領域 | データベースのボリューム、OSブートボリューム、低レイテンシが必要なトランザクション処理 |
+| スケーラビリティ | 事実上無制限(自動) | 自動でペタバイト規模までスケール(EFS) | ボリュームごとに事前にサイズ・IOPSを指定(gp3/io2は変更可能) |
+
+出典: [Amazon S3の概要](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) / [Amazon EFSとは](https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html) / [Amazon EBSの概要](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html)
+
+### ストレージ選定の判断フロー
+
+```mermaid
+flowchart TD
+ Start["ストレージが必要"] --> Q1{"複数のインスタンス/ クライアントから同時に 読み書きするか?"}
+ Q1 -- はい --> Q2{"アクセスプロトコルは?"}
+ Q1 -- いいえ (単一インスタンス専有) --> EBS["Amazon EBS (ブロックストレージ)"]
+
+ Q2 -- "HTTP(S) API 大量の非構造化データ" --> S3["Amazon S3 (オブジェクトストレージ)"]
+ Q2 -- "NFS (Linux)" --> EFS["Amazon EFS (ファイルストレージ)"]
+ Q2 -- "SMB (Windows) / 高性能HPC/NetApp互換" --> FSx["Amazon FSx (Windows File Server / Lustre / NetApp ONTAP / OpenZFS)"]
+
+ EBS --> Q3{"最高レベルの IOPS/スループットが必要か? (例: 大規模DB)"}
+ Q3 -- はい --> io2["io2 Block Express"]
+ Q3 -- いいえ,汎用 --> gp3["gp3(汎用SSD)"]
+
+ style S3 fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style EFS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style FSx fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style EBS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+```
+
+### 3.1.2 Amazon S3: ストレージクラスとライフサイクル管理
+
+Amazon S3は**単一のバケット内でオブジェクトごとに異なるストレージクラスを混在**させることができます。パフォーマンス試験対策では「アクセス頻度」と「取得速度要件」の2軸でクラスを選ぶ考え方が重要です。
+
+| ストレージクラス | 想定アクセス頻度 | 取得時間 | 可用性/耐久性の特徴 |
+|---|---|---|---|
+| S3 Standard | 頻繁 | ミリ秒 | 複数AZに複製、汎用の高性能 |
+| S3 Intelligent-Tiering | 不明・変動する | ミリ秒(頻繁/低頻度層)〜時間(アーカイブ層) | アクセスパターンを自動監視し階層間を自動移動 |
+| S3 Standard-IA | 低頻度だが即時アクセス必要 | ミリ秒 | Standardと同等の低レイテンシ、保存コストは低い |
+| S3 One Zone-IA | 低頻度・再作成可能なデータ | ミリ秒 | 単一AZのみに保存(AZ障害でロスト可能)、最安のIA |
+| S3 Express One Zone | 超高頻度・低レイテンシ最優先 | 1桁ミリ秒 | 単一AZ、S3で最速のアクセス速度 |
+| S3 Glacier Instant Retrieval | 四半期に1回程度だが即時性が必要 | ミリ秒 | アーカイブ用途で最安クラスの即時取得層 |
+| S3 Glacier Flexible Retrieval | 年1〜2回程度のアクセス | 数分〜数時間 | 低コストアーカイブ、非同期取得 |
+| S3 Glacier Deep Archive | ほぼアクセスしない長期保管 | 標準12時間以内 | S3で最安、コンプライアンス/長期保存向け |
+
+出典: [Amazon S3 ストレージクラス(公式)](https://aws.amazon.com/s3/storage-classes/) / [S3 Intelligent-Tiering](https://aws.amazon.com/s3/storage-classes/intelligent-tiering/) / [S3 Glacierストレージクラス](https://aws.amazon.com/s3/storage-classes/glacier/)
+
+### S3ライフサイクルポリシーによる自動階層化
+
+```mermaid
+flowchart LR
+ Upload["オブジェクトを アップロード"] --> Standard["S3 Standard (0〜29日)"]
+ Standard -- "30日経過 (ライフサイクルルール)" --> IA["S3 Standard-IA (30〜89日)"]
+ IA -- "90日経過" --> GIR["S3 Glacier Instant Retrieval"]
+ GIR -- "180日経過" --> DeepArchive["S3 Glacier Deep Archive (長期保管)"]
+
+ Standard -. "アクセスパターンが 不明・変動する場合" .-> IT["S3 Intelligent-Tiering (自動で階層を最適化)"]
+
+ style Standard fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style IA fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style GIR fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style DeepArchive fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style IT fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+```
+
+> **ベストプラクティス:** アクセスパターンが読めない、または変動するワークロード(例: 新規サービスのログデータ)には、手動でライフサイクルルールを設計するより先に **S3 Intelligent-Tiering** を検討する。監視・自動化の追加料金のみで、取得料金や早期削除料金が発生しない。
+
+### 3.1.3 Amazon EBS: ボリュームタイプの選択
+
+| ボリュームタイプ | 種別 | 最大IOPS目安 | 主な用途 |
+|---|---|---|---|
+| gp3(汎用SSD) | SSD | 最大16,000 IOPS(IOPSとスループットを個別に課金・調整可能) | ほとんどの汎用ワークロード、仮想デスクトップ、開発/テスト環境 |
+| gp2(汎用SSD・旧世代) | SSD | ボリュームサイズに比例(バーストクレジット方式) | レガシー互換、小規模ワークロード |
+| io2 Block Express(プロビジョンドIOPS SSD) | SSD | 最大256,000 IOPS | ミッションクリティカルな大規模DB(Oracle、SAP HANA等)、サブミリ秒レイテンシが必要な用途 |
+| io1/io2(プロビジョンドIOPS SSD) | SSD | 最大64,000 IOPS | 高いIOPSが必要なI/O集約型DB |
+| st1(スループット最適化HDD) | HDD | — (スループット課金) | ビッグデータ、データウェアハウス、ログ処理などシーケンシャルI/O |
+| sc1(コールドHDD) | HDD | — | アクセス頻度が低い大容量データ、最安コスト |
+
+出典: [Amazon EBSボリュームタイプ(公式)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html)
+
+```mermaid
+flowchart TD
+ Q1{"ワークロードの I/Oパターンは?"}
+ Q1 -- "ランダムI/O (トランザクション処理・DB)" --> Q2{"必要なIOPSは 16,000を超えるか?"}
+ Q1 -- "シーケンシャルI/O (大容量読み書き)" --> Q3{"アクセス頻度は?"}
+
+ Q2 -- "はい(超高性能DB)" --> io2["io2 Block Express"]
+ Q2 -- "いいえ(汎用)" --> gp3["gp3(汎用SSD)"]
+
+ Q3 -- "頻繁(ログ処理/DWH)" --> st1["st1(スループット最適化HDD)"]
+ Q3 -- "低頻度(コールドデータ)" --> sc1["sc1(コールドHDD)"]
+
+ style gp3 fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style io2 fill:#2c5480,stroke:#e2716f,color:#eef4ff
+ style st1 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style sc1 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+> **ベストプラクティス:** EC2インスタンスとEBSボリュームの間の帯域(EBS最適化)がボトルネックにならないよう、**EBS最適化対応インスタンスタイプ**を選ぶ。また、単一EC2インスタンス停止時にもデータを永続化したい場合はEBS(インスタンスストアは一時的でインスタンス停止/終了時にデータが消える点に注意)。
+
+### 3.1.4 Amazon EFS: 弾力性のある共有ファイルストレージ
+
+Amazon EFSはLinuxベースのワークロード向けにNFSプロトコルで複数のAZ・複数のインスタンスから同時マウントできるマネージド型ファイルストレージです。
+
+**パフォーマンスモード:**
+- **General Purpose**: 低レイテンシ優先。大半のユースケースのデフォルト。
+- **Max I/O**: 数百〜数千のクライアントからの高い並列アクセスが必要な場合(レイテンシは若干犠牲)。
+
+**スループットモード:**
+- **Bursting Throughput**: ストレージ容量に比例してスループットがスケール(バーストクレジット方式)。
+- **Elastic Throughput**: ワークロードのI/Oパターンに応じて自動的にスループットをスケール(予測不能なワークロードに最適)。
+- **Provisioned Throughput**: 容量に依存せず必要なスループットを明示的に指定。
+
+**ストレージクラス(S3同様のライフサイクル管理):**
+- EFS Standard / EFS Standard-IA
+- EFS One Zone / EFS One Zone-IA(単一AZでコスト削減)
+
+出典: [Amazon EFSのパフォーマンス](https://docs.aws.amazon.com/efs/latest/ug/performance.html) / [Amazon EFSストレージクラス](https://docs.aws.amazon.com/efs/latest/ug/storage-classes.html)
+
+```mermaid
+flowchart TD
+ subgraph AZ1["Availability Zone A"]
+ EC2a["EC2インスタンス"]
+ end
+ subgraph AZ2["Availability Zone B"]
+ EC2b["EC2インスタンス"]
+ end
+ subgraph AZ3["Availability Zone C"]
+ EC2c["EC2インスタンス"]
+ end
+
+ EC2a -- "NFSマウント" --> EFS["Amazon EFS (リージョン全体で 複数AZにまたがる 共有ファイルシステム)"]
+ EC2b -- "NFSマウント" --> EFS
+ EC2c -- "NFSマウント" --> EFS
+
+ EFS -. "アクセス頻度に応じて ライフサイクル管理" .-> EFSIA["EFS Standard-IA (低頻度アクセス層)"]
+
+ style EFS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style EFSIA fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+> **ベストプラクティス:** EFSは「複数のAZ・複数のEC2から同時に同じファイルへアクセスする」要件(例: CMS、コンテンツリポジトリ、共有ホームディレクトリ)で採用する。単一インスタンス専有の高性能ブロックストレージが必要ならEBS、Windows(SMB)やHPC(Lustre)、NetApp ONTAP互換が必要ならFSxファミリーを検討する。
+
+### 3.1.5 ハイブリッドストレージ: AWS Storage Gateway
+
+オンプレミス環境とAWSのストレージをシームレスに統合するためのサービスです。試験では「オンプレミスのレガシーアプリをそのまま使いながらクラウドのストレージを活用したい」という要件で問われます。
+
+| Storage Gatewayタイプ | 提供プロトコル | 主なユースケース |
+|---|---|---|
+| Amazon S3 File Gateway | NFS / SMB | オンプレミスアプリからファイルとしてS3にアクセス(S3上はネイティブオブジェクトとして保存) |
+| Amazon FSx File Gateway | SMB | オンプレミスからFSx for Windows File Serverへ低レイテンシアクセス |
+| Volume Gateway(キャッシュ型) | iSCSI | オンプレミスのプライマリデータをS3に保存しつつ、よく使うデータのみローカルにキャッシュ |
+| Volume Gateway(保管型) | iSCSI | オンプレミスにプライマリデータを保持しつつ、非同期でS3にバックアップ(災害復旧用) |
+| Tape Gateway | iSCSI仮想テープライブラリ(VTL) | 既存のテープバックアップソフトウェアをそのまま使い、実体はS3/Glacierに保存 |
+
+出典: [AWS Storage Gatewayとは(公式)](https://docs.aws.amazon.com/storagegateway/latest/userguide/WhatIsStorageGateway.html)
+
+```mermaid
+flowchart LR
+ OnPrem["オンプレミス アプリケーション/サーバー"]
+
+ OnPrem -- "NFS/SMB" --> FileGW["S3 File Gateway"]
+ OnPrem -- "iSCSI" --> VolGW["Volume Gateway"]
+ OnPrem -- "仮想テープ装置(VTL)" --> TapeGW["Tape Gateway"]
+
+ FileGW --> S3["Amazon S3 (オブジェクトとして保存)"]
+ VolGW --> S3B["Amazon S3 (EBSスナップショット形式)"]
+ TapeGW --> Glacier["S3 Glacier (仮想テープアーカイブ)"]
+
+ style FileGW fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style VolGW fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style TapeGW fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+```
+
+> **ベストプラクティス:** 「オンプレミスのファイルサーバーを段階的にクラウド移行したい」→ **File Gateway**。「オンプレミスのブロックストレージ(iSCSI)をバックアップ/DR目的でクラウド化したい」→ **Volume Gateway**。「既存のテープバックアップ運用を変えずにコストだけ削減したい」→ **Tape Gateway**。大量データの一括移行なら **AWS Snow Family**、継続的な同期・転送が必要なら **AWS DataSync**(Task 3.5で詳述)を使い分ける。
+
+---
+
+## Task 3.2: 高性能で弾力性のあるコンピューティングソリューション
+
+### 出題される知識・スキル項目(公式)
+
+**知識:**
+- 適切なユースケースを伴うAWSコンピューティングサービス(例: AWS Batch、Amazon EMR、AWS Fargate)
+- AWSのグローバルインフラストラクチャとエッジサービスがサポートする分散コンピューティングの概念
+- キューイングとメッセージングの概念(例: パブリッシュ/サブスクライブ)
+- 適切なユースケースを伴うスケーラビリティ機能(例: Amazon EC2 Auto Scaling、AWS Auto Scaling)
+- サーバーレステクノロジーとパターン(例: AWS Lambda、Fargate)
+- コンテナのオーケストレーション(例: Amazon ECS、Amazon EKS)
+
+**スキル:**
+- コンポーネントが独立してスケールできるようにワークロードを疎結合化する
+- スケーリングアクションを実行するための指標と条件の特定
+- ビジネス要件を満たす適切なコンピューティングオプションと機能の選択(例: EC2インスタンスタイプ)
+- ビジネス要件を満たす適切なリソースタイプとサイズの選択(例: Lambdaメモリの量)
+
+出典: [Task 3.2(AWS公式Exam Guide)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html#solutions-architect-associate-03-domain3-task2)
+
+### 3.2.1 コンピューティングサービスの全体マップ
+
+```mermaid
+flowchart TD
+ Start["ワークロードの 特性は?"]
+
+ Start --> Q1{"サーバー管理を 自分で行うか?"}
+ Q1 -- "はい(OSレベルの制御が必要)" --> EC2["Amazon EC2 + EC2 Auto Scaling"]
+ Q1 -- "いいえ(サーバーレス)" --> Q2{"実行時間の目安は?"}
+
+ Q2 -- "短時間(最大15分) イベント駆動" --> Lambda["AWS Lambda"]
+ Q2 -- "コンテナ化された 長時間実行" --> Q3{"オーケストレーションの 好みは?"}
+
+ Q3 -- "AWSネイティブでシンプル" --> ECS["Amazon ECS (on Fargate または EC2)"]
+ Q3 -- "Kubernetes標準/ マルチクラウド互換" --> EKS["Amazon EKS (on Fargate または EC2)"]
+
+ Start --> Q4{"大規模バッチ処理・ ジョブスケジューリングか?"}
+ Q4 -- "はい(数百〜数千の並列ジョブ)" --> Batch["AWS Batch"]
+
+ Start --> Q5{"ビッグデータ処理 (Spark/Hadoop/Hive)か?"}
+ Q5 -- "はい" --> EMR["Amazon EMR"]
+
+ style EC2 fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style Lambda fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style ECS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style EKS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style Batch fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style EMR fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+| サービス | 管理レベル | 典型的ユースケース | ソース |
+|---|---|---|---|
+| Amazon EC2 | ユーザーがOS/ミドルウェアまで管理 | 汎用ワークロード、レガシーアプリ移行、細かなインスタンスタイプ選択が必要な場合 | [EC2ユーザーガイド](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html) |
+| AWS Lambda | サーバーレス(コードのみ管理) | イベント駆動処理、APIバックエンド、ETLの軽量変換、非同期処理 | [Lambda開発者ガイド](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) |
+| AWS Fargate | サーバーレス(コンテナのみ管理) | サーバー管理をしたくないコンテナワークロード(ECS/EKS上で稼働) | [AWS Fargateとは](https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html) |
+| Amazon ECS | AWSネイティブなコンテナオーケストレーション | AWS標準機能で完結させたいコンテナ運用 | [Amazon ECSとは](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) |
+| Amazon EKS | マネージドKubernetes | 既にKubernetesを運用中/マルチクラウド前提の組織 | [Amazon EKSとは](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html) |
+| AWS Batch | フルマネージドバッチスケジューラ | 大量の計算集約型バッチジョブ(ゲノム解析、金融シミュレーション等) | [AWS Batchとは](https://docs.aws.amazon.com/batch/latest/userguide/what-is-batch.html) |
+| Amazon EMR | マネージドHadoop/Sparkクラスタ | ビッグデータ処理、ETL、機械学習の前処理 | [Amazon EMRとは](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-what-is-emr.html) |
+
+### 3.2.2 EC2 Auto Scalingによる弾力性の実現
+
+高性能アーキテクチャの中核は「需要に応じて自動的にリソースを増減する」弾力性(Elasticity)です。EC2 Auto Scalingは **起動テンプレート** と **Auto Scalingグループ(ASG)** を使ってこれを実現します。
+
+```mermaid
+flowchart TD
+ Users["ユーザーからの トラフィック"] --> ALB["Application Load Balancer"]
+ ALB --> ASG["Auto Scalingグループ (複数AZにまたがる)"]
+
+ subgraph AZ_A["AZ-A"]
+ EC2_1["EC2インスタンス"]
+ end
+ subgraph AZ_B["AZ-B"]
+ EC2_2["EC2インスタンス"]
+ end
+
+ ASG --> EC2_1
+ ASG --> EC2_2
+
+ CW["Amazon CloudWatch (CPU使用率などを監視)"] -- "しきい値超過を検知" --> ASG
+ ASG -- "スケールアウト/イン" --> LaunchTemplate["起動テンプレートに基づき インスタンスを増減"]
+
+ style ALB fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style ASG fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style CW fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+```
+
+**スケーリングポリシーの種類:**
+
+| ポリシー種別 | 動作 | 適したシナリオ |
+|---|---|---|
+| ターゲット追跡スケーリング | 指定した指標(例: 平均CPU使用率50%)を維持するよう自動調整 | 最も一般的でシンプル。多くのケースで第一選択 |
+| ステップスケーリング | しきい値超過の度合いに応じて段階的にスケール量を変える | 負荷の急増に細かく対応したい場合 |
+| シンプルスケーリング | 1つのアラームに基づき固定量でスケール | レガシー的な手法、現在はターゲット追跡が推奨 |
+| スケジュールに基づくスケーリング | 既知の時間帯(例: 毎朝9時)に合わせて事前にスケール | 予測可能なトラフィックパターン(月末バッチ等) |
+| 予測スケーリング | 機械学習で将来の負荷を予測し事前にスケール | 周期的なトラフィックパターンがある場合 |
+
+出典: [Amazon EC2 Auto Scalingのスケーリングポリシー](https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html) / [AWS Auto Scalingとは](https://docs.aws.amazon.com/autoscaling/plans/userguide/what-is-aws-auto-scaling.html)
+
+> **ベストプラクティス:** **AWS Auto Scaling**(複数サービス横断)と**Amazon EC2 Auto Scaling**(EC2専用)の違いに注意。前者はEC2・ECS・DynamoDB・Auroraなど複数リソースのスケーリングを一元管理するための上位サービスであり、後者はEC2のASGそのものを指す。試験では文脈でどちらを指しているか読み分ける。
+
+### 3.2.3 サーバーレスコンピューティング: AWS Lambda
+
+| 項目 | 仕様の目安 |
+|---|---|
+| メモリ設定範囲 | 128 MB 〜 10,240 MB(1 MB単位で調整可能) |
+| CPU | メモリ量に比例して自動割り当て(メモリを増やすとCPUも増える) |
+| 最大実行時間 | 900秒(15分) |
+| /tmp一時ストレージ | デフォルト512 MB、最大10,240 MBまで拡張可能 |
+| デプロイパッケージサイズ | 展開後250 MBまで(レイヤー含む) |
+
+出典: [Lambda関数のメモリ設定](https://docs.aws.amazon.com/lambda/latest/dg/configuration-memory.html) / [Lambdaの設定に関するトラブルシューティング](https://docs.aws.amazon.com/lambda/latest/dg/troubleshooting-configuration.html)
+
+> **ベストプラクティス:** CPU集約的な処理で実行時間が長い場合、まず**メモリを増やす**ことを検討する(メモリ増加=CPU増加のため、処理が速くなり結果的にコストが変わらない、あるいは下がることがある)。15分の実行時間上限を超えるバッチ処理は、**AWS Step Functions**で複数のLambda関数をオーケストレーションするか、**AWS Batch**/**Amazon EMR**などLambda以外のコンピューティングオプションを検討する。
+
+### 3.2.4 コンテナオーケストレーション: ECS vs EKS vs Fargate
+
+「オーケストレーター(何が管理するか)」と「起動タイプ(どこで実行されるか)」は独立した軸として理解する。
+
+```mermaid
+flowchart LR
+ subgraph Orchestrator["オーケストレーター(コントロールプレーン)"]
+ ECS_o["Amazon ECS (AWS独自)"]
+ EKS_o["Amazon EKS (マネージドKubernetes)"]
+ end
+
+ subgraph LaunchType["起動タイプ(実行基盤)"]
+ EC2_l["EC2起動タイプ (自分でEC2を管理)"]
+ Fargate_l["Fargate起動タイプ (サーバーレス)"]
+ end
+
+ ECS_o --> EC2_l
+ ECS_o --> Fargate_l
+ EKS_o --> EC2_l
+ EKS_o --> Fargate_l
+
+ style ECS_o fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style EKS_o fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style Fargate_l fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style EC2_l fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+出典: [AWS Fargateとは](https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html) / [Amazon ECSとEKSの選択に関する考え方](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html)
+
+> **ベストプラクティス:** 「サーバーのパッチ適用やキャパシティ管理から解放されたい」→ **Fargate起動タイプ**。「既にKubernetesの知識・マニフェストが社内に蓄積されている、またはオンプレミス/他クラウドとの一貫性が必要」→ **EKS**。「AWSに閉じたシンプルな構成にしたい」→ **ECS**。
+
+### 3.2.5 ワークロードの疎結合化: キューイングとパブリッシュ/サブスクライブ
+
+高性能アーキテクチャでは、コンポーネント同士を疎結合にすることで、それぞれが独立してスケールできるようにします。代表的な2つのパターンです。
+
+```mermaid
+flowchart LR
+ subgraph PubSub["パブリッシュ/サブスクライブ(ファンアウト)"]
+ Producer["注文サービス"] -- "発行(Publish)" --> SNS["Amazon SNS (トピック)"]
+ SNS -- "配信" --> Sub1["請求処理 (SQSキュー)"]
+ SNS -- "配信" --> Sub2["在庫更新 (SQSキュー)"]
+ SNS -- "配信" --> Sub3["通知メール送信 (Lambda)"]
+ end
+
+ style SNS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+```
+
+```mermaid
+sequenceDiagram
+ participant Producer as メッセージ送信側
+ participant SQS as Amazon SQSキュー
+ participant Consumer as ワーカー(EC2/Lambda/ECS)
+
+ Producer->>SQS: メッセージを送信
+ Note over SQS: メッセージは処理されるまで キューに保持される
+ Consumer->>SQS: メッセージをポーリングして取得
+ SQS-->>Consumer: メッセージを返す(可視性タイムアウト開始)
+ Consumer->>Consumer: メッセージを処理
+ Consumer->>SQS: 処理完了後にメッセージを削除
+```
+
+| 概念 | サービス | 特徴 |
+|---|---|---|
+| キュー(1対1) | Amazon SQS | 複数のコンシューマーが異なるメッセージを並列取得できる。取得された各メッセージは可視性タイムアウト中、その取得元コンシューマーに占有される。バックログを吸収するバッファとして送信側と受信側の速度差を吸収する |
+| パブリッシュ/サブスクライブ(1対多) | Amazon SNS | 1つのメッセージを複数のサブスクライバー(SQSキュー、Lambda、HTTPSエンドポイント等)に同時配信(ファンアウト) |
+| イベントルーティング | Amazon EventBridge | イベントの内容に基づき複数のターゲットへルールベースでルーティング。SaaS連携も可能 |
+
+出典: [Amazon SQSとは](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html) / [Amazon SNSとは](https://docs.aws.amazon.com/sns/latest/dg/welcome.html) / [Amazon EventBridgeとは](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html)
+
+> **ベストプラクティス:** フロントエンドが受け付けた大量のリクエストをバックエンドの処理速度に関わらず受け止めたい場合、間に**SQSキュー**を挟んで疎結合化する。これにより、バックエンドの処理が一時的に遅れてもリクエストが失われず、バックエンド側は自分のペースでAuto Scalingしながら処理できる(バッファリングによる負荷平準化)。
+
+---
+
+## Task 3.3: 高性能なデータベースソリューション
+
+### 出題される知識・スキル項目(公式)
+
+**知識:**
+- AWSグローバルインフラストラクチャ(例: アベイラビリティーゾーン、AWSリージョン)
+- キャッシング戦略とサービス(例: Amazon ElastiCache)
+- データアクセスパターン(例: 読み取り集約型と書き込み集約型の比較)
+- データベースのキャパシティプランニング(例: キャパシティユニット、インスタンスタイプ、プロビジョンドIOPS)
+- データベース接続とプロキシ
+- 適切なユースケースを伴うデータベースエンジン(例: 異種間移行、同種間移行)
+- データベースレプリケーション(例: 読み取りレプリカ)
+- データベースのタイプとサービス(例: サーバーレス、リレーショナルと非リレーショナルの比較、インメモリ)
+
+**スキル:**
+- ビジネス要件を満たす読み取りレプリカの構成
+- データベースアーキテクチャの設計
+- 適切なデータベースエンジンの決定(例: MySQLとPostgreSQLの比較)
+- 適切なデータベースタイプの決定(例: Amazon Aurora、Amazon DynamoDB)
+- ビジネス要件を満たすキャッシングの統合
+
+出典: [Task 3.3(AWS公式Exam Guide)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html#solutions-architect-associate-03-domain3-task3)
+
+### 3.3.1 データベースタイプの選択フロー
+
+```mermaid
+flowchart TD
+ Start["データの構造・ アクセスパターンは?"]
+
+ Start --> Q1{"厳密なスキーマ・ 複雑なJOIN・ トランザクション整合性が必要?"}
+ Q1 -- はい --> Q2{"クラウドネイティブな 可用性・スケーラビリティ 重視か?"}
+ Q2 -- はい --> Aurora["Amazon Aurora (MySQL/PostgreSQL互換)"]
+ Q2 -- "いいえ(オンプレミスDBの そのまま移行など)" --> RDS["Amazon RDS (MySQL/PostgreSQL/ MariaDB/Oracle/SQL Server)"]
+
+ Q1 -- "いいえ(柔軟なスキーマ・ キーバリュー中心)" --> Q3{"超低レイテンシ・ 大規模スケールが必要?"}
+ Q3 -- はい --> DynamoDB["Amazon DynamoDB (サーバーレスNoSQL)"]
+ Q3 -- "いいえ(ドキュメント/グラフ等)" --> Other["Amazon DocumentDB / Amazon Neptune 等"]
+
+ Start --> Q4{"インメモリの 超高速アクセスが目的?"}
+ Q4 -- はい --> ElastiCache["Amazon ElastiCache (キャッシュ/インメモリDB)"]
+
+ style Aurora fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style RDS fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style DynamoDB fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style ElastiCache fill:#2c5480,stroke:#e2716f,color:#eef4ff
+```
+
+出典: [Amazon RDSとは](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html) / [Amazon Auroraとは](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html) / [Amazon DynamoDBとは](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html)
+
+### 3.3.2 Amazon RDS: マルチAZ配置と読み取りレプリカ
+
+**マルチAZ(高可用性)** と **読み取りレプリカ(性能スケーリング)** は目的が異なる点に注意が必要です。
+
+```mermaid
+flowchart TD
+ App["アプリケーション"]
+
+ subgraph Primary_AZ["AZ-A(プライマリ)"]
+ Primary[("RDSプライマリ (読み書き両方)")]
+ end
+ subgraph Standby_AZ["AZ-B(スタンバイ)"]
+ Standby[("RDSスタンバイ (同期レプリケーション) ※通常は直接読み取り不可")]
+ end
+ subgraph ReadReplica_Region["同一/別リージョン"]
+ RR1[("読み取りレプリカ1 (非同期レプリケーション)")]
+ RR2[("読み取りレプリカ2")]
+ end
+
+ App -- "書き込み" --> Primary
+ Primary == "同期レプリケーション (自動フェイルオーバー用)" ==> Standby
+ Primary -. "非同期レプリケーション" .-> RR1
+ Primary -. "非同期レプリケーション" .-> RR2
+ App -- "読み取り専用クエリを分散" --> RR1
+ App -- "読み取り専用クエリを分散" --> RR2
+
+ style Primary fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style Standby fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style RR1 fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+ style RR2 fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+```
+
+| 目的 | 機能 | ポイント |
+|---|---|---|
+| **可用性(HA)** | マルチAZ配置 | 同期レプリケーション。障害時に自動フェイルオーバー。スタンバイは通常のクエリには使えない |
+| **読み取り性能のスケーリング** | 読み取りレプリカ | 非同期レプリケーション。読み取り集約型ワークロードの負荷を複数レプリカに分散。同一リージョン内だけでなくクロスリージョンにも作成可能(DR用途にも活用) |
+
+出典: [Amazon RDSのマルチAZ配置](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html) / [Amazon RDS読み取りレプリカの操作](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html)
+
+> **ベストプラクティス:** 「読み取りが集中してDBがボトルネックになっている」→ **読み取りレプリカ**を追加してSELECTクエリを分散する。「DB障害時にダウンタイムを最小化したい」→ **マルチAZ配置**で自動フェイルオーバーを構成する。両方を組み合わせる(マルチAZ+複数の読み取りレプリカ)のが一般的な高性能・高可用性構成。
+
+### 3.3.3 Amazon Aurora: クラウドネイティブなストレージアーキテクチャ
+
+Auroraは、コンピュート層とストレージ層を分離し、ストレージを自動的に複数AZ・6つのコピーへ複製する独自アーキテクチャにより、RDS標準のMySQL/PostgreSQLより高いスループットと耐障害性を実現します。
+
+```mermaid
+flowchart TD
+ Writer["Auroraライター インスタンス"]
+ Reader1["Auroraリーダー インスタンス1"]
+ Reader2["Auroraリーダー インスタンス2"]
+
+ subgraph Storage["Auroraストレージ層 (3つのAZに自動複製、各AZ2コピー=合計6コピー)"]
+ S1[("コピーAZ-A #1")]
+ S2[("コピーAZ-A #2")]
+ S3[("コピーAZ-B #1")]
+ S4[("コピーAZ-B #2")]
+ S5[("コピーAZ-C #1")]
+ S6[("コピーAZ-C #2")]
+ end
+
+ Writer --> Storage
+ Reader1 --> Storage
+ Reader2 --> Storage
+
+ style Writer fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style Reader1 fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+ style Reader2 fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+ style Storage fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+**Aurora特有の高性能機能:**
+- **Aurora Serverless**: トラフィックに応じてコンピュート容量を自動でスケールアップ/ダウン(断続的・予測不能なワークロードに最適)
+- **Aurora Global Database**: 複数リージョンにまたがるレプリケーション(1秒未満のレプリケーションラグ)でグローバルな読み取り性能とDRを両立
+- **Auroraレプリカ**: 最大15台まで作成可能(標準MySQLは最大5台)で読み取りスケーリングの上限が高い
+
+出典: [Amazon Auroraのストレージと信頼性](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.StorageReliability.html) / [Aurora Serverless v2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html) / [Aurora Global Database](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-global-database.html)
+
+### 3.3.4 データベースエンジンの移行: 同種間 vs 異種間
+
+| 移行タイプ | 定義 | 使用するツール |
+|---|---|---|
+| **同種間移行(Homogeneous)** | 同じデータベースエンジン間の移行(例: オンプレミスMySQL → Amazon RDS for MySQL) | AWS DMS(ネイティブレプリケーション/バックアップリストアも可) |
+| **異種間移行(Heterogeneous)** | 異なるデータベースエンジン間の移行(例: Oracle → Amazon Aurora PostgreSQL) | AWS SCT(スキーマ変換)+ AWS DMS(データ移行) |
+
+出典: [AWS Database Migration Serviceとは](https://docs.aws.amazon.com/dms/latest/userguide/Welcome.html) / [AWS Schema Conversion Toolとは](https://docs.aws.amazon.com/SchemaConversionTool/latest/userguide/CHAP_Welcome.html)
+
+> **ベストプラクティス:** 試験で「MySQL同士」のようにエンジンが同じ移行が問われたら**DMSのみ**で完結できると考える。「Oracle→Aurora PostgreSQL」のようにエンジンが異なる移行では、まず**SCT**でスキーマ・ストアドプロシージャ等を変換し、その後**DMS**でデータそのものを移行する2段階アプローチになる。
+
+### 3.3.5 Amazon DynamoDB: キャパシティモードとアクセスパターン
+
+| キャパシティモード | 特徴 | 適したシナリオ |
+|---|---|---|
+| オンデマンドモード | リクエスト数に応じて自動的にスケール、使った分だけ課金 | トラフィックが予測不能・変動が激しいワークロード |
+| プロビジョンドモード | 読み取り/書き込みキャパシティユニット(RCU/WCU)を事前に指定 | トラフィックが予測可能で、コストを最適化したい場合(Auto Scalingと組み合わせも可) |
+
+**データアクセスパターンの設計:**
+- **読み取り集約型(Read-heavy)**: DynamoDB Accelerator (DAX) によるマイクロ秒レベルのインメモリキャッシュ、または ElastiCache/アプリケーション側キャッシュの活用を検討する。DAX のレプリカは DynamoDB 本体ではなくキャッシュ層内部の機能として扱う
+- **書き込み集約型(Write-heavy)**: パーティションキーの設計を分散させ「ホットパーティション」を避ける。オンデマンドモードやWCUの適切な設計が重要
+
+出典: [DynamoDBの読み取り/書き込みキャパシティモード](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html) / [DynamoDB Accelerator (DAX)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.html)
+
+### 3.3.6 キャッシング戦略: Amazon ElastiCache
+
+ElastiCacheは3つのエンジン(**Valkey**・**Redis OSS**・**Memcached**)から選択できるフルマネージド型インメモリデータストアです。AWSは新規構築のワークロードに対して、オープンソースでコスト効率の高い **Valkey** を推奨しています(Redis OSSからのコマンド・クライアント互換のドロップイン代替)。
+
+| エンジン | 特徴 |
+|---|---|
+| Valkey | Linux Foundation管理のオープンソース、Redis OSS完全互換、AWSが新規ワークロードに推奨、料金面で優位 |
+| Redis OSS | 複雑なデータ構造、レプリケーション、Pub/Sub、トランザクションをサポート |
+| Memcached | シンプルなマルチスレッド型キャッシュ、水平分割(パーティショニング)に強いが永続化・複製機能はない |
+
+出典: [Amazon ElastiCache for Valkeyの発表](https://aws.amazon.com/about-aws/whats-new/2024/10/amazon-elasticache-valkey) / [ElastiCacheのエンジン選択](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/SelectEngine.html)
+
+**代表的なキャッシング戦略(Redis OSS/Valkey互換):**
+
+```mermaid
+sequenceDiagram
+ participant App as アプリケーション
+ participant Cache as ElastiCache
+ participant DB as データベース
+
+ Note over App,DB: 遅延読み込み(Lazy Loading)パターン
+ App->>Cache: データを問い合わせ
+ alt キャッシュヒット
+ Cache-->>App: キャッシュされたデータを返す
+ else キャッシュミス
+ Cache-->>App: データなし
+ App->>DB: データベースに問い合わせ
+ DB-->>App: データを返す
+ App->>Cache: 取得したデータをキャッシュに書き込む
+ end
+```
+
+```mermaid
+sequenceDiagram
+ participant App as アプリケーション
+ participant Cache as ElastiCache
+ participant DB as データベース
+
+ Note over App,DB: ライトスルー(Write-Through)パターン
+ App->>DB: データを書き込む
+ App->>Cache: 同時にキャッシュにも書き込む
+ Note over Cache: 常に最新データが キャッシュに存在する
+```
+
+| 戦略 | メリット | デメリット |
+|---|---|---|
+| 遅延読み込み(Lazy Loading) | 実際にリクエストされたデータのみキャッシュ(無駄が少ない) | 初回アクセス時はキャッシュミスによる遅延(cache penalty)が発生 |
+| ライトスルー(Write-Through) | キャッシュのデータが常に最新 | 書き込みのたびにキャッシュ更新が発生し書き込みレイテンシが増える。使われないデータもキャッシュされがち |
+
+出典: [キャッシング戦略のベストプラクティス](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/Strategies.html)
+
+> **ベストプラクティス:** 読み取り集約型で、同じデータに何度もアクセスされるパターン(商品カタログ、セッション情報等)には**遅延読み込み**が適している。データの鮮度が極めて重要な場合(在庫数など)は**ライトスルー**を検討するが、TTL(有効期限)を併用して古いデータの残留リスクを軽減する。
+
+### 3.3.7 データベース接続とプロキシ: Amazon RDS Proxy
+
+サーバーレス(Lambda)やマイクロサービスのように大量の短命なコネクションを発生させるアーキテクチャでは、RDS/Auroraのコネクション数上限に達しやすいという課題があります。
+
+```mermaid
+flowchart LR
+ subgraph Clients["大量の短命なクライアント"]
+ L1["Lambda呼び出し1"]
+ L2["Lambda呼び出し2"]
+ L3["Lambda呼び出し...N"]
+ end
+
+ L1 --> Proxy["Amazon RDS Proxy (コネクションプーリング)"]
+ L2 --> Proxy
+ L3 --> Proxy
+
+ Proxy -- "少数のプールされた 永続的コネクション" --> DB[("Amazon RDS / Aurora")]
+
+ style Proxy fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style DB fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+出典: [Amazon RDS Proxyとは](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html)
+
+> **ベストプラクティス:** 「Lambda関数からRDSへの接続で"too many connections"エラーが発生する」という試験の典型的シナリオには**RDS Proxy**が正解になりやすい。RDS Proxyはコネクションプーリングに加え、フェイルオーバー時の切り替え時間も短縮する。
+
+---
+
+## Task 3.4: 高性能・スケーラブルなネットワークアーキテクチャ
+
+### 出題される知識・スキル項目(公式)
+
+**知識:**
+- 適切なユースケースを伴うエッジネットワーキングサービス(例: Amazon CloudFront、AWS Global Accelerator)
+- ネットワークアーキテクチャの設計方法(例: サブネット階層、ルーティング、IPアドレッシング)
+- ロードバランシングの概念(例: Application Load Balancer)
+- ネットワーク接続オプション(例: AWS VPN、AWS Direct Connect、AWS PrivateLink)
+
+**スキル:**
+- さまざまなアーキテクチャ(グローバル、ハイブリッド、マルチティア等)向けのネットワークトポロジの作成
+- 将来のニーズに対応してスケールできるネットワーク構成の決定
+- ビジネス要件を満たす適切なリソース配置の決定
+- 適切なロードバランシング戦略の選択
+
+出典: [Task 3.4(AWS公式Exam Guide)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html#solutions-architect-associate-03-domain3-task4)
+
+### 3.4.1 VPCのマルチティア・サブネット設計
+
+高性能・高可用性の基本形は「**複数AZにまたがる、階層化されたサブネット設計**」です。
+
+```mermaid
+flowchart TD
+ IGW["Internet Gateway"]
+
+ subgraph VPC["VPC (例: 10.0.0.0/16)"]
+ subgraph AZ_A["Availability Zone A"]
+ PubA["パブリックサブネット 10.0.1.0/24 (ALB, NATゲートウェイ)"]
+ AppA["プライベートサブネット(アプリ層) 10.0.11.0/24 (EC2/ECS)"]
+ DataA["プライベートサブネット(データ層) 10.0.21.0/24 (RDS)"]
+ end
+ subgraph AZ_B["Availability Zone B"]
+ PubB["パブリックサブネット 10.0.2.0/24 (ALB, NATゲートウェイ)"]
+ AppB["プライベートサブネット(アプリ層) 10.0.12.0/24 (EC2/ECS)"]
+ DataB["プライベートサブネット(データ層) 10.0.22.0/24 (RDS)"]
+ end
+ end
+
+ IGW --> PubA
+ IGW --> PubB
+ PubA --> AppA
+ PubB --> AppB
+ AppA --> DataA
+ AppB --> DataB
+
+ style PubA fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style PubB fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style AppA fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style AppB fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style DataA fill:#1a2f4a,stroke:#e2716f,color:#eef4ff
+ style DataB fill:#1a2f4a,stroke:#e2716f,color:#eef4ff
+```
+
+出典: [VPCとサブネット](https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html) / [VPCのシナリオとサンプル構成](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-example-private-subnets-nat.html)
+
+**サブネット設計のポイント:**
+- **パブリックサブネット**: インターネットゲートウェイへの経路を持つルートテーブルに関連付けられたサブネット(ALB、NATゲートウェイ、踏み台サーバー等)
+- **プライベートサブネット**: インターネットゲートウェイへの直接経路を持たないサブネット(アプリケーション層・データ層)。アウトバウンド通信が必要な場合はNATゲートウェイを経由
+- **CIDR設計**: 将来の拡張を見越して、各サブネットに十分なIPアドレス余裕を持たせる(/24なら251個の使用可能IPアドレス、AWSは各サブネットの先頭4個+末尾1個を予約)
+
+> **ベストプラクティス:** 最低でも**2つのAZ**にまたがるサブネット設計を行い、単一AZ障害でもサービスを継続できるようにする。データ層のサブネットには**インターネットゲートウェイへの経路を一切持たせない**ことで、データベースへの直接的なインターネットアクセスを構造的に排除する(多層防御)。
+
+### 3.4.2 ロードバランシング戦略の選択
+
+```mermaid
+flowchart TD
+ Start["どのレイヤーで ロードバランシングするか?"]
+
+ Start --> Q1{"HTTP/HTTPSの コンテンツに基づく 高度なルーティングが必要か? (パスベース/ホストベース)"}
+ Q1 -- はい --> ALB["Application Load Balancer (レイヤー7)"]
+
+ Start --> Q2{"超低レイテンシ・ 大量のTCP/UDP接続 (数百万リクエスト/秒)が必要か? 静的IPが必要か?"}
+ Q2 -- はい --> NLB["Network Load Balancer (レイヤー4)"]
+
+ Start --> Q3{"サードパーティの 仮想アプライアンス (ファイアウォール/IDS/IPS)を 透過的に経由させたいか?"}
+ Q3 -- はい --> GWLB["Gateway Load Balancer (レイヤー3/4)"]
+
+ style ALB fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style NLB fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style GWLB fill:#2c5480,stroke:#e2716f,color:#eef4ff
+```
+
+| ロードバランサー | レイヤー | 主な特徴 | 典型的ユースケース |
+|---|---|---|---|
+| Application Load Balancer (ALB) | L7 | パスベース/ホストベースルーティング、WebSocket、gRPC対応、コンテナ向けのターゲットグループ | マイクロサービス、コンテナ化されたWebアプリケーション |
+| Network Load Balancer (NLB) | L4 | 超高スループット、静的/Elastic IP対応、TLSパススルー | 極端に高いパフォーマンスが必要なTCP/UDPワークロード、レガシーアプリ |
+| Gateway Load Balancer (GWLB) | L3/L4 | GENEVEプロトコルでトラフィックを透過的に仮想アプライアンスへ転送 | ファイアウォール等のセキュリティアプライアンスの水平スケーリング |
+
+出典: [Elastic Load Balancingの機能比較](https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/introduction.html) / [Application Load Balancerとは](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) / [Network Load Balancerとは](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html)
+
+### ALBによるパスベース/ホストベースルーティング
+
+```mermaid
+flowchart LR
+ Client["クライアント"] --> ALB["Application Load Balancer"]
+
+ ALB -- "/api/* へのリクエスト" --> TG1["ターゲットグループ1 (APIサービス)"]
+ ALB -- "/images/* へのリクエスト" --> TG2["ターゲットグループ2 (画像配信サービス)"]
+ ALB -- "shop.example.com (ホストベース)" --> TG3["ターゲットグループ3 (ECサイト)"]
+
+ style ALB fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+```
+
+### 3.4.3 エッジネットワーキング: CloudFront と Global Accelerator
+
+| サービス | 動作原理 | 適したシナリオ |
+|---|---|---|
+| Amazon CloudFront | エッジロケーションで**コンテンツをキャッシュ**するCDN | 静的コンテンツ(画像・動画・JS/CSS)、動的コンテンツのTLS終端、DDoS吸収 |
+| AWS Global Accelerator | AWSのグローバルネットワークを経由し**最適な経路にルーティング**(キャッシュはしない) | 非HTTP(S)プロトコル(TCP/UDP)、複数リージョンでのフェイルオーバー、静的Anycast IPが必要な場合 |
+
+出典: [Amazon CloudFrontとは](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html) / [AWS Global Acceleratorとは](https://docs.aws.amazon.com/global-accelerator/latest/dg/what-is-global-accelerator.html)
+
+```mermaid
+flowchart TD
+ User["世界中のユーザー"]
+
+ User -- "コンテンツをキャッシュして配信" --> CF["Amazon CloudFront (CDN)"]
+ CF --> Origin["オリジン (S3 / ALB / EC2)"]
+
+ User -- "AWSのバックボーン経由で 最適経路にルーティング (キャッシュなし)" --> GA["AWS Global Accelerator"]
+ GA --> RegionA["リージョンA のエンドポイント"]
+ GA --> RegionB["リージョンB のエンドポイント (フェイルオーバー先)"]
+
+ style CF fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style GA fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+```
+
+> **ベストプラクティス:** 「静的コンテンツの配信を高速化したい」「動画配信のキャッシュ効率を上げたい」→ **CloudFront**。「TCP/UDPベースのゲームサーバー・IoTなどHTTP以外のプロトコルを高速化したい」「複数リージョン間で瞬時にフェイルオーバーしたい」→ **Global Accelerator**。両者は併用可能(例: CloudFrontで静的コンテンツ、Global AcceleratorでAPIのTCP接続を最適化)。
+
+### 3.4.4 ハイブリッド接続: VPN・Direct Connect・PrivateLink
+
+```mermaid
+flowchart TD
+ Start["オンプレミスと AWSをどう接続するか?"]
+
+ Start --> Q1{"迅速に接続したい・ 予算重視か?"}
+ Q1 -- はい --> VPN["AWS Site-to-Site VPN (インターネット経由の暗号化トンネル)"]
+
+ Start --> Q2{"専用線による安定した 帯域・低レイテンシが必要か? (大容量データ転送)"}
+ Q2 -- はい --> DX["AWS Direct Connect (専用線接続)"]
+
+ Start --> Q3{"特定のAWSサービス/ 他社VPC内サービスへ プライベート接続したいだけか? (VPCピアリング不要)"}
+ Q3 -- はい --> PL["AWS PrivateLink (サービス単位のプライベート接続)"]
+
+ style VPN fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style DX fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style PL fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+```
+
+| 接続方式 | 経路 | 特徴 |
+|---|---|---|
+| AWS Site-to-Site VPN | パブリックインターネット(IPsecで暗号化) | 短期間で構築可能、帯域はインターネット状況に依存 |
+| AWS Direct Connect | 専用の物理線 | 一貫した低レイテンシ・高帯域、データ転送コスト削減、DXとVPNを組み合わせた暗号化専用線構成も可能 |
+| AWS PrivateLink | AWSのプライベートネットワーク内 | VPC間・オンプレミス間でIPアドレス重複やルーティング設定を意識せず、特定サービスにインターフェースエンドポイント経由で接続 |
+
+出典: [AWS Site-to-Site VPNとは](https://docs.aws.amazon.com/vpn/latest/s2svpn/VPC_VPN.html) / [AWS Direct Connectとは](https://docs.aws.amazon.com/directconnect/latest/UserGuide/Welcome.html) / [AWS PrivateLinkとは](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html)
+
+> **ベストプラクティス:** VPCピアリングやルートテーブルの複雑な管理を避けつつ、特定のサービス(自社のマイクロサービスや、SaaSベンダーが提供するサービス)にだけ安全にアクセスしたい場合は**PrivateLink(インターフェースVPCエンドポイント)**を使う。IPアドレス空間が重複していても問題なく接続できる点が、VPCピアリングに対する大きな利点。
+
+---
+
+## Task 3.5: 高性能なデータ取り込み・変換ソリューション
+
+### 出題される知識・スキル項目(公式)
+
+**知識:**
+- 適切なユースケースを伴うデータ分析・可視化サービス(例: Amazon Athena、AWS Lake Formation、Amazon QuickSuite)
+- データ取り込みパターン(例: 頻度)
+- 適切なユースケースを伴うデータ転送サービス(例: AWS DataSync、AWS Storage Gateway)
+- 適切なユースケースを伴うデータ変換サービス(例: AWS Glue)
+- 取り込みアクセスポイントへのセキュアなアクセス
+- ビジネス要件を満たすために必要なサイズと速度
+- 適切なユースケースを伴うストリーミングデータサービス(例: Amazon Kinesis)
+
+**スキル:**
+- データレイクの構築とセキュリティ確保
+- データストリーミングアーキテクチャの設計
+- データ転送ソリューションの設計
+- 可視化戦略の実装
+- データ処理に適したコンピューティングオプションの選択(例: Amazon EMR)
+- 取り込みに適した構成の選択
+- フォーマット間のデータ変換(例: .csvから.parquetへ)
+
+出典: [Task 3.5(AWS公式Exam Guide)](https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html#solutions-architect-associate-03-domain3-task5)
+
+> **補足(サービス名の変遷):** Amazon QuickSightは2025年10月に **Amazon Quick Suite** へと進化し、AIエージェント機能(Quick Research、Quick Flows等)が追加されました。既存のQuickSightのダッシュボード・データセット・権限設定はそのまま引き継がれます。出典: [Amazon QuickSightからAmazon Quick Suiteへの進化(AWS公式ブログ)](https://aws.amazon.com/blogs/business-intelligence/reimagine-business-intelligence-amazon-quicksight-evolves-to-amazon-quick-suite/)。同様に、Amazon Kinesis Data Firehoseは**Amazon Data Firehose**という名称に変更されています。
+
+### 3.5.1 データレイクアーキテクチャの全体像
+
+```mermaid
+flowchart TD
+ subgraph Sources["データソース"]
+ DB["業務データベース"]
+ Logs["アプリケーションログ"]
+ Stream["リアルタイムイベント"]
+ OnPrem["オンプレミスファイル"]
+ end
+
+ subgraph Ingestion["取り込み層"]
+ DMS_i["AWS DMS"]
+ Kinesis_i["Amazon Kinesis"]
+ DataSync_i["AWS DataSync"]
+ end
+
+ subgraph Lake["データレイク"]
+ S3_dl[("Amazon S3 (データレイク本体)")]
+ LF["AWS Lake Formation (アクセス権限・ガバナンス)"]
+ Glue_Catalog["AWS Glueデータカタログ (メタデータ管理)"]
+ end
+
+ subgraph Processing["処理・変換層"]
+ Glue_ETL["AWS Glue ETL"]
+ EMR_p["Amazon EMR"]
+ end
+
+ subgraph Consumption["分析・可視化層"]
+ Athena_c["Amazon Athena (SQLクエリ)"]
+ Redshift_c["Amazon Redshift (DWH)"]
+ Quick_c["Amazon Quick Suite (BI/可視化)"]
+ end
+
+ DB --> DMS_i --> S3_dl
+ Logs --> Kinesis_i --> S3_dl
+ Stream --> Kinesis_i
+ OnPrem --> DataSync_i --> S3_dl
+
+ S3_dl <--> LF
+ S3_dl <--> Glue_Catalog
+ S3_dl --> Glue_ETL --> S3_dl
+ S3_dl --> EMR_p --> S3_dl
+
+ S3_dl --> Athena_c --> Quick_c
+ S3_dl --> Redshift_c --> Quick_c
+
+ style S3_dl fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style LF fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+ style Glue_Catalog fill:#1a2f4a,stroke:#5fd4a8,color:#eef4ff
+```
+
+出典: [AWS Lake Formationとは](https://docs.aws.amazon.com/lake-formation/latest/dg/what-is-lake-formation.html) / [AWS Glueとは](https://docs.aws.amazon.com/glue/latest/dg/what-is-glue.html) / [Amazon Athenaとは](https://docs.aws.amazon.com/athena/latest/ug/what-is.html)
+
+| コンポーネント | 役割 |
+|---|---|
+| Amazon S3 | データレイクの実体(オブジェクトストレージ)。ほぼ無制限にスケール |
+| AWS Lake Formation | データレイクの構築を自動化し、テーブル・列・行レベルのきめ細かなアクセス制御を一元管理 |
+| AWS Glue データカタログ | S3上のデータのメタデータ(スキーマ、パーティション等)を一元管理し、Athena/EMR/Redshiftから参照可能にする |
+| AWS Glue ETL | サーバーレスのSparkベースETL。クローラでスキーマを自動検出し、ジョブでデータを変換 |
+| Amazon Athena | S3上のデータに対してサーバーレスでSQLクエリを直接実行(事前のロード不要) |
+
+> **ベストプラクティス:** 複数の部門・チームがデータレイクにアクセスする場合、IAMポリシーだけで細かい権限(特定の列だけ、特定の行だけ)を管理するのは煩雑になりがちなので、**Lake Formation**の一元的な権限管理を使う。「S3に溜まったデータをすぐにSQLで分析したいが、DWHを構築するほどではない」という要件には**Athena**が適している。
+
+### 3.5.2 ストリーミングデータの取り込み: Amazon Kinesis
+
+```mermaid
+flowchart LR
+ Producer["データ生成元 (IoTデバイス/ アプリログ/ クリックストリーム)"]
+
+ Producer --> KDS["Kinesis Data Streams (リアルタイム取り込み シャード単位でスケール)"]
+
+ KDS --> Firehose["Amazon Data Firehose (旧Kinesis Data Firehose) (配信・変換をマネージド化)"]
+ KDS --> KDA["Managed Service for Apache Flink (旧Kinesis Data Analytics) (ストリーム上でリアルタイム分析)"]
+
+ Firehose --> S3_f[("Amazon S3")]
+ Firehose --> Redshift_f[("Amazon Redshift")]
+ Firehose --> OpenSearch_f[("Amazon OpenSearch Service")]
+
+ KDA --> Dashboard["リアルタイム ダッシュボード/アラート"]
+
+ style KDS fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style Firehose fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style KDA fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+出典: [Amazon Kinesis Data Streamsとは](https://docs.aws.amazon.com/streams/latest/dev/introduction.html) / [Amazon Data Firehoseとは](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html)
+
+| サービス | 役割 | ポイント |
+|---|---|---|
+| Kinesis Data Streams | リアルタイムのストリームデータをシャード単位で取り込み、複数のコンシューマーが同時に読み取り可能 | 取り込み後のカスタム処理ロジックを自分で書きたい場合に選択 |
+| Amazon Data Firehose | ストリームデータをS3/Redshift/OpenSearch等へ自動的に配信するフルマネージドサービス | サーバーレスで運用不要、ETLの軽微な変換(Lambda連携)も可能 |
+| Managed Service for Apache Flink | ストリーム上でリアルタイムに集計・異常検知等の分析を実行 | ウィンドウ集計やパターンマッチングが必要な場合 |
+
+> **ベストプラクティス:** 「取り込んだストリームデータを複数の異なるアプリケーションが同時に処理する必要がある」→ **Kinesis Data Streams**(コンシューマーを複数アタッチ可能)。「単純にストリームデータをS3やRedshiftに流し込みたいだけで、運用の手間を減らしたい」→ **Amazon Data Firehose**。
+
+### 3.5.3 バッチ vs ストリーミング: 取り込み頻度の設計
+
+```mermaid
+flowchart TD
+ Start["データ取り込みの 頻度要件は?"]
+
+ Start --> Q1{"リアルタイム性が 必要か? (秒〜分単位の遅延許容度)"}
+ Q1 -- "はい" --> Streaming["ストリーミング取り込み Kinesis Data Streams / Amazon MSK"]
+ Q1 -- "いいえ(時間〜日単位で十分)" --> Batch["バッチ取り込み AWS Glueジョブ / Amazon EMR / DataSync"]
+
+ style Streaming fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style Batch fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+```
+
+> **ベストプラクティス:** 不正取引検知やリアルタイムダッシュボードなど「今すぐの反応」が価値を持つユースケースはストリーミングを選ぶ。日次バッチのレポーティングなど、多少の遅延が許容できコスト効率を優先する場合はバッチ処理(Glue/EMRのスケジュール実行)を選ぶ。
+
+### 3.5.4 データ変換: AWS Glue ETLとフォーマット変換
+
+```mermaid
+flowchart LR
+ S3_raw[("Amazon S3 (生データ: CSV/JSON)")]
+ Crawler["AWS Glueクローラ (スキーマを自動検出)"]
+ Catalog["AWS Glueデータカタログ (テーブル定義を登録)"]
+ ETL["AWS Glue ETLジョブ (Apache Spark)"]
+ S3_processed[("Amazon S3 (変換後: Parquet等)")]
+
+ S3_raw --> Crawler --> Catalog --> ETL
+ S3_raw --> ETL
+ ETL -- "CSV → Parquet (列指向・圧縮フォーマットへ変換)" --> S3_processed
+
+ style Crawler fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style Catalog fill:#1a2f4a,stroke:#7c9eff,color:#eef4ff
+ style ETL fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+```
+
+出典: [AWS Glueクローラとは](https://docs.aws.amazon.com/glue/latest/dg/add-crawler.html) / [AWS GlueのETLプログラミング](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl.html)
+
+> **ベストプラクティス:** Athenaでの分析コストとクエリ性能を最適化するために、CSV/JSONのような行指向フォーマットから **Parquet** のような列指向・圧縮フォーマットへ変換する(スキャンするデータ量が減り、クエリ料金・実行時間の両方を削減できる)。この変換は**AWS Glue ETLジョブ**で自動化するのが一般的なパターン。
+
+### 3.5.5 データ転送: DataSync と Storage Gateway の使い分け
+
+```mermaid
+flowchart TD
+ Start["オンプレミスから AWSへのデータ転送要件は?"]
+
+ Start --> Q1{"継続的な同期・ 大量ファイルの 高速なワンタイム/定期転送か?"}
+ Q1 -- はい --> DataSync["AWS DataSync (ネットワーク経由の高速転送)"]
+
+ Start --> Q2{"オンプレミスアプリから 継続的にファイル/ブロックとして アクセスし続けたいか?"}
+ Q2 -- はい --> SGW["AWS Storage Gateway (常時アクセス可能なハイブリッドストレージ)"]
+
+ Start --> Q3{"ペタバイト級の 一括移行で、 ネットワーク帯域が不足か?"}
+ Q3 -- はい --> Snow["AWS Snow Family (物理デバイスによるオフライン転送)"]
+
+ style DataSync fill:#2c5480,stroke:#7c9eff,color:#eef4ff
+ style SGW fill:#2c5480,stroke:#5fd4a8,color:#eef4ff
+ style Snow fill:#1a2f4a,stroke:#e2716f,color:#eef4ff
+```
+
+出典: [AWS DataSyncとは](https://docs.aws.amazon.com/datasync/latest/userguide/what-is-datasync.html) / [AWS Storage Gatewayとは](https://docs.aws.amazon.com/storagegateway/latest/userguide/WhatIsStorageGateway.html) / [AWS Snow Familyとは](https://docs.aws.amazon.com/snowball/latest/developer-guide/whatisSnowball.html)
+
+| サービス | 転送方式 | 適したシナリオ |
+|---|---|---|
+| AWS DataSync | ネットワーク経由(専用エージェント使用) | 定期的な同期、移行のワンタイム転送、NFS/SMB/S3/EFS/FSx間のデータ移動 |
+| AWS Storage Gateway | ネットワーク経由(常時稼働のゲートウェイ) | オンプレミスアプリからの継続的なファイル/ブロックアクセス(Task 3.1参照) |
+| AWS Snow Family | 物理デバイスの配送 | 数十TB〜PB級のデータで、ネットワーク帯域では現実的な時間で転送できない場合 |
+
+**セキュアな取り込みアクセスポイントの設計:**
+- 取り込み用のS3バケットへは **VPCエンドポイント(Gateway型/Interface型)** 経由でアクセスし、インターネットを経由させない
+- IAMポリシーとS3バケットポリシーで、取り込み専用のロール/ユーザーに最小権限(PutObjectのみ等)を付与
+- Kinesisへのデータ投入元は、**IAM認証**や**VPCエンドポイント**を使って未認可のクライアントからの投入を防ぐ
+
+出典: [Amazon S3向けVPCエンドポイント](https://docs.aws.amazon.com/AmazonS3/latest/userguide/privatelink-interface-endpoints.html)
+
+---
+
+## 参考文献
+
+### 試験ガイド(公式)
+
+| ソース | URL |
+|---|---|
+| AWS Certified Solutions Architect - Associate (SAA-C03) Exam Guide | https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03.html |
+| Content Domain 3: Design High-Performing Architectures | https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/solutions-architect-associate-03-domain3.html |
+| In-Scope AWS Services | https://docs.aws.amazon.com/aws-certification/latest/solutions-architect-associate-03/saa-03-in-scope-services.html |
+
+### Task 3.1: ストレージ関連
+
+| ソース | URL |
+|---|---|
+| Amazon S3 ユーザーガイド | https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html |
+| Amazon S3 ストレージクラス(公式) | https://aws.amazon.com/s3/storage-classes/ |
+| S3 Intelligent-Tiering | https://aws.amazon.com/s3/storage-classes/intelligent-tiering/ |
+| S3 Glacier ストレージクラス | https://aws.amazon.com/s3/storage-classes/glacier/ |
+| Amazon EBS の概要 | https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html |
+| Amazon EBS ボリュームタイプ | https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html |
+| Amazon EFS とは | https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html |
+| Amazon EFS のパフォーマンス | https://docs.aws.amazon.com/efs/latest/ug/performance.html |
+| Amazon EFS ストレージクラス | https://docs.aws.amazon.com/efs/latest/ug/storage-classes.html |
+| AWS Storage Gateway とは | https://docs.aws.amazon.com/storagegateway/latest/userguide/WhatIsStorageGateway.html |
+
+### Task 3.2: コンピューティング関連
+
+| ソース | URL |
+|---|---|
+| Amazon EC2 の概念 | https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html |
+| AWS Lambda 開発者ガイド | https://docs.aws.amazon.com/lambda/latest/dg/welcome.html |
+| Lambda 関数のメモリ設定 | https://docs.aws.amazon.com/lambda/latest/dg/configuration-memory.html |
+| Lambda の設定に関するトラブルシューティング | https://docs.aws.amazon.com/lambda/latest/dg/troubleshooting-configuration.html |
+| AWS Fargate とは | https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html |
+| Amazon ECS とは | https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html |
+| Amazon EKS とは | https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html |
+| AWS Batch とは | https://docs.aws.amazon.com/batch/latest/userguide/what-is-batch.html |
+| Amazon EMR とは | https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-what-is-emr.html |
+| EC2 Auto Scaling のスケーリングポリシー | https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-simple-step.html |
+| AWS Auto Scaling とは | https://docs.aws.amazon.com/autoscaling/plans/userguide/what-is-aws-auto-scaling.html |
+| Amazon SQS とは | https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html |
+| Amazon SNS とは | https://docs.aws.amazon.com/sns/latest/dg/welcome.html |
+| Amazon EventBridge とは | https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-what-is.html |
+
+### Task 3.3: データベース関連
+
+| ソース | URL |
+|---|---|
+| Amazon RDS とは | https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html |
+| Amazon RDS マルチAZ配置 | https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html |
+| Amazon RDS 読み取りレプリカ | https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html |
+| Amazon Aurora の概要 | https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html |
+| Amazon Aurora のストレージと信頼性 | https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.StorageReliability.html |
+| Aurora Serverless v2 | https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html |
+| Aurora Global Database | https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-global-database.html |
+| Amazon DynamoDB 開発者ガイド | https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html |
+| DynamoDB 読み取り/書き込みキャパシティモード | https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html |
+| DynamoDB Accelerator (DAX) | https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.html |
+| AWS Database Migration Service とは | https://docs.aws.amazon.com/dms/latest/userguide/Welcome.html |
+| AWS Schema Conversion Tool とは | https://docs.aws.amazon.com/SchemaConversionTool/latest/userguide/CHAP_Welcome.html |
+| Amazon ElastiCache for Valkey の発表 | https://aws.amazon.com/about-aws/whats-new/2024/10/amazon-elasticache-valkey |
+| ElastiCache のエンジン選択 | https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/SelectEngine.html |
+| ElastiCache キャッシング戦略 | https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/Strategies.html |
+| Amazon RDS Proxy とは | https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html |
+
+### Task 3.4: ネットワーク関連
+
+| ソース | URL |
+|---|---|
+| VPC とサブネットの設定 | https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html |
+| VPC プライベートサブネット+NATのシナリオ | https://docs.aws.amazon.com/vpc/latest/userguide/vpc-example-private-subnets-nat.html |
+| Elastic Load Balancing の概要 | https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/introduction.html |
+| Application Load Balancer とは | https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html |
+| Network Load Balancer とは | https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html |
+| Amazon CloudFront とは | https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html |
+| AWS Global Accelerator とは | https://docs.aws.amazon.com/global-accelerator/latest/dg/what-is-global-accelerator.html |
+| AWS Site-to-Site VPN とは | https://docs.aws.amazon.com/vpn/latest/s2svpn/VPC_VPN.html |
+| AWS Direct Connect とは | https://docs.aws.amazon.com/directconnect/latest/UserGuide/Welcome.html |
+| AWS PrivateLink とは | https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html |
+
+### Task 3.5: データ分析・取り込み関連
+
+| ソース | URL |
+|---|---|
+| AWS Lake Formation とは | https://docs.aws.amazon.com/lake-formation/latest/dg/what-is-lake-formation.html |
+| AWS Glue とは | https://docs.aws.amazon.com/glue/latest/dg/what-is-glue.html |
+| AWS Glue クローラ | https://docs.aws.amazon.com/glue/latest/dg/add-crawler.html |
+| AWS Glue の ETL プログラミング | https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl.html |
+| Amazon Athena とは | https://docs.aws.amazon.com/athena/latest/ug/what-is.html |
+| Amazon QuickSightからAmazon Quick Suiteへの進化(AWS公式ブログ) | https://aws.amazon.com/blogs/business-intelligence/reimagine-business-intelligence-amazon-quicksight-evolves-to-amazon-quick-suite/ |
+| Amazon Kinesis Data Streams とは | https://docs.aws.amazon.com/streams/latest/dev/introduction.html |
+| Amazon Data Firehose とは | https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html |
+| AWS DataSync とは | https://docs.aws.amazon.com/datasync/latest/userguide/what-is-datasync.html |
+| AWS Snow Family とは | https://docs.aws.amazon.com/snowball/latest/developer-guide/whatisSnowball.html |
+| Amazon S3 向け VPC エンドポイント(PrivateLink) | https://docs.aws.amazon.com/AmazonS3/latest/userguide/privatelink-interface-endpoints.html |
+
+---
+
+*本ガイドは2026年7月時点のAWS公式ドキュメントおよびAWS公式ブログの情報に基づいて作成しています。AWSのサービス仕様・料金・名称は変更される可能性があるため、実際の試験対策・設計判断の際は必ず最新の公式ドキュメントを参照してください。*
diff --git a/AWS-Certified-Solutions-Architect-Associate.md b/archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate.md
similarity index 100%
rename from AWS-Certified-Solutions-Architect-Associate.md
rename to archive/Aws/SAA/md/AWS-Certified-Solutions-Architect-Associate.md
diff --git a/Gcp-app-dev-environment-complete-guide.html b/archive/Gcl_Archive/Associate-Cloud-Engineer/html/Gcp-app-dev-environment-complete-guide.html
similarity index 100%
rename from Gcp-app-dev-environment-complete-guide.html
rename to archive/Gcl_Archive/Associate-Cloud-Engineer/html/Gcp-app-dev-environment-complete-guide.html
diff --git a/Gcp-security-fundamentals-guide.html b/archive/Gcl_Archive/Hands-on/html/Gcp-security-fundamentals-guide.html
similarity index 100%
rename from Gcp-security-fundamentals-guide.html
rename to archive/Gcl_Archive/Hands-on/html/Gcp-security-fundamentals-guide.html
diff --git a/cli.html b/cli.html
index 1ee65ae45..ef2612eff 100644
--- a/cli.html
+++ b/cli.html
@@ -594,7 +594,7 @@ 2.2 実践ワンライナー表
拡張子ごとのファイル数を集計する
find . -type f -exec basename {} + | sed -n 's/.*\.\([^.]*\)$/\1/p' | sort | uniq -c |
+ >find . -type f -exec basename {} \; | sed -n 's/.*\.\([^.]*\)$/\1/p' | sort | uniq -c |
sort -rn
diff --git a/cli.md b/cli.md
index dba173914..ef6d3cadc 100644
--- a/cli.md
+++ b/cli.md
@@ -101,7 +101,7 @@ find [検索開始パス] [条件] [アクション]
| 直近60分以内に更新されたファイルを探す | `find . -mmin -60 -type f` | `-mmin -60` は「60分以内に更新」を意味する |
| 7日以上前の `.log` ファイルを確認する | `find /var/log -name "*.log" -mtime +6` | `-mtime +6` は「更新から7日以上経過」。まずは確認のみで削除しない |
| 空のディレクトリを一括削除する | `find . -type d -empty -delete` | `-delete` は該当した空ディレクトリのみ削除 |
-| 拡張子ごとのファイル数を集計する | `find . -type f -exec basename {} + | sed -n 's/.*\.\([^.]*\)$/\1/p' | sort | uniq -c | sort -rn` | `find` 結果を `basename` で変換後、拡張子のみ抽出して集計するパイプライン |
+| 拡張子ごとのファイル数を集計する | `find . -type f -exec basename {} \; \| sed -n 's/.*\.\([^.]*\)$/\1/p' \| sort \| uniq -c \| sort -rn` | `find` 結果を `basename` で変換後、拡張子のみ抽出して集計するパイプライン |
| 特定文字列を含むファイル一覧を安全に取得する | `find . -name "*.js" -print0 \| xargs -0 grep -l "TODO"` | `-print0` と `xargs -0` の組み合わせでファイル名の空白・改行に対応 |
| パーミッションが777のファイルを検出する | `find . -type f -perm 0777` | 権限が緩すぎるファイルの棚卸しに使う |
| 一時ファイルを確認しながら削除する | `find . -name "*.tmp" -print0 \| xargs -0 -n 1 -p rm` | `-p` で1件ずつ実行確認が出るため学習中も安全 |
diff --git a/components/MermaidDiagram.module.css b/components/MermaidDiagram.module.css
index 62b8d2c3d..9f2afcb47 100644
--- a/components/MermaidDiagram.module.css
+++ b/components/MermaidDiagram.module.css
@@ -4,7 +4,9 @@
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
- overflow-x: auto;
+ /* スクロールコンテナは外側の .diagram-wrap が担う。
+ ここで overflow:auto にすると二重スクロールになりSVGが縮小される。 */
+ overflow: visible;
}
.mermaidTarget {
diff --git a/lib/featureFlags.ts b/lib/featureFlags.ts
new file mode 100644
index 000000000..b970ab29c
--- /dev/null
+++ b/lib/featureFlags.ts
@@ -0,0 +1,5 @@
+/**
+ * Hands-on guides are available by default for local development and builds.
+ * Netlify explicitly sets this variable to "false" in netlify.toml.
+ */
+export const HANDS_ON_ENABLED = process.env.NEXT_PUBLIC_ENABLE_HANDS_ON !== 'false';
diff --git a/netlify.toml b/netlify.toml
index 2c3809fca..5e47e2409 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -1,5 +1,5 @@
[build]
- command = "bun run build"
+ command = "NEXT_PUBLIC_ENABLE_HANDS_ON=false bun run build"
publish = ".next"
[build.environment]