Kubernetes トラブルシューティング実践:診断から修復まで

DevOps

Kubernetes トラブルシューティング方法論

Kubernetes クラスタのトラブルシューティングは「当てずっぽう」ではなく、階層的に進む体系的な診断アプローチです。アプリケーション層からインフラ層まで段階的に調査することで、根本原因を効率的に特定できます。

階層別診断モデル

階層 範囲 主要コマンド
L1 アプリケーション層 Pod ステータス、コンテナログ kubectl logs, kubectl describe pod
L2 サービス層 Service、Endpoint、DNS kubectl get endpoints, nslookup
L3 スケジューリング層 Deployment、ReplicaSet、HPA kubectl rollout status, kubectl get hpa
L4 ストレージ層 PVC、PV、StorageClass kubectl describe pvc, kubectl get pv
L5 ノード層 Node ステータス、リソース圧迫 kubectl describe node, kubectl top nodes
L6 インフラ層 ネットワークプラグイン、API Server、etcd systemctl status kubelet, crictl ps

診断のゴールデンワークフロー

# ステップ1:クラスタ全体の健全性を確認
kubectl get nodes
kubectl get componentstatuses

# ステップ2:問題のあるネームスペースを特定
kubectl get all -n <namespace>

# ステップ3:異常リソースに焦点を当てる
kubectl describe <resource> <name> -n <namespace>

# ステップ4:イベントとログを確認
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
kubectl logs <pod-name> -n <namespace> --previous

# ステップ5:コンテナに入って調査
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh

Pod トラブルシューティング

Pod は Kubernetes の最小スケジューリング単位であり、最も障害が集中するリソースです。各異常状態の診断方法を習得することが重要です。

CrashLoopBackOff

コンテナが起動後すぐに終了し、Kubelet が繰り返し再起動してクラッシュループが発生します。

# 現在のログを確認
kubectl logs <pod-name> -n <namespace>

# 前回クラッシュ時のログを確認(重要!)
kubectl logs <pod-name> -n <namespace> --previous

# コンテナの終了コードを確認
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Last State"

一般的な原因と修正:

終了コード 意味 修正方法
0 正常終了だが長期実行モード未設定 エントリコマンドを確認、フォアグラウンド実行を確保
1 アプリケーションエラー(未キャッチ例外) アプリログを確認、コードロジックを修正
137 OOMKilled(SIGKILL 受信) resources.limits.memory を増加
139 Segmentation Fault 依存ライブラリの互換性を確認
143 SIGTERM グレースフル終了 preStop フックまたは終了シグナル処理を確認
# 修正例:スタートアッププローブを追加してスロー起動時の強制終了を防止
spec:
  containers:
    - name: app
      image: my-app:v1
      startupProbe:
        httpGet:
          path: /healthz
          port: 8080
        failureThreshold: 30
        periodSeconds: 10
      livenessProbe:
        httpGet:
          path: /healthz
          port: 8080
        periodSeconds: 15
        failureThreshold: 3

ImagePullBackOff

イメージのプルに失敗します。通常、イメージアドレス、認証、またはネットワークの問題が原因です。

# 詳細なエラー情報を確認
kubectl describe pod <pod-name> | grep -A10 "Events"

# 一般的なエラーメッセージ
# ErrImagePull: registry.k8s.io/pause:3.9 - 接続タイムアウト
# ImagePullBackOff: unauthorized: authentication required

トラブルシューティングチェックリスト:

  1. イメージアドレスの誤り:image フィールドのスペルを確認、タグの存在を確認
  2. プライベートレジストリ認証:imagePullSecrets が設定されていることを確認
  3. ネットワーク到達不能:ノードがイメージレジストリにアクセスできない(ファイアウォール/プロキシ)
  4. イメージが存在しない:タグのスペルミスまたはイメージがプッシュされていない
# 修正:プライベートレジストリ認証を設定
spec:
  imagePullSecrets:
    - name: registry-credentials
  containers:
    - name: app
      image: harbor.company.com/project/app:v1.2.3
# imagePullSecret を作成
kubectl create secret docker-registry registry-credentials \
  --docker-server=harbor.company.com \
  --docker-username=admin \
  --docker-password=Harbor12345 \
  -n <namespace>

OOMKilled

コンテナのメモリが制限を超え、カーネルの OOM Killer によって強制終了されます。

# OOMKilled を確認
kubectl describe pod <pod-name> | grep -A5 "Last State"
# Last State:     Terminated  Reason: OOMKilled  Exit Code: 137

