Kubernetes入門 第11回:トラブルシューティングの作法

Kubernetes入門
第11回:トラブルシューティングの作法

連載最終回となる本稿では、K8s特有の「ステータス異常」を体系的に理解し、4種の神器(get / describe / logs / events) を駆使してトラブルを切り分けるスキルを身につけます。そして最後に、全11回を走破したあなたに、次のステップへの道標を示します。

本稿の実行環境について

本稿は2つのパートで構成されています。

  • 11.1 診断セクション:概念理解のための架空の出力例を使用しています。
  • 11.2 実践セクション:第1回で構築したkindクラスタ上で、実際に手を動かして追体験できるハンズオン演習です。

11.2以降を実行する場合は、kindクラスタが起動していることを確認してください。


11.1 診断:K8s特有のステータス異常を読み解く

11.1.1 「なぜ動かない?」を可視化する:ステータス欄が教えるヒント

VMの世界では、仮想マシンの状態は比較的シンプルでした。「起動中」「停止」「一時停止」「異常」——せいぜいこの程度です。vSphere Clientを開けば、赤い警告アイコンが目に飛び込んできて、「あ、こいつが死んでるな」と一目で分かりました。

Kubernetesも、実は同じ思想で設計されています。ただし、可視化の粒度がはるかに細かいのです。

[Execution User: developer]

kubectl get pods

実行結果:

NAME                        READY   STATUS             RESTARTS      AGE
web-server-7d8f9b6c5-abc12  1/1     Running            0             2d
api-gateway-5f4d3c2b1-xyz99 0/1     CrashLoopBackOff   127 (5m ago)  1d
batch-job-8e7f6d5c4-def34   0/1     ImagePullBackOff   0             3h
scheduler-9a8b7c6d5-ghi56   0/1     Pending            0             45m

この出力を見て、VMエンジニアのあなたはどう感じるでしょうか?

Running は分かる。Pending は「保留中」だから、まだ起動していないのだろう。でも CrashLoopBackOff とは何だ?ImagePullBackOff は?——そう思うのは当然です。

ここで重要なのは、K8sのステータスは「現在の状態」だけでなく「何を試みて失敗したか」まで教えてくれるということです。VMで言えば、「起動失敗」というステータスだけでなく、「BIOSは通過したがブートローダーで停止」「OSカーネルのロード中にパニック」といった詳細が、最初から表示されているようなものです。

ステータスの読み方:3つのカテゴリ

K8sのPodステータスは、大きく3つのカテゴリに分類できます。

カテゴリ代表的なステータスVMでの類似状況
スケジューリング待ちPendingvMotion先のホストが見つからない
イメージ取得失敗ImagePullBackOff, ErrImagePullISOマウント失敗、テンプレート破損
コンテナ起動失敗CrashLoopBackOff, ErrorゲストOSのブート失敗、アプリクラッシュ

VMの経験があれば、この対応表を見るだけで「ああ、あの感覚か」と腑に落ちるはずです。

11.1.2 死因ランキング:CrashLoopBackOff, ImagePullBackOff, Pending の正体

現場で遭遇する頻度の高い「死因」を、ランキング形式で解説します。

第1位:CrashLoopBackOff(墜落と再起動の無限ループ)

意味: コンテナは起動できたが、何らかの理由ですぐに終了(クラッシュ)し、K8sが再起動を試み、また失敗し……を繰り返している状態。

VMでの類似状況: ゲストOSは起動するが、起動直後にアプリケーションがクラッシュしてサービスが使えない。自動再起動を設定しているため、何度も再起動がかかる。

よくある原因:

  • アプリケーションの設定ミス(DB接続先が間違っている等)
  • 必要な環境変数が設定されていない
  • メモリ不足でOOMKilled(Out Of Memory)
  • ヘルスチェック(Liveness Probe)の設定が厳しすぎる

切り分けの第一歩: kubectl logs <pod-name> でアプリケーションのログを確認。

第2位:ImagePullBackOff(イメージが取得できない)

意味: 指定されたコンテナイメージをレジストリから取得できず、バックオフ(待機時間を延ばしながら再試行)している状態。

