Kubernetes Pod障害トラブルシューティング:包括的ガイド

この包括的ガイドでKubernetes Pod障害の複雑さをナビゲートします。CrashLoopBackOff、ImagePullBackOff、リソース枯渇などの一般的な問題を診断するための構造化プロセスを学びます。`kubectl describe`や`kubectl logs --previous`などの重要なツールを活用して根本原因を特定し、コンテナ終了状態を解釈し、実用的な修正を実装してアプリケーションの稼働時間と安定性を維持する方法を詳しく説明します。

Kubernetes Pod障害トラブルシューティング:包括的ガイド

Kubernetes Pod障害のトラブルシューティングは、すべてのステータスを暗記することよりも、Kubernetesがどこに手がかりを残すかを学ぶことです。Podが「静かに」失敗することはほとんどありません。スケジューラー、kubelet、コンテナランタイム、イメージレジストリ、ボリュームプラグイン、そしてアプリケーションはすべて、異なる場所に痕跡を残します。コツは、それらを正しい順序で確認することです。そうしないと、イメージをプルできなかったPodのためにアプリケーションログを20分も読むことになります。

私は通常、1つの質問から始めます:Podはコンテナが起動する前に失敗したのか、コンテナの起動中に失敗したのか、それともアプリケーションの実行が開始された後に失敗したのか?この単一の分類により、調査の焦点が定まります。Pendingは通常、スケジューリング、ストレージ、またはイメージのセットアップを示します。ImagePullBackOffは、レジストリのパス、タグ、認証情報、またはノードのエグレスを示します。CrashLoopBackOffは通常、プロセスが起動して終了することを意味しますが、その理由は設定、ファイルの欠落、不正なコマンド、依存関係の失敗、またはメモリプレッシャーである可能性があります。


Pod診断の3つの柱

トラブルシューティングは、3つの主要なkubectlコマンドを使用して、失敗しているPodに関するすべての利用可能な情報を収集することから始まります。