# コンテナのメモリ使用傾向を確認
kubectl top pod <pod-name> -n <namespace>

# ノードの cgroup ログを確認
dmesg | grep -i oom

修正戦略:

# 戦略1:メモリ制限を増加
resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "1Gi"      # 512Mi から 1Gi に増加
    cpu: "500m"

# 戦略2:アプリケーションのメモリ使用を最適化(JVM の例)
env:
  - name: JAVA_OPTS
    value: "-Xms256m -Xmx512m -XX:+UseG1GC"

# 戦略3:QoS を Guaranteed に設定(requests == limits)
resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
  limits:
    memory: "512Mi"
    cpu: "500m"

Pod Pending

Pod がどのノードにもスケジュールできません。通常、リソース不足または制約が満たされていないことが原因です。

# スケジューリング失敗の理由を確認
kubectl describe pod <pod-name> | grep -A20 "Events"
# 一般的なメッセージ:
# 0/3 nodes are available: 3 Insufficient cpu.
# 0/3 nodes are available: 1 node(s) had taints that the pod didn't tolerate.

一般的な原因と解決策:

原因 イベントメッセージ 解決策
CPU 不足 Insufficient cpu requests を下げるかノードを追加
メモリ不足 Insufficient memory requests を下げるかノードを追加
PVC 未バインド pod has unbound immediate PersistentVolumeClaims PVC ステータスを先に調査
ノードセレクタ不一致 node(s) didn't match node selector nodeSelector / nodeAffinity を確認
ティント非許容 node(s) had taints that the pod didn't tolerate tolerations を追加
PodDisruptionBudget Cannot evict pod PDB 設定を確認

Service とネットワークのトラブルシューティング

DNS 解決失敗

# DNS デバッグ Pod をデプロイ
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/agnhost:2.39 \
  --restart=Never -- sleep infinity

# クラスタ内 DNS 解決をテスト
kubectl exec -it dnsutils -- nslookup kubernetes.default.svc.cluster.local
kubectl exec -it dnsutils -- nslookup <service-name>.<namespace>.svc.cluster.local

# CoreDNS ステータスを確認
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns

CoreDNS の一般的な問題:

  1. CoreDNS Pod が実行されていない:kube-system の coredns ステータスを確認
  2. ConfigMap 設定ミスcoredns ConfigMap のプラグイン設定を確認
  3. ndots によるタイムアウト/etc/resolv.conf の ndots=5 が複数クエリを引き起こす
# Pod DNS 設定を最適化
spec:
  dnsConfig:
    options:
      - name: ndots
        value: "2"
      - name: single-request-reopen
      - name: timeout
        value: "3"
  dnsPolicy: ClusterFirst

Endpoint が空

Service の Endpoint リストが空で、トラフィックがどの Pod にもルーティングされません。

# Endpoint を確認
kubectl get endpoints <service-name> -n <namespace>

# 一般的な原因の調査
# 1. ラベルセレクタの不一致
kubectl get pods -n <namespace> --show-labels
kubectl get svc <service-name> -n <namespace> -o yaml | grep -A5 selector

# 2. Pod が Ready でない
kubectl get pods -n <namespace>  # READY 列が 1/1 か確認

# 3. ポートの不一致
kubectl describe svc <service-name> -n <namespace>
# Service セレクタが Pod ラベルと一致することを確認
# Service
spec:
  selector:
    app: my-app        # Pod ラベルと一致する必要がある
  ports:
    - port: 80
      targetPort: 8080  # コンテナポートと一致する必要がある

# Pod
metadata:
  labels:
    app: my-app        # Service セレクタに対応
spec:
  containers:
    - name: app
      ports:
        - containerPort: 8080

接続拒否

# デバッグ Pod から接続性をテスト
kubectl run debug --image=busybox --rm -it --restart=Never -- sh
# デバッグ Pod 内で
wget -qO- http://<service-name>.<namespace>:<port>/healthz
curl -v telnet://<service-name>.<namespace>:<port>

# NetworkPolicy がブロックしていないか確認
kubectl get networkpolicy -n <namespace>

# kube-proxy モードと iptables/ipvs ルールを確認
kubectl logs -n kube-system -l k8s-app=kube-proxy
iptables-save | grep <service-cluster-ip>

Deployment トラブルシューティング

ロールバック操作

# rollout 履歴を確認
kubectl rollout history deployment/<name> -n <namespace>