VMでの類似状況: vSphereでテンプレートからデプロイしようとしたら、「テンプレートが見つかりません」エラー。あるいは、ISOイメージのマウントに失敗。

よくある原因:

  • イメージ名のタイポ(nginx:latestt など)
  • プライベートレジストリの認証情報(ImagePullSecrets)が設定されていない
  • レジストリへのネットワーク疎通がない
  • 指定したタグが存在しない

切り分けの第一歩: kubectl describe pod <pod-name> でEventsセクションを確認。具体的なエラーメッセージが記載されている。

第3位:Pending(配置先が見つからない)

意味: Podをどのノードに配置するか決められず、待機している状態。

VMでの類似状況: DRS(Distributed Resource Scheduler)が「このVMを配置できるホストがありません」と言っている。リソースプールの制約、アフィニティルール、ストレージの空き容量不足など。

よくある原因:

  • リソース不足(CPU/メモリの requests がどのノードでも満たせない)
  • nodeSelectoraffinity で指定した条件に合うノードがない
  • PersistentVolumeClaim(PVC)がバインドされていない
  • Taint(汚染マーク)が設定されたノードに対するToleration(許容設定)がない

切り分けの第一歩: kubectl describe pod <pod-name> のEventsセクションで、スケジューラーからのメッセージを確認。

11.1.3 インフラの視点:Podが「Node」に配置されないときのチェック項目

Pending 状態が続くとき、VMエンジニアの視点が最も活きるのがここです。本質的には「リソースの空きがあるホストを探す」という、DRSと同じ仕事をK8sのスケジューラーが行っています。

チェックリスト:Pending の原因切り分け

1. ノードの状態を確認する

[Execution User: developer]

kubectl get nodes

実行結果:

NAME                 STATUS   ROLES           AGE   VERSION
kind-control-plane   Ready    control-plane   7d    v1.32.0
kind-worker          Ready    <none>          7d    v1.32.0
kind-worker2         Ready    <none>          7d    v1.32.0

全ノードが Ready であることを確認。NotReady のノードがあれば、そこにPodは配置されません。

2. ノードのリソース状況を確認する

[Execution User: developer]

kubectl describe node kind-worker | grep -A 10 "Allocated resources"

実行結果:

Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1900m (95%)  3000m (150%)
  memory             1800Mi (90%) 2500Mi (125%)
  ephemeral-storage  0 (0%)       0 (0%)

95% のCPUが既に予約されている! 新しいPodがCPUを requests: 200m で要求しても、配置できません。

3. PodのEvents(イベント)を確認する

[Execution User: developer]

kubectl describe pod scheduler-9a8b7c6d5-ghi56 | grep -A 20 "Events:"

実行結果:

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  45m   default-scheduler  0/3 nodes are available: 
           1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 
           2 node(s) had insufficient memory.

ここに答えが書いてあります。「コントロールプレーンにはTaintがあり配置できない」「残り2ノードはメモリ不足」——。VMのリソースプール制約と同じ感覚で理解できるはずです。


11.2 実践:4種の神器(get/describe/logs/events)を使い倒す

ここからは、意図的に「壊れた」環境を作り、4つのコマンドを駆使してトラブルを特定・修正する実践演習を行います。

準備:問題のあるマニフェストを適用する

まず、いくつかの「地雷」を仕込んだマニフェストを作成します。

[Execution User: developer]

cat << 'EOF' > ~/broken-manifests.yaml
---
# 問題1: イメージ名のタイポ
apiVersion: v1
kind: Pod
metadata:
  name: broken-image
  namespace: default
spec:
  containers:
  - name: nginx
    image: nginx:latestt  # 存在しないタグ
    ports:
    - containerPort: 80
---
# 問題2: 環境変数の欠落によるアプリクラッシュ
apiVersion: v1
kind: Pod
metadata:
  name: broken-env
  namespace: default
