トラブルシューティング:Kubernetes PodがPendingまたはCrashLoopBackOffでスタックする理由

Kubernetes Podが`Pending`または`CrashLoopBackOff`でスタックすると、デプロイが停止する可能性があります。この包括的なガイドでは、これらの一般的な状態を解明し、実践的なステップバイステップのトラブルシューティングを提供します。`kubectl`コマンドを使用して、リソース制約、イメージプルエラー、アプリケーション障害、プローブ設定ミスなどの問題を診断する方法を学びます。Podの問題を迅速に解決し、堅牢で信頼性の高いKubernetes環境を維持し、アプリケーションが常に稼働していることを確認するための実用的な洞察とベストプラクティスを身につけましょう。

トラブルシューティング:Kubernetes PodがPendingまたはCrashLoopBackOffでスタックする理由

PendingCrashLoopBackOffは、ロールアウトを待っているときに似ているように見えますが、意味は大きく異なります。Pendingは通常、KubernetesがPodを配置または準備できなかったことを意味します。CrashLoopBackOffは、コンテナが起動した後に終了し、Kubernetesが次の再起動を遅延していることを意味します。

この違いは重要です。Pending状態のPodは、多くの場合、スケジューラー、イメージ、またはストレージの問題です。クラッシュするPodは、通常、アプリケーション、コマンド、プローブ、権限、またはメモリの問題です。この分割から始めると、トラブルシューティングの道のりがはるかに短くなります。

Podの状態を理解する:Pending vs. CrashLoopBackOff

トラブルシューティングに進む前に、これら2つの状態が何を意味するのかを理解することが不可欠です。

Podステータス:Pending

Pending状態のPodは、KubernetesがPodオブジェクトを受け入れたが、まだ実行中のコンテナ状態に完全に移行していないことを意味します。ノードにスケジュールされていない場合もあります。ノードが割り当てられているが、イメージプル、ボリュームアタッチ、またはサンドボックスセットアップが完了していない場合もあります。

Podステータス:CrashLoopBackOff

CrashLoopBackOff状態のPodは、Pod内のコンテナが起動、クラッシュ、再起動を繰り返していることを意味します。Kubernetesは、ノードへの過負荷を防ぐために、再起動の間に指数関数的なバックオフ遅延を実装しています。この状態は、ほとんどの場合、コンテナ内部で実行されているアプリケーション自体またはその直接の環境に問題があることを示しています。

微妙なケースの1つ:コンテナが終了コード0で終了しても、ワークロードが長時間実行サーバーであるべき場合、再起動ループに入ることがあります。これは、移行スクリプトや即座に終了するシェルコマンドなど、ワンショットコマンドが誤ってDeploymentで実行された場合によく発生します。

Pending状態のPodのトラブルシューティング

PodがPendingの場合、最初に確認すべきはスケジューラーと、Podが配置されようとしているノードです。以下に、一般的な原因と診断手順を示します。

1. ノードのリソース不足

PodがPendingになる最も頻繁な理由の1つは、Podのrequestsを満たすのに十分な利用可能なリソース(CPU、メモリ)を持つノードがクラスター内にないことです。スケジューラーは適切なノードを見つけることができません。

診断手順:

  1. Podを記述するkubectl describe podコマンドはここで最良の味方です。Podをスケジュールできない理由を詳述したイベントが表示されることがよくあります。

    kubectl describe pod <pod-name> -n <namespace>
    

    "FailedScheduling"や"0/3 nodes are available: 3 Insufficient cpu"、"memory"などのメッセージを含むイベントを探します。

  2. ノードリソースを確認する:ノードの現在のリソース使用量と容量を確認します。

    kubectl get nodes
    kubectl top nodes # (metrics-serverが必要)
    

解決策:

  • クラスター容量を増やす:Kubernetesクラスターにノードを追加します。
  • Podリソース要求を調整する:PodマニフェストのCPUとメモリのrequestsが高すぎる場合は、それらを減らします。
    resources:
      requests:
        memory: "128Mi"
        cpu: "250m"
    
  • 他のPodを退避させる:ノードから優先度の低いPodを手動で退避させてリソースを解放します(注意して使用してください)。