# 前バージョンにロールバック
kubectl rollout undo deployment/<name> -n <namespace>

# 特定リビジョンにロールバック
kubectl rollout undo deployment/<name> -n <namespace> --to-revision=2

# rollout の一時停止と再開(カナリアデプロイ用)
kubectl rollout pause deployment/<name> -n <namespace>
kubectl set image deployment/<name> app=my-app:v2 -n <namespace>
kubectl rollout resume deployment/<name> -n <namespace>

スケーリング失敗

# 手動スケール
kubectl scale deployment/<name> --replicas=5 -n <namespace>

# HPA ステータスを確認
kubectl get hpa -n <namespace>
kubectl describe hpa <hpa-name> -n <namespace>

# HPA の一般的な失敗原因
# 1. metrics-server がデプロイされていない
kubectl get pods -n kube-system -l k8s-app=metrics-server

# 2. リソース requests が未設定(HPA が利用率を計算できない)
kubectl describe pod <pod-name> | grep -A5 "Requests"

# 3. スケーリングが最大レプリカ数制限に到達
kubectl get hpa <hpa-name> -o yaml | grep maxReplicas
# 正しい HPA 設定
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

PVC/PV ストレージトラブルシューティング

PVC Pending

# PVC ステータスとイベントを確認
kubectl describe pvc <pvc-name> -n <namespace>

# 一般的な原因
# 1. 一致する PV がない(静的プロビジョニング)
kubectl get pv | grep <storage-class>

# 2. StorageClass が存在しない(動的プロビジョニング)
kubectl get storageclass

# 3. 動的プロビジョナーが実行されていない
kubectl get pods -n <namespace> | grep provisioner

PV Lost

# PV ステータスを確認
kubectl get pv
# NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM
# pv-data    50Gi       RWO            Delete           Lost     default/pvc-data

# 修正:バックエンドストレージが復旧したか確認
# ストレージが復旧した場合、Lost ステータスを手動でクリア
kubectl patch pv <pv-name> -p '{"spec":{"claimRef": null}}'
# PVC を再バインド

AccessMode の不一致

# PVC が ReadWriteMany を要求しているが PV は ReadWriteOnce のみサポート
# PVC
spec:
  accessModes:
    - ReadWriteMany    # RWX が必要
  resources:
    requests:
      storage: 10Gi
  storageClassName: nfs

# StorageClass がサポートする accessMode を確認
kubectl describe storageclass nfs
AccessMode 略語 説明 代表的なバックエンド
ReadWriteOnce RWO 単一ノード読み書き AWS EBS, Ceph RBD
ReadOnlyMany ROX 複数ノード読み取り専用 NFS
ReadWriteMany RWX 複数ノード読み書き NFS, CephFS
ReadWriteOncePod RWOP 単一 Pod 排他読み書き CSI サポート

Node トラブルシューティング

NotReady ステータス

# ノードステータスを確認
kubectl get nodes
kubectl describe node <node-name>

# kubelet サービスを確認
systemctl status kubelet
journalctl -u kubelet -n 100 --no-pager

# コンテナランタイムを確認
crictl ps
systemctl status containerd

# 一般的な原因
# 1. kubelet 証明書の期限切れ
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates

# 2. ディスク容量不足
df -h /var/lib/kubelet

# 3. ネットワークプラグインの異常
kubectl get pods -n kube-system -o wide | grep <node-name>

リソース圧迫条件

# ノード条件を確認
kubectl describe node <node-name> | grep -A20 "Conditions"

# DiskPressure:ノードのディスク使用率が閾値を超過
# MemoryPressure:ノードの利用可能メモリが不足
# PIDPressure:プロセス数が多すぎる
# kubelet 駆除閾値を調整(本番では下げることを推奨しない)
kind: KubeletConfiguration
evictionHard:
  memory.available: "100Mi"
  nodefs.available: "10%"
  imagefs.available: "15%"
evictionSoft:
  memory.available: "200Mi"
  nodefs.available: "20%"
evictionSoftGracePeriod:
  memory.available: "1m30s"
  nodefs.available: "2m"

kubectl デバッグコマンド早見表

リソース確認

コマンド 用途
kubectl get all -n <ns> ネームスペースの全リソースを確認
kubectl get pods -o wide ノード情報付きで Pod を確認
kubectl get pods --show-labels Pod ラベルを確認
kubectl top pods --sort-by=memory メモリ順でソート
kubectl api-resources 全 API リソースタイプを確認