spec:
  containers:
  - name: demo-app
    image: busybox:1.36
    command: ["/bin/sh", "-c"]
    args:
    - |
      if [ -z "$REQUIRED_CONFIG" ]; then
        echo "ERROR: REQUIRED_CONFIG is not set!" >&2
        exit 1
      fi
      echo "Config: $REQUIRED_CONFIG"
      sleep 3600
    # REQUIRED_CONFIG 環境変数が設定されていない!
---
# 問題3: 過大なリソース要求
apiVersion: v1
kind: Pod
metadata:
  name: broken-resources
  namespace: default
spec:
  containers:
  - name: resource-hog
    image: nginx:1.27
    resources:
      requests:
        memory: "128Gi"  # 128GB!? そんなノードはない
        cpu: "64"        # 64コア!? 
EOF

これらを適用します。

[Execution User: developer]

kubectl apply -f ~/broken-manifests.yaml

実行結果:

pod/broken-image created
pod/broken-env created
pod/broken-resources created

30秒ほど待ってから、状況を確認しましょう。

11.2.1 まずは kubectl get pods:異変の兆候を素早く掴む

[Execution User: developer]

kubectl get pods

実行結果:

NAME               READY   STATUS             RESTARTS      AGE
broken-env         0/1     CrashLoopBackOff   3 (12s ago)   45s
broken-image       0/1     ImagePullBackOff   0             45s
broken-resources   0/1     Pending            0             45s

見事に3種類の「死因」が揃いました。VMの管理画面で赤いアイコンが3つ並んでいるのと同じ状況です。

ここでのポイントは、get pods は「一覧表示」であり「詳細診断」ではないということ。vSphere Clientでサマリー画面を見ているようなものです。何かがおかしいことは分かるが、原因までは分からない。

次のステップに進みましょう。

11.2.2 kubectl describe:K8sからの「公式な診断書」を読み解く術

kubectl describe は、K8sがそのリソースについて知っているすべての情報を、人間が読める形式で出力します。VMで言えば、vCenterの「イベント」タブと「設定」タブを合体させたようなものです。

ケース1:ImagePullBackOff の診断

[Execution User: developer]

kubectl describe pod broken-image

出力は長いですが、最も重要なのは末尾の Events セクションです。

実行結果(Eventsセクション抜粋):

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  2m10s              default-scheduler  Successfully assigned default/broken-image to kind-worker
  Normal   Pulling    45s (x4 over 2m)   kubelet            Pulling image "nginx:latestt"
  Warning  Failed     44s (x4 over 2m)   kubelet            Failed to pull image "nginx:latestt": 
           rpc error: code = NotFound desc = failed to pull and unpack image 
           "docker.io/library/nginx:latestt": failed to resolve reference 
           "docker.io/library/nginx:latestt": docker.io/library/nginx:latestt: not found
  Warning  Failed     44s (x4 over 2m)   kubelet            Error: ErrImagePull
  Normal   BackOff    32s (x5 over 2m)   kubelet            Back-off pulling image "nginx:latestt"
  Warning  Failed     32s (x5 over 2m)   kubelet            Error: ImagePullBackOff

診断書には明確に書いてあります:nginx:latestt: not found。タグ名のタイポです。

修正方法:

[Execution User: developer]

# 壊れたPodを削除
kubectl delete pod broken-image

# 正しいマニフェストを適用
kubectl run fixed-image --image=nginx:latest --port=80

[Execution User: developer]

kubectl get pods fixed-image

実行結果:

NAME          READY   STATUS    RESTARTS   AGE
fixed-image   1/1     Running   0          10s

ケース2:CrashLoopBackOff の診断

[Execution User: developer]

kubectl describe pod broken-env

実行結果(Eventsセクション抜粋):

Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  3m                   default-scheduler  Successfully assigned default/broken-env to kind-worker2
  Normal   Pulled     2m (x5 over 3m)      kubelet            Container image "busybox:1.36" already present on machine
  Normal   Created    2m (x5 over 3m)      kubelet            Created container demo-app
  Normal   Started    2m (x5 over 3m)      kubelet            Started container demo-app
  Warning  BackOff    90s (x10 over 3m)    kubelet            Back-off restarting failed container demo-app in pod broken-env_default(...)

