Kubernetes Pod CrashLoopBackOffの原因特定と5ステップ解決法

Kubernetes環境でPodが繰り返しクラッシュする「CrashLoopBackOff」状態に陥った場合、ログの確認、リソース制限の検証、依存関係の確認という3つの診断方法で90%以上のケースを解決できます。本記事では実務で即座に対応できる具体的なコマンドと実例を紹介します。

CrashLoopBackOffとは何か

CrashLoopBackOffはKubernetesのPodが起動直後にクラッシュし、再起動を試みるが再びクラッシュするループ状態です。Backoffは「指数バックオフ」を意味し、再起動の間隔が徐々に延長されます(5秒→10秒→20秒...最大5分)。

一度このループに入るとPodは稼働せず、クラスタ内のトラフィック処理ができなくなります。本番環境では即座の対応が必要なため、迅速な原因特定が重要です。

第1ステップ:Podの状態を確認する

Podリストの確認とイベント情報の取得

まず、クラスタ内のPodの現在の状態を把握しましょう。以下のコマンドで、問題のあるPodを特定します。

# 全Podの状態確認
kubectl get pods -n default

# 詳細情報を表示(RESTARTS列でクラッシュ回数が確認できる)
kubectl get pods -n default -o wide

# 特定のPodの詳細情報とイベント情報を表示
kubectl describe pod <pod-name> -n default

describeコマンドの出力で「Last State」セクションを確認してください。ここにクラッシュの理由(ExitCode、Signal、Reason)が表示されます。ExitCode137は通常メモリ不足(OOMKill)、ExitCode1はアプリケーション内のエラーを示します。

第2ステップ:ログを確認してエラーの詳細を特定する

現在のログと前回のクラッシュログを取得

Podが起動できていない状態でも、前回のコンテナログは保持されています。以下のコマンドで確認しましょう。

# 現在のコンテナログを表示
kubectl logs <pod-name> -n default

# 前回クラッシュしたコンテナのログを表示(-p フラグ使用)
kubectl logs <pod-name> -n default -p

# リアルタイムでログを監視
kubectl logs <pod-name> -n default -f

# 複数のコンテナがある場合は特定コンテナを指定
kubectl logs <pod-name> -n default -c <container-name> -p

ログに「Connection refused」や「Port already in use」が表示される場合は、ポート設定のエラーです。「ModuleNotFoundError」や「ImportError」が表示される場合は、依存ライブラリの欠落が考えられます。

第3ステップ:リソース制限とメモリ不足を検査する

メモリ枯渇とCPUスロットリングの確認

ExitCode137やOOMKilledというメッセージが見られた場合、Podに割り当てたメモリが不足しています。以下の手順で確認して調整します。

# Podのリソース使用状況をリアルタイム確認
kubectl top pods -n default

# 特定Podの詳細なリソース情報
kubectl describe pod <pod-name> -n default | grep -A 5 "Limits\|Requests"

DeploymentやStatefulSetのマニフェストファイルで、resources.limits.memoryresources.requests.memoryを確認してください。以下は修正例です。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:1.0
        resources:
          requests:
            memory: "256Mi"  # 初期リクエスト量
            cpu: "100m"
          limits:
            memory: "512Mi"  # 最大上限を設定
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30  # アプリ起動を待つ時間
          periodSeconds: 10

ハマりポイント:initialDelaySecondsが短すぎる

Podが起動後、アプリケーションの初期化に時間がかかる場合、livenessProbeのヘルスチェックが初期化完了前に実行され、Podがクラッシュと判定されることがあります。initialDelaySecondsをアプリの起動時間より長めに設定することが重要です。

第4ステップ:イメージとレジストリの問題を確認する

イメージプルエラーの診断

Deploymentが参照するDockerイメージが存在しない、またはプルできない場合も、Podはクラッシュループに陥ります。

# イメージプルの失敗を確認
kubectl describe pod <pod-name> -n default | grep -A 5 "Events:"

# イメージが存在するか確認(プライベートレジストリの場合)
kubectl get events -n default --sort-by='.lastTimestamp'

# Podが参照しているイメージを確認
kubectl get pod <pod-name> -n default -o jsonpath='{.spec.containers[].image}'

出力に「ImagePullBackOff」と表示される場合は、以下の原因が考えられます。

  • イメージタグが間違っている(タイプミス、存在しないバージョン)
  • プライベートレジストリの認証情報が不足している
  • ネットワーク接続の問題でレジストリに接続できない