デバッグ・トラブルシューティング

コマンド 用途
kubectl logs <pod> -c <container> --previous 前回のコンテナログ
kubectl logs <pod> --since=1h 過去1時間のログ
kubectl logs <pod> -f --tail=100 直近100行をリアルタイム追跡
kubectl exec -it <pod> -- /bin/sh コンテナターミナルに入る
kubectl debug <pod> -it --image=busybox 一時デバッグコンテナ
kubectl port-forward svc/<svc> 8080:80 ローカルデバッグ用ポートフォワード

クラスタ管理

コマンド 用途
kubectl cordon <node> ノードをスケジュール不可にマーク
kubectl drain <node> --ignore-daemonsets ノードから全 Pod を駆除
kubectl uncordon <node> ノードのスケジューリングを復元
kubectl taint nodes <node> key=value:NoSchedule ティントを追加
kubectl label node <node> key=value ラベルを追加

ログとイベント分析

集中ログ収集

# マルチコンテナ Pod のログを集約
kubectl logs <pod-name> --all-containers=true

# ラベルセレクタで一括確認
kubectl logs -l app=my-app -n <namespace> --since=5m

# ログをファイルにエクスポートして分析
kubectl logs <pod-name> -n <namespace> > pod-debug.log

イベント分析

# ネームスペースの全イベントを確認(時系列ソート)
kubectl get events -n <namespace> --sort-by='.lastTimestamp'

# タイプでフィルタ
kubectl get events -n <namespace> --field-selector type=Warning

# イベントを継続監視
kubectl get events -n <namespace> --watch

# 特定リソースのイベントを確認
kubectl events --for pod/<pod-name> -n <namespace>

ログレベルと形式

# API Server 監査ログ
cat /var/log/kubernetes/audit.log | jq '. | select(.responseStatus.code >= 400)'

# kubelet ログ
journalctl -u kubelet -f

# etcd ログ
kubectl logs -n kube-system etcd-<node-name> --since=10m

リソース制限とリクエストのベストプラクティス

QoS クラス

QoS クラス 条件 駆除優先度 ユースケース
Guaranteed requests == limits(CPU+メモリ) 最低(最後に強制終了) コアサービス
Burstable requests < limits 一般サービス
BestEffort requests/limits 未設定 最高(最初に強制終了) バッチジョブ
# 本番環境推奨設定テンプレート
resources:
  requests:
    cpu: "250m"
    memory: "256Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

# LimitRange でネームスペースのデフォルト値を設定
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:
        cpu: "500m"
        memory: "512Mi"
      defaultRequest:
        cpu: "100m"
        memory: "128Mi"
      max:
        cpu: "2"
        memory: "4Gi"

一般的なリソース問題

# ノードのリソース使用状況を確認
kubectl top nodes
kubectl describe node <node-name> | grep -A10 "Allocated resources"

# ネームスペースのリソースクォータを確認
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota -n <namespace>

NetworkPolicy デバッグ

ポリシーブロックの調査

# ネームスペースの全 NetworkPolicy を確認
kubectl get networkpolicy -n <namespace>

# ポリシーの詳細を確認
kubectl describe networkpolicy <policy-name> -n <namespace>
# 一般的な NetworkPolicy 設定ミス
# エラー:DNS トラフィックの許可を忘れている
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress
spec:
  podSelector:
    matchLabels:
      app: my-app
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP

ネットワーク接続性テスト

# ネットワークデバッグツールをデプロイ
kubectl run netshoot --image=nicolaka/netshoot --rm -it --restart=Never

# デバッグ Pod 内でテスト
# 同一ネームスペース
curl http://<service-name>:<port>/healthz

# クロスネームスペース
curl http://<service-name>.<namespace>.svc.cluster.local:<port>/healthz

# 外部接続性をテスト
curl -I https://external-api.example.com

Helm Chart の一般的な問題

Release インストール失敗

# Release ステータスを確認
helm status <release-name> -n <namespace>
helm history <release-name> -n <namespace>

# レンダリング後の YAML を確認(インストールせずに)
helm template <chart> --debug

# 失敗した Release の値を確認
helm get values <release-name> -n <namespace>

# Helm Release をロールバック
helm rollback <release-name> <revision> -n <namespace>

一般的な Helm エラー

