单机 Kubernetes 与 GPU Operator:让 GPU 成为可调度资源
当一块 GPU 能用
nvidia.com/gpu: 1这样一行资源请求被调度时,它才从“机器的部件”变成“平台的资源”。
你现在有了随时可开的云上 GPU(上一章)和能注入 GPU 的容器(再上一章)。但“哪块卡给谁”仍由人手决定:换节点要改参数、配额靠口头约定、用量无人记账。 Kubernetes 把这些变成 API 与控制循环,而 GPU Operator 把接入 GPU 所需的一整套组件(设备插件、容器运行时配置、监控导出器)变成一次 Helm 安装。本章在单机集群上完成这最后一步,并用一张“能力边界表”告诉你:跑通之后,你真正拥有了什么、还缺什么。
为什么 Kubernetes 是 GPU 的操作系统
回忆上一章:GPU 容器的关键在运行时注入设备与驱动库。Kubernetes 面对的额外问题是多租与调度——集群里的 GPU 分属不同节点,Pod 的创建由调度器而非人决定。K8s 的解法是设备插件(Device Plugin)机制:
- 设备插件 DaemonSet 在每个节点上向 kubelet 上报“本节点有 N 块 GPU”(
nvidia.com/gpu); - kubelet 把它纳入节点可分配资源(Allocatable),调度器据此决定 Pod 落点;
- Pod 请求
nvidia.com/gpu并被绑定时,kubelet 调用与上一章同源的注入机制把卡交给容器。
这套“上报—调度—注入”的链路是理解后续一切的基础:HAMi 的显存切分、Kueue 的队列治理、MIG 的动态重配,全部挂在这条链路的延长线上。设备抽象的表达力边界(为什么 GPU 不是“另一种 CPU”)见设备资源抽象与演进,各厂商加速器接入 K8s 的统一模式见Kubernetes 管理模型。
三种单机集群怎么选
| 工具 | GPU 支持 | 特点 | 适用 |
|---|---|---|---|
| minikube | 官方支持,--gpus=all 一个参数 | 部署最简单,节点是 Docker 容器 | 本章实验、快速验证 |
| kind | 需手动挂载设备节点 | 多节点集群轻量,CI 常用 | 无 GPU 或愿意手工配置者 |
| k3s | 需配置 containerd 的 nvidia 运行时 | systemd 服务、最接近生产形态 | 单机常驻、边缘节点 |
本章用 minikube(路径最短),k3s 作为课后练习:把同样的 GPU Operator 流程搬到 k3s 上,你会遇到真实的 containerd 运行时配置问题——那正是上一章 Toolkit 章节的延续。
GPU Operator 解决了什么
没有 Operator 时,接入一块 GPU 需要:装 nvidia-container-toolkit 并改运行时配置、部署 device plugin、部署 DCGM exporter、(必要时)装驱动——四件事四个版本矩阵,且要逐节点做。GPU Operator 把它们全部 DaemonSet 化:由 Operator 按节点状态自动安装、升级、对齐版本。
本实验的场景是“宿主机已有驱动”(云上 GPU 优化镜像,或按准备 GPU 运行环境一章手动安装),因此安装时显式关闭 Operator 的驱动组件:
--set driver.enabled=false其余组件(toolkit、device-plugin、DCGM exporter 等)仍由 Operator 管理。何时该让 Operator 连驱动一起管(裸金属、驱动即代码的场景),见GPU Operator的完整讨论。
动手实验:调度你的第一个 GPU Pod
实验目标
在 minikube 单机集群上部署 GPU Operator,让 nvidia.com/gpu 出现在节点可分配资源中,并成功调度一个请求 GPU 的 Pod 跑通 nvidia-smi。
前置条件与成本预估
- 上一章完成的机器:宿主机驱动正常、
nvidia-ctk runtime configure --runtime=docker已执行且 Docker 已重启、docker run --rm --gpus all ... nvidia-smi通过。 - 已装 kubectl、Helm、minikube;机器建议 ≥ 4 vCPU / 8 GiB 内存(Operator 全家桶与集群本体都要占资源)。
- 成本:全程本地/实例内完成,无额外云资源;云实例时长约半小时。
操作步骤
# 步骤 1:启动带 GPU 的单机集群(Docker 驱动,把 GPU 透传给 minikube 节点)
minikube start --driver=docker --gpus=all
kubectl get nodes # 预期一个 Ready 节点
# 步骤 2:部署 GPU Operator(宿主机已有驱动,关闭 driver 组件)
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia --force-update
helm repo update
helm upgrade --install gpu-operator nvidia/gpu-operator \
--namespace gpu-operator --create-namespace \
--set driver.enabled=false
# 步骤 3:等待 Operator 组件就绪(toolkit、device-plugin、dcgm-exporter 等)
kubectl -n gpu-operator get pods --watch
# 预期:所有 Pod Running,其中 nvidia-device-plugin-*. .. 1/1 Running
# 步骤 4:确认 GPU 已进入可分配资源
kubectl get nodes -o jsonpath='{.items[0].status.allocatable.nvidia\.com/gpu}{"\n"}'
# 预期输出:1(单卡机器;多卡则是对应数量)
# 步骤 5:调度第一个 GPU Pod
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: gpu-smoke
spec:
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:12.4.1-base-ubuntu22.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
EOF
kubectl wait --for=condition=complete pod/gpu-smoke --timeout=300s
kubectl logs gpu-smoke验证清单
- 步骤 3 中 device-plugin Pod 稳定 Running(反复重启说明节点侧注入环境有问题,见下方排障表)
- 步骤 4 输出与物理卡数一致
-
kubectl logs gpu-smoke输出的Driver Version与宿主机基线卡一致 -
kubectl describe pod gpu-smoke的 Events 里能看到调度成功与镜像拉取记录 - 实验数据(卡数、allocatable 值、Pod 日志)记入基线卡
故障排障速查
| 现象 | 常见原因 | 第一反应 |
|---|---|---|
| allocatable 输出 0 | minikube 容器没拿到 GPU / device-plugin 未就绪 / 宿主机驱动异常 | 进节点验证:minikube ssh nvidia-smi;再查 device-plugin 日志 |
| Operator Pod 一直 Pending | 单机资源不足 | 确认 ≥ 4C/8G;kubectl describe pod 看被谁卡住 |
| gpu-smoke 一直 ContainerCreating | toolkit 组件未就绪或运行时配置冲突 | kubectl -n gpu-operator logs -l app=nvidia-container-toolkit;等 toolkit DaemonSet 就绪后重试 |
Pod 起了但报 insufficient | 镜像 CUDA 版本高于宿主机驱动上限 | 对照基线卡 CUDA 上限(见“准备 GPU 运行环境”的规则 3),换镜像 tag |
minikube start 卡死或报 GPU 相关错误 | Docker 的 nvidia 运行时未配置或未重启 | 重做上一章三步配置;先用 docker run --rm --gpus all 自证 |
原理速览
你在步骤 4–5 看到的就是本章开头那条链路:device plugin 上报 → kubelet 更新 Allocatable → 调度器把 gpu-smoke 绑到唯一节点 → kubelet 用 toolkit(Operator 部署的那套)完成与 docker run --gpus 同源的注入。上一章与本章是同一机制在两种编排下的两个投影——理解了这一点,换到任何 K8s 发行版都不再神秘。
清理与止损
kubectl delete pod gpu-smoke
helm uninstall gpu-operator -n gpu-operator && kubectl delete ns gpu-operator
minikube delete --all云实例若不再使用,执行云上第一台 GPU 机器一章的 terminate 流程并确认零残留。
你还没有得到什么
诚实的能力边界——这也是全书剩余部分的目录:
| 能力 | 状态 | 答案所在 |
|---|---|---|
GPU 以 nvidia.com/gpu 被调度 | ✅ 已拥有 | 本章 |
| 一块卡分给多个 Pod / 显存级配额 | ❌ 整卡为最小单位 | 数据平面:MIG、HAMi、DRA |
| 队列、配额、优先级、公平共享 | ❌ 尚无治理 | 控制面:Kueue、Volcano |
| 拓扑感知放置(NVLink/NUMA) | ❌ 调度器无拓扑视野 | NUMA 感知调度、放置策略 |
| GPU 利用率/显存/健康度的指标语义 | ⚠️ exporter 已部署,语义未建 | 可观测与验收 |
| 异构加速器接入(NVIDIA 之外) | ❌ 仅 NVIDIA 路径 | 加速器全景、昇腾案例 |
总结
上手篇到此完成闭环:环境验证 → 云上开机 → GPU 容器 → 可调度的 GPU Pod。你现在已经拥有一台随时可开、可被 Kubernetes 调度的 GPU 机器,以及一张记录了驱动版本、CUDA 上限、实测 TFLOPS、allocatable 的环境基线卡。上面的边界表就是你的下一步菜单:如果“分卡”是你最迫切的痛点,去数据平面;如果“治理”是,去控制面;如果你想先弄懂每一步背后发生了什么,认识机器与问题会从硅片开始讲起。