2026年5月22日
2026年5月22日
KubernetesのPodのデバッグ方法|Pending・CrashLoopBackOff・ImagePullBackOffの原因の調べ方
Podが動かないときは、いきなり設定を書き換えるのではなく「Podの状態(STATUS)→ describe のイベント → コンテナのログ」の順に見ると、原因の多くは数分で絞り込めます。この記事では、状態ごとに考えられる原因を起こりやすい順に並べ、それぞれの確認コマンドと直し方を説明します。シェルの入っていないイメージを調べるための kubectl debug の使い方も扱います。
コマンドは、Kubernetesの現行のサポート対象の版(2026年10月現在)で共通して使えるものに絞っています。クラスタの版が古い場合は、kubectl version でクライアントとサーバーの版を先に確かめてください。
まず全体を見る3つのコマンド
どの症状でも、最初に打つコマンドは同じです。
# 1. 状態と、どのノードに載っているか
kubectl get pods -n <namespace> -o wide
# 2. Podの詳細と、末尾の Events
kubectl describe pod <pod名> -n <namespace>
# 3. 名前空間のイベントを時系列で
kubectl get events -n <namespace> --sort-by=.lastTimestamp
get pods の STATUS 列と RESTARTS 列で、どの種類の問題かがほぼ決まります。describe の末尾にある Events は、スケジューラやkubeletが「なぜ動かせなかったか」を文章で残している場所です。調査の手がかりの大半はここにあります。
注意点として、イベントは一定時間(既定では1時間程度)で消えます。古い障害を調べるときに Events が空なのは「何も起きていない」ではなく「もう消えた」可能性があります。長く残したい場合は、イベントを外部のログ基盤に送る仕組みを別に用意します。
状態別の原因と直し方
Pending:どのノードにも載せられない
Pendingは、スケジューラがPodを置けるノードを見つけられていない状態です。describe の Events に FailedScheduling があり、0/3 nodes are available: に続けて理由が書かれます。起こりやすい順に次のとおりです。
- リソース不足(
Insufficient cpu/Insufficient memory):スケジューラは実際の使用量ではなく、各Podのresources.requestsの合計で空きを判断します。requestsが大きすぎないか、ノードを増やす必要があるかを確認します。kubectl describe node <ノード名>の「Allocated resources」で、requestsの割り当て状況が分かります。 - taintを許容していない(
had untolerated taint):ノードに付いたtaintに対応するtolerationがPodにありません。意図した専用ノードなのか、障害で自動的に付いたtaintなのかを見分けます。 - PVCが割り当てられない(
unbound immediate PersistentVolumeClaims):kubectl get pvcでSTATUSがBoundか確認します。StorageClassの名前違いや、ゾーンの不一致がよくある原因です。 - nodeSelector・affinityの条件に合うノードが無い:ラベルの綴り違いを
kubectl get nodes --show-labelsで確認します。
ImagePullBackOff / ErrImagePull:イメージを取得できない
Events の Failed to pull image の行に、レジストリからの応答が書かれています。
- イメージ名・タグの誤り:
not foundやmanifest unknownならこれです。タグの打ち間違いや、まだpushしていないタグを指定していないか確認します。 - 認証情報が無い:プライベートレジストリで
unauthorizedやdeniedが出る場合、imagePullSecretsの指定漏れか、Secretの名前空間違いです。Secretは使うPodと同じ名前空間に作る必要があります。 - レート制限:公開レジストリの匿名取得には回数制限がある場合があり、
toomanyrequestsが出ます。認証付きで取得するか、自前のレジストリにミラーします。
CrashLoopBackOff:起動しては落ちるを繰り返す
コンテナが起動直後に終了し、kubeletが間隔を延ばしながら再起動している状態です。間隔は最大で5分程度まで延びるため、待っても直りません。まず前回落ちたコンテナのログを見ます。
# 現在のコンテナではなく、直前に落ちたコンテナのログ
kubectl logs <pod名> -n <namespace> --previous
# 複数コンテナのPodは -c で指定
kubectl logs <pod名> -c <コンテナ名> --previous
# 終了理由と終了コード
kubectl get pod <pod名> -o jsonpath='{.status.containerStatuses[*].lastState.terminated}'
--previous を付け忘れると、再起動直後の空のログしか見えず、手がかりが無いように見えます。ここがいちばんのつまずきどころです。
終了コードは原因の切り分けに役立ちます。
- 1 などアプリ由来のコード:アプリの設定ミスです。環境変数の不足、DBへの接続失敗、設定ファイルのパス違いがよくあります。WordPressなら、DB接続情報を渡すSecretのキー名違いで起動に失敗する例が典型です。
- 137:SIGKILLで止められています。reasonが
OOMKilledならメモリのlimits超過です。limitsを上げるか、アプリのメモリ使用量を見直します。 - 126 / 127:コマンドが実行できない、または見つかりません。
commandやargsの誤り、イメージのCPUアーキテクチャ違い(Armのノードでamd64専用イメージを動かす等)を疑います。
ログに異常が無いのに再起動を繰り返す場合は、livenessProbeの失敗を疑います。Eventsに Liveness probe failed が出ていれば、起動に時間がかかるアプリに対して判定が厳しすぎる可能性があります。startupProbeを足して、起動完了までlivenessの判定を待たせるのが定石です。
CreateContainerConfigError:設定が揃っていない
参照しているConfigMapやSecret、またはそのキーが存在しない状態です。Eventsに secret "xxx" not found や couldn't find key と出ます。名前とキーの綴り、名前空間を確認します。
Running なのにアクセスできない(READY が 0/1)
STATUSはRunningでも、READY列が 0/1 ならreadinessProbeが失敗しています。この間、ServiceはそのPodにトラフィックを送りません。Serviceの転送先に入っているかは次で確認できます。
kubectl get endpointslices -n <namespace> -l kubernetes.io/service-name=<service名>
転送先が空なら、readinessProbeのパスやポートが実際のアプリと合っているか、Serviceの selector がPodのラベルと一致しているかを確認します。
Terminating のまま消えない
ノードが落ちている、またはfinalizerが外れていないことが多い原因です。kubectl get pod <pod名> -o yaml で metadata.finalizers を確認します。--grace-period=0 --force での強制削除は、Kubernetesが実際にコンテナが止まったかを確認しないまま記録を消す操作です。StatefulSetのPodでは同じPodが二重に動く危険があるため、ノードの状態を確かめてから最後の手段として使います。
コンテナの中を調べる
kubectl exec
シェルが入っているイメージなら、中に入って設定ファイルや名前解決を確認できます。
kubectl exec -it <pod名> -n <namespace> -- sh
kubectl debug(エフェメラルコンテナ)
distrolessなどシェルの無いイメージや、すぐ落ちてしまうコンテナには kubectl debug を使います。動いているPodに、調査用のコンテナを一時的に追加する機能です。
# 動作中のPodに調査用コンテナを追加し、対象コンテナのプロセスを見られるようにする
kubectl debug -it <pod名> -n <namespace> --image=busybox:1.36 --target=<コンテナ名>
# CrashLoopBackOff のPodは、コマンドを差し替えたコピーを作って調べる
kubectl debug <pod名> -n <namespace> -it --copy-to=<pod名>-debug --container=<コンテナ名> -- sh
--target を付けると対象コンテナのプロセス名前空間を共有でき、ps で本体のプロセスが見えます。ただしランタイムによっては共有できない場合があります。--copy-to で作ったコピーは自動では消えないため、調査が終わったら kubectl delete pod <pod名>-debug で削除します。
ノード自体を調べたいときは kubectl debug node/<ノード名> -it --image=busybox:1.36 で、ノードのファイルシステムを /host にマウントしたPodを起動できます。ノードに対して強い権限を持つ操作のため、本番での使用はRBACで限られた人に絞ります。
調査を楽にするための備え
- requests・limitsを全コンテナに設定する:Pendingや OOMKilled の原因を数値で説明できるようになります。
- probeを用途どおりに分ける:起動待ちはstartupProbe、トラフィックの受け入れ可否はreadinessProbe、固まったときの再起動はlivenessProbeです。
- ログは標準出力に出す:ファイルにだけ書くと
kubectl logsで見えません。 - **
kubectl top podを使えるようにする**:実際のCPU・メモリ使用量を見るには、metrics-serverの導入が必要です。
本番で設定を変える前には、現在のマニフェストを kubectl get <リソース> -o yaml で保存し、検証用の名前空間やクラスタで再現を確かめてから反映すると、戻せなくなる事態を避けられます。
まとめ
- 調べる順番は「
get podsの状態 →describeの Events →logs --previous」です。 - Pendingはスケジューリングの問題で、requests・taint・PVC・ラベル条件の順に確認します。
- CrashLoopBackOffは前回のログと終了コード(137はOOMKilledなど)で切り分け、ログに異常が無ければlivenessProbeを疑います。
- Runningでもアクセスできないときは、READY列とEndpointSliceでreadinessProbeとselectorを確認します。
- シェルの無いイメージは
kubectl debugのエフェメラルコンテナやコピーで調べ、使い終えたら片付けます。