エラー 原因 修正
rendered manifests contain a resource that already exists リソースの競合 --replace を使用または旧リソースをクリーンアップ
Release "xxx" has not been installed yet Release が存在しない namespace と release name を確認
timed out waiting for the condition Pod が Ready でない --timeout 値と Pod ステータスを確認
YAML parse error values.yaml の構文エラー /encode/yaml で検証
# 失敗した Release をクリーンアップ
helm uninstall <release-name> -n <namespace>

# 強制インストール(開発環境)
helm install <release-name> <chart> --replace -n <namespace>

Prometheus 監視アラート

主要アラートルール

groups:
  - name: kubernetes-alerts
    rules:
      - alert: PodCrashLooping
        expr: rate(kube_pod_container_status_restarts_total[5m]) > 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"

      - alert: NodeNotReady
        expr: kube_node_status_condition{condition="Ready",status="unknown"} == 1
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Node {{ $labels.node }} is not ready"

      - alert: PVCAlmostFull
        expr: kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes > 0.85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} is >85% full"

      - alert: DeploymentReplicasMismatch
        expr: kube_deployment_spec_replicas != kube_deployment_status_available_replicas
        for: 10m
        labels:
          severity: warning

一般的な PromQL クエリ

# Pod メモリ使用率
container_memory_working_set_bytes / container_spec_memory_limit_bytes * 100

# ノード CPU 使用率
rate(node_cpu_seconds_total{mode!="idle"}[5m]) * 100

# リクエストエラー率
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100

# PVC 使用率
kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes

緊急対応プレイブック

P0:クラスタ利用不可

# 1. API Server を確認
kubectl get componentstatuses
curl -k https://<api-server>:6443/healthz

# 2. etcd を確認
kubectl logs -n kube-system etcd-<node> --tail=50
ETCDCTL_API=3 etcdctl endpoint health --cluster

# 3. kubelet を確認
systemctl status kubelet
journalctl -u kubelet --since "10 minutes ago"

# 4. 緊急復旧:コントロールプレーンコンポーネントを再起動
systemctl restart kubelet
systemctl restart containerd

P1:サービス全面利用不可

# 1. クイックロールバック
kubectl rollout undo deployment/<name> -n <namespace>

# 2. 緊急スケールアップ
kubectl scale deployment/<name> --replicas=10 -n <namespace>

# 3. ノードの緊急メンテナンス
kubectl cordon <node>
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data

P2:ストレージ障害

# 1. PV ステータスを確認
kubectl get pv | grep -v Bound

# 2. CSI Driver を確認
kubectl get pods -n <csi-namespace>

# 3. 緊急処理:スタックした Pod を強制削除
kubectl delete pod <pod-name> --force --grace-period=0 -n <namespace>

FAQ

Q: Pod が ContainerCreating ステータスのまま怎么办?

A: 通常、イメージプルが遅いか StorageClass のマウント失敗が原因です。kubectl describe pod で Events を確認し、イメージプルとボリュームマウントのイベントに焦点を当てます。

Q: 削除された Pod のログを確認するには?

A: ログ集約(EFK/PLG)が設定されていれば、ログプラットフォームから検索できます。それ以外の場合、削除された Pod のログは復元できません。本番環境では必ずログ収集ソリューションをデプロイしてください。

Q: kubectl コマンドがタイムアウトする場合の調査方法は?

A: 通常、API Server の過負荷またはネットワーク問題が原因です。~/.kube/config のサーバーアドレスを確認し、kubectl get --request-timeout=5s nodes で接続性をテストします。

Q: どのノードのリソースが不足しているかを素早く特定するには?

A: kubectl top nodes でリアルタイムリソースを確認するか、kubectl describe node | grep -A5 "Allocated resources" で割り当て量を確認します。

Q: Service ClusterIP にアクセスできない場合の調査方法は?

A: 以下の順序で確認:① kube-proxy が正常に実行されているか ② iptables/ipvs ルールが正しいか ③ Endpoint が空でないか ④ NetworkPolicy がブロックしていないか ⑤ CNI プラグインのステータス。

Q: Pod が予期せず駆除されるのを防ぐには?

A: PodDisruptionBudget を設定し、重要サービスの最小利用可能レプリカ数を保証します。

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: my-app

Q: Helm upgrade が失敗し Release ステータスがスタックした場合の対処法は?

A: helm rollback でロールバックするか、helm history で過去のバージョンを確認します。Release がロックされている場合は、helm rollback <release> <last-good-revision> で復旧します。

ブラウザローカルツールを無料で試す →

#Kubernetes#K8s#故障排查#运维#教程