ステータス: ドラフト(2026-10-07 更新)、一部実測値を反映。 公式ドキュメントで確認した記述がベースであり、**第5節の containerd が SIGHUP によって再起動される経路(使い捨て仮想マシンで直接確認)**と、ノードの containerd バージョンおよびユニット設定を読み取った部分のみが直接確認した値です。残りはドキュメントを基準としています。 記事の終わりの「確認したバージョンと日付」および「確認できなかったこと」を先にお読みください。 作成ルール: 実測データを基本とし、計測できなかったものは「未計測」と表示します。
目次
- 概要
- 1. 構成要素: 何が何をするのか
- 2. Pod が GPU を取得するまで
- 3. インストール方式の選択
- 4. 症状別の診断手順
- 5. よくある罠: ドロップインが CRI 設定をごっそり書き換えた
- 6. キャパシティプランニング
- 7. 実際に試してみる(読み取り専用)
- 8. 面接で聞かれそうな質問
- 9. よくある誤解
- 10. 参考資料と確認したバージョン
概要
GPU Operator は、GPU ノードごとに手動で行っていたドライバ、コンテナランタイムの設定、デバイスプラグイン、メトリック収集エージェントのインストールを ClusterPolicy という1つのオブジェクトで宣言できるようにしたオペレーターです。この記事では、構成要素の役割と失敗時の挙動、Pod が GPU を受け取るまでに通過する2つの関門、症状別の診断手順、キャパシティプランニングの罠を順番に解説します。
この記事を読むことで面接で説明できるようになること
- オペレーターが起動する各 DaemonSet の役割と依存順序、失敗時にエラーが露見する場所
- スケジューラーが確認する拡張リソースと、ランタイムが確認するハンドラー・CDIという2つの関門、そして CDI デフォルトに変更された現在の経路
Insufficient nvidia.com/gpuが異なる原因から同じ文言として出力される理由とその見分け方
1. 構成要素: 何が何をするのか
オペレーターはノードラベルを使って、どのノードに何をデプロイするかを決定します。NFD(Node Feature Discovery)が PCI ベンダーラベル(feature.node.kubernetes.io/pci-10de.present など)を付与すると、オペレーターが GPU ノードと判定して nvidia.com/gpu.present と nvidia.com/gpu.deploy.<이름>=true を付与し、各 DaemonSet はそのラベルを nodeSelector として読み込みます(オペレーターのソースコードを確認)。順序の依存関係は init コンテナが担当します。ツールキットは driver-validation、デバイスプラグインは toolkit-validation を先に通過しなければ起動しません。
| 構成要素 | 役割 | 失敗時の挙動 |
|---|---|---|
| NFD, GFD | ハードウェアの情報をラベル化する。GFD は gpu.product·gpu.count など |
ラベルがない場合や、テイントに耐えられない場合はエラーなしで DaemonSet の DESIRED が 0 になる |
| ドライバ | カーネルモジュール。コンテナまたはホストに事前インストール | nvidia-smi 失敗、他の Pod が Init 状態で待機(nouveau の競合を含む) |
| Container Toolkit | nvidia ランタイムハンドラーを containerd の設定に登録 |
no runtime for "nvidia" is configured |
| Device Plugin | kubelet への登録、デバイスリストの広告、 Allocate, XID の監視 |
nvidia.com/gpu なし、または減少 |
| DCGM Exporter | メトリックの公開 | 分離された DCGM に接続できない場合は CrashLoopBackOff |
| MIG Manager | MIG プロファイルの適用 | MIG 非対応の GPU では無意味 |
| Validator | driver, toolkit, cuda, plugin の順で検証 | NVSwitch ノードで Fabric Manager が存在しない場合 system not yet initialized |
Fabric Manager は、NVSwitch システム(HGX など)において GPU 間メモリファブリックを構築するための別サービスです。オペレーターのドライバコンテナは NVSwitch を検出するとこれをインストールし、ホストのドライバを使用している場合は手動でインストールする必要があります。
2. Pod が GPU を取得するまで
flowchart TD
P["디바이스 플러그인<br/>kubelet 소켓에 등록, 장치 목록 전달"] --> N["kubelet<br/>노드 status 의 capacity·allocatable 갱신"]
N --> S["kube-scheduler (NodeResourcesFit)<br/>요청 ≤ allocatable − 이미 배정 ?"]
S --> A["kubelet: Allocate 호출<br/>응답: 장치 노드·환경변수·마운트·CDI 이름"]
A --> R["containerd<br/>CDI 스펙 적용 또는 nvidia 런타임이 OCI 스펙 수정"]
R --> C["runc 가 컨테이너 시작"]
関門 1 は数値です。 スケジューラーはデバイスではなく、ノード status の数値を参照します。GPU は limits にのみ使用され(requests を使用する場合は同じ値である必要がある)、整数となります。プラグインとの接続が切断されると、kubelet はデバイスを unhealthy に変更して allocatable を 0 に下げ、5分の猶予期間が過ぎるとリソース名を削除します(kubelet のソースコード)。ヘルス監視によって1台のデバイスが unhealthy になっても capacity はそのまま残り、allocatable のみが減少します(公式ドキュメント)。
関門 2 はインジェクション方式です。 以前は nvidia-container-runtime が runc をラップして prestart フックで nvidia-container-cli を呼び出す方式でしたが、オペレーター 25.10.0 からは CDI(Container Device Interface)がデフォルトとなっています。通常のワークロードには runtimeClassName は不要です。ただし、オペレーター自体の operand Pod はコントローラーが runtimeClassName: nvidia を付与するため(NRI プラグイン使用時を除く、ソースコードで確認)、ハンドラーが消失するとプラグインすら起動できなくなり広告が途絶えます。
3. インストール方式の選択
| 方式 | メリット | コスト |
|---|---|---|
| ドライバーコンテナ | オペレーターがアップグレードステートマシン(cordon、待機、GPUポッド削除、オプションのdrain)として管理 | すべてのGPUノードのOSバージョンを揃える必要がある。カーネルが上がったらドライバーを再調整する必要がある |
ホスト事前インストール (driver.enabled=false) |
OSが混ざっていてもよい | オペレーターはホストドライバーのライフサイクルを管理しない |
| 事前コンパイル済みイメージ | ノードでビルドしない | タグが <브랜치>-<커널>-<OS> 形式であるため、カーネルバージョンが一致するイメージが必要である |
ドライバーはカーネルモジュールであるため、置き換える前にGPUクライアントをすべて停止する必要があります。ドキュメント(Ubuntu 22.04の脚注)は unattended-upgrades の自動カーネルアップグレードをオフにすることを推奨し、アップグレードが停止したら nvidia.com/gpu-driver-upgrade-state=upgrade-failed ラベルの付いたノードを探すように伝えています。筆者の実験環境ではホストドライバー(595系)と toolkit.enabled=true としてv26.3.3を使用しています。Secure BootノードではDKMSモジュールが署名拒否により読み込まれない場合があり、ディストリビューションが署名したモジュールパッケージを使用する場合、そのモジュールがカーネルバージョンに固定されているため、カーネルバージョンをドライバーに合わせる必要があります。26.3.xはサポート状態が「Deprecated」であり、26.7.xが「Supported」です。コンシューマー向けGPUはドキュメントのサポート製品リストに載っていないため、筆者の環境はサポート対象外と読み取るべきです。
4. 症状別の診断手順
GPU 파드가 안 된다
├─ 파드 오브젝트가 없다 ........ 어드미션: ResourceQuota, 없는 RuntimeClass (apply 때 에러)
└─ 있다
├─ spec.nodeName 이 비어 있다 (Pending)
│ ├─ 이벤트에 taint/selector ... 톨러레이션, 라벨
│ └─ Insufficient nvidia.com/gpu → 노드별로 capacity / allocatable / 배정 합을 대조
│ ├─ 자원 이름이 없다 ........ 플러그인 미등록 (5분 유예 경과 포함)
│ ├─ capacity > allocatable .. unhealthy 장치 (XID), 또는 플러그인 연결 끊김 직후
│ └─ 배정 합 = allocatable ... 진짜 자리 소진
└─ 노드에 붙었다
├─ no runtime for "nvidia" ..... containerd config dump 의 런타임 목록
├─ operand 가 Init 에서 정지 .... 선행 단계(드라이버, 툴킷) 로그
└─ Running 인데 nvidia-smi 실패 . limits 누락, 드라이버/NVML, 장치 접근 상실
(노드에서 dmesg | grep -iE 'NVRM|Xid')
スケジューラーは 요청 > allocatable − 이미 배정 であれば原因を問わず Insufficient <자원 이름> と記録します(kube-schedulerのソース)。そのため、イベントの1行だけで原因を断定してはなりません。
5. よくある罠:ドロップインがCRI設定を丸ごと書き換えてしまった
筆者の実験環境で、GPU 4枚が1週間にわたり広告されませんでした。要約すると以下の通りです。
- HTTPでアクセスするプライベートレジストリのために
conf.dにレジストリ設定のドロップインを1つ追加しました。内容はCRIプラグインのregistry.config_pathの3行だけでした。 - 同じCRIプラグインに触れた最後のファイルが設定全体を上書きします。containerd 1.7.27の実測値は
99-nvidia.toml単独で6個、レジストリドロップイン単独で0個、両方ある場合は0個でした。 cat 99-nvidia.tomlは正常であり、config dumpにもruncともっともらしいデフォルト値が表示されていたため、2回誤診しました。ドロップインを1つずつ外しつつdumpを再度取得したことで、原因が判明しました。- 記録によると、タイムスライシングを4つのノードに一度に適用した際、ツールキットが再実行されて設定が有効になりました。以降は、1ノードずつ適用する手順に変更しました。
外部の根拠で確認できた内容は以下の通りです。containerd 1.7のソースの mergeConfig はコメントの通り「Pluginsの値を丸ごと置き換える」ものであり、公式manページの「mapはappendされる」という文言はこの動作を説明できません。NVIDIAツールキットのソースには、1.7のような古いバージョンではプラグインセクションがキー単位でしかマージされないため、ドロップインにCRI設定全体を複製して記述するというコメントがあります。2.xのソースにはこの置換コードは見当たりませんが、同様の再現が起こるかは確認できていません。ツールキットは終了時に自身のドロップインファイルを削除します。筆者が目にしたある解説では「SIGHUPではハンドラーが登録されない」と書かれていますが、containerdのソースの処理シグナルリスト(1.7、main)にSIGHUPはなく、ツールキットのデフォルトの再起動方式がSIGHUPであるため、筆者の解釈としてはプロセスが終了し、systemdが再起動させるパスとなります(アップストリームのユニットは Restart=always)。このパスを使い捨てのVMで直接確認しました。Ubuntuパッケージのcontainerd 2.2.1を別サービスとして起動し、 systemctl kill -s HUP でSIGHUPを3回送信したところ、3回とも プロセスが終了して新しいプロセスに置き換わりました(PIDが変更され、状態が activating を経由して activeになりました)。ジャーナルには Sent signal SIGHUP to main process の次に Scheduled restart job, Started containerd.service が記録され、ユニットは Restart=always、再起動待機時間は5秒でした。ログレベルを debug に変更した設定ファイルを配置した状態でSIGHUPを送信した際も新しいプロセスが起動して新しい設定を読み込んだため、これは設定の再読み込みではなく 再起動を通じた反映です。筆者環境のノードを確認したところ、containerdは一部が1.7.27、残りが2.3.5であり、ユニットはすべて Restart=always、再起動待機時間は5秒でした(確認のみ行い、SIGHUPは送信していません)。制限事項:検証したバージョンは2.2.1であり、1.7でのSIGHUPは検証していません(ソース上の処理シグナルリストにSIGHUPがないことまでは確認済み)。
6. 容量計画
- capacityとallocatable:健全ではないデバイスはallocatableからのみ除外されます。容量はallocatableベースでカウントします。
- allocatableと実際の使用量:スケジューラーは割り当て(allocation)を確認し、DCGMは使用量を確認します。筆者の3時間の測定では、24GB級のコンシューマー向けGPU 1枚は使用率0であるものの、フレームバッファ13.7 GiBを占有し、21 Wを消費していました。アイドル状態ではあるものの、空き状態ではないカードです。
- タイムスライシング:
replicas: Nは同じデバイスをN回登録するものであり、メモリやエラーは分離されません。例えば、24 GBのGPUを5分割して広告した場合、スロットあたりの平均4.8 GBは説明用の算術に過ぎず、保証値は0です。failRequestsGreaterThanOne: trueは2以上のリクエストを拒否します。オペレーターは設定ConfigMapの変更を監視せず、時分割中にはDCGMがコンテナの紐付けを行えません(公式ドキュメント)。 - MIG:A100 40GBは5 GBのメモリのスライス8個とSMのスライス7個で構成されているため、
1g.5gbは最大7個であり、5 GBが余ります(ドキュメントの構造から算出)。サポートリストにコンシューマー向けGPUがないため、筆者の実験環境では使用できません。
7. 実際に試してみる(読み取り専用)
# 노드별 광고량
kubectl get nodes -o custom-columns=N:.metadata.name,CAP:.status.capacity.nvidia\.com/gpu,ALLOC:.status.allocatable.nvidia\.com/gpu
# operand 현황과 NFD 라벨
kubectl -n gpu-operator get ds,po -o wide
kubectl get nodes -L nvidia.com/gpu.present,nvidia.com/device-plugin.config
# 각 GPU 노드에서: 파일이 아니라 로드된 것을 본다 (0 이면 사고)
containerd config dump | grep -c runtimes.nvidia; ls /etc/containerd/conf.d
# 툴킷이 SIGHUP 을 보낸 시각에 containerd 가 어떻게 되었나
sudo journalctl -u containerd --since "-7d" | grep -E "Started|Stopped|signal" | tail
一時的なポッドで確認を完了させるには、 nvidia-smi -L を1行実行します(kubectl -n gpu-operator run gputest --rm -it --restart=Never ... --limits=nvidia.com/gpu=1 -- nvidia-smi -L)。このポッドはスロットを一時的に使用し、 --rm によってクリーンアップされます。その他の変更コマンドは実行していません。
8. 面接で出そうな質問
- GPUポッドがPendingになっています。どこから確認しますか? イベントの文章よりも、ノードのstatusにあるcapacity、allocatable、割り当ての合計を突合します。同じ
Insufficientが、プラグイン未登録、unhealthyなデバイス、リソース枯渇のいずれの場合でも出力されます。 - ポッドが GPU を割り当てられる経路を説明してください。 プラグインの登録、ノード status の広告、スケジューラの数値判定、
Allocate, CDI または NVIDIA ランタイムのインジェクション、runc の順序です。前者はスケジューラの関門、後者はランタイムの関門です。 - ドライバーをコンテナに配置することとホストに配置することの違いは何ですか? コンテナの場合はアップグレードの自動化と OS 一致の制約があり、ホストの場合は自由度と直接管理の負担があります。カーネル変更時にドライバーが追随しなければならない点が共通のリスクです。
- タイムスライシングと MIG の容量はどのように計算しますか? タイムスライシングは広告が N 倍になるだけでメモリ保証は 0 です。MIG はハードウェアスライスのため、合計が物理容量より小さくなることがあります。
- containerd のドロップイン事故から何を学びましたか? 同じプラグインに手を加えた最後のファイルが設定全体を引き継ぐというマージルールを知っておく必要があります。判定はファイルではなく
containerd config dumpで行い、変更は 1 ノードずつ適用します。
9. よくある誤解
- 「
nvidia-smi「ホストで動くならポッドでも動く」:ランタイムハンドラーと広告は別物です。 - 「
requests「に 0.5 と書けば半分」:GPU は整数拡張リソースです。 - 「
replicas: 5はメモリを 5 等分する」:同じ VRAM を共有します。 - 「オペレーターをデプロイすれば containerd の設定は安全である」:ツールキットがホストファイルを書き込みます。
この記事の構成要素の表をポッド単位で分かりやすく解説した記事が 23 編(GPU Operator のポッド構成要素と役割、ホストドライバーがなくてもよいのか)です。
10. 参考資料と確認したバージョン
すべて 2026-10-05 に直接アクセスしました。
- https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/ の release-notes、platform-support、getting-started、troubleshooting、cdi、gpu-driver-upgrades、gpu-sharing、gpu-operator-mig の各ページ
- https://github.com/NVIDIA/gpu-operator (controllers/object_controls.go, state_manager.go, assets/)
- https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/ (install-guide, arch-overview, cdi-support)
- https://github.com/NVIDIA/nvidia-container-toolkit (cmd/nvidia-ctk-installer, pkg/config/engine/containerd)
- https://github.com/NVIDIA/k8s-device-plugin (README.md)
- https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/
- https://github.com/kubernetes/kubernetes (noderesources/fit.go, pkg/kubelet/cm/devicemanager, master)
- https://github.com/containerd/containerd (config.go 1.7, main_unix.go, docs/man/containerd-config.toml.5.md)
- https://docs.nvidia.com/datacenter/tesla/mig-user-guide/ の supported-gpus、concepts の各ページ
確認したバージョン:GPU Operator 26.7.1(2026-08-21、ツールキット 1.20.1、デバイスプラグイン 0.20.1、DCGM Exporter 4.6.1-4.8.4)、Kubernetes 安定版 v1.37.1、containerd 2.4.1 および 1.7.36。筆者の実験環境は GPU Operator v26.3.3。
確認できなかったこと:26.7.0 の containerd 最小バージョンがドキュメントに「1.8 から 2.0」と記載されていた理由(1.8 は誤記とみられる)、containerd 1.7 での SIGHUP の観測(2.2.1 でのみテスト)、2.x でのマージ動作、インストールのドキュメントにおける「ドライバーがない場合プラグインが静かに 0 個と報告する」(プラグインの README には FAIL_ON_INIT_ERROR デフォルトが true なので失敗すると記載)、障害当時のオペランドポッドの状態の記録。