K8s GPU 指南宝典
myluzh 发布于 阅读:1 Kubernetes
适合已经会 Kubernetes、但第一次运维 GPU 集群的人。
目标不是让你马上变成 CUDA 开发者,而是让你能把 GPU 节点稳定接入 K8s、让业务 Pod 正确使用 GPU,并且知道怎么排障、扩展、监控和规划后续能力。
0. 你应该先建立的整体模型
K8s GPU 集群可以拆成 6 层:
硬件 GPU
↓
宿主机 NVIDIA Driver
↓
容器运行时 GPU 注入层:NVIDIA Container Toolkit / CDI
↓
Kubernetes GPU 资源发现:nvidia-device-plugin / GPU Operator / HAMi
↓
调度与资源声明:nvidia.com/gpu、RuntimeClass、nodeSelector、affinity
↓
业务容器:CUDA、cuDNN、PyTorch、TensorFlow、推理服务
新手最容易混淆的是:
- 宿主机必须有 NVIDIA Driver。
- 宿主机通常不需要完整 CUDA Toolkit。
- 业务镜像里通常自带 CUDA runtime / cuDNN / 框架依赖。
- K8s 不会天然知道 GPU,要靠 device plugin 或 GPU Operator 注册资源。
- 容器不是天然能看到 GPU,要靠 NVIDIA Container Toolkit / CDI 注入设备和驱动库。
一句话:
Driver 让宿主机能用 GPU;Container Toolkit 让容器能看见 GPU;device plugin 让 K8s 能调度 GPU。
1. 你需要学哪些东西,学到什么深度
1.1 CUDA
你不需要一开始学习 CUDA 编程,但要理解这些概念:
- CUDA 是 NVIDIA 的 GPU 计算平台。
- CUDA Toolkit 包含编译器、库、头文件、调试工具等。
- CUDA Runtime 是业务程序运行时需要的用户态库。
- 宿主机 NVIDIA Driver 和容器里的 CUDA 版本要兼容。
运维侧最重要的是:
nvidia-smi
它可以看:
- 驱动版本
- GPU 型号
- 显存使用
- 当前进程
- 温度、功耗
- MIG 模式
1.2 NVIDIA Driver
Driver 是 GPU 节点最核心的宿主机依赖。没有 Driver,nvidia-smi 就不能正常工作。
生产建议:
- 优先用系统包管理器或 NVIDIA 官方 repo 安装驱动。
- 尽量不要混用
.run安装和apt/yum/dnf安装。 - 驱动版本尽量新一些,容器 CUDA 可以相对旧一些。
- 升级内核后要确认 NVIDIA kernel module 是否正常。
1.3 NVIDIA Container Toolkit
它负责让容器运行时能把 GPU 设备和 NVIDIA 驱动库注入到容器里。
常见组件:
nvidia-container-toolkitnvidia-container-runtimenvidia-ctklibnvidia-container
在 containerd 场景下,常见配置命令:
nvidia-ctk runtime configure --runtime=containerd
systemctl restart containerd
你还会看到 CDI 这个词。CDI 是 Container Device Interface,用来把设备注入容器。新的 GPU Operator 默认走 CDI 方向;手工安装 NVIDIA runtime 时,很多环境仍会用 RuntimeClass 指向 nvidia runtime。两条路线都能工作,但不要把两套配置混在一起理解。
1.4 nvidia-device-plugin
这是 NVIDIA 官方 Kubernetes device plugin。
它负责把 GPU 注册给 kubelet,默认资源名是:
nvidia.com/gpu
Pod 通过下面这种方式申请 GPU:
resources:
limits:
nvidia.com/gpu: 1
1.5 GPU Operator
NVIDIA GPU Operator 是更自动化的路线。
它可以帮你管理:
- Driver
- Container Toolkit
- device plugin
- GPU Feature Discovery
- DCGM Exporter
- MIG Manager
- Node Feature Discovery
如果是新集群、节点环境标准化、希望少手工维护,优先考虑 GPU Operator。
1.6 HAMi
HAMi 是 GPU 共享和异构设备虚拟化方案。
它常用于:
- 多个 Pod 共享一张 GPU
- 按显存申请 GPU
- 按算力比例申请 GPU
- 多种加速卡统一调度
HAMi 不是第一天必须上。建议先掌握官方 device plugin,再看 HAMi。
2. 最小可行学习路线
建议按这个顺序学:
第 1 阶段:单机 GPU 基础
目标:
- 知道 GPU 型号。
- 安装并验证 NVIDIA Driver。
- 会用
nvidia-smi看状态。
要会的命令:
lspci | grep -i nvidia
lsmod | grep nvidia
nvidia-smi
journalctl -k | grep -i nvidia
第 2 阶段:容器里使用 GPU
目标:
- Docker/containerd 能运行 GPU 容器。
- 容器里执行
nvidia-smi成功。
要理解:
- 宿主机 Driver
- 容器镜像 CUDA runtime
- NVIDIA Container Toolkit
第 3 阶段:K8s 整卡调度
目标:
- K8s node 上能看到
nvidia.com/gpu。 - Pod 能申请整张 GPU。
- Pod 里能执行
nvidia-smi。
要理解:
- device plugin
- extended resource
- RuntimeClass
- node label / taint / toleration
第 4 阶段:生产化
目标:
- GPU 节点分组。
- 驱动升级策略。
- 监控显存、温度、功耗、利用率。
- 业务镜像版本管理。
- 故障排查流程。
第 5 阶段:共享和高级调度
目标:
- MIG
- time-slicing
- MPS
- HAMi
- 多型号 GPU 调度
3. 安装 GPU 节点:手工路线
下面以 Ubuntu/Debian 系为例。生产环境请先在测试节点验证。
3.1 确认 GPU 硬件
lspci | grep -i nvidia
如果没有输出,先检查:
- 物理机是否真的有 GPU。
- BIOS 是否屏蔽了设备。
- 云厂商实例类型是否正确。
- PCIe passthrough 是否正确。
3.2 禁用 nouveau
nouveau 是开源 NVIDIA 显卡驱动,通常会和官方 NVIDIA Driver 冲突。
查看是否加载:
lsmod | grep nouveau
写入 blacklist:
cat >/etc/modprobe.d/blacklist-nouveau.conf <<'EOF'
blacklist nouveau
options nouveau modeset=0
EOF
更新 initramfs:
update-initramfs -u
如果系统使用 GRUB,可以追加内核参数:
vim /etc/default/grub
找到类似这一行:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
改成:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nouveau.modeset=0"
更新 GRUB:
update-grub
reboot
重启后确认:
lsmod | grep nouveau
没有输出通常表示未加载。
3.3 安装 NVIDIA Driver
推荐用包管理器安装,便于升级和卸载。
Ubuntu 常见方式:
ubuntu-drivers devices
选择推荐版本后安装,例如:
apt-get update
apt-get install -y nvidia-driver-550
reboot
重启后验证:
nvidia-smi
看到 GPU 型号、驱动版本、显存信息就算第一步成功。
3.4 关于 CUDA .run 安装包
别人笔记里的方式是:
wget https://developer.download.nvidia.com/compute/cuda/12.1.0/local_installers/cuda_12.1.0_530.30.02_linux.run
chmod +x cuda_12.1.0_530.30.02_linux.run
./cuda_12.1.0_530.30.02_linux.run
这确实能安装 CUDA Toolkit,并且安装时可以选择安装 Driver。
但在 K8s GPU 节点运维里,我建议你理解为:
- 这是开发机常见方式。
- 不是集群节点的首选方式。
.run安装和包管理器安装不要混用。- 大多数 K8s GPU 节点只需要 Driver,不需要完整 CUDA Toolkit。
业务需要的 CUDA runtime 通常在镜像里,例如:
nvidia/cuda:12.3.2-base-ubuntu20.04
pytorch/pytorch:...
tensorflow/tensorflow:...
3.5 安装 NVIDIA Container Toolkit
Ubuntu/Debian 示例:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| 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' \
| tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt-get update
apt-get install -y nvidia-container-toolkit
生产环境建议固定版本。NVIDIA 官方安装页在 2026-05-25 的示例版本是:
export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.19.1-1
apt-get install -y \
nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}
配置 containerd:
nvidia-ctk runtime configure --runtime=containerd
systemctl restart containerd
检查 containerd 配置:
containerd config dump | grep -A20 -B5 nvidia
你应该能看到 nvidia runtime handler。
4. containerd、RuntimeClass 和 GPU 容器
4.1 两种常见模式
模式 A:默认 runtime 仍是 runc
这是我更推荐的新手和生产混合节点方式。
特点:
- CPU Pod 继续用
runc。 - GPU Pod 显式指定
runtimeClassName: nvidia。 - 行为清晰,排障方便。
模式 B:默认 runtime 改成 nvidia
特点:
- 所有 Pod 默认经过 NVIDIA runtime。
- GPU Pod 可以不写
runtimeClassName。 - 简单,但混合节点上不够清晰。
NVIDIA runtime 对非 GPU 容器通常可以作为 no-op,但生产上我仍建议你先用 RuntimeClass 显式区分。
4.2 创建 RuntimeClass
前提:宿主机 containerd 已经有 nvidia runtime handler。
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: nvidia
handler: nvidia
应用:
kubectl apply -f runtimeclass-nvidia.yaml
确认:
kubectl get runtimeclass
5. 安装 nvidia-device-plugin
5.1 作用
NVIDIA device plugin 会在 GPU 节点上运行 DaemonSet,然后向 kubelet 注册 GPU 资源。
默认资源名:
nvidia.com/gpu
5.2 安装
可以先用官方静态 YAML 学习。生产环境建议固定版本,不要长期直接追 main。
截至 2026-05-25,NVIDIA k8s-device-plugin GitHub release 页面显示的最新版本是 v0.19.1。示例:
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.19.1/deployments/static/nvidia-device-plugin.yml
如果你要确认当前最新版本:
open https://github.com/NVIDIA/k8s-device-plugin/releases
版本号请按你实际验证过的版本调整。注意:官方 README 里的示例版本有时会滞后,release 页面更适合作为版本判断入口。
5.3 验证资源
查看 DaemonSet:
kubectl -n kube-system get ds | grep nvidia
kubectl -n kube-system get pods -o wide | grep nvidia
查看节点资源:
kubectl describe node <gpu-node-name> | grep -A10 -B5 nvidia.com/gpu
或:
kubectl get nodes -o custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\\.com/gpu
如果能看到 nvidia.com/gpu,说明 K8s 已经认识 GPU。
6. 第一个 GPU Pod
6.1 整卡测试 Pod
apiVersion: v1
kind: Pod
metadata:
name: gpu-test
spec:
runtimeClassName: nvidia
restartPolicy: Never
containers:
- name: gpu-test
image: nvidia/cuda:12.3.2-base-ubuntu20.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
应用:
kubectl apply -f gpu-test.yaml
kubectl logs gpu-test
如果日志里看到 nvidia-smi 输出,说明链路成功:
Driver -> Container Toolkit -> containerd -> RuntimeClass -> device plugin -> Pod
6.2 常驻测试 Pod
有时你想进容器里手动检查:
apiVersion: v1
kind: Pod
metadata:
name: gpu-shell
spec:
runtimeClassName: nvidia
restartPolicy: Never
containers:
- name: gpu-shell
image: nvidia/cuda:12.3.2-base-ubuntu20.04
command: ["sleep", "infinity"]
resources:
limits:
nvidia.com/gpu: 1
进入容器:
kubectl exec -it gpu-shell -- bash
nvidia-smi
6.3 不要把型号写成资源名
默认情况下不要写:
resources:
limits:
nvidia.com/t4: 1
NVIDIA 官方 device plugin 默认不是这样暴露资源。
正确写法通常是:
resources:
limits:
nvidia.com/gpu: 1
如果你要选择 T4、A10、A100 这种型号,用 node label:
kubectl label node <node-name> accelerator=nvidia-t4
Pod:
nodeSelector:
accelerator: nvidia-t4
7. GPU 节点的标签、污点和调度
7.1 给 GPU 节点打标签
例子:
kubectl label node gpu-node-1 node-role.kubernetes.io/gpu=true
kubectl label node gpu-node-1 accelerator=nvidia-t4
kubectl label node gpu-node-1 gpu.vendor=nvidia
业务 Pod 可以使用:
nodeSelector:
accelerator: nvidia-t4
7.2 给 GPU 节点打污点
如果不希望普通 CPU Pod 跑到 GPU 节点:
kubectl taint node gpu-node-1 nvidia.com/gpu=true:NoSchedule
GPU Pod 增加 toleration:
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
7.3 GPU Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: gpu-test-t4
spec:
runtimeClassName: nvidia
restartPolicy: Never
nodeSelector:
accelerator: nvidia-t4
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
containers:
- name: gpu-test
image: nvidia/cuda:12.3.2-base-ubuntu20.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
8. CUDA 版本和 Driver 兼容性
8.1 基本原则
你要记住:
- Driver 在宿主机。
- CUDA runtime 通常在容器镜像里。
- 宿主机 Driver 必须支持容器里的 CUDA 版本。
- Driver 新一些通常可以跑旧 CUDA runtime。
- 反过来不一定可以。
8.2 举例
如果宿主机驱动来自 CUDA 12.1 安装包:
Driver 530.30.02
然后你跑:
nvidia/cuda:12.3.2-base-ubuntu20.04
这时就要查 CUDA 12.3 对 Driver 的要求。
为了减少这类问题,生产上常用策略:
- 宿主机 Driver 统一升级到较新的稳定版本。
- 业务镜像 CUDA 版本不要随意漂移。
- 镜像版本写死,不要用
latest。 - 在测试 GPU 节点跑完整验证。
8.3 常见现象
如果 Driver 太旧,容器里可能报:
CUDA driver version is insufficient for CUDA runtime version
或:
Failed to initialize NVML
排查顺序:
nvidia-smi
kubectl logs <pod>
kubectl describe pod <pod>
kubectl describe node <node>
9. GPU Operator 路线
9.1 什么时候用 GPU Operator
适合:
- 新建 GPU 集群。
- 节点系统比较统一。
- 希望自动部署 Driver、Container Toolkit、device plugin、监控组件。
- 需要 MIG 管理、DCGM Exporter、GPU Feature Discovery。
不适合:
- 节点驱动已经由云厂商或系统团队统一管理。
- 节点上已有复杂的自定义驱动安装方式。
- 你还没有搞清楚手工链路,希望靠 Operator 掩盖基础问题。
9.2 GPU Operator 管理哪些东西
常见包括:
- NVIDIA Driver
- NVIDIA Container Toolkit
- NVIDIA Device Plugin
- GPU Feature Discovery
- DCGM Exporter
- MIG Manager
- Node Feature Discovery
9.3 基本安装思路
通常用 Helm 安装:
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--version=v26.3.1
截至 2026-05-25,NVIDIA GPU Operator 官方安装文档标注当前 patch release 是 v26.3.1。安装前仍然要看 release notes 和组件矩阵。
生产前一定要看 values:
helm show values nvidia/gpu-operator > gpu-operator-values.yaml
你要重点确认:
- 是否由 Operator 安装 Driver。
- 是否由 Operator 安装 Container Toolkit。
- containerd 配置路径是否正确。
- 是否启用 MIG。
- 是否部署 DCGM Exporter。
- 节点是否已经有旧组件冲突。
特别注意:GPU Operator v26.3.1 默认 cdi.enabled=true。在 CDI 模式下,GPU 注入逻辑和手工 RuntimeClass: nvidia 路线不同,很多场景里 Pod 只需要申请 nvidia.com/gpu,不需要再写 runtimeClassName: nvidia。如果你是手工安装 NVIDIA runtime,才按第 4 章的 RuntimeClass 路线理解。
9.4 手工路线 vs GPU Operator
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 手工安装 | 容易理解每一层,排障清晰 | 节点多了维护成本高 | 学习、少量节点、强自定义 |
| GPU Operator | 自动化程度高,组件完整 | 黑盒感更强,和已有环境可能冲突 | 新集群、标准化生产环境 |
我的建议:
先手工跑通 1 台测试节点,再考虑 GPU Operator 管理生产集群。
10. GPU 共享方案
GPU 默认是按整卡分配的:
nvidia.com/gpu: 1
但很多场景里,一张 GPU 太大,一个小模型或推理服务用不满。这时会考虑共享。
10.1 time-slicing
特点:
- 多个 Pod 共享同一张 GPU。
- 主要是时间片共享。
- 配置相对简单。
- 隔离弱,不保证显存硬隔离。
适合:
- 轻量推理。
- 开发测试。
- GPU 利用率不高的场景。
不适合:
- 强隔离多租户。
- 显存必须严格限制。
- 训练任务互相抢资源敏感。
10.1.1 原理
NVIDIA device plugin 本身就是一个 DaemonSet Pod。它的 ConfigMap 里有一个 sharing.timeSlicing 配置项,可以把一张物理 GPU "复制"成多份上报给 K8s 调度器。
物理 GPU 0 → replicas: 4 → K8s 看到 4 个 nvidia.com/gpu
物理 GPU 1 → replicas: 4 → K8s 看到 4 个 nvidia.com/gpu
总共 2 张物理卡,K8s 认为有 8 张
本质是时间片轮转,不是显存硬隔离。多个 Pod 共享同一张卡的 GPU 时间,但共享全部显存,没有显存上限限制。
重要提醒:nvidia.com/gpu 仍然是整数型资源,Pod 里还是写 nvidia.com/gpu: 1。只是节点上报的数量变多了。
10.1.2 方式一:ConfigMap 配置(推荐)
适用于单独安装 nvidia-device-plugin 的场景。
第 1 步:创建 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: nvidia-device-plugin-config
namespace: kube-system
data:
config.yaml: |
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4
# devices: ["0", "1"] # 可选:只对指定 GPU 生效,不填则全部
# flags:
# failOnInitError: true
# migStrategy: none
# deviceListStrategy: envvar
参数说明:
| 参数 | 说明 |
|---|---|
replicas |
每张物理 GPU 上报的份数。replicas: 4 表示 1 张卡变 4 份 |
devices |
可选,指定对哪些 GPU 编号生效。不填则对节点上所有 GPU 生效 |
第 2 步:部署或修改 Device Plugin DaemonSet
需要让 DaemonSet 挂载这个 ConfigMap 并通过 --config-file 参数引用。
完整 DaemonSet 示例:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nvidia-device-plugin-daemonset
namespace: kube-system
labels:
app: nvidia-device-plugin
spec:
selector:
matchLabels:
app: nvidia-device-plugin
template:
metadata:
labels:
app: nvidia-device-plugin
spec:
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
priorityClassName: system-node-critical
containers:
- name: nvidia-device-plugin
image: nvcr.io/nvidia/k8s-device-plugin:v0.19.1
args:
- --config-file=/etc/nvidia-device-plugin/config.yaml
env:
- name: PASS_DEVICE_SPECS
value: "true"
- name: FAIL_ON_INIT_ERROR
value: "true"
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
- name: config
mountPath: /etc/nvidia-device-plugin
volumes:
- name: device-plugin
hostPath:
path: /var/lib/kubelet/device-plugins
- name: config
configMap:
name: nvidia-device-plugin-config
第 3 步:应用并验证
# 应用 ConfigMap 和 DaemonSet
kubectl apply -f nvidia-device-plugin-config.yaml
kubectl apply -f nvidia-device-plugin-ds.yaml
# 等待 Pod 重建
kubectl -n kube-system rollout status ds/nvidia-device-plugin-daemonset
# 验证节点 GPU 资源(应该看到数量变多了)
kubectl describe node <gpu-node-name> | grep -A10 nvidia.com/gpu
如果原来有 2 张 GPU,配置 replicas: 4 后应该看到:
nvidia.com/gpu: 8 # 2 × 4 = 8
10.1.3 方式二:GPU Operator 场景
如果使用 GPU Operator 安装,通过 ClusterPolicy 或 Helm values 配置。
Helm values 示例:
# values-time-slicing.yaml
devicePlugin:
enabled: true
config:
name: nvidia-device-plugin-config
share:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4
或者通过 patch ClusterPolicy:
kubectl patch clusterpolicy cluster-policy \
-n gpu-operator \
--type=merge \
-p '{"devicePlugin":{"config":{"name":"nvidia-device-plugin-config"}}}'
10.1.4 只对部分节点生效
如果你只想让某些 GPU 节点开启 time-slicing(比如推理节点),其他节点保持整卡分配(比如训练节点),可以用 label 区分:
# 给推理节点打标签
kubectl label node gpu-inference-01 nvidia.com/device-plugin.config=time-slicing
# 训练节点不打标签,使用默认整卡模式
然后在 DaemonSet 上加 nodeAffinity,或者使用 GPU Operator 的 node-specific 配置。
10.1.5 Time-Slicing 的局限
| 方面 | 说明 |
|---|---|
| 显存隔离 | ❌ 无。所有共享 Pod 可以访问全部显存 |
| 算力隔离 | ❌ 无硬隔离,时间片轮转 |
| OOM 风险 | 高。一个 Pod 吃满显存会导致其他 Pod 崩溃 |
| 调度感知 | 调度器只看数量,不知道实际显存用量 |
| 适用场景 | 多个轻量推理、开发测试、GPU 利用率低的场景 |
对比 HAMi 的显存隔离:
Time-Slicing:
Pod A: nvidia.com/gpu: 1 → 可用全部显存(如 16G)
Pod B: nvidia.com/gpu: 1 → 可用全部显存(如 16G)
Pod C: nvidia.com/gpu: 1 → 可用全部显存(如 16G)
Pod D: nvidia.com/gpu: 1 → 可用全部显存(如 16G)
→ 实际物理显存只有 16G,谁用多了别人就没了
HAMi:
Pod A: nvidia.com/gpu: 1, nvidia.com/gpumem: 4000 → 最多 4G
Pod B: nvidia.com/gpu: 1, nvidia.com/gpumem: 4000 → 最多 4G
Pod C: nvidia.com/gpu: 1, nvidia.com/gpumem: 4000 → 最多 4G
Pod D: nvidia.com/gpu: 1, nvidia.com/gpumem: 4000 → 最多 4G
→ 每个有硬上限,互不影响
10.1.6 验证 Time-Slicing 是否生效
# 查看节点 GPU 资源
kubectl describe node <gpu-node-name> | grep nvidia.com/gpu
# 查看 device plugin 日志,确认加载了 config
kubectl -n kube-system logs <nvidia-device-plugin-pod> | grep -i "time.slicing\|replicas\|sharing"
# 部署多个 Pod 测试
for i in $(seq 1 4); do
kubectl run gpu-test-$i --restart=Never \
--image=nvidia/cuda:12.3.2-base-ubuntu20.04 \
--limits=nvidia.com/gpu=1 \
-- nvidia-smi
done
# 确认都能 Running
kubectl get pods -o wide | grep gpu-test
10.2 MPS
MPS 是 NVIDIA Multi-Process Service。
特点:
- 改善多个 CUDA 进程共享 GPU 的执行效率。
- 比简单 time-slicing 更适合部分多进程场景。
- 对业务和环境有要求。
适合:
- 多个 CUDA 进程共享同一 GPU。
- 需要降低上下文切换开销。
10.3 MIG
MIG 是 Multi-Instance GPU。
特点:
- 硬件级切分 GPU。
- 每个 MIG 实例有独立的部分计算和显存资源。
- 隔离性强。
- 只支持部分 GPU,例如 A100/H100 等数据中心卡。
适合:
- 多租户。
- 推理服务。
- 希望显存和算力切分更稳定。
不适合:
- 不支持 MIG 的 GPU,例如 T4。
- 需要整卡大显存训练的任务。
10.4 HAMi
HAMi 提供更细粒度的 GPU 虚拟化和调度。
常见资源写法类似:
resources:
limits:
nvidia.com/gpu: 1
nvidia.com/gpumem: 3000
也可能用百分比方式:
resources:
limits:
nvidia.com/gpu: 1
nvidia.com/gpumem-percentage: 50
以你安装的 HAMi 版本文档为准。
HAMi 适合:
- 要按显存申请。
- 多个小模型共享 GPU。
- 需要调度器感知 GPU 显存。
- 多种异构设备统一调度。
不建议一开始就上 HAMi 的原因:
- 概念更多。
- 调度链路更复杂。
- 排障维度更多。
- 和官方 device plugin 的资源模型不完全一样。
建议路线:
nvidia-device-plugin 整卡
↓
time-slicing / MIG
↓
HAMi
11. 监控和可观测性
11.1 你要监控什么
GPU 节点至少要监控:
- GPU 利用率
- 显存总量、已用显存
- GPU 温度
- 功耗
- ECC 错误
- Xid 错误
- Pod 与 GPU 使用关系
- GPU 节点上的 containerd/kubelet 状态
11.2 DCGM Exporter
NVIDIA DCGM Exporter 可以把 GPU 指标暴露给 Prometheus。
常见指标包括:
DCGM_FI_DEV_GPU_UTILDCGM_FI_DEV_FB_USEDDCGM_FI_DEV_FB_FREEDCGM_FI_DEV_POWER_USAGEDCGM_FI_DEV_GPU_TEMP
如果使用 GPU Operator,通常可以一起部署 DCGM Exporter。
11.3 节点日志
常用:
journalctl -u kubelet -f
journalctl -u containerd -f
journalctl -k | grep -i nvidia
dmesg | grep -i xid
NVIDIA Xid 错误很重要,通常说明驱动或 GPU 层面发生过异常。
12. 常见排障手册
12.1 宿主机 nvidia-smi 不工作
先看:
nvidia-smi
lsmod | grep nvidia
journalctl -k | grep -i nvidia
dmesg | grep -i nvidia
常见原因:
- 驱动没装好。
- 内核升级后 NVIDIA kernel module 没重新编译。
- nouveau 没禁用。
- Secure Boot 阻止内核模块加载。
- GPU 硬件或 PCIe 问题。
12.2 宿主机正常,但容器里看不到 GPU
先看 containerd runtime:
containerd config dump | grep -A20 -B5 nvidia
看 toolkit:
nvidia-container-cli info
看 Pod:
kubectl describe pod <pod>
kubectl logs <pod>
常见原因:
- 没安装
nvidia-container-toolkit。 - 没执行
nvidia-ctk runtime configure --runtime=containerd。 - containerd 没重启。
- Pod 没有指定
runtimeClassName: nvidia,而默认 runtime 是runc。 - 镜像 CUDA runtime 和宿主机 Driver 不兼容。
12.3 K8s 节点没有 nvidia.com/gpu
看 device plugin:
kubectl -n kube-system get pods -o wide | grep nvidia
kubectl -n kube-system logs <nvidia-device-plugin-pod>
看节点:
kubectl describe node <node-name>
常见原因:
- device plugin 没部署。
- device plugin Pod 没跑到 GPU 节点。
- 节点 taint/toleration 不匹配。
- 宿主机
nvidia-smi本身失败。 - kubelet device plugin socket 异常。
12.4 Pod 一直 Pending
查看:
kubectl describe pod <pod>
常见事件:
Insufficient nvidia.com/gpu
原因:
- GPU 资源被占满。
- 申请数量超过节点可用 GPU。
- nodeSelector 选错节点。
- taint 没有 toleration。
- 资源名写错,例如写成了
nvidia.com/t4。
12.5 Pod 启动失败,提示 runtime handler 不存在
类似:
RuntimeHandler "nvidia" not supported
排查:
kubectl get runtimeclass
containerd config dump | grep -A20 -B5 nvidia
systemctl restart containerd
systemctl restart kubelet
原因:
- RuntimeClass 的
handler和 containerd 中的 runtime 名不一致。 - containerd 没配置
nvidiaruntime。 - 配置后没有重启 containerd。
12.6 nvidia-smi 报 NVML 错误
常见错误:
Failed to initialize NVML
排查:
nvidia-smi
ldconfig -p | grep nvidia-ml
journalctl -k | grep -i nvidia
常见原因:
- 驱动用户态库和内核模块版本不一致。
- 驱动升级后没有重启。
- 容器里注入的库不完整。
13. 卸载和清理
13.1 原则
不要随便复制网上的卸载命令直接跑。先确认安装方式:
.run安装apt/yum/dnf安装- GPU Operator 安装
- 云厂商镜像预装
13.2 .run 安装的常见卸载方式
如果用 CUDA .run 安装:
nvidia-uninstall
/usr/local/cuda-*/bin/cuda-uninstaller
路径以实际为准。
13.3 apt 安装的常见卸载方式
先看包:
dpkg -l | grep -E 'nvidia|cuda|cudnn'
再按需卸载:
apt-get remove --purge 'nvidia-*'
apt-get remove --purge 'cuda-*'
apt-get remove --purge 'libcudnn*'
apt-get autoremove -y
13.4 清理 Container Toolkit
apt-get remove --purge nvidia-container-toolkit
systemctl restart containerd
如果你改过 containerd 配置,要确认是否需要回滚 runtime 配置。
14. 生产实践建议
14.1 不要直接在生产节点试命令
建议:
- 先准备一台测试 GPU 节点。
- 固化安装脚本或 Ansible。
- 记录 Driver 版本、Toolkit 版本、device plugin 版本。
- 通过测试 Pod 验证。
- 再滚动到生产节点。
14.2 节点分组
建议用 label 区分:
kubectl label node gpu-a accelerator=nvidia-t4
kubectl label node gpu-b accelerator=nvidia-a10
kubectl label node gpu-c accelerator=nvidia-a100
如果有训练/推理隔离:
kubectl label node gpu-a workload.gpu/usage=inference
kubectl label node gpu-b workload.gpu/usage=training
14.3 驱动版本治理
建议维护一张表:
| 节点组 | GPU 型号 | Driver | CUDA 镜像基线 | device plugin | 备注 |
|---|---|---|---|---|---|
| inference-t4 | T4 | 550.xx | CUDA 12.3 | v0.19.1 | 推理 |
| training-a100 | A100 | 550.xx | CUDA 12.4 | v0.19.1 | MIG 可选 |
14.4 镜像版本治理
不要用:
nvidia/cuda:latest
建议固定:
nvidia/cuda:12.3.2-base-ubuntu20.04
业务镜像也要明确:
- CUDA 版本
- cuDNN 版本
- PyTorch/TensorFlow 版本
- Python 版本
14.5 GPU 节点是否跑 CPU Pod
建议:
- 昂贵 GPU 节点尽量只跑 GPU 工作负载。
- 用 taint 阻止普通 CPU Pod 调度上来。
- 必要的 DaemonSet 通过 toleration 放行。
- 监控、日志、网络、存储插件要确认能在 GPU 节点上运行。
15. 学习实验清单
你可以按这个清单一点点做。
实验 1:宿主机 GPU 可用
目标:
nvidia-smi
能正常输出。
实验 2:containerd 识别 NVIDIA runtime
目标:
containerd config dump | grep -A20 -B5 nvidia
能看到 nvidia runtime。
实验 3:K8s 识别 GPU
目标:
kubectl get nodes -o custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\\.com/gpu
能看到 GPU 数量。
实验 4:Pod 使用整张 GPU
目标:
kubectl logs gpu-test
能看到 nvidia-smi 输出。
实验 5:GPU 节点调度控制
目标:
- 给 GPU 节点加 label。
- 给 GPU 节点加 taint。
- Pod 用 nodeSelector 和 toleration 调度上去。
实验 6:监控 GPU
目标:
- 部署 DCGM Exporter。
- Prometheus 能采集 GPU 指标。
- Grafana 能看到显存和利用率。
实验 7:共享方案验证
目标:
- 了解 time-slicing。
- 如果硬件支持,了解 MIG。
- 再评估 HAMi。
16. 一张排障决策树
Pod 用不了 GPU
↓
宿主机 nvidia-smi 正常吗?
├─ 不正常:先修 Driver / nouveau / kernel module / Secure Boot
└─ 正常
↓
K8s node 上有 nvidia.com/gpu 吗?
├─ 没有:查 nvidia-device-plugin / GPU Operator / kubelet
└─ 有
↓
Pod 是否 Pending?
├─ 是:查资源名、GPU 数量、nodeSelector、taint/toleration
└─ 否
↓
Pod 是否启动失败?
├─ runtime handler 错:查 RuntimeClass 和 containerd runtime
├─ NVML/CUDA 错:查 Driver 和容器 CUDA 兼容性
└─ 业务错误:查框架、模型、显存、权限、镜像依赖
17. 常用命令速查
宿主机
lspci | grep -i nvidia
lsmod | grep -E 'nvidia|nouveau'
nvidia-smi
nvidia-smi -L
journalctl -k | grep -i nvidia
dmesg | grep -i xid
containerd
containerd config dump | grep -A20 -B5 nvidia
systemctl status containerd
journalctl -u containerd -f
Kubernetes
kubectl get runtimeclass
kubectl get nodes
kubectl describe node <node-name>
kubectl get pods -A -o wide | grep -i nvidia
kubectl -n kube-system logs <nvidia-device-plugin-pod>
kubectl describe pod <pod-name>
kubectl logs <pod-name>
GPU 资源
kubectl get nodes -o custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\\.com/gpu
kubectl describe node <node-name> | grep -A10 -B5 nvidia.com/gpu
18. 推荐参考资料
优先看官方资料:
-
Kubernetes Device Plugins
https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/ -
NVIDIA Container Toolkit
https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/ -
NVIDIA Kubernetes Device Plugin
https://github.com/NVIDIA/k8s-device-plugin -
NVIDIA GPU Operator
https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/ -
CUDA Compatibility
https://docs.nvidia.com/deploy/cuda-compatibility/
19. 新手最容易踩的坑
- 把
nvidia.com/gpu写成nvidia.com/t4。 - 宿主机 Driver 太旧,容器 CUDA 太新。
- 混用
.run和 apt 安装驱动。 - 忘记禁用 nouveau。
- 配了 containerd 但没重启。
- 创建了 RuntimeClass,但 handler 名字和 containerd 不一致。
- GPU Pod 没写
runtimeClassName: nvidia,而默认 runtime 还是runc。 - device plugin 没跑起来,却直接排查业务容器。
- GPU 节点没有 taint,普通 CPU Pod 占了昂贵节点。
- 没有监控温度、功耗、显存和 Xid 错误。
20. 最后给你的学习建议
你已经会 Kubernetes,所以不要从 CUDA 编程开始学。你应该从运维链路开始:
Driver
→ Container Toolkit
→ containerd runtime
→ RuntimeClass
→ nvidia-device-plugin
→ nvidia.com/gpu
→ GPU Pod
→ 监控和排障
→ 共享和高级调度
掌握这条链路后,再去学:
- MIG
- time-slicing
- MPS
- HAMi
- GPU Operator 深度配置
- 多租户资源治理
先把一张卡稳定跑起来,再谈共享和复杂调度。这是最稳的学习路径。
k8s kubernetes gpu nvidia hami