2. イメージプルエラー

KubernetesがPodをノードにスケジュールできても、ノードがコンテナイメージをプルできない場合、PodはPendingのままになります。

一般的な原因:

  • 誤ったイメージ名/タグ:イメージ名のタイプミスや存在しないタグの使用。
  • プライベートレジストリ認証:プライベートレジストリのImagePullSecretsがない、または間違っている。
  • ネットワークの問題:ノードがイメージレジストリに到達できない。

診断手順:

  1. Podを記述する:ここでも、kubectl describe podが鍵です。"Failed"、"ErrImagePull"、"ImagePullBackOff"などのイベントを探します。

    kubectl describe pod <pod-name> -n <namespace>
    

    出力イベントの例:Failed to pull image "my-private-registry/my-app:v1.0": rpc error: code = Unknown desc = Error response from daemon: pull access denied for my-private-registry/my-app, repository does not exist or may require 'docker login'

  2. ImagePullSecretsを確認する:PodまたはServiceAccountにimagePullSecretsが正しく設定されていることを確認します。

    kubectl get secret <your-image-pull-secret> -o yaml -n <namespace>
    

解決策:

  • イメージ名/タグを修正する:デプロイメントマニフェストのイメージ名とタグを再確認します。
  • ImagePullSecretsを設定するdocker-registryシークレットを作成し、それをPodまたはServiceAccountにリンクしていることを確認します。
    kubectl create secret docker-registry my-registry-secret \
      --docker-server=your-registry.com \
      --docker-username=your-username \
      --docker-password=your-password \
      --docker-email=your-email -n <namespace>
    
    次に、Pod仕様に追加します:
    spec:
      imagePullSecrets:
      - name: my-registry-secret
      containers:
      ...
    
  • ネットワーク接続:ノードからイメージレジストリへのネットワーク接続を確認します。

プライベートレジストリを使用している場合は、ServiceAccountも確認してください。多くのチームは、すべてのDeploymentではなく、名前空間のデフォルトServiceAccountにimagePullSecretsをアタッチしています:

kubectl get serviceaccount default -n <namespace> -o yaml

シークレットが存在してもプルが失敗する場合は、シークレット内のレジストリホスト名がイメージ参照のホスト名と完全に一致していることを確認してください。registry.example.com/app:v1https://registry.example.com/app:v1は同じ参照ではありません。

3. ボリューム関連の問題

PodがPersistentVolumeClaim(PVC)を必要とし、対応するPersistentVolume(PV)をプロビジョニングまたはバインドできない場合、PodはPendingのままになります。

診断手順:

  1. Podを記述する:ボリュームに関連するイベントを探します。

    kubectl describe pod <pod-name> -n <namespace>
    

    イベントには、FailedAttachVolumeFailedMount、または同様のメッセージが表示される場合があります。

  2. PVCとPVのステータスを確認する:PVCとPVのステータスを検査します。

    kubectl get pvc <pvc-name> -n <namespace>
    kubectl get pv
    

    PendingでスタックしているPVCや、バインドされていないPVを探します。

解決策:

  • StorageClassを確認する:特に動的プロビジョニングを使用する場合は、StorageClassが定義され、利用可能であることを確認します。
  • PVの可用性を確認する:静的プロビジョニングを使用する場合は、PVが存在し、PVCの条件を満たしていることを確認します。
  • アクセスモードを確認する:アクセスモード(例:ReadWriteOnceReadWriteMany)に互換性があることを確認します。

また、Podがボリュームをアタッチできるゾーンにスケジュールされているかどうかも確認してください。クラウドクラスターでは、あるアベイラビリティゾーンで作成されたディスクは、別のゾーンのノードにアタッチできない場合があります。イベントには通常、ボリュームノードアフィニティまたはアタッチ障害が記載されています。その場合、修正方法は、スケジューリング制約、別のStorageClass、または適切なゾーンでのボリュームの再作成です。

4. Taint、Toleration、Node Selector

クラスターに十分なCPUとメモリがある場合でも、PodはPendingのままになることがあります。スケジューラーは配置ルールも尊重する必要があります。

