k8s: 问题显卡运行时问题排查
点朗读开始;播放中可点段落跳转。屏幕默认常亮。
现象
安装好了gpu-operator后,安装hami2.9.0 后,创建测试任务:
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
runtimeClassName: nvidia # 错误根源不要用 nvidia,正确nvidia-legacy
containers:
- name: ubuntu-container
image: ubuntu:18.04
command: ["bash", "-c", "sleep 86400"]
resources:
limits:
nvidia.com/gpu: 1
nvidia.com/gpumem: 10240
requests:
nvidia.com/gpu: 1
nvidia.com/gpumem: 10240
然后任务一起起不来:报错下
[root@10-30-15-55 hcitos]# kubectl describe pod gpu-pod
Name: gpu-pod
Namespace: default
Priority: 0
Runtime Class Name: nvidia
Service Account: default
Node: 10-30-15-55/10.30.15.55
Start Time: Wed, 29 Jul 2026 14:46:05 +0800
Labels: hami.io/vgpu-node=10-30-15-55
Annotations: cni.projectcalico.org/containerID: 3a81a1d5de8b4304499bf8f058535b26466d8746286f07b6891c7c9d98cab123
cni.projectcalico.org/podIP: 192.168.72.245/32
cni.projectcalico.org/podIPs: 192.168.72.245/32
hami.io/bind-phase: success
hami.io/bind-time: 1785307565
hami.io/vgpu-devices-allocated: GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e,NVIDIA,24564,100:;
hami.io/vgpu-devices-to-allocate: ;;
hami.io/vgpu-node: 10-30-15-55
hami.io/vgpu-time: 1785307565
k8s.v1.cni.cncf.io/network-status:
[{
"name": "k8s-pod-network",
"interface": "eth0",
"ips": [
"192.168.72.245"
],
"mac": "ce:6e:e5:ee:df:87",
"default": true,
"dns": {}
}]
Status: Running
IP: 192.168.72.245
IPs:
IP: 192.168.72.245
Containers:
ubuntu-container:
Container ID: containerd://b7a8bdc793a48b31fe697421fa228615df91393de74ecdf74e582e9b7aed19b1
Image: ubuntu:18.04
Image ID: docker.io/library/ubuntu@sha256:152dc042452c496007f07ca9127571cb9c29697f42acbfad72324b2bb2e43c98
Port: <none>
Host Port: <none>
Command:
bash
-c
sleep 86400
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: StartError
Message: failed to create containerd task: failed to create shim task: OCI runtime create failed: could not apply required modification to OCI specification: error modifying OCI spec: failed to inject CDI devices: unresolvable CDI devices management.nvidia.com/gpu=GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
Exit Code: 128
Started: Thu, 01 Jan 1970 08:00:00 +0800
Finished: Wed, 29 Jul 2026 14:46:06 +0800
Ready: False
Restart Count: 1
Limits:
nvidia.com/gpu: 1
nvidia.com/gpucores: 100
Requests:
nvidia.com/gpu: 1
nvidia.com/gpucores: 100
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-fl2cz (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-fl2cz:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 10s hami-scheduler Successfully assigned default/gpu-pod to 10-30-15-55
Normal FilteringSucceed 10s hami-scheduler find fit node(10-30-15-55), 1 nodes not fit, 1 nodes fit(10-30-15-55:0.00)
Normal BindingSucceed 10s hami-scheduler Successfully binding node [10-30-15-55] to default/gpu-pod
Normal AddedInterface 9s multus Add eth0 [192.168.72.245/32] from k8s-pod-network
Normal Pulled 9s (x2 over 9s) kubelet Container image "ubuntu:18.04" already present on machine
Normal Created 9s (x2 over 9s) kubelet Created container: ubuntu-container
Warning Failed 9s (x2 over 9s) kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: could not apply required modification to OCI specification: error modifying OCI spec: failed to inject CDI devices: unresolvable CDI devices management.nvidia.com/gpu=GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
Warning BackOff 7s (x2 over 8s) kubelet Back-off restarting failed container ubuntu-container in pod gpu-pod_default(84a1a608-a41e-4ba6-b888-4978c10e0d7c)
解决
关闭cdi:
#关之前
[root@10-30-15-55 conf.d]# grep -R "default-kind\|mode" /etc/nvidia-container-runtime/ /usr/local/nvidia/toolkit/.config/ 2>/dev/null
/etc/nvidia-container-runtime/config.toml:mode = "auto"
/etc/nvidia-container-runtime/config.toml:[nvidia-container-runtime.modes]
/etc/nvidia-container-runtime/config.toml:[nvidia-container-runtime.modes.cdi]
/etc/nvidia-container-runtime/config.toml:default-kind = "nvidia.com/gpu"
/etc/nvidia-container-runtime/config.toml:[nvidia-container-runtime.modes.csv]
/etc/nvidia-container-runtime/config.toml:skip-mode-detection = false
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: mode = "cdi"
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes.cdi]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: default-kind = "management.nvidia.com/gpu"
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes.csv]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes.legacy]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: cuda-compat-mode = "ldconfig"
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: skip-mode-detection = true
#关之后
[root@10-30-15-55 cdi]# grep -R "default-kind\|mode" /etc/nvidia-container-runtime/ /usr/local/nvidia/toolkit/.config/ 2>/dev/null
/etc/nvidia-container-runtime/config.toml:mode = "auto"
/etc/nvidia-container-runtime/config.toml:[nvidia-container-runtime.modes]
/etc/nvidia-container-runtime/config.toml:[nvidia-container-runtime.modes.cdi]
/etc/nvidia-container-runtime/config.toml:default-kind = "nvidia.com/gpu"
/etc/nvidia-container-runtime/config.toml:[nvidia-container-runtime.modes.csv]
/etc/nvidia-container-runtime/config.toml:skip-mode-detection = false
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: mode = "auto"
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes.cdi]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: default-kind = "management.nvidia.com/gpu"
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes.csv]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: [nvidia-container-runtime.modes.legacy]
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: cuda-compat-mode = "ldconfig"
/usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml: skip-mode-detection = true
探索
根因很明确:调度和 GPU 都正常,失败发生在容器启动时的 CDI 设备注入。
结论
HAMi 已成功分配 GPU(UUID 也对),但 containerd/OCI 运行时要注入:
management.nvidia.com/gpu=GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
这个 CDI 设备解析不到,所以报:
unresolvable CDI devices management.nvidia.com/gpu=...
你在 /var/run/cdi/nvidia.yaml 里能看到同名 GPU,通常只说明存在:
nvidia.com/gpu=GPU-2bd7f9eb-...
和运行时要找的 management.nvidia.com/gpu=... 不是同一个 CDI 厂商名,对不上就会 CrashLoopBackOff。
建议排查
在节点上执行:
# 1. 看 CDI 实际厂商名(重点)
head -50 /var/run/cdi/nvidia.yaml
# 2. 有没有 management.nvidia.com 的 CDI 文件
ls -l /var/run/cdi/ /etc/cdi/ 2>/dev/null
grep -R "management.nvidia.com\|GPU-2bd7f9eb" /var/run/cdi/ /etc/cdi/ 2>/dev/null
[root@10-30-15-55 compute_test]# grep -R "management.nvidia.com\|GPU-2bd7f9eb" /var/run/cdi/ /etc/cdi/ -A 50 2>/dev/null
#/etc/cdi/nvidia.yaml: name: GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
#/etc/cdi/nvidia.yaml-- containerEdits:
#....
#/etc/cdi/nvidia.yaml-kind: nvidia.com/gpu 就是这个没有匹配上
# 3. 确认 containerd 已启用 CDI
grep -A20 -E 'cdi|enable_cdi' /etc/containerd/config.toml
# 4. 工具链版本
nvidia-ctk --version
nvidia-container-runtime --version
# toolkit 生成配置文件
[root@10-30-15-55 compute_test]# cat /usr/local/nvidia/toolkit/.config/nvidia-container-runtime/config.toml 2>/dev/null
...
[nvidia-container-runtime]
debug = "/dev/null"
log-level = "info"
mode = "auto"
runtimes = ["runc", "crun"]
[nvidia-container-runtime.modes]
[nvidia-container-runtime.modes.cdi]
annotation-prefixes = ["cdi.k8s.io/"]
default-kind = "management.nvidia.com/gpu" # 长这样子
spec-dirs = ["/etc/cdi", "/var/run/cdi"]
[nvidia-container-runtime.modes.csv]
mount-spec-path = "/etc/nvidia-container-runtime/host-files-for-container.d"
[nvidia-container-runtime.modes.legacy]
cuda-compat-mode = "ldconfig"
[nvidia-container-runtime-hook]
path = "/usr/local/nvidia/toolkit/nvidia-container-runtime-hook"
skip-mode-detection = true
[nvidia-ctk]
path = "/usr/local/nvidia/toolkit/nvidia-ctk"
99-nvidia.toml:
| RuntimeClass | Handler / Binary | 含义 |
|---|---|---|
| nvidia | nvidia-container-runtime |
默认推荐入口。由 toolkit 当前默认模式决定(新版本常默认走 CDI,也可配成 auto/legacy)。Pod 写runtimeClassName: nvidia 时用这个。 |
| nvidia-cdi | nvidia-container-runtime.cdi |
强制 CDI:按 CDI spec(/etc/cdi、/var/run/cdi)把 GPU 注入容器。适合纯 GPU Operator / device plugin CDI 路径。 |
| nvidia-legacy | nvidia-container-runtime.legacy |
传统模式:主要靠NVIDIA_VISIBLE_DEVICES 等环境变量 + prestart hook 挂设备,不依赖 CDI 解析。兼容老用法,也常更适合 HAMi 这类切分方案。 |
底层逻辑
用一条链路把整件事串起来就好理解了。
先分清三层名字
RuntimeClass 名(YAML 里写的)
↓ 映射到
containerd runtime handler(99-nvidia.toml)
↓ 真正执行
nvidia-container-runtime 二进制 + 它的工作模式(legacy / cdi)
你机器上大致是这样:
| 你写的 RuntimeClass | 实际二进制 | 行为 |
|---|---|---|
nvidia |
nvidia-container-runtime |
默认模式(新 toolkit 常是 CDI) |
nvidia-cdi |
...runtime.cdi |
强制 CDI |
nvidia-legacy |
...runtime.legacy |
强制旧模式 |
所以:
写 nvidia ≠ 走 nvidia-legacy,也 ≠ 名字叫 CDI 才走 CDI。
nvidia 这个“默认入口”,在 GPU Operator + 新版 toolkit(你是 1.17.4)里,内部往往已经默认按 CDI 模式 工作。
因此你会感觉:写了 nvidia,却像走了 nvidia-cdi。
Pod 启动时实际发生了什么
简化成 5 步:
1. HAMi 调度成功
注解里记下:这块卡 UUID = GPU-2bd7f9eb-...
2. kubelet 用 RuntimeClass=nvidia 创建容器
→ 调用 nvidia-container-runtime
3. runtime 发现自己是 CDI 模式
→ 不走(或不全走)老的 NVIDIA_VISIBLE_DEVICES hook
→ 改成:向 OCI spec 注入 CDI 设备
4. CDI 查找规则大致是:
kind + name
默认 kind = management.nvidia.com/gpu
name = GPU-2bd7f9eb-...
即查找:
management.nvidia.com/gpu = GPU-2bd7f9eb-...
5. 去 /var/run/cdi、/etc/cdi 里找这个「kind+name」
→ 找不到 → unresolvable CDI devices → StartError
报错里的这句话,拆开就是:
unresolvable CDI devices
management.nvidia.com/gpu = GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
│ │ │
│ │ └─ 设备名(UUID)
│ └─ 分隔符
└─ CDI 的 kind(厂商/类型)
意思是:不是没有这块物理 GPU,而是 CDI 目录里没有“这个 kind 下叫这个名字的设备条目”。
为什么“nvidia-smi 能看到,CDI 却找不到”
物理 GPU 和 CDI 条目是两回事:
硬件层: nvidia-smi 能看到 UUID ✅
调度层: HAMi 已分配这个 UUID ✅
CDI 层: 必须在某个 yaml/json 里注册成
「某个 kind」下的「某个 name」才算可注入
你节点上实际注册情况是:
nvidia.com/gpu = GPU-2bd7f9eb-... ✅ 有
k8s.device-plugin.nvidia.com/gpu = GPU-2bd7f9eb-... ✅ 有
management.nvidia.com/gpu = GPU-2bd7f9eb-... ❌ 没有(或名字不是 UUID)
而 nvidia(CDI 模式)默认去查的是 第三行,所以失败。
可以把它想成:
图书馆里书在「科技区」和「设备插件区」都有, 管理员却按默认规则去「管理区」找同名书, 管理区没有这本书 → 报 unresolvable。
为啥默认 kind 是 management.nvidia.com/gpu
这是 NVIDIA Container Toolkit / GPU Operator 在 CDI 模式下的常见默认约定:
- 设备插件 / runtime 用 CDI 时,常把要注入的 GPU 记成
management.nvidia.com/gpu=<id> - 同时节点上还会生成别的 kind(
nvidia.com/gpu、k8s.device-plugin.nvidia.com/gpu)
理想情况:management.nvidia.com-gpu.yaml 里也应有同名 UUID。
你这边:management.nvidia.com 的文件在,但 没有这个 UUID 设备名;
HAMi 又按 UUID 分配,两边约定不一致 → 必炸。
这也是 HAMi + GPU Operator(CDI)叠在一起时很常见的坑:
HAMi: 我按 UUID 分卡
Operator CDI runtime: 我按 management.nvidia.com/gpu=<UUID> 去 CDI 库取卡
CDI 库: 这个 kind 下没有该 UUID
结果: 调度成功,容器起不来
和三个 RuntimeClass 的对应关系(心智模型)
nvidia-legacy
→ 主要靠环境变量挂 GPU(老路)
→ 不太依赖 CDI kind 是否齐全
→ HAMi 往往更适合先走这条
nvidia / nvidia-cdi(CDI)
→ 必须 CDI 库里 kind+name 完全匹配
→ 差一点名字就 unresolvable
所以你不是“选了 nvidia 却误进 nvidia-cdi 这个 Class”,
而是:nvidia 这个 Class 对应的默认二进制,内部模式已经是 CDI。
nvidia-cdi 只是把这件事写死、更明确而已。
CDI 认的是「kind + name」,不是只认 name
完整设备 ID 长这样:
management.nvidia.com/gpu = GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
└──────── kind ─────────┘ └────────────── name ──────────────────┘
你在 nvidia.yaml 里看到的:
name: GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
只说明 name 对了。这个文件的 kind 一般是:
kind: nvidia.com/gpu
所以它登记的是:
nvidia.com/gpu = GPU-2bd7f9eb-... ✅ 存在
而报错要找的是:
management.nvidia.com/gpu = GPU-2bd7f9eb-... ❌ 不存在(或不叫这个 name)
报错里的 kind 是谁给的?
主要是节点上的 nvidia-container-runtime(CDI 模式)自己拼出来的,不是你在 Pod YAML 里写的。
链路是这样:
HAMi / device plugin
→ 告诉容器:要用这块卡 UUID = GPU-2bd7f9eb-...
nvidia-container-runtime(mode=cdi)
→ 读取配置里的 default-kind
→ 默认常是:management.nvidia.com/gpu
→ 拼成:management.nvidia.com/gpu=GPU-2bd7f9eb-...
→ 交给 containerd 做 CDI inject
containerd
→ 只在 kind=management.nvidia.com/gpu 的 CDI 文件里找这个 name
→ 找不到 → 报你看到的错
也就是说:
- UUID(name):多半来自 HAMi 分配结果
- kind=
management.nvidia.com/gpu:来自 NVIDIA runtime 的 CDI 默认配置(default-kind),有时也会被 device plugin 的 CDI 注解带上
总结
- 写
runtimeClassName: nvidia→ 用默认 NVIDIA runtime - 新版 toolkit 下默认常是 CDI 模式(所以像走了 CDI)
- CDI 要精确匹配
kind=name:这里是management.nvidia.com/gpu=UUID - 你的 UUID 注册在别的 kind 下,不在
management.nvidia.com/gpu - 于是:卡在、调度成了,注入失败
management.nvidia.com-gpu.yaml 文件对比
#
[root@10-30-15-55 cdi]# cat management.nvidia.com-gpu.yaml
---
cdiVersion: 0.5.0
kind: management.nvidia.com/gpu
devices:
- name: all
containerEdits:
deviceNodes:
- path: /dev/nvidia-modeset
hostPath: /dev/nvidia-modeset
- path: /dev/nvidia-uvm
hostPath: /dev/nvidia-uvm
- path: /dev/nvidia-uvm-tools
hostPath: /dev/nvidia-uvm-tools
- path: /dev/nvidia0
hostPath: /dev/nvidia0
- path: /dev/nvidiactl
hostPath: /dev/nvidiactl
- path: /dev/nvidia-caps/nvidia-cap1
hostPath: /dev/nvidia-caps/nvidia-cap1
- path: /dev/nvidia-caps/nvidia-cap2
hostPath: /dev/nvidia-caps/nvidia-cap2
# 重新生成
[root@10-30-15-55 compute_test]#/usr/local/nvidia/toolkit/nvidia-ctk cdi generate --vendor management.nvidia.com --device-name-strategy uuid --output /var/run/cdi/management.nvidia.com-gpu.yaml
[root@10-30-15-55 compute_test]# cat /var/run/cdi/management.nvidia.com-gpu.yaml
---
cdiVersion: 0.3.0
kind: management.nvidia.com/gpu
devices:
- name: GPU-2bd7f9eb-5916-6dc2-dea4-1d97d1fb848e
containerEdits:
deviceNodes:
- path: /dev/nvidia0
- path: /dev/dri/card0
- path: /dev/dri/renderD128
hooks:
- hookName: createContainer
path: /usr/local/nvidia/toolkit/nvidia-cdi-hook
args:
- nvidia-cdi-hook
- create-symlinks
- --link
- ../card0::/dev/dri/by-path/pci-0000:3b:00.0-card
- --link
- ../renderD128::/dev/dri/by-path/pci-0000:3b:00.0-render
env:
- NVIDIA_CTK_DEBUG=false
- name: all
containerEdits:
deviceNodes:
- path: /dev/nvidia0
- path: /dev/dri/card0
- path: /dev/dri/renderD128
hooks:
- hookName: createContainer
path: /usr/local/nvidia/toolkit/nvidia-cdi-hook
args:
- nvidia-cdi-hook
- create-symlinks
- --link
- ../card0::/dev/dri/by-path/pci-0000:3b:00.0-card
- --link
- ../renderD128::/dev/dri/by-path/pci-0000:3b:00.0-render
env:
- NVIDIA_CTK_DEBUG=false