← 返回列表

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/gpuk8s.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 注解带上
总结
  1. runtimeClassName: nvidia → 用默认 NVIDIA runtime
  2. 新版 toolkit 下默认常是 CDI 模式(所以像走了 CDI)
  3. CDI 要精确匹配 kind=name:这里是 management.nvidia.com/gpu=UUID
  4. 你的 UUID 注册在别的 kind 下,不在 management.nvidia.com/gpu
  5. 于是:卡在、调度成了,注入失败

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