kubectl デバッグパターン
5部 (運用 · デバッグ · コスト) の最初の章です。運用クラスタで最もよく出会う事故 (CrashLoopBackOff、OOMKilled、ImagePullBackOff、Pending、Service に届かない) の診断ツリーを集約します。describe · events · logs の3つのコマンドから出発し、kubectl debug の ephemeral container、ネットワーク診断パターン、19章の可観測性スタックとの連携まで、新人 SRE の最初の参照資料になるマニュアルとして整理します。
5部 (運用 · デバッグ · コスト) の最初の章です。4部までを経て EKS クラスタの上で myshop-api が一連の流れで回る状態に到達しましたが、実際の運用は事故から自由ではありません。どんな事故がどんな形で入ってくるのか、そのときどこから見るべきか、どの道具がどんな信号を見せてくれるのかという深さは、自動化では解けない領域です。本章はその要点を一冊のデバッグマニュアルとして整理します。
運用クラスタで1人が1年に出会う事故の半分以上は、5つのパターンに圧縮されます — CrashLoopBackOff、OOMKilled、ImagePullBackOff、Pending、Service に届かない。この5つの診断ツリーを頭の中に置いておくと、深夜に PagerDuty が鳴っても5分以内に一次原因の候補が絞られます。本章の目標は、その5つのツリーと、その上に乗った kubectl debug · ephemeral container · ネットワーク診断の道具が、1人の頭の中に入っている状態です。
デバッグの出発線 — 3つのコマンド #
ほぼすべてのデバッグの出発点は次の3つのコマンドです。順序が決まっており、各コマンドが見せる情報が異なります。
kubectl describe pod <name> -n <ns>
kubectl get events -n <ns> --sort-by='.lastTimestamp'
kubectl logs <pod> -n <ns> [-c <container>] [--previous]各コマンドの責任を一行で整理します。
describe— Pod の現在の spec + status + 直近の events が一画面に。事故の最初の5行で答えが見える場合が多いです。events— クラスタ全体の時系列イベント。scheduler · kubelet · controller が誰が何を言ったかが一つの流れで掴めます。logs— コンテナ自体の stdout / stderr。アプリケーションの本当のエラーはここにあります。
3つのコマンドだけで解けない事故だけが kubectl debug や可観測性スタックへ進みます。事故を見たら無条件にこの3つをまず が運用デバッグの最初のルールです。
Pod 状態の意味 — 9種類と次のアクション #
kubectl get pods の STATUS カラムによく登場する状態を一表に整理します。
| 状態 | 意味 | 最初のアクション |
|---|---|---|
Pending | スケジューリング待ち | describe の Events で scheduler メッセージ |
ContainerCreating | kubelet がコンテナ準備中 | describe の Events (イメージプル、ボリュームマウント) |
Running | コンテナ実行中 (ただし ready とは別) | kubectl get pod -o wide で READY カラム確認 |
CrashLoopBackOff | コンテナが反復クラッシュ | logs --previous と exit code |
OOMKilled | メモリ上限超過で終了 | events の reason + 11章 の上限 |
ImagePullBackOff | イメージを取得できない | events の reason (権限 / レジストリ / タグ) |
Error | 終了 (再起動なし) | restartPolicy と exit code |
Completed | 成功終了 (Job の正常状態) | 正常 — Job かどうか確認 |
Init:Error など | initContainer 段階の失敗 | logs -c <init-container-name> |
READY カラムが 0/1 なのに STATUS が Running なら、12章 ヘルスチェック の readinessProbe が失敗中だという信号です。STATUS が Running だからといって正常ではありません。 運用デバッグでよく見落とす一枠です。
describe pod の events セクションを読む #
kubectl describe pod の出力末尾にある Events セクションが事故の90%を見せてくれます。どのコンポーネントが送ったかによって意味が異なります。
Source | 見る結
default-scheduler | スケジューリング段階 — Pending の原因 (taint, affinity, resources, PVC)
kubelet | ノードの上での段階 — image pull, mount, probe, OOM
controller-manager | 上位コントローラのアクション — ReplicaSet の Pod 生成 / 削除
attachdetach | EBS / EFS ボリュームのマウント段階運用デバッグで最もよく見る出所は kubelet と default-scheduler です。kubelet が送った Failed to pull image と default-scheduler が送った 0/3 nodes are available: 3 Insufficient memory が、それぞれ ImagePullBackOff と Pending の本当の原因を直接教えてくれます。
kubectl logs のパターン #
# リアルタイムストリーム
kubectl logs -f <pod>
# 最後の N 行
kubectl logs --tail=200 <pod>
# マルチコンテナ Pod
kubectl logs <pod> -c <container>
# 直前に死んだコンテナ (CrashLoopBackOff の核心)
kubectl logs <pod> --previous
# Deployment / Label 単位
kubectl logs -l app.kubernetes.io/name=myshop-api --tail=100CrashLoopBackOff デバッグの95%は --previous という1つのオプションです。現在のコンテナは起動すらできていないので現在のログがなく、直前に死んだコンテナのログを見てこそ本当のエラーメッセージが出ます。この一行を知らないと「ログが空なのに何を見ればいいか分からない」の罠にはまります。
ラベルセレクタで複数の Pod のログを一度に見るパターンは、22章 アプリ配備の骨格 の app.kubernetes.io/name 標準ラベルのおかげで自然に回ります。
kubectl exec の限界 と kubectl debug #
kubectl exec -it <pod> -n <ns> -- /bin/shexec の限界は明確です。
- コンテナが死んでいたら exec 不可 — CrashLoopBackOff の Pod に exec ができません。
- distroless / scratch イメージには shell がない —
/bin/shがないコンテナには exec が無意味です。 - デバッグ道具がコンテナにない —
curl、dig、tcpdumpのような道具がないのが普通です。
この3つの限界を解く道具が kubectl debug です。Pod の同じネームスペースの中に ephemeral container を挟み込み、そこで診断を行います。
# Pod の中に busybox コンテナを挟み込む
kubectl debug -it <pod> -n <ns> \
--image=busybox:1.36 \
--target=<container-name>
# distroless Pod のファイルシステムを見る (--target で同じ PID namespace 共有)
kubectl debug -it <pod> -n <ns> \
--image=nicolaka/netshoot \
--target=<container-name>--target オプションが核心 — 同じ PID namespace を共有することで元のコンテナのプロセスを見ることができ、--profile=netadmin オプションを加えればネットワーク診断まで可能です。
kubectl debug node/<node-name> -it --image=busyboxノードのホストファイルシステムが /host にマウントされたコンテナが立ち上がり、/host/var/log/ の kubelet ログを見ることができます。EBS CSI Driver のマウントエラーのようなノードレベルの事故で決定的な道具です。
CrashLoopBackOff 診断ツリー #
最もよくある事故です。Pod が起動直後に死に、再び起動し、また死ぬサイクルです。
1. kubectl logs <pod> --previous
-> 直前のコンテナの stderr が本当の原因。95% の場合ここで答えが出る。
2. kubectl describe pod <pod>
-> Events の Last State / Reason / Exit Code 確認。
-> Exit Code 137 = SIGKILL (OOM または強制終了)
-> Exit Code 139 = SIGSEGV (segfault)
-> Exit Code 1 = 一般エラー
3. probe 失敗を疑う
-> [12章 ヘルスチェック](./health-checks/) の liveness が早く fail しすぎると
アプリケーションが生きていても kubelet がコンテナを殺す。
-> initialDelaySeconds が十分かを点検。
4. initContainer 段階を疑う
-> STATUS が Init:Error なら init 段階の失敗。
-> kubectl logs <pod> -c <init-container-name>
5. ConfigMap / Secret 欠落
-> describe Events の "MountVolume.SetUp failed"。
-> [6章 ConfigMap · Secret](./configmap-and-secret/) の名前一致を確認。backoff の指数増加も知っておくと役立ちます — 10秒 → 20秒 → 40秒 → … → 最大5分へと伸びます。「1時間も同じ事故が解けない」のではなく「kubelet が5分に1回ずつ再試行する正常状態」 の場合があります。
OOMKilled 診断ツリー #
1. kubectl get events -n <ns> --field-selector reason=OOMKilling
-> どの Pod がいつ OOMKilled されたかの時系列リスト。
2. kubectl describe pod <pod>
-> Last State -> Terminated -> Reason: OOMKilled, Exit Code: 137。
3. [11章 リソース要求と上限](./resources-and-limits/) の limits.memory 確認
-> コンテナの実際の使用量が limits を超えた時点で終了。
-> [25章 モニタリング·アラート](./monitoring-and-alerts/) の
container_memory_working_set_bytes メトリクスでパターン確認。
4. memory leak を疑う
-> 時間グラフで右肩上がりの直線ならコードレベルのリーク。
-> heap profile (Java jmap, Go pprof, Python memray) で追加診断。
5. 誤った limits を疑う
-> JVM の -Xmx が container limits より大きく設定されている場合がある。
-> Native ライブラリのメモリ使用は heap 以外に別途。cgroup v2 環境 (カーネル 4.5+ / EKS 1.25+ デフォルト) では OOMKilled の動作が少し異なります。cgroup v1 ではコンテナの中の PID 1 が死にましたが、v2 ではメモリを最も多く使うプロセスが先に死ぬことがあります。マルチプロセスコンテナではどのプロセスが死んだかの確認が必要です。
ImagePullBackOff 診断ツリー #
1. kubectl describe pod <pod>
-> Events の reason で6つの原因を絞る:
- "manifest unknown" / "not found"
-> イメージタグの誤字または ECR へのプッシュ失敗。
[24章 CI/CD](./cicd-pipeline/) の ECR push 段階を確認。
- "unauthorized" / "denied"
-> ECR 権限不足。Node IAM Role の ECR read 権限を確認、
または imagePullSecret の認証情報を確認。
- "no basic auth credentials"
-> imagePullSecret 自体がないか名前の誤字。
- "x509: certificate signed by unknown authority"
-> private registry の証明書の信頼問題。
- "context deadline exceeded"
-> ネットワーク遅延。NAT Gateway / VPC Endpoint の経路を確認。
- "ECR repository ... does not exist"
-> 別の region / 別の account の ECR を指している場合。EKS 環境では 21章 EKS クラスタセットアップ でノード IAM Role に AmazonEC2ContainerRegistryReadOnly ポリシーが付いていることが ECR pull の基本的な権限経路です。別の account の ECR を使うなら、repository policy で cross-account 権限を明示的に開いてやる必要があります。
Pending 診断ツリー #
1. kubectl describe pod <pod>
-> Events の default-scheduler メッセージが即座に答えを教えてくれる。
2. "0/N nodes are available: ... Insufficient cpu/memory"
-> [11章](./resources-and-limits/) の requests がノードの可用量を超過。
-> [13章 オートスケーリング](./autoscaling/) の Karpenter / Cluster Autoscaler が
新しいノードを立ち上げている途中か確認。
3. "Insufficient nodes match node selector / affinity"
-> nodeSelector のラベルキーの誤字、またはそのラベルを持つノードがない。
-> kubectl get nodes --show-labels でラベル確認。
4. "had taints that the pod didn't tolerate"
-> ノードの taint と Pod の toleration の不一致。
-> Karpenter の NodePool が spot のみ立ち上げるのに Pod に toleration なし、など。
5. "pod has unbound immediate PersistentVolumeClaims"
-> [9章 PV/PVC/StorageClass](./pv-pvc-storageclass/) の動的プロビジョニング失敗。
-> StorageClass の EBS CSI Driver が正常動作するか確認。
6. Karpenter の応答時間を疑う
-> 新しいノードのプロビジョニングまで30秒 ~ 2分。
-> その時間の間 Pending は正常。Pending の診断は 「なぜスケジューラが拒否したか」 という1つの問いに絞られます。describe の Events の一行が常に答えを持っているという点が、OOMKilled / CrashLoop と異なる性質です。
Service / Ingress に届かないとき #
1. Pod 自体が ready か?
-> kubectl get pods -l <selector> -o wide
-> READY が 1/1 で STATUS が Running か。
-> readinessProbe が失敗中なら Endpoints から自動除外される。
2. Service の selector が Pod ラベルと一致するか?
-> kubectl describe service <svc>
-> kubectl get endpoints <svc> -- Endpoints が空なら selector の不一致。
3. Service の port が Pod の containerPort と合っているか?
-> targetPort は Pod の中のポート (またはポート名)。
-> port は Service 自体の仮想ポート。
4. NetworkPolicy が遮っているか?
-> [14章 RBAC/NetworkPolicy/ResourceQuota](./rbac-networkpolicy-quota/) の ingress ルールを確認。
5. Ingress の ALB が実際に立ち上がったか?
-> kubectl describe ingress -- Address フィールド。
-> kubectl logs -n kube-system deployment/aws-load-balancer-controller。
-> ALB target group の healthy / unhealthy 状態 (AWS コンソール)。
6. DNS 解決自体の点検
-> kubectl run -it --rm dns-test --image=busybox:1.36 \
-- nslookup <svc>.<ns>.svc.cluster.local5章 Service の selector → endpoints → port の3段チェーンが運用デバッグの基本的な思考モデルです。3つのうちどこが切れているかを絞るのが Service デバッグの核心です。
ネットワーク診断道具 #
# 最も軽い一時コンテナ
kubectl run -it --rm net-test --image=busybox:1.36 \
-n <ns> -- /bin/sh
# フルパッケージ (curl, dig, traceroute, tcpdump, nslookup)
kubectl run -it --rm net-test --image=nicolaka/netshoot \
-n <ns> -- /bin/bashnicolaka/netshoot が事実上の標準診断イメージです。次のシナリオを1つのコンテナの中ですべて解けます。
# DNS
dig myshop-api.myshop.svc.cluster.local
# HTTP 接続
curl -v http://myshop-api.myshop:80/health/ready
# 経路追跡
traceroute api.myshop.example.com
# ノード IP と Pod IP のルーティング
ip route15章 CNI 深掘り の VPC CNI モデル (Pod が直接 ENI の IP を受け取る構造) のおかげで、EKS では Pod の IP がノードのルーティングテーブルに自然に入ります。だから tcpdump がノードの host network からも Pod トラフィックを捕まえられます。
kubectl debug node/<node-name> -it --image=nicolaka/netshoot
# その中で:
tcpdump -i any -n 'host <pod-ip>'この道具が手に入っていれば、10章 Ingress の ALB → Pod 経路のどこでパケットが消えるかを一度に追跡できます。
可観測性との接続 — いつどこへ行くか #
kubectl describe と Grafana ダッシュボードのどちらへ先に行くべきかは、事故の種類によって異なります。
[単一 Pod の問題] — describe / logs / events のほうが速い
- CrashLoopBackOff, ImagePullBackOff, OOMKilled
- 「特定の Pod 1個だけがダメ」
[分散 / 統計的な問題] — 可観測性スタックのほうが速い
- 「エラー率が 5% を超える」
- 「P95 latency が1秒を超える」
- 「直近1時間比でトラフィック 30%」
- 事故時点の他のワークロードとの相関19章 可観測性 と 25章 モニタリング · アラート のスタックが、本章の単一 Pod デバッグと連携して事故対応の両軸を成します。Grafana の分散トレースが「どのサービスのどのハンドラが遅い」まで絞ってくれれば、次に kubectl logs がその Pod の本当の stderr を見せてくれます。
24章 CI / CD パイプライン の ArgoCD UI もデバッグ道具の1つです。OutOfSync 状態の Application をクリックすると、どのリソースが desired state と異なるかが視覚的に見えます — describe のテキスト出力より速い場合が多いです。
事故対応の標準フロー #
本章の道具を事故1件にどう絡めるかの標準フローを整理します。
1. PagerDuty / Slack でアラート本文を確認 (alertname, severity, runbook_url)
2. runbook の「一次点検」セクションをたどる
3. Grafana ダッシュボードで事故範囲を確認 (1 Pod / 1 Service / 全体)
4. 範囲が 1 Pod なら kubectl describe + logs --previous
範囲が 1 Service なら endpoints + NetworkPolicy
範囲が全体ならノード / CNI / クラスタコンポーネント
5. 一次対応 (スケールアップ、再起動、トラフィック遮断)
6. Slack 事故チャンネルに状態共有 + 事後 RCA 準備このフローが1人の頭の中に入っていれば、深夜の事故でも5分以内に一次診断が可能です。25章 の runbook_url が本章の診断ツリーへ直接つながるのが運用クラスタの目標です。
練習問題 #
- dev クラスタの myshop-api Deployment にわざと誤ったイメージタグ (
myshop-api:does-not-exist) を適用して Pod が ImagePullBackOff になる状態を作ります。kubectl describe podの Events セクションでどんな reason が出るかを記録し、本章の §「ImagePullBackOff 診断ツリー」の6つの原因のうちどれに該当するかを整理します。同じ事故をわざと権限不足 (ECR アクセス権限のない IAM Role) でも再現し、2つの reason の違いを比較します。 - メモリを徐々にリークするコンテナ (例: Python の
while True: data.append(...)) を小さな limits.memory とともに配備して OOMKilled が発生する形を作ります。kubectl get events --field-selector reason=OOMKillingで時間を捉え、25章 モニタリング · アラート のMyshopApiPodMemoryHighアラートが OOMKilled より先に鳴るように閾値を調整します。アラートが事故を予測する形 と 事故が発生した後にアラートが鳴る形 の運用価値の違いを一段落で整理します。 - Service の selector をわざと一文字の誤字に変えて (「app.kubernetes.io/name: myshop-apia」)
kubectl get endpointsが空の状態を作ります。本章の §「Service / Ingress に届かないとき」の6段階のツリーを上からたどり、どの段階で事故が発見されるかを記録し、同じ事故を可観測性スタック (Grafana の traffic パネル + Loki ログ) で発見する経路と比較します。どちらが速いか、その理由が何かを一段落で整理します。
一行まとめ: 運用クラスタのデバッグの出発線は
describe+events+logsの3つのコマンドであり、その上にkubectl debugの ephemeral container が distroless · 死んだコンテナ · ノード診断の限界を解く。CrashLoopBackOff はlogs --previousの1オプション、OOMKilled は 11章 の limits とメトリクス、ImagePullBackOff は events の reason 6種、Pending は default-scheduler の一行、Service は selector → endpoints → port の3段チェーンが核心の診断ツリー。単一 Pod の問題はkubectlが速く、分散 · 統計的な問題は可観測性スタックが速い — 2つの道具を事故の種類に合わせて選んで使うのが運用の標準。
次の章 #
本章では事故が発生したときどこから見るべきかの要点を1つのマニュアルとして整理しました。次の章では事故ではなく 請求書 の要点を扱います。
28章 コスト最適化 では、26章 運用チェックリスト で5つの出所として挙げたコスト項目を本格的に扱います。コンピュートと付加の2軸、requests のコストの意味、VPA / Goldilocks の right-sizing、Karpenter と Cluster Autoscaler の決定ツリー、namespace · ラベル単位のコスト配分までの一連の流れを扱います。13章 オートスケーリング と 11章 リソース要求と上限 の決定が本格的な運用コストの要点へとつながる段階です。