1. 初期ステータス確認(kubectl get pods

最初のステップは常に、Podとそのコンテナの現在の状態を確認することです。STATUS列とREADY列に特に注意してください。

kubectl get pods -n my-namespace

Podステータスの解釈

ステータス 意味 初期アクション
Running 少なくとも1つのコンテナが実行中。必ずしもアプリがトラフィックを処理しているとは限らない。 READY、再起動回数、 readinessイベントを確認。
Pending PodはKubernetesに受け入れられたが、コンテナはまだ作成されていない。 スケジューリングイベントまたはイメージプルステータスを確認。
CrashLoopBackOff コンテナが起動し、クラッシュし、Kubeletが繰り返し再起動を試みる。 アプリケーションログを確認(kubectl logs --previous)。
ImagePullBackOff Kubeletが指定されたコンテナイメージをプルできない。 イメージ名、タグ、レジストリ認証情報を確認。
Error ランタイムエラーまたは起動コマンドの失敗によりPodが終了した。 ログとdescribeイベントを確認。
Terminating/Unknown Podはシャットダウン中、またはKubeletホストが応答しない。 ノードの健全性を確認。

2. 詳細調査(kubectl describe pod

ステータスがRunning以外の場合、describeコマンドは重要なコンテキストを提供し、スケジューリングの決定、リソース割り当て、コンテナの状態を詳述します。

kubectl describe pod [POD_NAME] -n my-namespace

出力の以下のセクションに注目してください:

  • Containers/Init Containers: State(特にWaitingまたはTerminated)とLast State(失敗理由が記録されることが多い、例:Reason: OOMKilled)を確認。
  • Resource Limits: LimitsRequestsが正しく設定されていることを確認。
  • Events: これが最も重要なセクションです。イベントは、スケジューリングの失敗、ボリュームマウントの問題、イメージプルの試行など、ライフサイクルの履歴を示します。

ヒント: Eventsセクションに「0/N nodes available」のようなメッセージが表示される場合、Podはリソース(CPU、メモリ)不足またはアフィニティルールが満たされていないためにスケジューリングに失敗している可能性があります。

最新の手がかりを得るにはイベントを下から上に読みますが、古いイベントを無視しないでください。Podには複数の問題がある可能性があります。たとえば、デプロイメントは、要求されたメモリが高すぎるためにFailedSchedulingで始まり、その後ノードが追加された後にImagePullBackOffに移行する場合があります。最終ステータスだけを見ると、問題を前進させた変更を見逃す可能性があります。

3. ログの確認(kubectl logs

ランタイムアプリケーションの問題の場合、ログはプロセスが終了した理由を説明するスタックトレースまたはエラーメッセージを提供します。

# 現在のコンテナログを確認
kubectl logs [POD_NAME] -n my-namespace

# Pod内の特定のコンテナのログを確認
kubectl logs [POD_NAME] -c [CONTAINER_NAME] -n my-namespace

# CrashLoopBackOffに重要:*前回*の失敗した実行のログを確認
kubectl logs [POD_NAME] --previous -n my-namespace

Podにサイドカーがある場合は、常に-cを含めてください。多くのイライラする調査は、失敗しているアプリケーションコンテナの代わりに正常なサイドカーのログを読むことから発生します。Initコンテナの失敗の場合は、-cとともにinitコンテナ名も使用します:

kubectl logs [POD_NAME] -c [INIT_CONTAINER_NAME] -n my-namespace

一般的なPod障害シナリオと解決策

ほとんどのPod障害は、いくつかの認識可能なパターンに分類され、それぞれに的を絞った診断アプローチが必要です。

シナリオ1: CrashLoopBackOff

これは最も頻繁なPod障害です。コンテナは正常に起動しているが、コンテナ内のアプリケーションがすぐに(ゼロ以外の終了コードで)終了していることを示します。

診断:

  1. kubectl logs --previousを使用して、トレースバックまたは終了理由を確認します。
  2. kubectl describeを使用して、Last StateセクションのExit Codeを確認します。

一般的な原因と修正:

  • 終了コード1/2: 一般的なアプリケーションエラー、設定ファイルの欠落、データベース接続障害、または不正な入力によるアプリケーションクラッシュ。
    • 修正: アプリケーションコードをデバッグするか、マウントされているConfigMap/Secretを確認します。
  • 依存関係の欠落: エントリポイントスクリプトに、存在しないファイルや環境が必要です。
    • 修正: Dockerfileとイメージビルドプロセスを確認します。
  • 不正なコマンドまたは引数: コンテナイメージは有効ですが、Pod仕様のコマンドがイメージのエントリポイントを誤ってオーバーライドしています。
    • 修正: Deploymentのcommandargsをイメージの期待される起動コマンドと比較します。可能であれば、同じイメージをローカルでテストします。
  • プローブによる再起動: livenessプローブが、ウォームアップを完了する前に起動の遅いアプリケーションを強制終了する可能性があります。
    • 修正: initialDelaySecondsを増やすか、startupProbeを使用するか、プローブをより軽量なヘルスエンドポイントに向けます。

実用的なパターンは、同じイメージで無害なコマンドを使用して一時的なコピーを1つデプロイし、ファイルシステムと環境を検査することです:

kubectl run debug-image \
  --image=registry.example.com/app:tag \
  --restart=Never \
  --command -- sleep 3600

kubectl exec -it debug-image -- /bin/sh

これはDeploymentの修正に代わるものではありませんが、設定ファイルが実際にイメージ内にあるか、バイナリが存在するか、コンテナに期待されるシェルがあるか、環境変数が存在するかなど、簡単な質問に迅速に答えるのに役立ちます。

シナリオ2: ImagePullBackOff / ErrImagePull

これは、KubeletがPod定義で指定されたコンテナイメージを取得できない場合に発生します。

診断:

  1. kubectl describeEventsセクションで、特定のエラーメッセージ(例:404 Not Foundauthentication required)を確認します。

一般的な原因と修正:

  • タイポまたは間違ったタグ: イメージ名またはタグが間違っています。
    • 修正: DeploymentまたはPod仕様のイメージ名を修正します。
  • プライベートレジストリアクセス: クラスターにプライベートレジストリ(Docker Hub、GCR、ECRなど)からプルするための認証情報がありません。
    • 修正: Pod仕様で適切なimagePullSecretが参照され、そのSecretが名前空間に存在することを確認します。
# プルシークレットを使用するためのPod仕様スニペットの例
spec:
  containers:
  ...
  imagePullSecrets:
  - name: my-registry-secret

また、プルシークレットがどこにあるかも確認してください。Kubernetesシークレットは名前空間スコープです。defaultregcredという名前のシークレットは、paymentsのPodには役立ちません。同じイメージが1つの名前空間で機能し、別の名前空間で失敗する場合は、レジストリが壊れていると想定する前に、サービスアカウントとイメージプルシークレットを比較します:

kubectl get serviceaccount default -n payments -o yaml
kubectl get secret regcred -n payments

シナリオ3: Pendingステータス(スタック)

PodがPendingステータスのままになるのは、通常、ノードにスケジュールできないか、リソース(PersistentVolumeなど)を待機していることを示します。

診断:

  1. kubectl describeを実行し、Eventsセクションを確認します。

一般的な原因と修正:

  • リソース不足: クラスターに、Podのrequestsを満たすのに十分なCPUまたはメモリが利用可能なノードがありません。
    • 修正: クラスターサイズを増やすか、Podのリソース要求を(可能であれば)減らします。
    • イベントメッセージ例: 0/4 nodes are available: 4 Insufficient cpu.
  • ボリュームバインディングの問題: Podは、基盤となるPersistentVolume(PV)にバインドできないPersistentVolumeClaim(PVC)を必要とします。
    • 修正: PVCのステータス(kubectl get pvc)を確認し、StorageClassが機能していることを確認します。
  • セレクターまたはアフィニティの不一致: Podは存在しないノードラベルを要求するか、必要なアフィニティルールがすべてのノードを除外します。
    • 修正: nodeSelectornodeAffinity、およびノードラベルをkubectl get nodes --show-labelsと比較します。
  • 許容されないテイント: ノードは利用可能ですが、Podに一致する toleration がないため、このPodをはじきます。
    • 修正: 意図した toleration をPodに追加するか、テイントが実際の配置ルールを表さなくなった場合は削除します。

シナリオ4: OOMKilled(メモリ不足による強制終了)

これは通常CrashLoopBackOffステータスになりますが、根本的な原因は特定です。コンテナが仕様で定義された制限よりも多くのメモリを使用し、ホストオペレーティングシステム(Kubelet経由)が強制的に終了させたということです。

診断:

  1. kubectl describe -> Last State -> Reason: OOMKilledを確認します。

修正:

  1. 制限を増やす: Pod仕様のメモリlimitを増やし、より多くのヘッドルームを提供します。
  2. アプリケーションを最適化する: アプリケーションをプロファイリングして、メモリフットプリントを削減します。
  3. リクエストを設定する: requestsを実際の定常状態の使用量に近づけて設定し、スケジューリングの信頼性を向上させます。
resources:
  limits:
    memory: "512Mi" # この値を増やす
  requests:
    memory: "256Mi"

制限を引き上げるだけのメモリ「修正」には注意してください。アプリケーションにリークがある場合、制限を高くしても次の障害を遅らせるだけで、ノードにより多くのリスクをもたらす可能性があります。メトリクスシステムで経時的なメモリを確認してください。ガベージコレクション後にベースラインに戻るのこぎり波パターンは、OOMまで着実に上昇するパターンとは異なります。

シナリオ5: CreateContainerConfigError と CreateContainerError

これらのステータスは、アプリケーションの失敗のように聞こえないため、見落とされがちです。通常、kubeletがコンテナ設定を組み立てられなかったことを意味します。

一般的な原因は次のとおりです:

  • 参照されたConfigMapまたはSecretが名前空間に存在しない。
  • ConfigMapまたはSecret内のキーのスペルが間違っている。
  • ボリュームマウントパスが別のマウントと競合している。
  • コンテナが無効なセキュリティコンテキストを参照している。

最速の確認方法は依然としてdescribeです:

kubectl describe pod [POD_NAME] -n my-namespace

secret "app-config" not foundconfigmap "settings" not foundなどのイベントメッセージを探します。次に、オブジェクトを確認します:

kubectl get secret app-config -n my-namespace
kubectl get configmap settings -n my-namespace -o yaml

これは一般的なデプロイメントパイプラインのミスです。アプリケーションマニフェストは適用されますが、シークレット作成ステップが失敗したか、間違った名前空間に対して実行されました。

シナリオ6: RunningだがReadyでない

PodはRunningを表示していても、使用できない場合があります。READY列は、 readinessプローブに従って準備ができているコンテナの数を示します。1/2または0/1のPodは生きている可能性がありますが、Serviceエンドポイントから削除されています。

トラフィックが失敗しているがPodが生きているように見える場合は、エンドポイントを確認します:

kubectl get endpoints [SERVICE_NAME] -n my-namespace
kubectl describe pod [POD_NAME] -n my-namespace

エンドポイントリストが空の場合、問題はreadinessプローブ、Serviceセレクターの不一致、またはアプリケーションがServiceの想定とは異なるポートでリッスンしていることにある可能性があります。実際のインシデントでは、これが時間を浪費する原因です。Podがトラフィック不足の理由ではないにもかかわらず、人々はPodを再起動し続けます。


将来の障害を防ぐ:ベストプラクティス

堅牢なアプリケーションには、一般的なデプロイメントの落とし穴を防ぐための注意深い設定が必要です。

LivenessプローブとReadinessプローブを使用する

プローブを適切に実装することで、Kubernetesはコンテナの健全性をインテリジェントに管理できます:

  • Livenessプローブ: コンテナが実行を継続するのに十分健全かどうかを判断します。livenessプローブが失敗すると、Kubeletはコンテナを再起動します(ソフトロックを解決します)。
  • Readinessプローブ: コンテナがトラフィックを処理する準備ができているかどうかを判断します。readinessプローブが失敗すると、PodはServiceエンドポイントから削除され、コンテナが回復する間のリクエスト失敗を防ぎます。

Kubernetesに依存関係の短い停止があるたびにコンテナを再起動させたいのでない限り、livenessプローブを深い依存関係チェックに向けないでください。データベースが30秒間利用できないことは、通常、プロセスが停止していることを証明するものではありません。Readinessは、「このPodに今トラフィックを送らないでください」と言うのに適した場所です。

リソース制限とリクエストを適用する

コンテナには常にリソース要件を定義してください。これにより、Podが過剰なリソースを消費するのを防ぎ(ノードの不安定性につながる)、スケジューラーが十分な容量を持つノードにPodを配置できるようにします。

セットアップにInitコンテナを利用する

メインアプリケーションが起動する前に、Podが依存関係のチェックやデータのセットアップ(例:データベースマイグレーションの完了を待つ)を必要とする場合は、Initコンテナを使用します。Initコンテナが失敗すると、Podはそれを繰り返し再起動し、セットアップエラーをアプリケーションのランタイムエラーから明確に分離します。

実用的なトリアージフロー

オンコールの際は、再現可能なパスを使用します:

  1. kubectl get pods -n <namespace> -o wideを確認して、ステータス、再起動回数、経過時間、ノード配置を確認します。
  2. kubectl describe podを実行し、Events、State、Last State、マウント、リソース設定を読み取ります。
  3. kubectl logsでログを取得し、再起動されたコンテナには--previousを、マルチコンテナPodには-cを追加します。
  4. PodがPendingの場合は、アプリログを読む前に、スケジューリング、テイント、ノードラベル、PVCを調査します。
  5. PodがRunningだがトラフィックを受信していない場合は、readinessプローブ、Serviceセレクター、エンドポイントを調査します。
  6. PodがOOMKilledの場合は、単純に数値を増やす前に、制限を実際のメモリグラフと比較します。

この順序により、Kubernetesがまだアプリケーションを起動していないのに、アプリケーションに直接飛びつくことを防ぎます。

最終確認

最も有用な習慣は、症状と原因を分離することです。CrashLoopBackOffは症状です。原因は、シークレットの欠落、不正なマイグレーション、livenessプローブ、またはメモリ制限である可能性があります。Pendingは症状です。原因は、CPUリクエスト、PVC、テイント、またはノードセレクターである可能性があります。Podのステータスにどこを見るべきかを教えてもらい、イベントとログに何が変わったかを教えてもらいましょう。