「コンテナは起動したが、すぐに終了してバックオフ中」——ここまでは分かります。しかし、なぜ終了したのかはEventsだけでは分かりません。

ここで kubectl logs の出番です。

[Execution User: developer]

kubectl logs broken-env

実行結果:

ERROR: REQUIRED_CONFIG is not set!

アプリケーションログに答えが書いてありました。環境変数 REQUIRED_CONFIG が設定されていないため、アプリケーションが異常終了しています。

修正方法:

[Execution User: developer]

kubectl delete pod broken-env

cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: fixed-env
spec:
  containers:
  - name: demo-app
    image: busybox:1.36
    command: ["/bin/sh", "-c"]
    args:
    - |
      if [ -z "$REQUIRED_CONFIG" ]; then
        echo "ERROR: REQUIRED_CONFIG is not set!" >&2
        exit 1
      fi
      echo "Config: $REQUIRED_CONFIG"
      sleep 3600
    env:
    - name: REQUIRED_CONFIG
      value: "production-settings-v1"
EOF

[Execution User: developer]

kubectl get pods fixed-env

実行結果:

NAME        READY   STATUS    RESTARTS   AGE
fixed-env   1/1     Running   0          15s

ケース3:Pending の診断

[Execution User: developer]

kubectl describe pod broken-resources

実行結果(Eventsセクション抜粋):

Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  4m     default-scheduler  0/3 nodes are available: 
           1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 
           2 node(s) didn't match Pod's node affinity/selector. 
           2 node(s) had insufficient memory. 
           2 node(s) had insufficient cpu.

スケジューラーからの診断書は極めて明確です。「128GBのメモリと64コアのCPUを要求しているが、そんなノードは存在しない」。

VMで言えば、「このVMには256GBのRAMを割り当ててください」と設定したのに、クラスタ内のどのホストも128GB以上のメモリを持っていない状況です。

修正方法:

[Execution User: developer]

kubectl delete pod broken-resources

cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: fixed-resources
spec:
  containers:
  - name: modest-nginx
    image: nginx:1.27
    resources:
      requests:
        memory: "64Mi"
        cpu: "100m"
      limits:
        memory: "128Mi"
        cpu: "200m"
EOF

[Execution User: developer]

kubectl get pods fixed-resources

実行結果:

NAME              READY   STATUS    RESTARTS   AGE
fixed-resources   1/1     Running   0          8s

11.2.3 kubectl events:時系列で「何が起きたか」の物語を追う

ここまでは個別のPodを診断してきました。しかし、本番環境ではもっと複雑な状況が発生します。「Deploymentを更新したら、何かがおかしくなった。でも、どのPodが、いつ、どんな順序で壊れたのか分からない」——。

こんなとき、Namespace全体のイベントを時系列で俯瞰することが重要になります。

[Execution User: developer]

kubectl events -n default --types Warning

実行結果:

LAST SEEN   TYPE      REASON             OBJECT                MESSAGE
5m          Warning   Failed             pod/broken-image      Failed to pull image "nginx:latestt"...
5m          Warning   Failed             pod/broken-image      Error: ErrImagePull
4m          Warning   BackOff            pod/broken-image      Back-off pulling image "nginx:latestt"
4m          Warning   BackOff            pod/broken-env        Back-off restarting failed container...
4m          Warning   FailedScheduling   pod/broken-resources  0/3 nodes are available...

これは、いわばK8sの監視カメラ映像を巻き戻しているようなものです。VMwareのvRealize Log Insightや、Windows Serverのイベントビューアで「警告」「エラー」をフィルタリングするのと同じ感覚で使えます。

実践Tips:特定リソースのイベントだけを見る

[Execution User: developer]

# 特定のPodのイベントだけを抽出
kubectl events --for=pod/broken-env

# 特定のDeploymentに関連するイベント
kubectl events --for=deployment/my-app

実践Tips:リアルタイムでイベントを監視する

障害対応中は、イベントをリアルタイムで監視したいことがあります。

[Execution User: developer]

kubectl events -n default -w