プライベートレジストリを使用している場合は、以下のようにSecretを作成して認証情報を登録します。

# レジストリの認証情報を保存するSecretを作成
kubectl create secret docker-registry regcred \
  --docker-server=myregistry.azurecr.io \
  --docker-username=<username> \
  --docker-password=<password> \
  -n default

# DeploymentマニフェストでSecretを参照
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  template:
    spec:
      imagePullSecrets:
      - name: regcred  # ここでSecretを指定
      containers:
      - name: app
        image: myregistry.azurecr.io/myapp:1.0

第5ステップ:アプリケーション依存関係と環境変数を確認する

環境変数とConfigMapの接続エラー

アプリケーションが環境変数やConfigMapから設定値を読み込めない場合、起動失敗につながります。

# ConfigMapの存在確認
kubectl get configmap -n default

# ConfigMapの内容を確認
kubectl get configmap <configmap-name> -n default -o yaml

# Podが参照する環境変数を確認
kubectl describe pod <pod-name> -n default | grep -A 20 "Environment:"

以下のマニフェストは、ConfigMapから環境変数を読み込む例です。ConfigMapが存在しない場合、Podは起動に失敗します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:1.0
        env:
        - name: DATABASE_URL
          valueFrom:
            configMapKeyRef:
              name: app-config      # 参照するConfigMap名
              key: database-url
        - name: LOG_LEVEL
          value: "DEBUG"
        startupProbe:              # コンテナが起動するまで待機
          exec:
            command:
            - /bin/sh
            - -c
            - test -f /app/ready
          failureThreshold: 30
          periodSeconds: 10

ハマりポイント:ConfigMapの順序問題

Deploymentが参照するConfigMapより先にDeploymentを適用した場合、Podは起動できません。必ずConfigMapやSecretを先に作成してから、Deploymentを適用してください。

実践的なトラブルシューティング例

以下は実際の診断フロー例です。

# ステップ1:問題のあるPodを特定
$ kubectl get pods -n production
NAME                    READY   STATUS             RESTARTS   AGE
api-server-5d4c8f9z     0/1     CrashLoopBackOff   5          2m

# ステップ2:前回のクラッシュログを確認
$ kubectl logs api-server-5d4c8f9z -n production -p
2024-01-15 10:23:45 FATAL: Connection refused to database at localhost:5432

# ステップ3:環境変数を確認
$ kubectl describe pod api-server-5d4c8f9z -n production
...
DATABASE_HOST:  <set to the key 'db-host' in secret 'db-credentials'>  Optional: false
...

# ステップ4:Secretの存在を確認
$ kubectl get secret -n production
No resources found

# 対処:Secretを作成してからPodを再起動
$ kubectl create secret generic db-credentials \
  --from-literal=db-host=postgres.database.svc.cluster.local \
  -n production
$ kubectl rollout restart deployment api-server -n production

予防策:本番環境での設定ベストプラクティス

CrashLoopBackOffを未然に防ぐために、デプロイ前に以下の項目をチェックリストとして確認してください。

  • ヘルスチェックの設定: livenessProbereadinessProbeの初期遅延時間をアプリの起動時間より長めに設定
  • リソース制限の適切な設定: 本番環境と同じ負荷をかけたステージング環境でテストし、適切なrequestslimitsを決定
  • 依存関係の事前確認: ConfigMap、Secret、PersistentVolumeをDeployment適用前に作成
  • イメージタグの明示: latestタグではなく、バージョン番号を明記して予期しないバージョン切り替えを防止
  • 段階的なデプロイ: 全Podを一度に置き換えず、strategy.rollingUpdateを使用

よくある質問

Podが完全に起動できていない場合、標準的なkubectl logsではログが取得できません。その場合は、-pフラグで前回のコンテナログを取得してください。それでもログがない場合は、コンテナイメージ自体に問題がある(イメージが壊れている、実行可能なエントリーポイントがない)ことが考えられます。

主な原因は環境差異です。Kubernetesではホストのファイルシステムにアクセスできない、ネットワーク解決(DNS)がローカルと異なる、環境変数が異なるなどが考えられます。Dockerコンテナをローカルで実行してテストしてから、Kubernetesにデプロイすることをお勧めします。

DeploymentやStatefulSetによって管理されているPodの場合、削除してもコントローラが新しいPodを自動生成します。根本原因(イメージエラー、設定ミス)を修正してからPodを削除する必要があります。例えば、kubectl set image deployment/myapp myapp=myapp:correct-tagでイメージを修正してから、Deploymentを更新してください。

まとめ
K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →