フルスタックアプリを EKS に配備する
6部キャップストーン — 本書の最後の章です。React の Next.js (App Router + RSC + Server Actions) アプリと、モダンPython の FastAPI (SQLAlchemy 2.x + Pydantic v2) アプリを、同じ TODO ドメインの上で1つの EKS クラスタに一緒に配備します。Terraform + Karpenter + IRSA + ALB Controller + ExternalDNS + cert-manager のクラスタセットアップから、RDS + External Secrets + RDS IAM auth の DB 連携、Helm + ArgoCD ApplicationSet の環境別配備、Prometheus + Grafana + Loki + OpenTelemetry の可観測性、HPA + Karpenter のオートスケーリング、k6 負荷テスト + OpenCost コスト推定、そして第26章 + 第30章の運用サイクルの適用までを13本の PR で一連の流れとして扱います。第1章〜第30章のすべての道具が1つのシステムの中でどう噛み合うのかの視野が、本キャップストーンで手に入ります。
本書の最後の章です。6部キャップストーンは、第1章〜第30章のすべての道具が1つのシステムの中でどう噛み合うのかを1つのプロジェクトとして束ねる総合実習です。架空の会社ではなく、本シリーズの他の2冊の本の成果物をそのまま入力として持ち込みます — React 6部の Next.js TODO アプリと モダンPython 4部の FastAPI TODO バックエンドが同じドメインの上で動作しています。本章ではその2つを1つの EKS クラスタの上に一緒に配備して、Kubernetes トラックのすべての要素を1つのシステムの中で再び確認します。
本章の目標は次のとおりです。
https://todo.example.comに Next.js が、https://api.todo.example.comに FastAPI が立ち上がっている状態- RDS PostgreSQL がバックアップ · Multi-AZ · External Secrets と連携された状態
- GitHub push → ECR → ArgoCD ApplicationSet 自動同期の一連の流れ
- Prometheus + Grafana + Loki + OpenTelemetry の可観測性スタックが2つのアプリを同じ方向で観測する状態
- HPA + Karpenter がトラフィックの変動に自動反応する状態
- 月に約 $80 ~ $120 の運用コストの仮説が OpenCost で検証された状態
進行は 13本の PR 単位です。各 PR が次の PR の入力になる累積構造で、1つの PR の変更量は意図的に小さく置いてレビュー可能な大きさに維持します。
目標のアーキテクチャ #
[Browser]
|
| HTTPS (Route 53 + ACM)
v
[ALB] -- AWS Load Balancer Controller
|
|-- / -> [Next.js Pod x N] (SSR + RSC + Server Actions)
`-- /api/* -> [FastAPI Pod x M] (REST + Pydantic v2)
|
| PgBouncer
v
[RDS PostgreSQL] (Multi-AZ)
^
|
[External Secrets] <- [AWS Secrets Manager]
^
| IRSA
[ServiceAccount]この図が本章の13 PR が到達する最終形態です。図の各矢印が本書の1章以上で解かれたつながりです — 本章はそれらを1つのシステムとして束ねる段階です。
PR #1 — ドメインとアーキテクチャの決定 #
最初の PR はコードなしの ADR (Architecture Decision Record) 1枚です。
# ADR-0001: フルスタック todo システムの K8s 配備アーキテクチャ
## コンテキスト
Next.js (App Router + RSC) + FastAPI + PostgreSQL の todo システムを
運用環境に配備しなければならない。
## オプション
1. ECS Fargate (マネージドコンテナ)
2. EKS (Kubernetes)
3. Lambda + RDS (サーバーレス)
## 決定
EKS を採用。
## 根拠
- 2つのアプリ (Next.js + FastAPI) の役割が異なり、ライフサイクルの隔離が必要
- HPA · Karpenter のオートスケーリングのモデルがトラフィックパターンに合う
- GitOps (ArgoCD) の運用標準モデルを活用
- 本書の 1 ~ 30 章のすべての道具の総合検証
## 結果
- 月 $80 ~ $120 のコスト仮説 (28章のモデルで検証予定)
- 運用カレンダー (26章) の定期サイクルを適用
- AWS 本の ECS Fargate 章と比較学習が可能AWS の同じキャップストーンが ECS Fargate のルートを扱うので、2冊の本を比べると 「K8s vs マネージドコンテナ」の運用上の違い が明確に見えます。本章は K8s を選んだあとの流れを本格的に扱います。
PR #2 — EKS クラスタの新規セットアップ #
21章 EKS クラスタセットアップ の Terraform マニフェストが入力です。本キャップストーンでは1つだけ前提を変えます — Karpenter を最初から導入 します。
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
# ... 21章のまま
}
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "todo-${var.env}"
cluster_version = "1.32"
enable_irsa = true
cluster_addons = {
coredns = { most_recent = true }
kube-proxy = { most_recent = true }
vpc-cni = { most_recent = true }
aws-ebs-csi-driver = {
most_recent = true
service_account_role_arn = module.ebs_csi_irsa.iam_role_arn
}
}
# 最小ノードのみ ON_DEMAND で維持、残りは Karpenter がその場で
eks_managed_node_groups = {
system = {
desired_size = 2
min_size = 2
max_size = 3
instance_types = ["t3.medium"]
capacity_type = "ON_DEMAND"
labels = { role = "system" }
taints = [{
key = "system"
value = "true"
effect = "NO_SCHEDULE"
}]
}
}
}
module "karpenter" {
source = "terraform-aws-modules/eks/aws//modules/karpenter"
cluster_name = module.eks.cluster_name
irsa_oidc_provider_arn = module.eks.oidc_provider_arn
}system ノードグループは Karpenter · CoreDNS · モニタリングスタックのようなシステムコンポーネントだけを置き、アプリケーション (Next.js / FastAPI) ワークロードは Karpenter が立ち上げるノードへ流すパターンです。13章 オートスケーリング の Karpenter モデル + 28章 コスト最適化 §「Karpenter — Cluster Autoscaler との決定ツリー」を組み合わせた形です。
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot", "on-demand"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["t", "m", "c"]
- key: karpenter.k8s.aws/instance-cpu
operator: In
values: ["2", "4", "8"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 30s
budgets:
- nodes: "10%"
duration: 10m
schedule: "0 9 * * mon-fri"disruption.budgets が 30章 アップグレード戦略 の blast radius の要点です — 平日の業務時間に一度に 10 % 以下のノードのみ置き換えます。
補助コンポーネントのセット #
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system --set clusterName=todo-prod --set serviceAccount.create=false
helm install external-dns external-dns/external-dns \
-n external-dns --create-namespace \
--set provider=aws --set "domainFilters[0]=todo.example.com"
helm install cert-manager jetstack/cert-manager \
-n cert-manager --create-namespace --set installCRDs=true22章 アプリ配備の骨格 §「cert-manager と external-dns」で挙げたセットアップそのままです。
PR #3 — ネームスペース / RBAC / NetworkPolicy の骨格 #
ワークロードを立ち上げる前に隔離の骨格を定めておきます。
---
apiVersion: v1
kind: Namespace
metadata:
name: todo-frontend
labels:
team: web
env: prod
role: frontend
---
apiVersion: v1
kind: Namespace
metadata:
name: todo-backend
labels:
team: backend
env: prod
role: backend
---
apiVersion: v1
kind: Namespace
metadata:
name: todo-data
labels:
team: backend
env: prod
role: data3つのネームスペースの分離 — frontend / backend / data — が本キャップストーンの隔離の単位です。7章 Namespace とラベル のラベル標準 (team / env / role) が 25章 モニタリング · アラート のグルーピングキーであり、28章 のコスト配分のキーとして活用されます。
NetworkPolicy — 隔離の本格 #
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: todo-backend-ingress
namespace: todo-backend
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: todo-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
role: frontend
- namespaceSelector:
matchLabels:
role: backend # 同じ backend の他のワークロードも許可
ports:
- port: 800014章 RBAC / NetworkPolicy / ResourceQuota の NetworkPolicy モデルが本格的な隔離へつながります。frontend が直接 RDS へ行けず、必ず backend を経由する 強制された流れです。
ResourceQuota — チーム別の上限 #
apiVersion: v1
kind: ResourceQuota
metadata:
name: todo-backend-quota
namespace: todo-backend
spec:
hard:
requests.cpu: "10"
requests.memory: "20Gi"
limits.cpu: "20"
limits.memory: "40Gi"
persistentvolumeclaims: "5"14章 の ResourceQuota がマルチチーム環境のコスト隔離の最初の保護線です。
PR #4 — PostgreSQL RDS + External Secrets #
23章 DB 連携 の Terraform マニフェストがほぼそのまま入ってきます。違いは dev で Aurora Serverless v2 をオプションとして置く点です。
module "rds" {
source = "terraform-aws-modules/rds/aws"
version = "~> 6.0"
identifier = "todo-${var.env}"
engine = "postgres"
engine_version = "16.3"
major_engine_version = "16"
instance_class = var.env == "prod" ? "db.t4g.small" : "db.t4g.micro"
allocated_storage = 20
manage_master_user_password = true
multi_az = var.env == "prod"
backup_retention_period = var.env == "prod" ? 30 : 7
performance_insights_enabled = true
deletion_protection = var.env == "prod"
}コスト仮説を小さく置くためにインスタンスクラスを db.t4g.small と置きます — 23 章の db.m6g.large より小さいオプションです。todo ドメインの負荷が小さいので十分です。
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: todo-api-db
namespace: todo-backend
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: todo-api-db
template:
data:
DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@pgbouncer.todo-backend.svc:5432/todo?sslmode=disable"
data:
- secretKey: username
remoteRef:
key: rds!cluster-todo-prod
property: username
- secretKey: password
remoteRef:
key: rds!cluster-todo-prod
property: password23章 のマニフェストそのままであり、29章 シークレット運用 §「パスワード0」の RDS IAM auth は本キャップストーンではオプションとして置きます — todo のトラフィックが小さいので PgBouncer + パスワードモデルで十分です。
PR #5 — FastAPI バックエンドの配備 #
モダンPython 4部キャップストーンの FastAPI todo バックエンドが入力です。(modern-python は旧 Python 講座との差別の意味を活かして「モダン」の接頭辞を維持します。) コンテナ化は本書の範囲外ですが、Dockerfile の核心を挙げておきます。
FROM python:3.13-slim AS builder
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen --no-dev
FROM python:3.13-slim AS runtime
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY src/ src/
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]Deployment #
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-api
namespace: todo-backend
labels:
app.kubernetes.io/name: todo-api
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: todo-api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: todo-api
spec:
serviceAccountName: todo-api
containers:
- name: api
image: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/todo-api:1.0.0
ports:
- containerPort: 8000
name: http
envFrom:
- secretRef:
name: todo-api-db
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: http
initialDelaySeconds: 30
periodSeconds: 10
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
terminationGracePeriodSeconds: 6022章 アプリ配備の骨格 の標準マニフェストに 30章 の graceful shutdown の要素 (preStop + terminationGracePeriodSeconds) まで組み合わせた形です。
ServiceAccount + IRSA #
apiVersion: v1
kind: ServiceAccount
metadata:
name: todo-api
namespace: todo-backend
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/todo-prod-api
automountServiceAccountToken: false # 16章のセキュリティの要点16章 RBAC / ServiceAccount 深掘り の IRSA + 29章 シークレット運用 §「automountServiceAccountToken: false」のセキュリティの要点が1つのマニフェストの中にあります。
PR #6 — Next.js フロントの配備 #
React 6部キャップストーンの Next.js TODO アプリが入力です。App Router + RSC + Server Actions のモデルが K8s の中では次のように動作します。
[Browser]
|
| HTTPS
v
[ALB]
|
v
[Next.js Pod] -- Node.js サーバー (next start)
|
| RSC レンダリング時 fetch
v
[todo-api Service] -- ClusterIP, FastAPI を指す
|
v
[todo-api Pod]Server Actions は Next.js Pod の中でそのまま実行されます。外部 API 呼び出しが必要な場合は同じクラスタの中の todo-api Service へ呼びます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-web
namespace: todo-frontend
spec:
replicas: 2
template:
spec:
serviceAccountName: todo-web
containers:
- name: web
image: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/todo-web:1.0.0
ports:
- containerPort: 3000
name: http
env:
- name: TODO_API_URL
value: "http://todo-api.todo-backend.svc.cluster.local:80"
- name: NODE_ENV
value: "production"
resources:
requests:
cpu: 200m
memory: 256Mi # SSR + RSC のメモリ仮説
limits:
cpu: 1
memory: 512MiNext.js Pod のメモリ仮説は 11章 リソース要求と上限 の測定結果で定めます — SSR + RSC の1リクエスト当たりのメモリ占有が一定程度累積されるので、requests を 256 Mi と置くのが保守的な出発点です。28章 コスト最適化 の VPA recommendation で1か月後に適正値へ収束させます。
PR #7 — Ingress + ALB #
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: todo
namespace: todo-frontend
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
alb.ingress.kubernetes.io/ssl-redirect: '443'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:...
alb.ingress.kubernetes.io/group.name: todo
external-dns.alpha.kubernetes.io/hostname: "todo.example.com,api.todo.example.com"
spec:
rules:
- host: todo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: todo-web
port:
number: 80
- host: api.todo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: todo-api.todo-backend
port:
number: 80alb.ingress.kubernetes.io/group.name: todo が決定的 — 2つのホストが 同じ ALB 1台を共有 します。28章 コスト最適化 §「ALB の LCU」で挙げたコスト削減パターンが本節で直接適用されます。
external-dns が2つのホストの A レコードを Route 53 に自動登録し、ACM 証明書はワイルドカード (*.todo.example.com) で1枚あれば十分です。22章 の Ingress マニフェストがマルチホストパターンへ拡張された形です。
PR #8 — Helm チャートで束ねる #
ここまで書いたマニフェストを Helm チャート2つで束ねます。
charts/
├── todo-web/
│ ├── Chart.yaml
│ ├── values.yaml
│ ├── values-dev.yaml
│ ├── values-prod.yaml
│ └── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── hpa.yaml
│ └── pdb.yaml
├── todo-api/
│ ├── Chart.yaml
│ ├── values.yaml
│ ├── values-dev.yaml
│ ├── values-prod.yaml
│ └── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ ├── externalsecret.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ └── servicemonitor.yaml
└── todo-infra/
├── Chart.yaml
└── templates/
├── namespaces.yaml
├── networkpolicy.yaml
├── resourcequota.yaml
└── ingress.yaml3つのチャートの分離が核心です。
todo-infra— ネームスペース · NetworkPolicy · ResourceQuota · Ingress。2つのアプリが共有するインフラ。todo-api— backend のすべてのマニフェスト。todo-web— frontend のすべてのマニフェスト。
22章 アプリ配備の骨格 §「Helm チャートで束ねる」のパターンがマルチアプリ環境でどう分かれるかの本格的な適用です。Chart.yaml の dependencies で束ねるオプションもありますが、シンプルさのために本キャップストーンはフラットな構造に置いて ArgoCD ApplicationSet で統合します。
PR #9 — GitOps: ArgoCD ApplicationSet #
20章 GitOps + 24章 CI / CD パイプライン のモデルが ApplicationSet 1つのマニフェストに整理されます。
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: todo
namespace: argocd
spec:
generators:
- matrix:
generators:
- list:
elements:
- app: todo-infra
- app: todo-api
- app: todo-web
- list:
elements:
- env: dev
cluster: https://kubernetes.default.svc
- env: prod
cluster: https://kubernetes.default.svc
template:
metadata:
name: '{{`{{.app}}`}}-{{`{{.env}}`}}'
spec:
project: todo
source:
repoURL: https://github.com/myorg/todo-manifests.git
targetRevision: main
path: charts/{{`{{.app}}`}}
helm:
valueFiles:
- values.yaml
- values-{{`{{.env}}`}}.yaml
destination:
server: '{{`{{.cluster}}`}}'
namespace: todo-{{`{{.app}}`}}
syncPolicy:
automated:
prune: true
selfHeal: truematrix generator が 3 アプリ × 2 環境 = 6 個の Application を1つのマニフェストで自動生成します。dev は自動 sync、prod は ApplicationSet の別のインスタンスで手動 sync モードへ分岐するのが運用の標準ですが、本キャップストーンは単純化のために両方とも automated に置きます。
24章 の GitHub Actions OIDC + ECR push + マニフェスト repo 自動 commit サイクルが本マニフェストの入力です — コード push 一度で dev / prod の両方の環境が自動 sync されます。
PR #10 — 可観測性 #
19章 可観測性 + 25章 モニタリング · アラート の kube-prometheus-stack がそのまま入ってきます。違いは OpenTelemetry Collector を追加して2つのアプリのトレースを束ねることです。
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel
namespace: monitoring
spec:
mode: daemonset
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
prometheus:
endpoint: 0.0.0.0:8889
otlp/tempo:
endpoint: tempo.monitoring.svc:4317
service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
exporters: [prometheus]Next.js の OpenTelemetry SDK と FastAPI の OTel instrumentation が同じ endpoint へトレースを送ると、2つのアプリをまたぐ1リクエストの全体経路 が Tempo で見えます。RSC レンダリング時の fetch 呼び出しが FastAPI のどのハンドラを経由して RDS まで到達したかが1つのトレース画面で追跡されます。
ServiceMonitor + PrometheusRule #
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: todo-api
namespace: todo-backend
labels:
release: prometheus
spec:
groups:
- name: todo-api.golden-signals
rules:
- alert: TodoApiHighErrorRate
expr: |
sum(rate(http_requests_total{app="todo-api",status=~"5.."}[5m]))
/ sum(rate(http_requests_total{app="todo-api"}[5m])) > 0.05
for: 5m
labels:
severity: critical
# ... latency, traffic, saturation も同様25章 のマニフェストそのままであり、同じルールが todo-web にも適用されます。アラートの severity ルーティングは 25 章の Alertmanager マニフェストがそのまま入ってきます。
PR #11 — オートスケーリング #
13章 オートスケーリング の HPA と 28章 の Karpenter NodePool が連携して2段階の自動反応を作ります。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: todo-api
namespace: todo-backend
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: todo-api
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
behavior:
scaleUp:
stabilizationWindowSeconds: 30
scaleDown:
stabilizationWindowSeconds: 300トラフィック増加
|
v
HPA: 30 秒で todo-api Pod 2 -> 5 -> 10 -> 20
|
| ノードのリソース不足
v
Karpenter: 30 秒 ~ 1 分で新しいノード (spot 優先) プロビジョニング
|
v
Pending だった Pod が新しいノードにスケジューリングされるこの2段階が一連の流れで回る形が K8s オートスケーリングの目標です。負荷テストでその形を次の PR で測定します。
PR #12 — 負荷テストとコスト推定 #
import http from "k6/http";
import { check } from "k6";
export const options = {
stages: [
{ duration: "2m", target: 50 },
{ duration: "5m", target: 200 },
{ duration: "2m", target: 500 },
{ duration: "5m", target: 500 },
{ duration: "2m", target: 0 },
],
};
export default function () {
const res = http.get("https://todo.example.com/api/todos");
check(res, {
"status is 200": (r) => r.status === 200,
"duration < 500ms": (r) => r.timings.duration < 500,
});
}k6 run k6/script.js測定する内容です。
- HPA scale-up の応答時間 — トラフィック 50 → 200 の間で Pod が何秒で増えるか
- Karpenter のノード追加時間 — Pod が Pending にとどまった時間
- P95 latency — 負荷のピーク (500 VUs) で latency がどう変わるか
- 5xx 比率 — 負荷中にエラー率が 25 章の閾値 (5 %) を超えるか
コスト検証 #
helm install opencost opencost/opencost \
-n opencost --create-namespaceOpenCost の出力で1か月の仮説コストを検証します。
| 項目 | 予想 (月) |
|---|---|
| EKS コントロールプレーン | $73 |
| ノード (system t3.medium × 2 ON_DEMAND) | $60 |
| ノード (アプリケーション spot 平均 1.5 台) | $20 |
| RDS db.t4g.small Multi-AZ | $30 |
| ALB (1 台, LCU) | $20 |
| NAT Gateway + データ転送 | $35 |
| ECR / Route 53 / その他 | $10 |
| 合計 | 約 $248 |
28章 §「請求書 review チェックリスト」の各項目を本実測値と比較します。prod の目標が本書の標準ガイド ($200 ~ $300) の中に収まったか が検証指標です。
学習用環境では次の調整で月 $40 ~ $80 まで減らせます。
- prod の Multi-AZ RDS を dev の単一 AZ へ
- ALB 1台 (すでに共有)
- system ノードグループも spot へ
- NAT Gateway を Single NAT へ
PR #13 — 運用チェックリストの適用 #
最後の PR は 26章 運用チェックリスト の定期カレンダーと 30章 アップグレード戦略 のアップグレードチェックリストを本システムに適用します。
# todo システム運用カレンダー
## 毎日
- Grafana の todo ダッシュボードの5パネル点検
- Alertmanager のアクティブアラートのレビュー
## 毎週
- ECR Trivy スキャン結果 (todo-api, todo-web 両方)
- 新規セキュリティパッチのレビュー
## 毎月
- OpenCost のチーム / ワークロード別コスト 1, 2, 3 位
- VPA recommendation の未反映ワークロード
- ArgoCD の OutOfSync 状態の点検
## 四半期
- EKS マイナーアップグレード (30章の13ステップ)
- RDS Performance Insights の right-sizing 信号
- RBAC audit
- 復旧訓練 (PITR シミュレーション)
- kube-bench CIS 点検
## 半期
- 外部セキュリティ監査
- DR シミュレーション (Velero restore)
## 年
- クラスタアーキテクチャのレビュー
- マニフェストの現代化このマニフェストが git に入るのが本キャップストーンの最後の PR です。コードだけでなく運用手順も git の単一ソースに 置くのが GitOps の本質的な目標です。
事後の振り返り — 30章がどう束ねられたか #
13 PR を経て、本書の章が1つのシステムの中でどう噛み合ったかを整理します。
| 本書の章 | 本キャップストーンでの役割 |
|---|---|
| 1 ~ 3章 | マニフェストの一行を読む視野 |
| 4章 Deployment | todo-api / todo-web の RollingUpdate 戦略 |
| 5章 Service | todo-api ↔ todo-web の cluster DNS 接続 |
| 6章 ConfigMap · Secret | 環境変数注入の標準 |
| 7章 Namespace とラベル | frontend / backend / data 分離 |
| 9章 PV / PVC / StorageClass | EBS CSI Driver (直接 PV は使わない — RDS) |
| 10章 Ingress | ALB 1台 + group.name で2つのホスト |
| 11章 リソース要求と上限 | Next.js 256 Mi · FastAPI 128 Mi の出発点 |
| 12章 ヘルスチェック | 3種 probe + graceful shutdown |
| 13章 オートスケーリング | HPA + Karpenter の2段階の自動反応 |
| 14章 RBAC / NetworkPolicy / Quota | ネームスペース隔離 + チーム別の上限 |
| 15章 CNI 深掘り | VPC CNI が Pod に直接 IP を付与 (背景) |
| 16章 IRSA | todo-api の AWS 認証情報 |
| 17章 Admission Controller | Kyverno ポリシー (選択) |
| 18章 CRD と Operator | ESO, Karpenter, ALB Controller, OTel |
| 19章 可観測性 | OpenTelemetry + Tempo のトレース |
| 20章 GitOps | ArgoCD ApplicationSet の1つのマニフェスト |
| 21章 EKS セットアップ | Terraform の出発点 |
| 22章 アプリ配備の骨格 | todo-api / todo-web の標準9つの束 |
| 23章 DB 連携 | RDS + ESO + PgBouncer |
| 24章 CI / CD パイプライン | GitHub Actions OIDC → ECR → ApplicationSet |
| 25章 モニタリング · アラート | PrometheusRule + Alertmanager ルーティング |
| 26章 運用チェックリスト | 毎日 / 毎週 / 毎月 / 四半期 / 半期 / 年 |
| 27章 kubectl デバッグ | 事故時の5分の標準フロー |
| 28章 コスト最適化 | OpenCost + Karpenter spot + ALB 共有 |
| 29章 シークレット運用 | ESO + automountServiceAccountToken |
| 30章 アップグレード戦略 | preStop · PDB · Karpenter disruption budgets |
この表が本キャップストーンの一行まとめです — 30章が1つのシステムの中でそれぞれの位置に収まる形 が K8s トラックの目標です。
AWS 本との比較 #
AWS 本(公開予定)の6部キャップストーンが同じ todo システムを ECS Fargate のルートで扱います。2冊の本を比較学習すると、同じドメインを2つのプラットフォームで実装したときの運用の違いが明確に見えます。
| 観点 | 本書 (EKS) | AWS (ECS Fargate) |
|---|---|---|
| 出発点のコスト | 月 $200 ~ $300 | 月 $80 ~ $150 |
| 運用の表面 | K8s の豊富さ + 学習曲線 | AWS コンソール + 少ないオブジェクト |
| 自動化道具 | Karpenter, HPA, ArgoCD | Service Auto Scaling, CodePipeline |
| 可観測性 | Prometheus + Grafana | CloudWatch Container Insights |
| マルチクラウドの可能性 | 可能 (K8s 標準) | AWS 依存 |
| チームの学習コスト | 大きい | 小さい |
小さなチーム + 単一ドメインなら ECS Fargate のほうが効率的で、マルチドメイン + GitOps + 豊富なワークロードパターン + マルチクラウドオプションが必要なら EKS が適しています。本キャップストーンの決定 (EKS) は学習価値と本書の30章の総合検証の結果 です。
片付け — クラスタの削除 #
学習用クラスタはキャップストーンが終わった後すぐに片付けるのがコスト面の標準です。
# 1. ArgoCD Application 削除 (ワークロード片付け)
kubectl delete applicationset todo -n argocd
# 2. RDS deletion_protection 解除後 terraform destroy
# (prod の場合 deletion_protection がオンなので terraform 変数で false 後 apply)
# 3. ALB / Route 53 の自動片付け確認
# external-dns が hostname の A レコードを自動削除
# 4. terraform destroy
terraform destroy
# 5. ECR repository 削除 (イメージの残り)
aws ecr delete-repository --repository-name todo-api --force
aws ecr delete-repository --repository-name todo-web --forceこの順序が安全な片付けの標準です — Application から片付けないと Terraform が ALB 依存性に阻まれて destroy が失敗します。
練習問題 #
- 本キャップストーンの13 PR を実際に自分の GitHub 組織に適用してみて、最後の負荷テストの結果を OpenCost のコスト出力とともに1ページに整理します。予想コスト仮説 ($248 程度) と実測値の格差がどこで発生したか (特に NAT データ転送 · ALB LCU · spot 比率) を 28章 コスト最適化 の §「請求書 review チェックリスト」と照合します。
- 本キャップストーンの ApplicationSet マニフェストを分岐して、dev と prod の sync ポリシーが異なる動作をするよう修正します (dev は automated + selfHeal、prod は手動 sync)。わざと dev のマニフェストに壊れた値 (例: 存在しないイメージタグ) を適用して selfHeal がどう保護するか、prod の手動 sync がどう人のゲートとして作動するかを一段落で比較します。
- AWS 本の同じ todo システムの ECS Fargate キャップストーンをたどった後、2つの実装の運用上の違いを自分のシナリオに照らして一表に比較します。どの時点でどのプラットフォームが適しているか の決定ツリーを自分のドメイン (トラフィックパターン · チーム規模 · クラウド依存の許容度) に合わせて1ページに整理します。
一行まとめ: 6部キャップストーンは modern-react の Next.js と modern-python の FastAPI を同じ EKS クラスタに13個の PR で一緒に配備する。Terraform + Karpenter + IRSA + ALB Controller + ExternalDNS + cert-manager のクラスタセットアップ、frontend / backend / data のネームスペース分離 + NetworkPolicy + ResourceQuota、RDS + External Secrets + PgBouncer の DB 連携、Helm チャート3つ (infra + api + web)、ArgoCD ApplicationSet の matrix generator が 3 アプリ × 2 環境の 6 Application を1つのマニフェストで自動生成、OpenTelemetry が2つのアプリをまたぐ1つのトレースを作り、HPA + Karpenter が2段階の自動反応を作り、k6 + OpenCost が1か月約 $248 のコスト仮説を検証し、最後の PR が毎日 / 毎週 / 毎月 / 四半期 / 半期 / 年 の運用カレンダーを git に置く流れ。1 ~ 30章の30種の道具が1つのシステムの中で1つの位置に収まる形が K8s トラックの目標。小さなチーム + 単一ドメインなら AWS の ECS Fargate のほうが効率的なことがあり、マルチドメイン + GitOps + マルチクラウドオプションが必要なら EKS が適している。
本の終わり — 次のステップ #
本キャップストーンで、本書の30章が1つのシステムの中でどう噛み合うのかの視野が完成しました。しかし本書は K8s のゴールではありません — 出発点です。次のトラックへ進める主題を挙げておきます。
- Service Mesh — Istio · Linkerd。mTLS · 細かいトラフィックルーティング · observability mesh。
- MLOps on K8s — Kubeflow · KServe · Argo Workflows。ML モデルの学習 · 配備 · サービングの専用スタック。
- マルチクラスタ — 単一クラスタの限界を越えるパターン。クラスタフェデレーション · マルチ region · ArgoCD ApplicationSet のマルチクラスタモード。
- eBPF 深掘り — Cilium のその先。セキュリティ / 可観測性 / ネットワーキングの次世代。
- eks-anywhere / on-prem K8s — マネージドを離れたクラスタ運用の要点。
これらの主題は別の本の領域であり、本書の30章がその出発点に立つ視野を作ってくれます。
最後に 付録A — docker-compose から K8s へ が入門読者のための移行ガイドとして本を閉じます。本書をすべてたどってきた読者には付録ですが、Docker / docker-compose まで来てみて本書を初めて開いた読者には出発点になります。