-w (watch) オプションを付けると、新しいイベントが発生するたびに画面に追加表示されます。VMwareのvCenter Serverで「タスクとイベント」タブを開きっぱなしにしている状況を再現できます。


11.3 総括:VMエンジニアが「K8sネイティブ」になるためのロードマップ

11.3.1 本連載で学んだこと:VMの常識をどうアップデートできたか

全11回の連載を通じて、あなたは以下の「常識のアップデート」を体験しました。

VMの常識K8sの新常識
第1回サーバーは「箱」、VMは「仮想の箱」Podは「プロセスの集合」、ノードは「リソースプール」
第2回OSインストール→ミドルウェア導入→設定コンテナイメージにすべて封入、宣言的に展開
第3回IPアドレスは固定、DNS手動登録ServiceがIPを抽象化、自動DNS登録
第4回外部公開はF5/NetScaler/HAProxyIngressでルーティング、TLS終端も自動化
第5回ストレージはSAN/NAS、LUNをマウントPV/PVCで抽象化、動的プロビジョニング
第6回設定ファイルは /etc に直接配置ConfigMap/Secretで分離、再デプロイ不要で更新可能
第7回監視は Zabbix/Nagios、エージェント導入メトリクスは標準化、Prometheus/Grafanaエコシステム
第8回台数増減は手動、vMotionで負荷分散HPAで自動スケール、Pod単位の柔軟性
第9回ジョブはcron、失敗したら手動再実行Job/CronJobで宣言的管理、リトライも自動
第10回RBAC?Active Directoryで十分K8s RBAC で最小権限、Namespace で論理分離
第11回障害時はvCenter見て、ログ見て、勘で切り分け4種の神器で体系的診断、イベントが物語を語る

振り返ってみてください。あなたが長年培ってきた「インフラ運用の勘所」——リソース管理、可用性設計、セキュリティ、監視——は、K8sの世界でもそのまま活きています。変わったのは「道具」と「手順」であって、根底にある設計思想は驚くほど共通しているのです。

11.3.2 次のステップ:GitOps、Helm、マネージドサービス(EKS/AKS/GKE)への展望

本連載で扱った kind クラスタは、あくまで学習・開発用の環境です。本番環境へ進むにあたって、以下の3つの方向性を紹介します。

1. GitOps:「あるべき状態」をGitで管理する

本連載では kubectl apply -f でマニフェストを適用してきました。しかし、本番環境では「誰が」「いつ」「何を」変更したかを追跡する必要があります。

GitOps は、Gitリポジトリを「信頼できる唯一の情報源(Single Source of Truth)」とし、リポジトリの内容とクラスタの状態を自動的に同期させるアプローチです。

代表的なツール:

  • Argo CD:K8sネイティブな継続的デリバリーツール
  • Flux:CNCF卒業プロジェクト、軽量で拡張性が高い

VMwareでいえば、vSphereの構成をすべてコードで管理し、変更はPull Requestを経てレビュー・承認されてから適用される——そんなワークフローです。

2. Helm:「パッケージマネージャー」でアプリをまとめる

複数のマニフェスト(Deployment, Service, ConfigMap, Secret…)を個別に管理するのは、やがて破綻します。

Helm は、K8sにおけるパッケージマネージャーです。複数のマニフェストを「Chart」としてまとめ、バージョン管理、パラメータ化、依存関係の解決を行います。

[Execution User: developer]

# 例:PrometheusスタックをHelmでインストール
helm install prometheus prometheus-community/kube-prometheus-stack

VMwareでいえば、OVAテンプレートでアプライアンスをデプロイするような手軽さで、複雑なアプリケーションを導入できます。

3. マネージドK8s:運用負荷をクラウドに委譲する

kindkubeadm で構築したクラスタは、コントロールプレーン(API Server, etcd等)の運用も自分で行う必要があります。本番環境では、この部分をクラウドプロバイダーに任せる選択肢があります。

サービスプロバイダー特徴
EKSAWSAWSサービスとの統合が強力、Fargateでサーバーレス運用も可能
AKSAzureAzure ADとの統合、Windows コンテナサポート
GKEGoogle CloudK8s開発元の知見、Autopilotモードで全自動運用

