総合演習:OrderFlowプロジェクトデプロイ
デプロイはラストマイルです。CI/CD自動化, K8sオーケストレーション, 監視アラートが, アプリケーションを開発から本番までシームレスに運びます。
1. 学ぶこと
- CI/CDパイプライン:GitHub Actionsでイメージビルド → レジストリにプッシュ → K8sローリングデプロイ
- K8s本番デプロイ:Deployment + HPAオートスケーリング + Ingressゲートウェイ
- オブザーバビリティデプロイ:Prometheus + Grafana + AlertManagerアラートルール
- 本番セキュリティ強化:Secret管理 / NetworkPolicy / Pod Security Standards
- BobはOrderFlowを本番環境にデプロイし, 1日数百万件の注文を処理するビジネス運用を支えました
2. DevOpsエンジニアの実話
(1) ペインポイント: 手動デプロイは地雷を踏むようなもの
BobはOrderFlowを手動で本番にデプロイしていました:ローカルでmvn package → docker build → イメージプッシュ → kubectl apply → ヘルスステータス確認。全体で30分かかり, ミスが頻発:設定の更新忘れ, 間違ったイメージバージョンのプッシュ, ローリングアップデート時の旧Podのグレースフルシャットダウン失敗による502エラー。
(2) CI/CDソリューション
自動化生産ラインのワンストップサービス:
graph LR
A["Git Push"] --> B["GitHub Actions Build + Test"]
B --> C["Docker Build + Push to Registry"]
C --> D["K8s Rolling Deploy"]
D --> E["Health Check + Smoke Test"]
E --> F["Monitor Grafana"]
(3) 成果
BobがCI/CDを構築した後, コードプッシュが自動デプロイをトリガーし, コードから本番まで5分で完了するようになりました。HPAが自動スケーリング, Prometheusが自動アラートを担当し, Bobは「消防士」から「監視オブザーバー」へと変わりました。
3. CI/CDパイプライン
(1) ▶ サンプル:GitHub Actionsワークフロー
# .github/workflows/deploy.yml
name: OrderFlow CI/CD
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: registry.example.com
IMAGE_NAME: orderflow-service
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Run tests
run: mvn verify -B
- name: Build Docker image
run: docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
- name: Login to Registry
run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin
- name: Push image
run: docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/orderflow \
orderflow=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n orderflow
kubectl rollout status deployment/orderflow -n orderflow --timeout=300s
- name: Smoke test
run: |
sleep 10
curl -sf https://api.orderflow.example.com/actuator/health | grep '"status":"UP"'
出力:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
| パイプラインステージ | 操作 | 失敗時の処理 |
|---|---|---|
| Build + Test | mvn verify |
デプロイをブロック |
| Docker Build | マルチステージイメージビルド | デプロイをブロック |
| Push Registry | イメージレジストリにプッシュ | 3回リトライ |
| K8s Deploy | kubectl set image + rollout |
自動ロールバック |
| Smoke Test | ヘルスチェック検証 | ロールバック + 通知 |
4. K8s本番デプロイ
(1) ▶ サンプル:本番グレードDeployment + HPA
apiVersion: apps/v1
kind: Deployment
metadata:
name: orderflow
namespace: orderflow
spec:
replicas: 3
selector:
matchLabels: { app: orderflow }
strategy:
rollingUpdate: { maxUnavailable: 0, maxSurge: 1 }
template:
metadata:
labels: { app: orderflow }
spec:
terminationGracePeriodSeconds: 60
containers:
- name: orderflow
image: registry.example.com/orderflow-service:latest
ports:
- { containerPort: 8080 }
- { containerPort: 8081 }
envFrom:
- configMapRef: { name: orderflow-config }
- secretRef: { name: orderflow-secrets }
resources:
requests: { memory: "512Mi", cpu: "250m" }
limits: { memory: "1Gi", cpu: "1000m" }
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: 8080 }
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: orderflow-hpa
namespace: orderflow
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: orderflow
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
出力:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
| K8s本番設定 | 値 | 理由 |
|---|---|---|
maxUnavailable: 0 |
ゼロダウンタイム | 常に目標レプリカ数を維持 |
terminationGracePeriodSeconds: 60 |
グレースフルシャットダウン | リクエスト排出を待機 |
preStop: sleep 10 |
遅延終了 | PodがServiceから除外された後もK8sがトラフィックを排出し続ける |
resources.requests/limits |
QoS設定 | リソース割り当て保証 + 超過制限 |
(2) ▶ サンプル:Ingress + TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orderflow-ingress
namespace: orderflow
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts: [api.orderflow.example.com]
secretName: orderflow-tls
rules:
- host: api.orderflow.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: orderflow, port: { number: 80 } }
出力:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
5. オブザーバビリティ立ち上げ
(1) ▶ サンプル:Prometheus + AlertManager
# prometheus.yml
scrape_configs:
- job_name: orderflow
metrics_path: /actuator/prometheus
scrape_interval: 15s
kubernetes_sd_configs:
- role: pod
namespaces:
names: [orderflow]
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: orderflow
出力:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
# alert_rules.yml
groups:
- name: orderflow-slo
rules:
- alert: SLOErrorRateExceeded
expr: |
sum(rate(http_server_requests_seconds_total{namespace="orderflow",status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_total{namespace="orderflow"}[5m])) > 0.001
for: 5m
labels: { severity: critical }
annotations:
summary: "OrderFlow error rate exceeds SLO (0.1%)"
- alert: SLOLatencyExceeded
expr: |
histogram_quantile(0.99,
sum(rate(http_server_requests_seconds_bucket{namespace="orderflow"}[5m])) by (le))
> 0.1
for: 5m
labels: { severity: warning }
annotations:
summary: "OrderFlow P99 latency exceeds SLO (100ms)"
(2) ▶ サンプル:Grafana SLOダッシュボードメトリクス
| ダッシュボード | PromQL | SLO目標 |
|---|---|---|
| P99レイテンシ | histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{namespace="orderflow"}[5m])) by (le)) |
< 100 ms |
| エラー率 | sum(rate(http_server_requests_seconds_total{namespace="orderflow",status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_total{namespace="orderflow"}[5m])) |
< 0.1% |
| 可用性 | 1 - (sum(rate(http_server_requests_seconds_total{namespace="orderflow",status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_total{namespace="orderflow"}[5m]))) |
> 99.9% |
| 注文スループット | rate(orderflow_orders_created_total[1m]) * 60 |
> 100/分 |
6. 本番セキュリティ強化
(1) ▶ サンプル:NetworkPolicy + Podセキュリティ
# NetworkPolicy: アクセス制限
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: orderflow-netpol
namespace: orderflow
spec:
podSelector:
matchLabels: { app: orderflow }
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels: { name: ingress-nginx }
ports:
- { port: 8080, protocol: TCP }
- from:
- namespaceSelector:
matchLabels: { name: monitoring }
ports:
- { port: 8081, protocol: TCP }
---
# Podセキュリティ: 非rootで実行
apiVersion: v1
kind: LimitRange
metadata:
name: orderflow-limits
namespace: orderflow
spec:
limits:
- type: Container
default:
memory: "1Gi"
cpu: "500m"
defaultRequest:
memory: "256Mi"
cpu: "100m"
出力:
Deployment.apps/my-app created
Service/my-app-service exposed
Ingress/my-app-ingress created
| セキュリティ強化項目 | 対策 | 説明 |
|---|---|---|
| 非root実行 | Dockerfile USER appuser |
コンテナ脱出を防止 |
| NetworkPolicy | 送信元の制限 | Ingressと監視アクセスのみ許可 |
| Secret暗号化 | etcd暗号化設定 | Secretの平文保存を防止 |
| TLS | cert-manager自動証明書 | 暗号化伝送 |
| リソース制限 | LimitRange | リソース競合を防止 |
7. 総合サンプル:OrderFlow完全デプロイチェックリスト
# k8s/orderflow-complete.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: orderflow
labels: { name: orderflow }
---
apiVersion: v1
kind: ConfigMap
metadata: { name: orderflow-config, namespace: orderflow }
data:
SPRING_PROFILES_ACTIVE: "prod"
DB_HOST: "mysql.orderflow.svc.cluster.local"
REDIS_HOST: "redis.orderflow.svc.cluster.local"
MANAGEMENT_SERVER_PORT: "8081"
---
apiVersion: v1
kind: Secret
metadata: { name: orderflow-secrets, namespace: orderflow }
type: Opaque
data:
DB_PASSWORD: <base64-encoded>
REDIS_PASSWORD: <base64-encoded>
JWT_PRIVATE_KEY: <base64-encoded>
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: orderflow, namespace: orderflow }
spec:
replicas: 3
selector: { matchLabels: { app: orderflow } }
strategy: { rollingUpdate: { maxUnavailable: 0, maxSurge: 1 } }
template:
metadata: { labels: { app: orderflow } }
spec:
terminationGracePeriodSeconds: 60
containers:
- name: orderflow
image: registry.example.com/orderflow-service:1.0.0
ports: [{ containerPort: 8080 }, { containerPort: 8081 }]
envFrom:
- { configMapRef: { name: orderflow-config } }
- { secretRef: { name: orderflow-secrets } }
resources:
requests: { memory: "512Mi", cpu: "250m" }
limits: { memory: "1Gi", cpu: "1000m" }
lifecycle:
preStop: { exec: { command: ["sh", "-c", "sleep 10"] } }
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
initialDelaySeconds: 60; periodSeconds: 30
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: 8080 }
initialDelaySeconds: 30; periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata: { name: orderflow, namespace: orderflow }
spec:
selector: { app: orderflow }
ports:
- { name: http, port: 80, targetPort: 8080 }
- { name: management, port: 8081, targetPort: 8081 }
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: orderflow-hpa, namespace: orderflow }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: orderflow }
minReplicas: 3
maxReplicas: 10
metrics:
- { type: Resource, resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } } }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orderflow-ingress
namespace: orderflow
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- { hosts: [api.orderflow.example.com], secretName: orderflow-tls }
rules:
- host: api.orderflow.example.com
http:
paths:
- { path: /, pathType: Prefix, backend: { service: { name: orderflow, port: { number: 80 } } } }
❓ よくある質問
readinessProbeで準備完了のPodのみServiceに追加; 3) preStop sleep 10でPodがServiceから除外された後もK8sがリクエストを排出する時間を確保; 4) server.shutdown=gracefulを設定。📖 まとめ
- CI/CDパイプライン:コードプッシュ → 自動テスト → イメージビルド → レジストリにプッシュ → K8sローリングデプロイ
- 本番デプロイ:Deployment + HPAオートスケーリング + Ingressゲートウェイ + TLS
- ゼロダウンタイムデプロイ:maxUnavailable=0 + readinessProbe + preStop + グレースフルシャットダウン
- オブザーバビリティ:Prometheusデータ収集 + Grafana SLOダッシュボード + AlertManagerアラート
- セキュリティ強化:非rootユーザー + NetworkPolicy + Secret暗号化 + TLS + リソース制限
- 設計からデプロイまでのエンドツーエンドデリバリー, 1日数百万件の注文を処理するビジネスをサポート
📝 練習問題
-
基本問題 (難易度:⭐): GitHub Actionsワークフローを書き, コードプッシュ後に自動テストとDockerイメージビルドを行ってください。
-
応用問題 (難易度:⭐⭐): 完全なK8s本番デプロイYAML (Deployment + Service + Ingress + HPA + ConfigMap + Secret)を書き, ゼロダウンタイムのローリングアップデートを実装してください。
-
チャレンジ (難易度:⭐⭐⭐): 完全なオブザーバビリティシステム (Prometheus + Grafana + AlertManager + Jaeger)を構築し, SLOダッシュボードとアラートルールを設定し, 障害をシミュレートしてアラートと自動復旧能力をテストしてください。