一般的な例:

  • Podに一致するノードがないnodeSelectorがある。
  • Podが必要とするノードアフィニティが厳しすぎる。
  • 一致する唯一のノードにtaintがあり、Podに一致するtolerationがない。
  • 名前空間に、要求されたリソースをブロックするクォータがある。

まずスケジューリングイベントを確認します:

kubectl describe pod <pod-name> -n <namespace>

次に、Podの配置ルールをノードラベルと比較します:

kubectl get pod <pod-name> -n <namespace> -o yaml
kubectl get nodes --show-labels
kubectl describe node <node-name>

イベントがtaintが許容されなかったことを示している場合は、Podを別の場所にスケジュールするか、そのワークロードが本当にそれらのノードに属している場合にのみtolerationを追加します。すべてのtaintを盲目的に許容しないでください。Taintは、多くの場合、特別なノード、GPUノード、インフラストラクチャノード、または負荷がかかっているノードを保護します。

CrashLoopBackOff状態のPodのトラブルシューティング

CrashLoopBackOff状態は、アプリケーションレベルの問題を示しています。コンテナは正常に起動しましたが、エラーで終了し、Kubernetesがそれを繰り返し再起動するように促しています。

1. アプリケーションエラー

最も一般的な原因は、アプリケーション自体の起動に失敗するか、起動後すぐに致命的なエラーが発生することです。

一般的な原因:

  • 依存関係/設定の欠落:アプリケーションが、依存する重要な設定ファイル、環境変数、または外部サービスを見つけられない。
  • 誤ったコマンド/引数:コンテナ仕様で指定されたcommandまたはargsが正しくないか、即座に終了する原因となっている。
  • アプリケーションロジックエラー:起動時にクラッシュを引き起こすアプリケーションコードのバグ。

診断手順:

  1. Podログを表示する:これは最も重要な手順です。ログには、アプリケーションがクラッシュした原因となった正確なエラーメッセージが表示されることがよくあります。

    kubectl logs <pod-name> -n <namespace>
    

    Podが繰り返しクラッシュしている場合、ログには最新の失敗した試行の出力が表示されることがあります。クラッシュしているコンテナの以前のインスタンスのログを表示するには、-p(previous)フラグを使用します:

    kubectl logs <pod-name> -p -n <namespace>
    
  2. Podを記述するContainersセクションのRestart Countを確認します。これは、コンテナがクラッシュした回数を示します。また、Last StateExit Codeも確認します。

    kubectl describe pod <pod-name> -n <namespace>
    

    終了コード0は通常、正常なシャットダウンを意味しますが、ゼロ以外の終了コードはエラーを示します。一般的なゼロ以外の終了コードには、1(一般的なエラー)、137(SIGKILL、多くの場合OOMKilled)、139(SIGSEGV、セグメンテーションフォールト)などがあります。

解決策:

  • アプリケーションログを確認する:ログに基づいて、アプリケーションコードまたは設定をデバッグします。必要なすべての環境変数、ConfigMapSecretsが正しくマウント/注入されていることを確認します。
  • ローカルでテストする:同じ環境変数とコマンドを使用してコンテナイメージをローカルで実行し、問題を再現してデバッグしてみます。

Podに複数のコンテナがある場合は、常にコンテナ名を指定します:

kubectl logs <pod-name> -c <container-name> -n <namespace>
kubectl logs <pod-name> -c <container-name> -p -n <namespace>

-cを指定しないと、メインアプリがクラッシュしている間にサイドカーのログを読んでいる可能性があります。

2. Liveness ProbeとReadiness Probeの失敗

KubernetesはLiveness ProbeとReadiness Probeを使用して、アプリケーションの健全性と可用性を判断します。Liveness Probeが継続的に失敗すると、Kubernetesはコンテナを再起動し、CrashLoopBackOffにつながります。

診断手順:

  1. Podを記述するContainersセクションでLivenessおよびReadinessプローブの定義とそのLast Stateを確認します。

    kubectl describe pod <pod-name> -n <namespace>
    

    "Liveness probe failed: HTTP probe failed with statuscode: 500"など、プローブの失敗を示すメッセージを探します。

  2. アプリケーションログを確認する:アプリケーションログが、プローブエンドポイントが失敗している理由のコンテキストを提供する場合があります。

