GPU-on-Kubernetes-8卡Worker使用单张GPU隔离

GPU-on-Kubernetes-8卡Worker使用单张GPU隔离

Deng YongJie's blog 2 2026-06-21

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 官方授权和产品形态评估。

one-pod-one-gpu-isolation

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
  • 如果同时写 requestslimits,两者必须相等。
  • 只写 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 面向动态设备分配的新机制。

它允许通过 ResourceClaimDeviceClassResourceSlice 表达更细的设备选择。

概念上可以写成:

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. 三种隔离/共享模式的边界

gpu-isolation-decision-flow

这张图是简化决策图:先区分“整卡独占、硬隔离、弱共享、平台调度”。完整开源生产选型建议看第 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 落地建议:

  1. 先把 GPU 型号打成 node label。
  2. 用 ResourceFlavor 对应 GPU 型号或节点池。
  3. 用 ClusterQueue 给团队分配 nominal quota。
  4. 用 LocalQueue 作为 namespace 内提交入口。
  5. 训练任务统一通过 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,我建议这样走:

  1. 先用 GPU Operator 或 device plugin 跑通 full GPU 独占。
  2. 业务统一用 limits.nvidia.com/gpu: 1 1 GPU。
  3. 用 namespace quota、taint/toleration、priority class 做基础治理。
  4. 用 DCGM Exporter 做 GPU 观测。
  5. 多团队普通训练 Job 排队,优先评估 Kueue。
  6. 分布式训练需要 gang scheduling,优先评估 Volcano。
  7. 想走 Run:ai 调度器的开源路线,再 PoC KAI Scheduler。
  8. 如果 GPU 支持 MIG,并且有多租户硬隔离诉求,再启用 MIG。
  9. 如果只是开发/低风险推理,需要提高利用率,再评估 time-slicing/MPS。
  10. 如果必须按 UUID/拓扑选择设备,规划 DRA,不要用环境变量硬指定。
  11. 如果需要完整平台 UI、企业支持、跨团队策略和审计,再评估商业 Run:ai。

一句话总结:

“1 Pod 使用 1 张 GPU”用 nvidia.com/gpu: 1 ;多团队排队优先 Kueue;分布式训练优先 Volcano;类 Run:ai 开源调度路线再看 KAI Scheduler;完整企业平台能力再评估商业 Run:ai。

参考资料