VMwareを使っていた方なら、VMware Cloud on AWSやAzure VMware Solutionを検討したことがあるかもしれません。マネージドK8sはその延長線上にあり、「コントロールプレーンの運用はプロに任せ、自分たちはアプリケーションに集中する」という分業です。

11.3.3 最後に:インフラエンジニアの価値は「道具」ではなく「運用の設計思想」にある

技術は変わり続けます。VMwareがK8sに、K8sがまた別の何かに置き換わる日が来るかもしれません。しかし、「システムをどう設計し、どう監視し、どう復旧させるか」という運用の設計思想は、技術を超えて生き続けます。

あなたがVMの世界で培った経験は、無駄にはなりません。それはK8sという新しい武器を手にすることで、より強力に、より柔軟に活かせるようになりました。

本連載で身につけた「4種の神器」——get で異変を察知し、describe で診断書を読み、logs でアプリの声を聴き、events で物語を追う——これらのスキルは、どのクラウドでも、どのK8sディストリビューションでも、普遍的に使えます。


11.4 2026年版:K8sデバッグ・チェックリスト

現場で使える簡易トラブルシューティングシートです。印刷して手元に置いておくことをお勧めします。

カテゴリ1:ネットワーク疎通

チェック項目確認コマンド期待する結果
Pod間の疎通kubectl exec <pod-a> -- wget -qO- <pod-b-ip>200 OK または期待するレスポンス
Service経由の疎通kubectl exec <pod> -- nslookup <service-name>IPアドレスが解決される
外部への疎通kubectl exec <pod> -- wget -qO- https://example.com200 OK
DNS解決kubectl exec <pod> -- cat /etc/resolv.confnameserver が CoreDNS の ClusterIP

カテゴリ2:リソース(CPU/メモリ)

チェック項目確認コマンド注目ポイント
ノードのリソース状況kubectl top nodesCPU/メモリの使用率が 80% 未満か
Podのリソース消費kubectl top podslimits に近づいていないか
OOMKilled の確認kubectl describe pod <name>Reason: OOMKilled がないか
リソースクォータkubectl describe resourcequota -n <ns>クォータに到達していないか

カテゴリ3:認証・認可(RBAC)

チェック項目確認コマンド期待する結果
自分の権限確認kubectl auth can-i <verb> <resource>yes または no
ServiceAccount の確認kubectl get pod <name> -o jsonpath='{.spec.serviceAccountName}'期待するSA名
RoleBinding の確認kubectl get rolebindings -n <ns>必要なバインディングが存在する
Secret のマウント確認kubectl exec <pod> -- ls /var/run/secrets/kubernetes.io/serviceaccount/token, ca.crt, namespace

カテゴリ4:ストレージ(PV/PVC)

チェック項目確認コマンド期待する結果
PVC のステータスkubectl get pvcBound 状態
PV の確認kubectl get pvAvailable または Bound
StorageClass の確認kubectl get storageclassデフォルトSCに (default) マーク
マウント状態の確認kubectl describe pod <name>Volumes: セクションでマウント成功

クイックリファレンス:4種の神器

[Execution User: developer]

# 1. 一覧と状態確認(まず最初に)
kubectl get pods -o wide

# 2. 詳細診断(Eventsが最重要)
kubectl describe pod <pod-name>

# 3. アプリケーションログ(CrashLoopBackOff時に必須)
kubectl logs <pod-name>
kubectl logs <pod-name> --previous  # 前回起動時のログ
kubectl logs <pod-name> -c <container-name>  # マルチコンテナPod

# 4. イベント俯瞰(Namespace全体の時系列)
kubectl events -n <namespace>
kubectl events -w  # リアルタイム監視

緊急時のエスカレーション判断基準

状況自己解決の目安エスカレーション検討
単一Pod の異常30分以内に原因特定原因不明が1時間継続
複数Pod の同時異常ノード障害を疑う全ノードに影響時は即座に
コントロールプレーン異常kubectl 自体が動かない即座にエスカレーション
ストレージ障害PVCがPendingデータ損失の可能性時は即座に