解決策:

  • プローブ設定を調整する:プローブのpathportcommandinitialDelaySecondsperiodSeconds、またはfailureThresholdを修正します。
  • プローブエンドポイントの健全性を確認する:プローブのターゲットとなるアプリケーションエンドポイントが実際に正常であり、期待どおりに応答していることを確認します。アプリケーションの起動に時間がかかりすぎている可能性があり、より大きなinitialDelaySecondsが必要です。

起動の遅いアプリケーションの場合は、startupProbeを検討してください。これにより、Liveness Probeが評価を開始する前に、アプリケーションが初期化するためのより多くの時間が与えられます。これは、再起動のたびに大きなliveness initialDelaySecondsを設定するよりもクリーンです。

3. リソース制限の超過

コンテナが一貫してmemory.limitを超えるメモリを使用しようとしたり、cpu.limitを超えてCPUスロットリングが発生したりすると、カーネルがプロセスを終了し、多くの場合OOMKilled(メモリ不足による強制終了)イベントが発生します。

診断手順:

  1. Podを記述するLast StateまたはEventsセクションでOOMKilledを探します。Exit Code: 137は、多くの場合OOMKilledイベントを示します。

    kubectl describe pod <pod-name> -n <namespace>
    
  2. kubectl topを確認するmetrics-serverがインストールされている場合は、kubectl top podを使用してPodの実際のリソース使用量を確認します。

    kubectl top pod <pod-name> -n <namespace>
    

解決策:

  • リソース制限を増やす:アプリケーションが本当により多くのリソースを必要とする場合は、Podマニフェストのmemoryおよび/またはcpulimitsを増やします。これには、ノードの容量を増やす必要がある場合があります。
    resources:
      requests:
        memory: "256Mi"
        cpu: "500m"
      limits:
        memory: "512Mi" # これを増やす
        cpu: "1000m"   # これを増やす
    
  • アプリケーションを最適化する:アプリケーションをプロファイリングして、リソース消費を特定し削減します。

4. 権限の問題

コンテナは、必要なファイル、ディレクトリ、またはネットワークリソースにアクセスするために必要な権限がない場合、クラッシュする可能性があります。

診断手順:

  1. ログを確認する:アプリケーションログに、権限拒否エラー(EACCES)が表示される場合があります。
  2. Podを記述する:使用されているServiceAccountと、マウントされているsecurityContext設定を確認します。

解決策:

  • securityContextを調整する:必要に応じてrunAsUserfsGroup、またはallowPrivilegeEscalationを設定します。
  • ServiceAccountの権限:Podに関連付けられたServiceAccountに、RoleBindingClusterRoleBindingを介してバインドされた必要なRolesClusterRolesがあることを確認します。
  • ボリュームの権限:マウントされたボリューム(例:emptyDirhostPathConfigMapSecret)が、コンテナのユーザーに対して正しい権限を持っていることを確認します。

高速な意思決定ツリー

誰かが「Podが壊れている」と言ったら、次の順序で実行します:

kubectl get pod <pod-name> -n <namespace> -o wide
kubectl describe pod <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> --all-containers=true --tail=100
kubectl logs <pod-name> -n <namespace> --all-containers=true -p --tail=100

次に、分岐します:

  • kubectl get pod -o wideにノードがない場合、スケジューリングに焦点を当てます:リクエスト、taint、アフィニティ、クォータ、ノードの可用性。
  • ノードはあるがイベントがイメージプルに言及している場合、イメージ名、タグ、レジストリ認証、ノードからレジストリへのネットワークアクセスに焦点を当てます。
  • イベントがマウントまたはアタッチに言及している場合、PVC、PV、StorageClass、アクセスモード、ゾーン配置に焦点を当てます。
  • Podが起動してから再起動する場合、ログ、終了コード、プローブ、コマンド/引数、設定、シークレット、メモリ制限に焦点を当てます。

この順序は、アプリケーションコンテナを実際に起動したことのないPodのアプリケーションログを読むというよくある間違いを回避します。

過剰反応せずに終了コードを読む

