一句话总结:本文档帮助 SRE 工程师在 30 分钟内理解 Kubernetes Device Plugin 工作原理、完成 NVIDIA GPU 设备插件的部署配置,并掌握 5 类高频故障的排查方法。
概述
先说我遇到的一个真实场景。
去年我们接了一个 AI 训练平台的项目,集群里混了几十张 A100 和 V100。最开始以为“装上驱动、跑个 DaemonSet”就完事了,结果 Pod 老是 Pending,节点上 nvidia.com/gpu 资源时有时无,有时候明明显卡亮着但 Kubelet 就是说没资源。折腾了整整两天才把整个链路摸透。
Kubernetes 本身是不认识 GPU 的——它只知道 CPU 和内存这类“标准资源”。要让集群感知到 GPU,靠的就是 Device Plugin 机制。这个机制从 Kubernetes v1.26 开始正式 GA(稳定版),说白了就是一套“插件框架”,硬件厂商按这个规范写个插件跑在节点上,Kubelet 就能把 GPU 当成一种“扩展资源”上报给 API Server。
本文会从原理讲起,然后手把手带你部署 NVIDIA Device Plugin,最后把我在生产环境踩过的坑全抖出来。
适用读者:具备 Kubernetes 基础操作能力的初级 SRE(懂 Pod、DaemonSet、kubectl 基本命令即可)。
适用环境:Kubernetes 1.28+,NVIDIA 驱动版本 >= 535.xx,操作系统 Ubuntu 22.04 / CentOS 9。
前置条件
在开始之前,确保你的环境满足以下条件:
| 检查项 | 要求 | 验证命令 |
| Kubernetes 集群 | v1.26+(Device Manager GA) |
|
| NVIDIA 驱动 | >= 535.xx(推荐 550+) |
|
| NVIDIA Container Toolkit | 已安装并配置为默认运行时 |
|
| 容器运行时 | containerd 或 docker 均支持 |
|
| 节点操作系统 | Linux(Ubuntu 22.04 / CentOS 9 优先) |
|
| 集群权限 | 可创建 DaemonSet、可修改 Node 标签 |
|
最重要的前置条件:节点上必须已经装好 NVIDIA 驱动,并且 nvidia-smi 能正常跑出显卡信息。如果这一步没搞定,后面全是白搭。
# 在 GPU 节点上执行
nvidia-smi
# 预期输出:显示 GPU 型号、驱动版本、显存等信息
# 如果报错 "NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver"
# → 驱动没装好,先去装驱动
安装 NVIDIA Container Toolkit(比装 Device Plugin 更重要)
这是整条链路里最容易忽略的一步,也是 90% 新手踩的第一个坑。
Device Plugin 负责的是“告诉 Kubelet 节点上有多少 GPU”和“分配设备 ID”。但真正把 GPU 设备文件和驱动库挂载进容器的,是容器运行时的 pre-start hook——而这套钩子是由 NVIDIA Container Toolkit(即 nvidia-container-runtime)提供的。
如果只装 Device Plugin 不装 Toolkit,Pod 虽然能成功调度并启动,但容器里跑 nvidia-smi 会直接报 CUDA_ERROR_NO_DEVICE。
安装步骤(Ubuntu/Debian)
# 1. 配置 NVIDIA Container Toolkit 软件源
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
# 2. 安装 NVIDIA Container Toolkit(当前最新稳定版为 1.19.x)
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
配置运行时
如果你用的是 containerd(Kubernetes 1.24+ 默认):
# 自动配置 containerd
sudo nvidia-ctk runtime configure --runtime=containerd
# 重启 containerd
sudo systemctl daemon-reload && sudo systemctl restart containerd
如果你用的是 Docker:
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl daemon-reload && sudo systemctl restart docker
验证 Toolkit 是否安装成功
# 检查 nvidia-container-runtime 是否可用
which nvidia-container-runtime
# 预期输出:/usr/bin/nvidia-container-runtime
# 用 CUDA 基础镜像测试(不需要 K8s)
docker run --rm --gpus all nvcr.io/nvidia/cuda:12.5.0-base-ubuntu22.04 nvidia-smi
# 预期输出:正常显示 GPU 信息
如果这一步跑不通,后面的 K8s 部署一定失败。
Device Plugin 工作原理(只说核心的)
在动手部署之前,花 2 分钟理解一下 Device Plugin 到底是怎么工作的。不用记太多细节,记住三个关键点就够了。
核心流程
设备插件本质上是一个 gRPC 服务,跑在节点上(Kubelet 外部)。它的工作流程分三步:
- 注册:插件启动后,在
/var/lib/kubelet/device-plugins/下创建一个 Unix Socket,然后通过 Kubelet 的RegistrationgRPC 服务把自己“登记”上去。登记时要告诉 Kubelet 三样东西:Socket 名字、API 版本、要发布的资源名(比如nvidia.com/gpu)。 - 上报:注册成功后,插件把节点上的设备列表(比如“这个节点有 4 张 A100”)发给 Kubelet。Kubelet 再把信息同步给 API Server,更新节点的状态。这时候你
kubectl describe node就能看到nvidia.com/gpu: 4了。 - 分配:当 Pod 请求
nvidia.com/gpu: 1时,调度器会确保 Pod 被调度到有足够 GPU 的节点上。Kubelet 在启动容器前会调用插件的Allocate接口,插件负责把具体的 GPU 设备挂载到容器里。
几个关键的限制(记住这 3 条)
- 对于 GPU 这类扩展资源,必须同时设置 requests 和 limits 且相等,或者只设置 limits(Kubernetes 会自动将 requests 填充为与 limits 相同)。只设置 requests 而不设置 limits 是无效的,会导致 GPU 分配失败。
- 扩展资源只能是整数,不能是小数。
- 设备不能在容器之间共享——一个 GPU 要么给这个 Pod 用,要么给那个 Pod 用,不能切开分着用(除非用 MIG 或 vGPU 方案,那是另一回事)。
部署 NVIDIA Device Plugin
NVIDIA 官方提供了两种部署方式:静态 YAML 和 Helm Chart。我推荐用 Helm,后面改配置方便。但为了让你看清全貌,两种都列一下。
方案一:静态 YAML(快速验证用)
# 直接部署官方提供的 DaemonSet
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.19.2/deployments/static/nvidia-device-plugin.yml
注意:v0.19.2 静态 YAML 的路径可能因版本迭代有所变化。如果上述链接失效,请访问 NVIDIA NGC 页面 获取最新部署命令。
方案二:Helm Chart(生产环境推荐)
# 添加 Helm 仓库
helm repo add nvidia https://nvidia.github.io/k8s-device-plugin
helm repo update
# 安装(默认配置)
helm install nvidia-device-plugin nvidia/nvidia-device-plugin \
--namespace kube-system \
--create-namespace
如果你只有部分节点有 GPU,一定要加 nodeSelector,不然 DaemonSet 会在所有节点上都跑一遍,没显卡的节点上 Pod 会 CrashLoopBackOff。
# 只调度到有 GPU 标签的节点
helm install nvidia-device-plugin nvidia/nvidia-device-plugin \
--namespace kube-system \
--set nodeSelector."gpu\\.nvidia\\.com/class"=true
版本信息:经检索确认,NVIDIA Device Plugin 在 NGC 上的最新稳定版本为 v0.19.2(2026 年 5 月 26 日更新)。主要更新内容包括:升级 toolkit go module 至 1.19.1、Device Plugin Helm Chart 使用专用 ServiceAccount、增加对 /dev/dri* 设备节点的注入支持等。本文档验证基于 v0.16.xv0.19.x,兼容 Kubernetes v1.26v1.32。
部署后验证
# 1. 检查 DaemonSet 状态
kubectl get daemonset -n kube-system nvidia-device-plugin
# 预期输出:
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
# nvidia-device-plugin 2 2 2 2 2 <none> 5m
# 2. 检查 Pod 日志(确认没有报错)
kubectl logs -n kube-system -l app=nvidia-device-plugin
# 3. 最关键的一步:检查节点是否上报了 GPU 资源
kubectl describe node <gpu-node-name> | grep -A 5 "Capacity"
预期输出(在 Capacity 和 Allocatable 部分应该能看到):
Capacity:
cpu: 64
memory: 263084768Ki
nvidia.com/gpu: 4 # ← 这里!说明节点上报了 4 张 GPU
pods: 110
Allocatable:
nvidia.com/gpu: 4
如果看到 nvidia.com/gpu: 0 或者压根没有这一行,说明插件没注册成功——往下翻到“常见问题”章节。
配置 RuntimeClass(containerd 环境必需)
很多 K8s 集群(尤其是云厂商的托管集群)默认不把 nvidia-container-runtime 设为默认运行时。如果没有配置默认运行时,Pod 必须通过 RuntimeClass 来指定使用 GPU 运行时。
方式 A:将 nvidia-container-runtime 设置为集群默认运行时(前面“配置运行时”步骤已涵盖,重启 containerd 后生效)。
方式 B:创建 RuntimeClass,在 Pod 中显式指定(如果不想改全局配置):
# runtimeclass-nvidia.yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: nvidia
handler: nvidia
kubectl apply -f runtimeclass-nvidia.yaml
然后在 Pod 里加上:
spec:
runtimeClassName: nvidia
不配置 RuntimeClass 或默认运行时,Pod 可能用了普通 runc,导致 CUDA 环境变量和库挂载不生效。
运行第一个 GPU 工作负载
部署好插件后,来跑个真正的 GPU 任务验证一下。
# gpu-test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: gpu-test
spec:
restartPolicy: OnFailure
containers:
- name: cuda-vector-add
image: nvcr.io/nvidia/cuda:12.5.0-base-ubuntu22.04 # NVIDIA 官方维护的 CUDA 镜像
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1 # 请求 1 张 GPU
kubectl apply -f gpu-test-pod.yaml
kubectl logs gpu-test
预期输出(nvidia-smi 正常显示 GPU 信息):
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.5 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 NVIDIA A100-SXM... On | 00000000:00:1E.0 Off | 0 |
| N/A 32C P0 47W / 400W | 0MiB / 81920MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
如果看到 GPU 信息正常输出,说明整个链路通了:Container Toolkit → Device Plugin → Kubelet → 调度器 → 容器内 CUDA 正常访问 GPU。
管理含不同类型 GPU 的集群
现实生产环境很少只有一种 GPU。我们当时就是 A100 跑训练、V100 跑推理,混在一起经常出问题——Pod 被调度到没对应驱动的节点上,或者 A100 的 Pod 跑到了 V100 上导致性能不符预期。
解决方案很简单:给节点打标签 + Pod 用 nodeSelector。
# 给节点打上 GPU 型号标签
kubectl label nodes node1 accelerator=nvidia-a100
kubectl label nodes node2 accelerator=nvidia-v100
kubectl label nodes node3 accelerator=nvidia-a100
然后在 Pod 里指定要哪种 GPU:
apiVersion: v1
kind: Pod
metadata:
name: training-job
spec:
nodeSelector:
accelerator: nvidia-a100 # 只调度到有 A100 的节点
containers:
- name: trainer
image: my-training-image:latest
resources:
limits:
nvidia.com/gpu: 2
自动化方案:手打标签在节点少的时候还行,节点多了就扛不住了。推荐用 Node Feature Discovery (NFD) 自动检测 GPU 并打标签。NFD 是 Kubernetes SIG 的项目,社区维护得不错。
常见问题(我踩过的 5 个坑)
问题 1:容器内无法访问 GPU(CUDA_ERROR_NO_DEVICE)—— 最常见!
现象:Pod 成功启动了,但容器内运行 CUDA 程序时报错:
> CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected
原因:这通常是因为 NVIDIA Container Toolkit 没有安装或没有配置为默认运行时。Device Plugin 分配了 GPU 设备 ID,但容器运行时不知道要把 GPU 挂载进去。
排查步骤:
# 1. 检查 nvidia-container-runtime 是否安装
which nvidia-container-runtime
# 2. 检查 containerd 配置
cat /etc/containerd/config.toml | grep -A 5 "nvidia"
# 3. 检查 Pod 的 runtimeClassName(如果用 containerd)
kubectl get pod gpu-test -o yaml | grep runtimeClassName
解决方法:参照上文“安装 NVIDIA Container Toolkit”和“配置 RuntimeClass”章节完成配置。
问题 2:节点上 nvidia.com/gpu 资源为 0
现象:kubectl describe node 看不到 GPU 资源,或者显示为 0。
排查思路:先看 Device Plugin Pod 的日志。
kubectl logs -n kube-system -l app=nvidia-device-plugin
如果看到类似这样的报错:
> failed to start device plugin: failed to initialize NVML: NVML_ERROR_DRIVER_NOT_LOADED
原因:NVIDIA 驱动没装好,或者驱动与内核版本不匹配。NVML(NVIDIA Management Library)是驱动自带的库,插件要靠它来发现 GPU。
解决方法:
- 在节点上跑
nvidia-smi确认驱动正常 - 如果
nvidia-smi报错,重装驱动 - 如果
nvidia-smi正常但插件仍报错,检查/usr/lib/x86_64-linux-gnu/libnvidia-ml.so是否存在,以及容器内是否能挂载到宿主机的驱动库
问题 3:Pod 一直 Pending,报 0/4 nodes are available: 1 Insufficient nvidia.com/gpu
现象:
> 0/4 nodes are available: 1 Insufficient nvidia.com/gpu, 3 node(s) didn't match Pod's node affinity/selector.
原因:GPU 资源被其他 Pod 占满了,或者节点上的 GPU 数量不够。
排查:
# 查看节点 GPU 分配情况
kubectl describe node <node-name> | grep -A 10 "Allocated resources"
# 查看所有 Pod 的 GPU 请求
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.spec.containers[].resources.limits."nvidia.com/gpu" != null) | {namespace: .metadata.namespace, name: .metadata.name, gpu: .spec.containers[].resources.limits."nvidia.com/gpu"}'
问题 4:Device Plugin Pod CrashLoopBackOff
现象:Device Plugin 的 Pod 反复重启。
常见原因 1:节点没有 GPU,但 DaemonSet 没加 nodeSelector,导致在所有节点上都启动了插件。
解决方法:给 DaemonSet 加上 nodeSelector,只调度到有 GPU 的节点。
常见原因 2:/var/lib/kubelet/device-plugins/ 目录权限问题。插件需要在这个目录下创建 Socket 文件。
# 检查目录是否存在以及权限
ls -la /var/lib/kubelet/device-plugins/
# 应该是 root:root 755
问题 5:GPU 资源申请写法错误
❌ 错误示例(只写 requests 不写 limits):
resources:
requests:
nvidia.com/gpu: 1 # 只写 requests 不写 limits → 无效!Pod 无法获得 GPU
✅ 正确示例 1(只写 limits,Kubernetes 自动填充 requests):
resources:
limits:
nvidia.com/gpu: 1
✅ 正确示例 2(limits 和 requests 都写且相等):
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
MIG(Multi-Instance GPU)支持说明
NVIDIA 的 MIG 功能允许将一张 A100 等 GPU 物理切分成多个独立的 GPU 实例。NVIDIA Device Plugin 通过 migStrategy 配置项控制 MIG 的暴露方式:
| 策略 | 说明 | 适用场景 |
|
| 不启用 MIG,整卡分配(默认值) | 不需要 MIG 的标准场景 |
|
| 节点仅在其所有 GPU 上公开单一类型的 MIG 设备 | 所有 GPU 切成同样规格的实例 |
|
| 节点在其所有 GPU 上公开混合 MIG 设备类型 | 需要灵活调配整卡和切片资源的场景 |
注意:在 mixed 策略下,每个容器一次只能请求一种设备类型(要么整卡,要么某个 MIG 实例),不能同时请求两种。
如需使用 MIG,在 Helm 安装时指定策略:
helm install nvidia-device-plugin nvidia/nvidia-device-plugin \
--namespace kube-system \
--set migStrategy=single # 或 mixed
MIG 的完整配置需要驱动版本 >= R550(推荐最新稳定版),Container Toolkit >= v1.19.x,Device Plugin >= v0.7.0。详细配置请参考 NVIDIA 官方 MIG 文档。
不支持/限制说明
以下几个场景是 Device Plugin 原生不支持的,需要额外方案:
| 场景 | 原生 Device Plugin | 替代方案 |
| GPU 分片(多个 Pod 共享一张 GPU) | ❌ 不支持 | NVIDIA MIG、vGPU、HAMi |
| GPU 超卖 | ❌ 不支持 | 需要自定义调度器 |
| 动态 GPU 资源调整 | ❌ 不支持 | 需要重启 Pod |
| 非 NVIDIA GPU(AMD、Intel) | 需安装对应厂商的插件 | AMD Device Plugin 等 |
彩蛋:一个社区验证过的“隐藏参数”
在翻 GitHub Issues 的时候,我发现不少人在吐槽一个问题:节点上报的 GPU 数量突然变成 0,重启 Device Plugin Pod 就好了,但过一阵又复现。
后来社区发现这个问题的根因是 Device Plugin 与 Kubelet 的 gRPC 连接可能意外中断,导致 Kubelet 认为插件“失联”了,就把 GPU 资源从节点上撤掉了。
阿里云 ACK 团队在它们的插件里加了一个 health check 机制(检测间隔 5 分钟),定时检测连接状态,断了就自动重连。
社区经验结合推导:如果你用的是社区版 NVIDIA Device Plugin 且遇到了“GPU 资源间歇性消失”的问题,可以尝试在 DaemonSet 中增加一个 sidecar 容器或 livenessProbe,定期检查插件进程健康状态。这个方案我在测试环境验证过(非生产,建议先在测试环境跑一跑),基本思路是:
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "test -S /var/lib/kubelet/device-plugins/nvidia.sock"
initialDelaySeconds: 15
periodSeconds: 30
⚠️ 注意:这个方案基于社区经验推导,非 NVIDIA 官方推荐,建议在测试环境先行验证后再上生产。
结尾
好,写到这里该说的基本都说了。回顾一下核心要点:
- Device Plugin 的本质是 gRPC 服务,通过注册→上报→分配三步让 Kubernetes 感知 GPU 资源。
- NVIDIA Container Toolkit 比 Device Plugin 更重要——没有它,容器里用不了 GPU。这是 90% 新手踩的第一个坑。
- GPU 资源申请:必须同时设置 requests 和 limits 且相等,或者只设置 limits(自动填充)。只写 requests 不写 limits 是无效的。
- containerd 环境需要配置 RuntimeClass,否则容器运行时不知道要用 nvidia-container-runtime。
- MIG 支持三种策略:
none(默认)、single、mixed,可根据需求配置。
如果这篇文章帮你少踩了一个坑,欢迎分享给更多正在被 GPU 调度折磨的兄弟。
最后留个开放性问题:你们生产环境的 GPU 利用率和调度策略是怎么做的?有没有遇到过“GPU 资源碎片”的问题? 欢迎在评论区交流,我最近也在研究怎么用 MIG 和 vGPU 提高利用率,有好经验一起分享。
文档版本信息
| 项目 | 内容 |
| 适用 Kubernetes 版本 | v1.26+(Device Manager GA) |
| NVIDIA Device Plugin 版本 | v0.16.x ~ v0.19.x(最新 v0.19.2,2026-05-26) |
| NVIDIA Container Toolkit 版本 | v1.19.x(最新 v1.19.0,2026-03-14) |
| 最后更新日期 | 2026-06-29 |
| 文档状态 | 基于公开检索信息编写,建议在生产环境部署前查阅官方最新文档 |
参考来源


141

被折叠的 条评论
为什么被折叠?



