GPU on Kubernetes:8 卡 Worker 上 1 个 Pod 只使用 1 张 GPU 的隔离与 Run:ai 调度实践
上一篇文章解决的是“GPU 节点如何接入 Kubernetes,让 Pod 能调度到 GPU Worker 上”。
这篇文章继续往下走一步:
一台 Worker 有 8 张 GPU,我不想让一个 Pod 使用整台机器,只想让它使用其中 1 张 GPU,应该怎么做隔离?能不能配合 NVIDIA Run:ai 做管控调度?Run:ai 是不是开源免费?
先给结论:
- 生产默认方案:使用 NVIDIA GPU Operator 或 NVIDIA device plugin 暴露
nvidia.com/gpu,Pod 声明limits.nvidia.com/gpu: 1。这表示 Pod 申请并独占 1 张 full GPU,不是独占整台 8 卡 Worker。 - 传统 device plugin 模式下:不能在普通 Pod spec 里可靠表达“我要这台机器上的物理 GPU 3”。Kubernetes 调度器看到的是节点资源数量,具体哪张卡由 device plugin/kubelet/runtime 分配。
- 需要硬隔离:优先考虑 MIG,但前提是 GPU 型号支持 MIG。
- 需要提升利用率、能接受弱隔离:可以考虑 time-slicing 或 MPS。
- 需要队列、配额、公平性、多租户治理:开源生产优先评估 Kueue、Volcano、KAI Scheduler;商业平台再评估 NVIDIA Run:ai。
- Run:ai 平台本身不要理解成开源免费平台。当前可公开确认的是 KAI Scheduler 是开源调度器;NVIDIA Run:ai 是面向企业 AI 集群的商业平台能力,需要按 NVIDIA/Run:ai 官方授权和产品形态评估。
1. nvidia.com/gpu: 1 到底是什么意思
假设一台 Worker 有 8 张 NVIDIA GPU。
NVIDIA device plugin 正常工作后,节点会向 kubelet 注册扩展资源:
status:
capacity:
nvidia.com/gpu: "8"
allocatable:
nvidia.com/gpu: "8"
业务 Pod 只需要声明:
resources:
limits:
nvidia.com/gpu: 1
这表示:
- 调度器只会把 Pod 放到至少还有 1 张可分配 GPU 的节点。
- kubelet 会向 device plugin 请求 1 个 GPU device。
- NVIDIA runtime 会把分配到的 GPU 注入容器。
- 容器内通常只看得到这一张 GPU。
所以,1 个 Pod 申请 nvidia.com/gpu: 1,不是占用整台 8 卡 Worker,而是占用该节点上的 1 张 GPU 资源。
Kubernetes 官方对 GPU 这类扩展资源还有一个重要规则:
- GPU 资源只能写在
limits。 - 如果同时写
requests和limits,两者必须相等。 - 只写
limits时,Kubernetes 会把它当作 request 使用。
因此,最推荐的写法就是只写:
resources:
limits:
nvidia.com/gpu: 1
2. 最小可落地 Pod 示例
下面是一个 1 Pod / 1 GPU 的最小验证示例。
apiVersion: v1
kind: Pod
metadata:
name: one-gpu
spec:
restartPolicy: Never
runtimeClassName: nvidia
nodeSelector:
nvidia.com/gpu.present: "true"
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: app
image: nvidia/cuda:12.4.1-base-ubuntu22.04
command: ["bash", "-lc", "nvidia-smi -L && echo GPU_OK && sleep infinity"]
resources:
limits:
nvidia.com/gpu: 1
如果你的集群把 NVIDIA runtime 配成了默认 runtime,runtimeClassName: nvidia 可以不写。
但我更建议显式写上。这样排障时不会混淆“资源已经分配”和“容器是否走 NVIDIA runtime”这两件事。
部署:
kubectl apply -f one-gpu.yaml
kubectl wait --for=condition=Ready pod/one-gpu --timeout=300s
kubectl logs one-gpu
验证容器内可见 GPU:
kubectl exec one-gpu -- nvidia-smi -L
kubectl exec one-gpu -- bash -lc 'echo $NVIDIA_VISIBLE_DEVICES'
容器里常见输出是:
GPU 0: NVIDIA ...
这里的 GPU 0 是容器内部可见设备编号,不等于宿主机物理 GPU0。
严谨验证应使用 GPU UUID 对齐。
宿主机侧:
nvidia-smi -L
容器侧:
kubectl exec one-gpu -- nvidia-smi -L
如果容器内只看到 1 个 GPU UUID,就说明“1 Pod / 1 GPU”的设备可见性隔离已经生效。
3. 隔离了什么,没有隔离什么
默认 nvidia.com/gpu: 1 提供的是:
- 调度层面的整数资源独占。
- 容器可见设备隔离。
- runtime 注入层面的 GPU device 控制。
它不提供:
- GPU 显存配额细分。
- 同一张 GPU 上多个租户之间的硬隔离。
- 防止特权容器绕过设备隔离的安全边界。
- 按物理 GPU index 的稳定调度契约。
换句话说,默认 full GPU 模式的语义是:
这张卡分给这个容器,这个容器只看得到这张卡。
不是:
这张卡的 20% 显存和 20% 算力分给这个容器。
如果你需要“显存/算力份额”这种能力,需要看 MIG、time-slicing、MPS 或 DRA,而不是普通 nvidia.com/gpu: 1。
4. 不要手写 NVIDIA_VISIBLE_DEVICES 来抢卡
有些人会尝试在 Pod 里写:
env:
- name: NVIDIA_VISIBLE_DEVICES
value: "3"
这不是生产推荐方式。
原因是:
- 它绕开 Kubernetes device plugin 的分配语义。
- 它可能和 kubelet/device plugin 的分配结果冲突。
- GPU index 并不是可靠的长期契约。
- 驱动、硬件枚举、节点维护后,index 可能造成误解。
更稳的做法是:
- 让 device plugin 分配 GPU。
- 用 GPU UUID 做观测和审计。
- 如果必须按特定 GPU 属性选择,评估 DRA。
5. 能不能指定某一张物理 GPU
传统 device plugin 模式下,普通 Pod spec 不能可靠表达:
我要 <gpu-worker-01> 上的 GPU-UUID-xxx
你可以做到的是:
5.1 固定到某台 GPU 节点
nodeSelector:
kubernetes.io/hostname: <gpu-worker-01>
或用 node affinity:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- <gpu-worker-01>
这只能固定节点,不能固定该节点里的某张卡。
5.2 用 UUID 做验证,不用 index 做契约
宿主机:
nvidia-smi -L
容器:
kubectl exec one-gpu -- nvidia-smi -L
把容器里看到的 UUID 和宿主机 UUID 对齐。
注意:容器内部看到的 GPU 0 通常只是容器视角的第 0 张可见卡。
5.3 需要按 UUID/PCI/拓扑调度,评估 DRA
DRA 是 Kubernetes 面向动态设备分配的新机制。
它允许通过 ResourceClaim、DeviceClass、ResourceSlice 表达更细的设备选择。
概念上可以写成:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-by-uuid
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.nvidia.com
selectors:
- cel:
expression: device.attributes["gpu.nvidia.com"].uuid == "GPU-xxxx"
但这不是所有集群都能直接使用。
它依赖:
- Kubernetes 版本。
- NVIDIA DRA driver。
- GPU Operator 版本。
- CDI 支持。
- driver 版本。
- ResourceSlice 实际暴露了哪些字段。
因此,DRA 更适合新集群规划或平台化建设,不建议在已有生产集群里临时硬塞。
6. 三种隔离/共享模式的边界
这张图是简化决策图:先区分“整卡独占、硬隔离、弱共享、平台调度”。完整开源生产选型建议看第 9 节,那里会把 Kueue、Volcano、KAI Scheduler 分开。
6.1 Full GPU 独占
这是最简单、最稳的默认方案。
写法:
resources:
limits:
nvidia.com/gpu: 1
适合:
- 训练任务。
- 性能敏感推理。
- 单租户或强可预测资源场景。
- 不希望互相干扰的业务。
优点:
- 调度语义清楚。
- 风险低。
- 排障简单。
- 和大多数框架兼容。
缺点:
- 小任务可能浪费 GPU。
- 无法表达“半张卡”。
6.2 MIG:硬切分隔离
MIG 可以把一张支持 MIG 的 GPU 切成多个硬件实例。
每个 MIG 实例有相对独立的计算、显存、缓存和带宽边界。
适合:
- 多租户推理。
- 需要更强隔离的小模型任务。
- A100/H100 等支持 MIG 的 GPU。
典型资源名可能类似:
nvidia.com/mig-1g.10gb
nvidia.com/mig-2g.20gb
Pod 示例:
resources:
limits:
nvidia.com/mig-1g.10gb: 1
注意:
- MIG 需要 GPU 硬件支持。
- MIG 模式/切分策略变更会影响节点上已有 workload。
- 生产上应通过 GPU Operator 的 MIG Manager 管理,而不是手工在节点上随意切换。
6.3 Time-slicing:弱隔离共享
time-slicing 会把一张 GPU 暴露成多个逻辑 replica。
比如 8 张 GPU,每张切成 10 个 replica,节点可能显示出 80 个逻辑共享资源。
适合:
- 开发调试。
- 轻量推理。
- 低优先级任务。
- 可以接受互相影响的场景。
风险:
- 没有显存硬隔离。
- 没有故障硬隔离。
- 一个进程 OOM 或异常可能影响共享体验。
- 申请多个共享 GPU 不代表获得线性算力。
最佳实践:
- 使用
renameByDefault=true把共享资源改名为nvidia.com/gpu.shared。 - 使用
failRequestsGreaterThanOne=true,避免用户误以为申请 2 个 shared GPU 就有 2 倍算力。
示意:
resources:
limits:
nvidia.com/gpu.shared: 1
6.4 MPS:共享但更可控,仍需谨慎
MPS 可以让多个 CUDA 进程共享一张 GPU,并通过 MPS daemon 做一定的计算和显存份额控制。
相比 time-slicing,它更可控一些。
但在 Kubernetes device plugin 语境里,MPS 支持仍应谨慎看待:
- 不是所有 GPU/模式都适合。
- 和 time-slicing 互斥。
- 不适合和 MIG-enabled 设备混用。
- 运维复杂度高于 full GPU 独占。
除非你明确知道 workload 特性,否则生产默认仍建议 full GPU 或 MIG。
7. Run:ai 能解决什么
NVIDIA Run:ai 可以理解为面向 AI 集群的 GPU 资源治理平台。
它关注的问题不是“一个容器怎样看到某张 GPU”,而是更上层的治理:
- 多租户项目。
- 队列。
- 配额。
- 公平调度。
- GPU 利用率提升。
- 训练/推理 workload 编排。
- 组织级资源可视化。
- 管理员策略控制。
也就是说,Run:ai 可以和 NVIDIA GPU Operator、device plugin、MIG、共享 GPU 等能力配合。
它更像是:
平台治理层 + 调度层 + 资源抽象层
而不是单纯替代 nvidia.com/gpu: 1。
8. Run:ai 是不是开源免费
这个问题要拆开说。
8.1 KAI Scheduler 是开源的
KAI Scheduler 是 NVIDIA 开源的 Kubernetes 原生调度器,来自 Run:ai 的调度能力。
它适合关注:
- 队列。
- 公平性。
- GPU sharing。
- AI workload 的调度优化。
如果你的目标是研究或落地开源 GPU 调度器,可以评估 KAI Scheduler。
8.2 NVIDIA Run:ai 平台不应默认理解成开源免费
NVIDIA Run:ai 是完整企业平台能力,包含远不止调度器的部分。
它通常涉及:
- 平台控制面。
- 用户/项目/租户。
- 配额和策略。
- 可视化。
- 企业集成。
- SaaS 或 self-hosted 部署形态。
- 商业支持。
因此,不能把“开源 KAI Scheduler”直接等同于“完整 Run:ai 平台开源免费”。
如果要采购或生产使用完整 Run:ai 平台,应以 NVIDIA/Run:ai 官方授权、报价和部署合同为准。
9. 完整开源生产落地方案
如果目标是“开源、可生产落地”,不要把所有能力都压到一个调度器里。
更稳的做法是分层:
底座层:
NVIDIA Driver
NVIDIA Container Toolkit
NVIDIA device plugin / GPU Operator
DCGM Exporter
资源隔离层:
full GPU 独占
MIG
time-slicing / MPS
DRA
平台治理层:
Kueue / Volcano / KAI Scheduler
namespace / quota / priority / queue
多团队资源池 / 队列 / 公平共享 / Gang Scheduling
9.1 方案 A:基础生产栈,最推荐先落地
这是最适合先上线的方案。
组件:
- NVIDIA GPU Operator
- NVIDIA device plugin
- GPU Feature Discovery
- DCGM Exporter
- kube-scheduler
- Namespace / RBAC / ResourceQuota
- Taint / Toleration
- Prometheus / Grafana
适合:
- 1 Pod / 1 full GPU。
- 小团队或单团队 GPU 集群。
- 训练任务数量还没有明显排队。
- 优先要求稳定、可观测、容易排障。
这套方案就可以回答本文最核心的问题:
8 卡 Worker 上,1 个 Pod 只使用 1 张 GPU。
Pod 侧只需要:
resources:
limits:
nvidia.com/gpu: 1
Namespace 配额控制团队上限:
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota
namespace: team-a
spec:
hard:
requests.nvidia.com/gpu: "4"
limits.nvidia.com/gpu: "4"
这套方案的优点是足够朴素。
朴素不是缺点。GPU 生产环境里,第一优先级通常不是“调度器功能最多”,而是:
- 资源语义清楚。
- 任务失败容易定位。
- 不发生隐性超卖。
- 不让用户误以为共享 GPU 是硬隔离。
- 监控和告警能解释 GPU 利用率、显存、温度、XID 错误。
9.2 方案 B:Kueue,多团队排队和配额治理
当 GPU 集群开始被多个团队共享,单纯 ResourceQuota 往往不够。
典型问题是:
- team-a 一次提交很多 Job,占满 GPU。
- team-b 有配额但抢不到队列顺序。
- 不同 GPU 型号需要做资源池区分。
- 希望任务先进入队列,等资源满足后再 admission。
这时可以引入 Kueue。
组件:
- NVIDIA GPU Operator
- Kueue
- Kubernetes Job / JobSet
- Kubeflow Training Operator
- ResourceFlavor
- ClusterQueue / LocalQueue
Kueue 的核心不是替代 kubelet 或 device plugin,而是做批任务 admission 和队列配额。
推荐结构:
ResourceFlavor:
表达资源类型,例如 A800、H800、L40S、RTX 4090
ClusterQueue:
表达集群级资源池和配额
LocalQueue:
namespace 内团队提交入口
Workload:
Kueue 接管的待 admission 任务
适合:
- 多团队训练。
- Job/JobSet 形态任务。
- Kubeflow PyTorchJob、MPIJob、RayJob 等可和 Kueue 集成的任务。
- 希望资源先排队,再被准入。
不适合:
- 只是要跑少量长驻 Deployment。
- 只需要最简单的 1 Pod / 1 GPU。
- 团队还没有明显的排队和公平共享诉求。
Kueue 落地建议:
- 先把 GPU 型号打成 node label。
- 用 ResourceFlavor 对应 GPU 型号或节点池。
- 用 ClusterQueue 给团队分配 nominal quota。
- 用 LocalQueue 作为 namespace 内提交入口。
- 训练任务统一通过 Job/JobSet 或 Training Operator 提交。
9.3 方案 C:Volcano,分布式训练和 Gang Scheduling
如果你的训练任务是分布式的,比如:
- 8 个 worker 必须一起启动。
- 部分 Pod 先启动也没意义。
- 训练框架需要 gang scheduling。
- Spark、Ray、Flink、MPI、PyTorch 分布式训练很多。
这时 Volcano 更合适。
组件:
- NVIDIA GPU Operator
- Volcano Scheduler
- Volcano Queue
- VolcanoJob
- PodGroup
- PyTorchJob / MPIJob / Spark/Ray 集成
Volcano 的核心价值是 batch scheduling 和 gang scheduling。
它适合解决:
一组 Pod 要么一起拿到资源并启动,要么都等着。
否则很容易出现:
- rank 0 启动了,rank 1~7 没资源。
- GPU 被部分 Pod 占住,但任务跑不起来。
- 集群资源碎片化。
- 分布式训练长时间卡住。
适合:
- 大规模分布式训练。
- 大数据和 AI 混部。
- 需要队列、优先级、Gang Scheduling 的集群。
不适合:
- 单 Pod / 单卡为主。
- 只需要普通 Deployment 或简单 Job。
- 团队还没有分布式训练队列治理需求。
9.4 方案 D:KAI Scheduler,Run:ai 调度器的开源路线
KAI Scheduler 是 NVIDIA 开源的 Kubernetes 原生 GPU 调度器,源自 Run:ai 的调度能力。
它适合评估这些方向:
- GPU-aware scheduling。
- AI/ML workload 调度优化。
- 队列和公平共享。
- gang scheduling。
- workload priority。
- 更贴近 Run:ai 调度器思路的开源方案。
但要注意两个边界:
第一,KAI Scheduler 是开源调度器,不是完整 Run:ai 平台。
第二,生产引入任何新调度器,都要 PoC:
- 是否和现有 workload API 兼容。
- 是否支持你当前的训练入口。
- 是否和 GPU Operator、MIG、time-slicing、MPS 策略一致。
- 是否能接入现有监控、审计和告警。
- 失败时是否容易回滚到默认 kube-scheduler。
我的建议是:
如果你明确想走 Run:ai 风格的开源调度路线,再评估 KAI Scheduler。
如果只是多团队排队,先看 Kueue。
如果是强 gang scheduling 和 AI/big data batch,先看 Volcano。
9.5 开源方案选型表
| 场景 | 推荐开源方案 | 是否建议第一阶段引入 |
|---|---|---|
| 1 Pod 独占 1 张 full GPU | GPU Operator + device plugin + ResourceQuota | 是 |
| 多团队 GPU 配额和排队 | Kueue + GPU Operator | 第二阶段 |
| 分布式训练,Pod 必须一起启动 | Volcano + GPU Operator | 按训练形态引入 |
| 类 Run:ai 的开源 GPU 调度方向 | KAI Scheduler + GPU Operator | PoC 后引入 |
| 一张 GPU 硬切分给多个租户 | GPU Operator + MIG | 仅支持 MIG 的 GPU |
| 一张 GPU 弱共享给多个小任务 | GPU Operator + time-slicing/MPS | 谨慎引入 |
| 按 GPU UUID/属性/拓扑精确选择 | NVIDIA DRA Driver + DRA | 新集群规划优先 |
9.6 我推荐的开源生产落地顺序
第一阶段:先稳住 GPU 底座。
GPU Operator
device plugin
GPU Feature Discovery
DCGM Exporter
ResourceQuota
taint / toleration
Prometheus / Grafana
目标:
- 节点正确上报
nvidia.com/gpu: 8。 - 业务 Pod 使用
nvidia.com/gpu: 1。 - 容器只看到 1 张 GPU。
- 团队 namespace 有 GPU 配额。
- 监控能看到 GPU 使用率和错误。
第二阶段:引入队列。
如果是普通训练 Job 排队,优先 Kueue。
如果是分布式训练和 gang scheduling,优先 Volcano。
第三阶段:评估高级调度。
如果想贴近 Run:ai 的 GPU 调度能力,再 PoC KAI Scheduler。
如果需要完整平台能力,比如 UI、租户、项目、策略、审计、企业支持,再评估商业 Run:ai。
第四阶段:精细化设备模型。
如果有硬隔离诉求,评估 MIG。
如果有低风险共享诉求,评估 time-slicing/MPS。
如果有按设备属性选择诉求,评估 DRA。
这个顺序的好处是每一步都能独立验收,不会一上来把 GPU 驱动、device plugin、队列、共享、平台全部混在一起排障。
10. 生产最佳实践清单
节点侧:
kubectl describe node <gpu-node> | grep -A10 -E 'Capacity|Allocatable'
kubectl get node <gpu-node> \
-L nvidia.com/gpu.present,nvidia.com/gpu.count,nvidia.com/gpu.product,nvidia.com/gpu.replicas,nvidia.com/gpu.sharing-strategy
期望:
- full GPU 独占模式下,8 卡节点显示
nvidia.com/gpu: 8。 - 如果显示远大于 8,要检查是否启用了 time-slicing。
- 如果出现
nvidia.com/gpu.shared,说明共享资源命名已经启用。
Pod 侧:
kubectl exec one-gpu -- nvidia-smi -L
kubectl exec one-gpu -- bash -lc 'echo $NVIDIA_VISIBLE_DEVICES'
调度侧:
kubectl describe pod one-gpu
kubectl get pod one-gpu -o wide
安全侧:
- 不给普通业务容器
privileged: true。 - 不手写
NVIDIA_VISIBLE_DEVICES。 - 不把 GPU 节点开放给任意 namespace。
- 用
ResourceQuota限制 namespace 可用 GPU 数量。 - 用 taint/toleration 控制 GPU 节点调度入口。
- 用 DCGM Exporter 监控 GPU 利用率、显存、温度和错误。
Namespace 配额示例:
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota
namespace: team-a
spec:
hard:
requests.nvidia.com/gpu: "4"
limits.nvidia.com/gpu: "4"
GPU 节点 taint 示例:
kubectl taint node <gpu-node> nvidia.com/gpu=true:NoSchedule
业务 Pod 显式容忍:
tolerations:
- key: nvidia.com/gpu
operator: Equal
value: "true"
effect: NoSchedule
11. 我的推荐路径
如果你是从 0 到 1 落地 GPU on Kubernetes,我建议这样走:
- 先用 GPU Operator 或 device plugin 跑通 full GPU 独占。
- 业务统一用
limits.nvidia.com/gpu: 11 GPU。 - 用 namespace quota、taint/toleration、priority class 做基础治理。
- 用 DCGM Exporter 做 GPU 观测。
- 多团队普通训练 Job 排队,优先评估 Kueue。
- 分布式训练需要 gang scheduling,优先评估 Volcano。
- 想走 Run:ai 调度器的开源路线,再 PoC KAI Scheduler。
- 如果 GPU 支持 MIG,并且有多租户硬隔离诉求,再启用 MIG。
- 如果只是开发/低风险推理,需要提高利用率,再评估 time-slicing/MPS。
- 如果必须按 UUID/拓扑选择设备,规划 DRA,不要用环境变量硬指定。
- 如果需要完整平台 UI、企业支持、跨团队策略和审计,再评估商业 Run:ai。
一句话总结:
“1 Pod 使用 1 张 GPU”用
nvidia.com/gpu: 1;多团队排队优先 Kueue;分布式训练优先 Volcano;类 Run:ai 开源调度路线再看 KAI Scheduler;完整企业平台能力再评估商业 Run:ai。
参考资料
- Kubernetes:Schedule GPUs
- Kubernetes:Device Plugins
- Kubernetes:Dynamic Resource Allocation
- NVIDIA k8s-device-plugin
- NVIDIA GPU Operator
- NVIDIA GPU Operator:GPU Sharing
- NVIDIA GPU Operator:MIG
- NVIDIA GPU Operator:DRA
- NVIDIA DRA Driver for GPUs
- Kueue
- Volcano
- KAI Scheduler
- NVIDIA Run:ai Documentation