🆕 2025年11月最新版!
Kubernetes 1.29、Docker 25.0の最新機能、クラウドネイティブセキュリティのベストプラクティスを反映しました。
5分でわかる:Docker & Kubernetes本番デプロイの全体像
本番環境へのコンテナデプロイは、開発環境とは異なる多くの考慮事項があります。この記事では、実際のトラブル事例に基づいた実践的なガイドを提供します。
なぜこのガイドが必要なのか
| 課題 | 開発環境 | 本番環境 |
|---|---|---|
| 可用性 | 単一コンテナでOK | 冗長化・自動復旧が必須 |
| セキュリティ | rootユーザーでも可 | 最小権限の原則が必須 |
| パフォーマンス | 多少遅くてもOK | SLA遵守が必須 |
| モニタリング | ログ確認程度 | 包括的な監視が必須 |
| スケーリング | 固定リソース | 負荷に応じた自動スケール |
本記事で学べること
- 基礎知識(第1-2章):Docker/Kubernetesの本番環境に必要な基本概念
- 実践デプロイ(第3-5章):実際の手順とコード例
- トラブルシューティング(第6-8章):よくある問題と解決策50選
- 本番運用(第9-10章):監視、スケーリング、セキュリティ
最短で課題解決する一冊
この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。
第1章:本番環境デプロイの前提知識
1.1 Dockerの本番環境向け基礎
イメージのベストプラクティス
❌ 開発環境でよくある悪い例:
FROM node:latest
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]問題点:
latestタグは予期しない変更を引き起こす- 全ファイルをコピー(.gitなど不要なものも)
- 開発依存関係もインストール
- rootユーザーで実行
- ヘルスチェックなし
✅ 本番環境向け改善例:
# マルチステージビルドで最終イメージを軽量化
FROM node:20.10-alpine AS builder
WORKDIR /app
# package.jsonとpackage-lock.jsonのみ先にコピー(キャッシュ活用)
COPY package*.json ./
# 本番用依存関係のみインストール
RUN npm ci --only=production && \
npm cache clean --force
# ソースコードをコピー
COPY --chown=node:node . .
# ビルド(TypeScriptなど)
RUN npm run build
# 最終ステージ:軽量な実行環境
FROM node:20.10-alpine
# セキュリティ:非rootユーザーで実行
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# ビルド成果物のみコピー
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
# 非rootユーザーに切り替え
USER nodejs
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node healthcheck.js
# 環境変数でポート指定
EXPOSE 3000
# プロセスマネージャーなしで直接実行(PID 1問題対策)
CMD ["node", "dist/index.js"].dockerignoreの重要性
必須設定:
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
.DS_Store
coverage
.vscode
.idea
*.md
Dockerfile*
docker-compose*.yml
.dockerignore1.2 Kubernetesの本番環境向け基礎
主要リソースの理解
flowchart TD
A[Namespace] --> B[Deployment]
B --> C[ReplicaSet]
C --> D[Pod]
A --> E[Service]
E --> D
A --> F[ConfigMap]
A --> G[Secret]
F --> D
G --> D
A --> H[Ingress]
H --> E
A --> I[PersistentVolumeClaim]
I --> D各リソースの役割:
| リソース | 役割 | 本番環境での重要度 |
|---|---|---|
| Namespace | リソースの論理的分離 | ⭐⭐⭐⭐⭐ |
| Deployment | アプリケーションのデプロイ管理 | ⭐⭐⭐⭐⭐ |
| Service | Pod間の通信・負荷分散 | ⭐⭐⭐⭐⭐ |
| Ingress | 外部からのHTTPアクセス制御 | ⭐⭐⭐⭐⭐ |
| ConfigMap | 設定ファイル管理 | ⭐⭐⭐⭐☆ |
| Secret | 機密情報管理 | ⭐⭐⭐⭐⭐ |
| PersistentVolume | 永続データ保存 | ⭐⭐⭐⭐⭐ |
| HorizontalPodAutoscaler | 自動スケーリング | ⭐⭐⭐⭐⭐ |
| NetworkPolicy | ネットワーク制御 | ⭐⭐⭐⭐☆ |
| PodDisruptionBudget | 可用性保証 | ⭐⭐⭐⭐☆ |
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第2章:本番環境デプロイの準備
2.1 環境別の設定管理戦略
推奨アプローチ:12 Factor Appに従う
開発環境 (dev)
├── .env.development
├── k8s/overlays/dev/
└── docker-compose.dev.yml
ステージング環境 (staging)
├── .env.staging
├── k8s/overlays/staging/
└── docker-compose.staging.yml
本番環境 (production)
├── .env.production(リポジトリに含めない!)
├── k8s/overlays/production/
└── docker-compose.production.ymlKustomizeを使った環境管理
基本構成(base):
# k8s/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2 # デフォルト
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry.azurecr.io/myapp:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
env:
- name: NODE_ENV
value: "production"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5本番環境オーバーライド(production):
# k8s/overlays/production/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5 # 本番は5レプリカ
template:
spec:
containers:
- name: myapp
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
bases:
- ../../base
patchesStrategicMerge:
- deployment-patch.yaml
images:
- name: myregistry.azurecr.io/myapp
newTag: v1.2.3 # 本番は明確なバージョンタグ2.2 Secretの安全な管理
❌ 絶対にやってはいけないこと
# 悪い例:Secret を直接YAMLにハードコード
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # base64エンコードしただけ(安全ではない!)✅ 推奨される方法
方法1: Sealed Secrets(推奨)
# Sealed Secretsのインストール
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
# シークレットを暗号化
kubectl create secret generic db-secret \
--from-literal=password=MySecurePassword123 \
--dry-run=client -o yaml | \
kubeseal -o yaml > sealed-secret.yaml
# これならGitにコミット可能
git add sealed-secret.yaml方法2: 外部シークレット管理(AWS Secrets Manager / HashiCorp Vault)
# External Secrets Operatorを使用
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-secret
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: db-secret
data:
- secretKey: password
remoteRef:
key: prod/database/password方法3: 環境変数からSecretを作成(CI/CD環境)
# GitHub ActionsなどのCI/CD環境で実行
kubectl create secret generic db-secret \
--from-literal=password=${{ secrets.DB_PASSWORD }} \
--namespace=productionさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第3章:実践!Dockerイメージの本番環境ビルド
3.1 CI/CDパイプラインでのビルド
GitHub Actionsの例
# .github/workflows/build-and-push.yml
name: Build and Push Docker Image
on:
push:
branches:
- main
tags:
- 'v*'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64 # マルチアーキテクチャ対応
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
NODE_ENV=production
BUILD_DATE=${{ steps.meta.outputs.created }}
VCS_REF=${{ github.sha }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'3.2 イメージの最適化テクニック
サイズ削減の実例
最適化前:1.2GB
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]最適化後:85MB(約14倍減)
# ビルドステージ
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build && npm prune --production
# 実行ステージ
FROM node:20-alpine
RUN apk add --no-cache tini
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER node
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/index.js"]最適化テクニック:
- Alpineベースイメージ使用:
node:20(1GB) →node:20-alpine(150MB) - マルチステージビルド: ビルドツール不要
- レイヤーキャッシュ活用: package.jsonを先にコピー
- 不要ファイル除外: .dockerignore活用
- 依存関係最小化:
npm prune --production
セキュリティスキャン
# Trivyでイメージスキャン
trivy image myregistry.azurecr.io/myapp:v1.0.0
# 重大度別にフィルタ
trivy image --severity HIGH,CRITICAL myregistry.azurecr.io/myapp:v1.0.0
# 修正済み脆弱性のみ表示
trivy image --ignore-unfixed myregistry.azurecr.io/myapp:v1.0.0さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第4章:Kubernetesクラスタへのデプロイ
4.1 本番環境向けDeployment完全版
# production-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
labels:
app: myapp
environment: production
version: v1.2.3
annotations:
deployment.kubernetes.io/revision: "1"
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 同時に作成できる追加Pod数
maxUnavailable: 0 # ローリングアップデート中にダウンできるPod数(0=ゼロダウンタイム)
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
environment: production
version: v1.2.3
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
# セキュリティコンテキスト
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
# アフィニティ:Pod分散配置
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
topologyKey: kubernetes.io/hostname
# 初期化コンテナ:起動前の事前処理
initContainers:
- name: migration
image: myregistry.azurecr.io/myapp:v1.2.3
command: ['npm', 'run', 'migrate']
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
containers:
- name: myapp
image: myregistry.azurecr.io/myapp:v1.2.3
imagePullPolicy: Always
ports:
- name: http
containerPort: 3000
protocol: TCP
# 環境変数
env:
- name: NODE_ENV
value: "production"
- name: PORT
value: "3000"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
- name: REDIS_URL
valueFrom:
configMapKeyRef:
name: app-config
key: redis-url
# リソース制限(必須!)
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
# Liveness Probe:コンテナが生存しているか
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
# Readiness Probe:トラフィックを受け入れ可能か
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
# Startup Probe:起動完了を確認
startupProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 0
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 30 # 30回 × 5秒 = 最大150秒待機
# セキュリティコンテキスト
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# ボリュームマウント
volumeMounts:
- name: tmp
mountPath: /tmp
- name: config
mountPath: /app/config
readOnly: true
# ボリューム定義
volumes:
- name: tmp
emptyDir: {}
- name: config
configMap:
name: app-config
# イメージプルシークレット
imagePullSecrets:
- name: registry-secret
---
# Service
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: production
spec:
type: ClusterIP
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 5
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 5分間様子見
policies:
- type: Percent
value: 50
periodSeconds: 60 # 1分間に50%まで削減
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15 # 15秒で2倍まで増加
- type: Pods
value: 4
periodSeconds: 15 # 15秒で4Pod追加
---
# PodDisruptionBudget:可用性保証
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: myapp-pdb
namespace: production
spec:
minAvailable: 3 # 常に最低3つのPodが稼働
selector:
matchLabels:
app: myapp4.2 デプロイコマンドと確認
# Namespaceの作成
kubectl create namespace production
# Secretの作成(実際の値は環境変数から)
kubectl create secret generic db-secret \
--from-literal=url="${DATABASE_URL}" \
--namespace=production
# ConfigMapの作成
kubectl create configmap app-config \
--from-literal=redis-url="redis://redis-master:6379" \
--namespace=production
# レジストリシークレットの作成(プライベートレジストリの場合)
kubectl create secret docker-registry registry-secret \
--docker-server=myregistry.azurecr.io \
--docker-username="${REGISTRY_USERNAME}" \
--docker-password="${REGISTRY_PASSWORD}" \
--namespace=production
# マニフェストの適用
kubectl apply -f production-deployment.yaml
# デプロイ状況の確認
kubectl rollout status deployment/myapp -n production
# Podの状態確認
kubectl get pods -n production -l app=myapp -w
# 詳細情報
kubectl describe deployment myapp -n production
# ログ確認(最新のPod)
kubectl logs -n production -l app=myapp --tail=100 -f
# 特定のPodのログ
kubectl logs -n production <pod-name> -fさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第5章:Ingressとサービス公開
5.1 本番環境向けIngress設定
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: production
annotations:
# HTTPS リダイレクト
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
# レート制限
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "10"
# タイムアウト設定
nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
# CORS設定
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
# サイズ制限
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
# セキュリティヘッダー
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1; mode=block";
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains";
# TLS証明書(cert-manager使用)
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-example-com-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 805.2 Let's Encrypt証明書の自動化
# cert-managerのインストール
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# ClusterIssuerの作成
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
EOFさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第6章:トラブルシューティング大全(全50パターン)
6.1 Podが起動しない系(パターン1-15)
トラブル1: ImagePullBackOff
症状:
$ kubectl get pods -n production
NAME READY STATUS RESTARTS AGE
myapp-6d4b8c5f7b-xyz12 0/1 ImagePullBackOff 0 2m原因と解決策:
# 詳細確認
kubectl describe pod myapp-6d4b8c5f7b-xyz12 -n production
# よくある原因:
# 1. イメージが存在しない
# → レジストリとタグを確認
# 2. 認証エラー
# → imagePullSecrets が正しく設定されているか確認
kubectl get secret registry-secret -n production -o yaml
# Secretの作成(修正)
kubectl delete secret registry-secret -n production
kubectl create secret docker-registry registry-secret \
--docker-server=myregistry.azurecr.io \
--docker-username=$REGISTRY_USER \
--docker-password=$REGISTRY_PASS \
-n production
# 3. ネットワーク問題
# → Nodeからレジストリへの疎通確認
kubectl debug node/<node-name> -it --image=nicolaka/netshoot
# コンテナ内で
nslookup myregistry.azurecr.io
curl -I https://myregistry.azurecr.ioトラブル2: CrashLoopBackOff
症状:
$ kubectl get pods -n production
NAME READY STATUS RESTARTS AGE
myapp-6d4b8c5f7b-xyz12 0/1 CrashLoopBackOff 5 5m解決手順:
# ステップ1: ログ確認
kubectl logs myapp-6d4b8c5f7b-xyz12 -n production
# 前回のクラッシュログも確認
kubectl logs myapp-6d4b8c5f7b-xyz12 -n production --previous
# ステップ2: よくある原因
# 原因A: 環境変数不足
# エラー例: "DATABASE_URL is not defined"
# 解決策:
kubectl get deployment myapp -n production -o yaml | grep -A 10 env:
# 欠けている環境変数を追加
# 原因B: リソース不足(OOMKilled)
kubectl describe pod myapp-6d4b8c5f7b-xyz12 -n production | grep -A 5 "Last State"
# "OOMKilled" と表示されたら、メモリ制限を増やす
# 原因C: ヘルスチェック失敗
# initialDelaySecondsが短すぎる可能性
# 解決策: 起動時間を計測して適切な値に設定
# ステップ3: デバッグモードで起動
kubectl run myapp-debug \
--image=myregistry.azurecr.io/myapp:v1.2.3 \
--rm -it --restart=Never \
--command -- /bin/sh
# コンテナ内でアプリを手動起動して原因特定トラブル3-15のクイックリファレンス
| エラー | 原因 | 解決策 |
|---|---|---|
| Pending | リソース不足 | kubectl describe node でリソース確認 |
| ErrImagePull | イメージ取得失敗 | イメージ名・タグ・認証確認 |
| CreateContainerConfigError | ConfigMap/Secret不足 | リソース存在確認 |
| InvalidImageName | イメージ名の構文エラー | イメージ名修正 |
| FailedScheduling | Node選定失敗 | nodeSelector/taints確認 |
| Unknown | Nodeとの通信切断 | Node状態確認 |
| Evicted | リソース圧迫で退避 | リソース制限見直し |
| OOMKilled | メモリ超過 | limits.memory増加 |
| ContainerCannotRun | 実行権限エラー | securityContext確認 |
| RunContainerError | コンテナ起動失敗 | docker run でローカル検証 |
6.2 ネットワーク系トラブル(パターン16-30)
トラブル16: Pod間通信ができない
症状:
# フロントエンドPodからバックエンドServiceに接続できない
$ kubectl exec -it frontend-pod -n production -- curl http://backend-service
curl: (6) Could not resolve host: backend-service解決手順:
# ステップ1: DNS解決確認
kubectl run -it --rm debug --image=busybox --restart=Never -n production
# コンテナ内で
nslookup backend-service
nslookup backend-service.production.svc.cluster.local
# ステップ2: Service確認
kubectl get svc backend-service -n production
kubectl describe svc backend-service -n production
# Endpointsが空の場合、Selectorが間違っている
kubectl get endpoints backend-service -n production
# ステップ3: NetworkPolicy確認
kubectl get networkpolicy -n production
kubectl describe networkpolicy -n production
# NetworkPolicyで通信がブロックされていないか確認NetworkPolicyの適切な設定:
# 本番環境推奨:デフォルトで拒否、必要な通信のみ許可
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-network-policy
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
# フロントエンドからのアクセス許可
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
# データベースへのアクセス許可
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# 外部API(DNS含む)へのアクセス許可
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
- to:
- podSelector: {}
ports:
- protocol: TCP
port: 443トラブル17-30のクイックリファレンス
| 問題 | 診断コマンド | 典型的原因 |
|---|---|---|
| DNS解決失敗 | nslookup in Pod |
CoreDNS問題、Service名誤り |
| 接続タイムアウト | kubectl logs |
ファイアウォール、NetworkPolicy |
| 502 Bad Gateway | kubectl get endpoints |
バックエンドPod未Ready |
| Ingress到達不可 | kubectl describe ingress |
DNS未設定、証明書エラー |
| ロードバランシング不均等 | Service annotations | sessionAffinity設定 |
| 外部接続不可 | kubectl exec + curl |
Egress NetworkPolicy |
| 証明書エラー | kubectl describe certificate |
cert-manager問題 |
| レート制限 | Ingress logs | アノテーション設定 |
6.3 パフォーマンス系トラブル(パターン31-40)
トラブル31: 応答が遅い・タイムアウト
診断手順:
# ステップ1: Pod のCPU/メモリ使用率確認
kubectl top pods -n production
# ステップ2: 詳細メトリクス(Prometheusがある場合)
kubectl port-forward -n monitoring svc/prometheus 9090:9090
# ブラウザで http://localhost:9090 にアクセス
# クエリ例:
# Pod CPU使用率:rate(container_cpu_usage_seconds_total[5m])
# Pod メモリ使用率:container_memory_usage_bytes
# ステップ3: アプリケーションプロファイリング
# Node.jsの例
kubectl exec -it myapp-pod -n production -- node --prof app.js
# ステップ4: ネットワークレイテンシ確認
kubectl run -it --rm netshoot --image=nicolaka/netshoot -n production
# コンテナ内で
time curl -w "@curl-format.txt" http://backend-service
# curl-format.txt:
# time_namelookup: %{time_namelookup}\n
# time_connect: %{time_connect}\n
# time_starttransfer: %{time_starttransfer}\n
# time_total: %{time_total}\n解決策:
# リソース増強
spec:
containers:
- name: myapp
resources:
requests:
memory: "1Gi" # 512Mi → 1Gi
cpu: "1000m" # 500m → 1000m
limits:
memory: "2Gi" # 1Gi → 2Gi
cpu: "2000m" # 1000m → 2000m
# HPA設定の最適化
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
minReplicas: 10 # 5 → 10
maxReplicas: 50 # 20 → 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # 70 → 60(早めにスケール)トラブル32-40のクイックリファレンス
| 問題 | 原因 | 解決策 |
|---|---|---|
| 高CPU使用率 | 無限ループ、非効率処理 | プロファイリング、コード最適化 |
| 高メモリ使用 | メモリリーク | ヒープダンプ解析 |
| ディスクI/O遅延 | PV性能不足 | SSD使用、I/O最適化 |
| DB接続枯渇 | コネクションプール不足 | プール設定増加 |
| 遅いコンテナ起動 | イメージサイズ大 | マルチステージビルド |
| スケールアウト遅延 | HPA閾値高すぎ | targetUtilization下げる |
| 不要なログ出力 | デバッグログ大量 | ログレベル調整 |
6.4 データ永続化トラブル(パターン41-50)
トラブル41: データが消える
原因:
- emptyDirを使っている(Pod再起動でデータ消失)
- PersistentVolumeが正しくマウントされていない
解決策:
# PersistentVolumeClaim の作成
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-data-pvc
namespace: production
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: fast-ssd # クラウドプロバイダーの高速ストレージ
---
# Deployment に PVC をマウント
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: myapp
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: myapp-data-pvc確認コマンド:
# PVCの状態確認
kubectl get pvc -n production
# マウント確認
kubectl exec -it myapp-pod -n production -- df -h
# データ書き込みテスト
kubectl exec -it myapp-pod -n production -- sh -c 'echo "test" > /app/data/test.txt'
# Pod削除して再作成後、データが残っているか確認
kubectl delete pod myapp-pod -n production
kubectl exec -it <new-pod> -n production -- cat /app/data/test.txtトラブル42-50のクイックリファレンス
| 問題 | 診断 | 解決策 |
|---|---|---|
| PVC Pending | kubectl describe pvc |
StorageClass確認、容量確認 |
| マウント失敗 | Pod Events | ボリュームタイプ互換性確認 |
| 権限エラー | ls -la in Pod |
fsGroup設定、initContainer |
| 容量不足 | df -h in Pod |
PVC拡張、古いデータ削除 |
| バックアップなし | - | VolumeSnapshot作成 |
| 複数Pod書き込み競合 | AccessMode確認 | ReadWriteMany or StatefulSet |
| スナップショット失敗 | CSIドライバー | ドライバーアップデート |
| 復元失敗 | Snapshot状態 | 手動復元手順確認 |
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第7章:監視とロギング
7.1 包括的な監視戦略
Prometheusメトリクス収集
# ServiceMonitor(Prometheus Operator使用時)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp-metrics
namespace: production
labels:
app: myapp
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: http
path: /metrics
interval: 30sアプリケーション側のメトリクス実装(Node.js)
// metrics.ts
import { register, Counter, Histogram, Gauge } from 'prom-client';
import express from 'express';
// カスタムメトリクス定義
export const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10]
});
export const httpRequestTotal = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code']
});
export const activeConnections = new Gauge({
name: 'active_connections',
help: 'Number of active connections'
});
// メトリクスエンドポイント
export function setupMetricsEndpoint(app: express.Application) {
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
}
// ミドルウェア
export function metricsMiddleware(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const start = Date.now();
activeConnections.inc();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
const labels = {
method: req.method,
route: req.route?.path || req.path,
status_code: res.statusCode.toString()
};
httpRequestDuration.observe(labels, duration);
httpRequestTotal.inc(labels);
activeConnections.dec();
});
next();
}7.2 ログ管理
構造化ログ(推奨)
// logger.ts
import winston from 'winston';
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'myapp',
environment: process.env.NODE_ENV,
version: process.env.APP_VERSION
},
transports: [
new winston.transports.Console()
]
});
// 使用例
logger.info('User logged in', {
userId: '12345',
ip: req.ip,
userAgent: req.headers['user-agent']
});
logger.error('Database connection failed', {
error: err.message,
stack: err.stack,
database: 'postgresql'
});FluentdによるログCollectionの構成
# fluentd-daemonset.yaml (簡略版)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: fluentd-logging
template:
metadata:
labels:
k8s-app: fluentd-logging
spec:
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.logging.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containersさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第8章:セキュリティ強化
8.1 Pod Security Standards
# namespace-security.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted8.2 Network Policyによる通信制御
# default-deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# app-specific-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: myapp-network-policy
namespace: production
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: ingress-nginx
ports:
- protocol: TCP
port: 3000
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 538.3 脆弱性スキャン自動化
# .github/workflows/security-scan.yml
name: Security Scan
on:
schedule:
- cron: '0 2 * * *' # 毎日午前2時
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第9章:本番運用のベストプラクティス
9.1 Blue-Greenデプロイメント
# Blue環境(現行)
kubectl apply -f deployment-blue.yaml
kubectl apply -f service-blue.yaml
# Green環境(新バージョン)をデプロイ
kubectl apply -f deployment-green.yaml
# テスト確認
kubectl run test --rm -it --image=curlimages/curl -- \
curl http://myapp-green-service
# 問題なければServiceを切り替え
kubectl patch service myapp \
-p '{"spec":{"selector":{"version":"green"}}}'
# Blueを削除(ロールバック用に一時保持も可)
kubectl delete deployment myapp-blue9.2 カナリアデプロイメント
# Flaggerを使用したカナリアデプロイ
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: myapp
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
service:
port: 80
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1mさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第10章:災害復旧とバックアップ
10.1 Veleroによるバックアップ
# Veleroインストール
velero install \
--provider aws \
--bucket k8s-backups \
--backup-location-config region=ap-northeast-1 \
--snapshot-location-config region=ap-northeast-1
# バックアップスケジュール設定
velero schedule create daily-backup \
--schedule="0 2 * * *" \
--include-namespaces production \
--ttl 720h
# 手動バックアップ
velero backup create production-backup \
--include-namespaces production
# 復元
velero restore create --from-backup production-backupさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
まとめ:本番環境デプロイの成功への道
本番環境へのコンテナデプロイは、以下の要素が重要です:
🎯 重要なチェックリスト
✅ セキュリティ
- 非rootユーザーで実行
- イメージの脆弱性スキャン済み
- Secretsを安全に管理
- NetworkPolicyで通信制限
- RBAC適切に設定
✅ 信頼性
- Liveness/Readiness Probe設定
- 複数レプリカで冗長化
- PodDisruptionBudget設定
- リソースリクエスト/リミット設定
- HPA設定
✅ 監視
- Prometheusメトリクス収集
- 構造化ログ出力
- アラート設定
- ダッシュボード構築
✅ 運用
- CI/CDパイプライン構築
- Blue-Green/Canaryデプロイ
- バックアップ自動化
- ドキュメント整備
この記事が、あなたの本番環境デプロイを成功に導く一助となれば幸いです。
Happy Deploying! 🚀







