Tasuke HubLearn · Solve · Grow
#Docker

【2025年決定版】Docker & Kubernetes本番環境デプロイ完全ガイド:初心者から実践までのトラブルシューティング大全

DockerとKubernetesを使った本番環境へのデプロイを完全網羅。基礎知識から本番運用まで、実際のトラブル事例と解決策、セキュリティ対策、パフォーマンス最適化、モニタリング戦略を豊富な実例とともに解説。

時計のアイコン28 November, 2025

🆕 2025年11月最新版!
Kubernetes 1.29、Docker 25.0の最新機能、クラウドネイティブセキュリティのベストプラクティスを反映しました。

TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者

5分でわかる:Docker & Kubernetes本番デプロイの全体像

本番環境へのコンテナデプロイは、開発環境とは異なる多くの考慮事項があります。この記事では、実際のトラブル事例に基づいた実践的なガイドを提供します。

なぜこのガイドが必要なのか

課題 開発環境 本番環境
可用性 単一コンテナでOK 冗長化・自動復旧が必須
セキュリティ rootユーザーでも可 最小権限の原則が必須
パフォーマンス 多少遅くてもOK SLA遵守が必須
モニタリング ログ確認程度 包括的な監視が必須
スケーリング 固定リソース 負荷に応じた自動スケール

本記事で学べること

  1. 基礎知識(第1-2章):Docker/Kubernetesの本番環境に必要な基本概念
  2. 実践デプロイ(第3-5章):実際の手順とコード例
  3. トラブルシューティング(第6-8章):よくある問題と解決策50選
  4. 本番運用(第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
.dockerignore

1.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.yml

Kustomizeを使った環境管理

基本構成(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"]

最適化テクニック:

  1. Alpineベースイメージ使用: node:20 (1GB) → node:20-alpine (150MB)
  2. マルチステージビルド: ビルドツール不要
  3. レイヤーキャッシュ活用: package.jsonを先にコピー
  4. 不要ファイル除外: .dockerignore活用
  5. 依存関係最小化: 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: myapp

4.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: 80

5.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: restricted

8.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: 53

8.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-blue

9.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! 🚀

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

この記事をシェア

続けて読みたい記事

編集部がピックアップした関連記事で学びを広げましょう。

#Kubernetes

Kubernetes本番デプロイ完全トラブルシューティングガイド【2025年実務解決策決定版】

2025/8/17
#ConoHa WING

【2025年完全版】ConoHa WING徹底ガイド:初心者からプロまでの完全マニュアル - 料金比較・速度検証・トラブルシューティング大全

2025/11/28
#WebSocket

WebSocketリアルタイム通信完全トラブルシューティングガイド【2025年実務解決策決定版】

2025/8/17
#PostgreSQL

PostgreSQL遅いクエリ完全解決ガイド【2025年実務トラブルシューティング決定版】

2025/8/17
#React

React メモリリーク完全対策ガイド【2025年実務トラブルシューティング決定版】

2025/8/17
#OpenTelemetry

OpenTelemetry観測可能性完全トラブルシューティングガイド【2025年実務実装解決策決定版】

2025/8/19