- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに — 層を決めてから降りる
- 第一層 — Podがスケジュールされない
- 第二層 — Podは起動したがGPUが見えない
- 第三層 — ドライバとツールキットのバージョン不一致
- 第四層 — メモリ不足とOOM
- 第五層 — ノードからGPUが消える
- おわりに — 順序こそが実力
- 試してみる
- シリーズ
- 参考資料
はじめに — 層を決めてから降りる
GPU障害対応で最大の時間の浪費は、順序なくあちこち突くことです。ノードに入ってnvidia-smiを叩き、Podのログを見て、DaemonSetを再起動してみて、そうこうするうちに一時間が消えます。
問題は層が五つあるのに症状が似ていることです。Podが起動しないのはスケジューリングの問題かもしれず、容量が無いからかもしれず、ドライバが死んでいるからかもしれません。だから順序を先に決めて上から下へ降りるほうが常に速いのです。
メトリクス名と設定は2026-08-12に公式ドキュメント・リポジトリで確認しました。バージョンによって異なる場合があるため、使用中のバージョンで再確認してください。
第一層 — Podがスケジュールされない
症状はpendingです。ここで確認することは二つだけです。ノードがリソースを広告しているか、そして残っているかです。
# なぜスケジュールされないかをイベントで確認
kubectl describe pod <pod-name> | tail -20
# ノードの総量と割り当て可能量を比較
kubectl get nodes -o custom-columns=\
'NODE:.metadata.name,CAP:.status.capacity.nvidia\.com/gpu,ALLOC:.status.allocatable.nvidia\.com/gpu'
# どのノードも広告していないならラベルから
kubectl get nodes -L nvidia.com/gpu.count,nvidia.com/gpu.product,nvidia.com/mig.strategy
ここで道が分かれます。リソース自体が全く無いなら第二層へ降ります。 リソースはあるが空きが無いなら、それは障害ではなく容量の話です。そして総量が8で割り当て可能量が7なら、それは強い手掛かりです。 Kubernetesのドキュメントが明示するとおり、デバイスが異常と示されるとkubeletは割り当て可能量だけを減らし総量はそのままにします。この非対称が見えたら第五層へ飛んで構いません。
ラベルが一つも付いていないならGPU Feature DiscoveryかNode Feature Discoveryが動いていないので、オペレータ側の問題です。
第二層 — Podは起動したがGPUが見えない
コンテナは起動したのにnvidia-smiが失敗する、あるいはCUDAがデバイスを見つけられない場合です。もしくはコンテナ生成の段階で失敗します。
公式のトラブルシューティングドキュメントが挙げる代表的なエラーメッセージがあります。no runtime for 'nvidia' is configuredです。これはNVIDIA Container Toolkitがコンテナエンジンにランタイムハンドラを登録できなかったという意味です。ドライバの問題ではなくツールキットの問題です。
# オペレータPodの状態を一覧で
kubectl get pods -n gpu-operator
# ツールキットとデバイスプラグインのログ
kubectl logs -n gpu-operator nvidia-container-toolkit-daemonset-<POD-ID>
kubectl logs -n gpu-operator nvidia-device-plugin-daemonset-<POD-ID>
# validatorがどの段階で止まったか
kubectl describe pod -n gpu-operator -l app=nvidia-operator-validator
nvidia-operator-validatorを先に見る習慣が重要です。このPodは前段が成功したかを順に検証するので、どこで止まったかを最も速く教えてくれます。オペレータのPodがInit段階で止まっているなら大抵はドライバのDaemonSetがまだ準備できていないということで、他のPodをいくら見ても答えは出ません。
第三層 — ドライバとツールキットのバージョン不一致
この層の症状は微妙です。昨日まで動いていたものがノード再起動後に動かない、あるいは新しく追加したノードでだけ動きません。
原因はたいてい三つのどれかです。カーネルが更新されてドライバモジュールが合わない、ノードごとにOSバージョンが異なりオペレータが配るドライバイメージが合わない、ホストに既にドライバが入っているのにオペレータもドライバを配ろうとしている、のいずれかです。
# ドライバコンテナのログ (カーネルモジュールのビルド失敗はここに出る)
kubectl logs -n gpu-operator nvidia-driver-daemonset-<POD-ID> -c nvidia-driver-ctr
# ノードごとのOSとカーネルバージョンを突き合わせる
kubectl get nodes -o wide
# GFDが付けたドライバ・ランタイムのバージョンラベルを突き合わせる
kubectl get nodes -L nvidia.com/cuda.driver-version.full,nvidia.com/cuda.runtime-version.full
最後のコマンドが特に有用です。GPU Feature Discoveryが付けるnvidia.com/cuda.driver-version.fullとnvidia.com/cuda.runtime-version.fullのラベルをノード同士で比べれば、どのノードだけ違うのかが一行で見えます。
ホストに既にドライバがある環境ならHelm値のdriver.enabledを偽にする必要があり、ツールキットが既に構成済みならtoolkit.enabledも同様です。この二つを誤ると症状は第三層に見えますが原因は設定です。
NVSwitchがあるシステムにはもう一つ条件があります。公式ドキュメントは、ファブリック管理が必要なシステムではvalidatorがシステムがまだ初期化されていないという趣旨のメッセージとともに失敗しうること、ドライバと併せてnvidia-fabricmanagerを導入する必要があることを案内しています。
第四層 — メモリ不足とOOM
GPUメモリ不足はCPUメモリ不足とは違う現れ方をします。cgroupが殺してくれるのではなく、アプリケーションが確保失敗の例外を投げるか、静かに性能だけが崩れます。
まず分けるべきものがあります。コンテナのシステムメモリが足りないのか、GPUのフレームバッファが足りないのかです。 前者はPodがOOMKilledで終了しイベントに残りますが、後者はPodが生きたままリクエストだけが失敗します。
# PodがOOMKilledかを確認 (システムメモリ側)
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{"\n"}'
# GPU側はメトリクスで見る
kubectl -n gpu-operator port-forward svc/nvidia-dcgm-exporter 9400:9400
curl -s localhost:9400/metrics | grep -E 'DCGM_FI_DEV_FB_(USED|FREE)'
メトリクスで見るとこうなります。
# フレームバッファ使用率が天井のGPU
DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) > 0.95
# Pod別のフレームバッファ占有 (Kubernetesマッピングが有効なとき)
sum by (namespace, pod) (DCGM_FI_DEV_FB_USED)
# 推論サーバならプリエンプションが答えを出す
sum by (model_name) (rate(vllm:num_preemptions_total[5m])) > 0
vLLMのような推論サーバは起動時にGPUメモリを先に確保するため、フレームバッファ使用量が高いこと自体は正常です。判断基準は使用量ではなくプリエンプションの有無です。メモリ予約の割合はvLLM設定のgpu_memory_utilizationが決め、この値はキャッシュ設定情報メトリクスのラベルとしても露出します。
同じカードに複数のワークロードをtime-slicingで載せている状態なら、第四層の問題は必然です。先に見たとおりレプリカ間にメモリ隔離が無いからです。
第五層 — ノードからGPUが消える
最も不快な層です。昨日8枚だったノードが今日は7枚です。
公式のトラブルシューティングドキュメントがこの状況を直接説明しています。デバイスプラグインがXidエラーによりデバイスを異常と示すと、ノードは物理的に挿さっているより少ない数のGPUを広告し、プラグインのログにデバイスを異常として印す旨の項目で確認できます。
# デバイスプラグインのログで異常マークを確認
kubectl logs -n gpu-operator nvidia-device-plugin-daemonset-<POD-ID> | grep -i unhealthy
# 総量と割り当て可能量の差を再確認
kubectl describe node <node-name> | sed -n '/Capacity/,/Allocated resources/p'
# 診断資料を一括収集
curl -o must-gather.sh -L https://raw.githubusercontent.com/NVIDIA/gpu-operator/main/hack/must-gather.sh
chmod +x must-gather.sh
./must-gather.sh
メトリクス側の証拠はDCGMにあります。既定CSVで有効なDCGM_FI_DEV_XID_ERRORSは最後に遭遇したXidエラーの値を持ちます。ただし最後の値なので履歴追跡には弱いです。リポジトリのREADMEによればexporterはこれを監視し、観測されたXidごとに時系列を分けるDCGM_EXP_XID_ERRORS_TOTALを提供し、xidラベルが付き値0はエラー無しとして数えません。このフィールドは既定CSVでコメントアウトされているので、使うには自分で有効にする必要があります。
ハードウェア劣化を見る指標は既定で有効です。DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS、DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS、DCGM_FI_DEV_ROW_REMAP_FAILUREの三つです。特に最後の値は行のリマップが失敗したことを意味するので、ここに値が出たならソフトウェアで解決する段階ではありません。クロックイベントを追うなら、DCGM_EXP_CLOCK_EVENTS_TOTALがDCGM_FI_DEV_CLOCKS_EVENT_REASONSを監視しclock_eventラベルで分かれる点を覚えておくとよいでしょう。
おわりに — 順序こそが実力
五つの層を一行ずつに圧縮するとこうです。リソースは広告されているか、コンテナはデバイスを見ているか、バージョンは合っているか、メモリは足りているか、カードは生きているか。 上から確認すれば大半の障害は二つ目か三つ目のコマンドで切り分きます。
一つ習慣を付け加えたいと思います。総量と割り当て可能量の比較を最初のコマンドにすることです。 この一行で第五層の問題かどうかが即座に決まり、その判断だけで診断経路が半分になります。
これでシリーズを終えます。オペレータでGPUをノードに載せ、リソースとして広告し、必要なら分けて使い、メトリクスで観測し、約束を立て、壊れたときに順に降りるところまでが一周でした。各段階で名前を一つ、推測ではなくドキュメントで確認する習慣が、結局は深夜に起きている時間を減らしてくれます。
試してみる
- kubectlコマンド検索 — 本プレイブックのコマンドを状況別に探し直せます。
- K8s 実習ラボ — describeとログ確認の流れを自分で繰り返してください。
- Kubestronautクイズ — ノードリソースとPod状態の診断を問題で点検してください。
シリーズ
- 前の記事: GPUサービングのSLOとアラート設計
- 次の記事: 本記事がシリーズの最終回です。
参考資料
- GPU Operator Troubleshooting: https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/troubleshooting.html
- dcgm-exporter 既定カウンタCSV: https://github.com/NVIDIA/dcgm-exporter/blob/main/etc/default-counters.csv
- dcgm-exporter README: https://github.com/NVIDIA/dcgm-exporter/blob/main/README.md
- Kubernetes Device Plugins: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/
- gpu-operatorリポジトリ values.yaml: https://github.com/NVIDIA/gpu-operator/blob/main/deployments/gpu-operator/values.yaml