終了コードは手がかりであり、完全な説明ではありません。

  • 1は通常、アプリケーションが一般的なエラーを返したことを意味します。数値よりもログの方が重要です。
  • 2は、多くのプログラムでコマンドラインの使用法エラーを示す可能性があります。
  • 126は、コマンドは存在するが実行できないことを意味することがよくあります。
  • 127は、コマンドが見つからなかったことを意味することがよくあります。
  • 137は、プロセスがSIGKILLを受信したときに一般的に表示されます。Kubernetesでは、これは多くの場合(常にではありませんが)OOMKilledに関連しています。
  • 143は、プロセスがSIGTERMを受信したことを意味します。これは通常の終了中に発生する可能性があります。

終了コードが137の場合は、メモリリークを想定する前に、PodのLast Stateとイベントを確認してください。ノードのドレイン、退避、または手動による強制終了でもコンテナを終了できます。

一般的な診断手順とツール

Podの問題に直面したときに実行するコマンドのクイックチェックリストは次のとおりです:

  • クイック概要を取得する:Podのステータスを確認します。
    kubectl get pods -n <namespace>
    kubectl get pods -n <namespace> -o wide
    
  • 詳細なPod情報:Podのイベント、状態、条件を理解するための最も重要なコマンド。
    kubectl describe pod <pod-name> -n <namespace>
    
  • コンテナログ:アプリケーションが報告している内容を確認します。
    kubectl logs <pod-name> -n <namespace>
    kubectl logs <pod-name> -p -n <namespace> # 以前のインスタンス
    kubectl logs <pod-name> -f -n <namespace> # ログを追跡
    
  • クラスター全体のイベント:問題が特定のPodではなく、クラスター全体のイベント(例:ノードのプレッシャー)である場合があります。
    kubectl get events -n <namespace>
    
  • インタラクティブなデバッグ:コンテナが起動してもすぐにクラッシュする場合は、短時間だけexecして入るか、設定されていれば別のデバッグコンテナに入ることができる場合があります。
    kubectl exec -it <pod-name> -n <namespace> -- bash
    
    (注:これは、コンテナがアタッチするのに十分な時間生きている場合にのみ機能します。)

Podの問題を回避するためのベストプラクティス

予防は治療に勝ります。以下のベストプラクティスに従うことで、PendingおよびCrashLoopBackOffのインシデントを大幅に削減できます:

  • 現実的なリソース要求と制限を設定する:妥当なrequestslimitsから始め、アプリケーションのプロファイリングと監視に基づいて微調整します。
  • 特定のイメージタグを使用する:本番環境ではlatestタグを避けてください。再現性のためにイミュータブルなタグ(例:v1.2.3commit-sha)を使用します。
  • 堅牢なプローブを実装する:アプリケーションの健全性を正確に反映するlivenessおよびreadinessプローブを設定します。initialDelaySecondsで起動時間を考慮します。
  • 集中ログ記録と監視:Prometheus、Grafana、ELKスタック、またはクラウドネイティブのログ記録サービスなどのツールを使用して、Podのログとメトリクスを収集および分析します。
  • マニフェストのバージョン管理:Kubernetesマニフェストをバージョン管理システム(例:Git)に保存して、変更を追跡し、ロールバックを容易にします。
  • 徹底的なテスト:本番環境にデプロイする前に、開発環境とステージング環境でコンテナイメージとKubernetesデプロイメントをテストします。
  • グレースフルシャットダウン:アプリケーションがSIGTERMシグナルを処理してグレースフルシャットダウンを行い、終了前にリソースを解放できるようにします。

通常、最も早く修正できるもの

Pendingの場合、kubectl describe podが通常最も早い方法です。スケジューラーとkubeletのイベントが、Kubernetesが何をできなかったかを説明するからです。CrashLoopBackOffの場合、以前のログが通常最も早い方法です。現在のコンテナは、ループを引き起こしたクラッシュを表示するには新しすぎる可能性があるからです。

当面の問題を修正した後は、予防策を探します:適切なサイズのリクエスト、より良いイメージタグ、スタートアッププローブ、CIでのシークレットチェックの欠落、またはより明確なランブック。最良のPodインシデントとは、次回認識しやすくなるインシデントです。