【中阶·云原生】GPU 感知调度与设备插件机制深度解析:从 Device Plugin 到 DRA 的架构演进
专栏:《AI 工程与安全深度实战》· 第2轮·第1篇
前言
- 核心痛点:AI 训练与推理工作负载对 GPU 资源的需求远超传统 CPU 工作负载,Kubernetes 原生调度器无法感知 GPU 拓扑、共享模式与设备健康状态,导致资源浪费、调度失败与性能下降
- 适配人群:具备 Kubernetes 基础知识的平台工程师、MLOps 工程师、AI 基础设施架构师,希望深入理解 GPU 调度底层机制并掌握生产级部署能力
- 收获能力:读完可掌握 Device Plugin 框架原理、DRA 新一代资源分配模型、NVIDIA GPU Operator 全栈组件、MIG/MPS/Time-slicing 三种 GPU 共享模式、KAI Scheduler 调度策略,并具备从零搭建生产级 GPU 集群的实战能力
目录
- 技术背景与演进逻辑
- Device Plugin 框架:GPU 调度的基石
- DRA:新一代设备资源分配模型
- NVIDIA GPU Operator:GPU 全栈管理引擎
- GPU 共享模式:MIG、MPS 与 Time-slicing
- KAI Scheduler:AI 工作负载感知调度
- 技术优缺点与适用场景
- 实战落地:生产级 GPU 集群部署
- 全文总结
- 参考资料
技术背景与演进逻辑
GPU 调度的行业痛点
2026 年,AI 基础设施面临的核心矛盾是:GPU 硬件成本高昂,但利用率普遍偏低。根据公开数据,企业 GPU 集群平均利用率仅为 30%-50%,大量算力在等待调度、跨 NUMA 访问、错误共享模式中被浪费。
传统 Kubernetes 调度模型将 GPU 视为"不透明的整数资源",通过 nvidia.com/gpu: 1 这样的扩展资源声明来请求设备。这种模型存在以下根本性缺陷:
| 缺陷维度 | 具体表现 | 影响 |
|---|---|---|
| 无拓扑感知 | 调度器不感知 NVLink、PCIe、NUMA 拓扑 | 跨 NUMA 的 GPU 通信带宽下降 50%+ |
| 无共享语义 | 整数资源不可超额分配 | 小模型推理独占整卡,资源浪费严重 |
| 无参数化选择 | 无法按显存、算力、型号筛选设备 | 混合型号集群调度失败率高 |
| 无生命周期管理 | Pod 删除即释放,无法跨 Pod 持有 | Notebook 场景 GPU 被抢占后丢失状态 |
| 无健康状态联动 | 设备故障后调度器无感知 | Pod 被调度到故障 GPU 上反复重启 |
技术演进时间线
2018 ──→ 2020 ──→ 2022 ──→ 2024 ──→ 2025 ──→ 2026
│ │ │ │ │ │
│ │ │ │ │ └── DRA GA (K8s 1.34)
│ │ │ │ │ KAI Scheduler 0.5
│ │ │ │ │ GPU Operator 24.9
│ │ │ │ │
│ │ │ │ └── NVIDIA 捐赠 DRA 驱动至 CNCF
│ │ │ │ KubeCon NA 2025
│ │ │ │
│ │ │ └── KAI Scheduler 开源
│ │ │ DRA Beta (K8s 1.31)
│ │ │
│ │ └── Device Plugin GA (K8s 1.26)
│ │ Topology Manager GA
│ │ GPU Operator 首发
│ │
│ └── Device Plugin Beta (K8s 1.12)
│ MIG 技术随 A100 发布
│
└── Device Plugin Alpha (K8s 1.8)
NVIDIA 首个 Device Plugin 实现
2026 年 GPU 调度技术全景
当前 Kubernetes GPU 调度已形成三层架构:
[AI 工作负载层]
│
├── 训练任务(Job/PyTorchJob/RayCluster)
│ │
│ └── 需求:多卡原子调度 + NVLink 拓扑 + 整卡独占
│
├── 推理服务(Deployment/Knative Service)
│ │
│ └── 需求:低延迟 + GPU 共享 + 弹性扩缩
│
└── 开发环境(Notebook/VS Code Server)
│
└── 需求:按需分配 + 超额订阅 + 快速回收
↓ 调度抽象层
[调度与资源管理层]
│
├── KAI Scheduler ──→ Gang 调度 + 队列优先级 + 抢占
│
├── DRA Driver ──→ ResourceClaim + DeviceClass + CEL 过滤
│
└── GPU Operator ──→ 驱动 + 容器工具 + MIG 管理 + DCGM 监控
↓ 设备抽象层
[硬件层]
│
├── NVIDIA GPU(A100 / H100 / H200 / B100)
│ │
│ ├── 整卡模式
│ ├── MIG 分区模式(最多 7 实例)
│ ├── MPS 共享模式(软件多路复用)
│ └── Time-slicing 时间片模式
│
├── AMD GPU(MI300X)
│
└── Intel GPU(Flex / Max 系列)
Device Plugin 框架:GPU 调度的基石
框架架构
Device Plugin 是 Kubernetes v1.8 引入的扩展机制(v1.26 GA),允许第三方厂商以标准化方式将硬件设备暴露给 kubelet,无需修改 Kubernetes 核心代码。GPU 调度的整个历史始于这一框架。
[Device Plugin 架构]
[Kubelet]
│
├── Device Manager(内建组件)
│ │
│ ├── 注册管理 ──→ 监听 /var/lib/kubelet/device-plugins/kubelet.sock
│ │
│ ├── 设备清单 ──→ 维护节点可分配设备列表
│ │
│ ├── 分配决策 ──→ 调用 Plugin.Allocate() 获取容器配置
│ │
│ └── 健康监控 ──→ 接收 ListAndWatch 流式更新
│
├── Topology Manager(v1.27 GA)
│ │
│ └── NUMA 感知 ──→ 协调 CPU/内存/设备的拓扑亲和性
│
└── PodResources API(v1.28 GA)
│
└── 监控发现 ──→ 暴露已分配设备信息给 DCGM 等监控组件
注册流程详解
Device Plugin 的注册是一个严格的有序流程,任何步骤错序都会导致注册失败。
[Device Plugin 启动与注册流程]
[Device Plugin Pod 启动]
│
├── Step 1: 初始化
│ │
│ └── 厂商特定初始化(加载驱动、探测设备、校验健康)
│
├── Step 2: 启动 gRPC Server
│ │
│ └── 监听 Unix Socket
│ 路径: /var/lib/kubelet/device-plugins/<plugin>.sock
│ ⚠️ 此路径为硬编码,不受 kubelet --root-dir 影响
│
├── Step 3: 调用 Registration.Register()
│ │
│ └── 向 kubelet 注册,声明:
│ ├── Socket 名称
│ ├── API 版本
│ └── ResourceName(如 nvidia.com/gpu)
│
└── Step 4: 进入 Serving 模式
│
├── ListAndWatch ──→ 持续上报设备列表与健康状态
│
└── 等待 Allocate 请求 ──→ 容器创建时被调用
关键设计约束 :Plugin 必须在 gRPC Server 启动之后才能调用 Register。如果先注册再启动服务,kubelet 会因无法连接到 Socket 而拒绝注册。
gRPC API 深度解析
Device Plugin 暴露五个 gRPC 方法,其中两个是必选的,三个是可选的:
service DevicePlugin {
// 必选方法
rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse)
rpc Allocate(AllocateRequest) returns (AllocateResponse)
// 可选方法
rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions)
rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse)
rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse)
}
ListAndWatch 是设备生命周期管理的核心。Plugin 通过流式响应持续向 kubelet 推送设备状态变更:
- 首次调用:返回当前所有健康设备的完整列表
- 设备状态变更:设备故障、恢复、热插拔时推送增量更新
- kubelet 重启检测:Plugin 监控自身 Socket 文件被删除的事件,自动重新注册
当设备被标记为不健康时,kubelet 会递减该节点上该资源的 Allocatable 计数(但不改变 Capacity),从而阻止新 Pod 被调度到故障设备上。已分配到故障设备的 Pod 会继续运行,直到依赖设备的应用代码失败并触发重启。
Allocate 是容器创建时的关键调用。当 kubelet 为一个 Pod 分配了设备后,会调用对应 Plugin 的 Allocate 方法,传入待分配的设备 ID 列表。Plugin 返回 AllocateResponse,其中包含:
- 设备节点挂载(/dev/nvidia0 等)
- 环境变量(NVIDIA_VISIBLE_DEVICES 等)
- 文件系统挂载(CUDA 运行时库、驱动库)
- CDI 设备名称(v1.31 GA,标准化设备发现机制)
yaml
# Device Plugin AllocateResponse 示例(简化)
container_responses:
- device_nodes:
- host_path: /dev/nvidia0
container_path: /dev/nvidia0
permissions: "rw"
- host_path: /dev/nvidiactl
container_path: /dev/nvidiactl
permissions: "rw"
mounts:
- host_path: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.550.127.05
container_path: /usr/lib/x86_64-linux-gnu/libnvidia-ml.so
read_only: true
envs:
- key: NVIDIA_VISIBLE_DEVICES
value: "GPU-xxxx-xxxx-xxxx"
Topology Manager 集成
Topology Manager 是 kubelet 内建组件(v1.27 GA),负责协调 CPU、内存、设备的 NUMA 拓扑亲和性。Device Plugin 通过 TopologyInfo 结构向 Topology Manager 报告设备的 NUMA 位置:
[Topology Manager 决策流程]
[Pod 创建请求]
│
├── CPU Manager ──→ 候选 CPU 集合(NUMA 0)
│
├── Memory Manager ──→ 候选内存节点(NUMA 0)
│
├── Device Manager ──→ 候选 GPU(NUMA 0, TopologyInfo)
│
└── Topology Manager ──→ 跨资源拓扑对齐决策
│
├── 最佳策略(BestEffort):尽可能对齐,允许不对齐
├── 严格策略(Strict):必须全部对齐,否则拒绝调度
└── 单 NUMA 策略(SingleNumaNode):所有资源必须在同一 NUMA 节点
在多 GPU 训练场景中,拓扑感知至关重要。以 8 卡 H100 节点为例,GPU 0-3 通常连接在 NUMA 0 上,GPU 4-7 连接在 NUMA 1 上。如果一个 4 卡训练任务被调度到跨 NUMA 的 GPU 组合(如 GPU 0,1,4,5),跨 NUMA 的 NVLink 通信带宽会显著下降,直接影响训练吞吐量。
PodResources API
PodResources API(v1.28 GA)是 kubelet 提供的只读 gRPC 服务,供监控组件发现节点上已分配的设备信息:
service PodResourcesLister {
rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse)
rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse)
rpc Get(GetPodResourcesRequest) returns (GetPodResourcesResponse)
}
DCGM Exporter 等监控组件通过此 API 获取每个 Pod 实际使用的 GPU 设备 ID,从而将 GPU 指标(利用率、显存、温度)与 Pod 维度关联,构建出细粒度的可观测性仪表盘。
DRA:新一代设备资源分配模型
从 Device Plugin 到 DRA 的范式转变
Dynamic Resource Allocation(DRA)在 Kubernetes 1.34(2026 年 3 月)正式 GA,是 Device Plugin 框架的演进替代方案。DRA 的核心设计理念是:将设备从"不透明的整数资源"升级为"声明式的 API 对象"。
[Device Plugin vs DRA 对比]
Device Plugin 模型:
Pod ──→ resources.limits: nvidia.com/gpu: 1
│
└── kubelet ──→ Device Manager ──→ 随机分配一个可用 GPU
无拓扑约束、无参数选择、无共享语义
DRA 模型:
Pod ──→ resourceClaims: [my-gpu-claim]
│
├── ResourceClaim ──→ DeviceClass: gpu.nvidia.com
│ │
│ ├── selectors: CEL 表达式(型号/显存/算力过滤)
│ ├── constraints: 拓扑约束(NVLink 域/NUMA 节点)
│ └── adminAccess: 共享语义(多 Pod 共享同一 Claim)
│
└── DRA Driver ──→ 在 bind 时解析 Claim 到真实设备
DRA 核心 API 对象
DRA 引入了三个核心 API 对象,形成声明式的设备分配模型:
DeviceClass 是集群管理员定义的设备类别,类似于 StorageClass 对存储的抽象:
yaml
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: gpu.nvidia.com
spec:
selectors:
- device:
driver: gpu.nvidia.com
ResourceClaim 是工作负载对设备的声明式请求,类似于 PersistentVolumeClaim:
yaml
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: my-h100-claim
spec:
devices:
requests:
- name: gpu
deviceClassName: gpu.nvidia.com
selectors:
- cel:
expression: >
device.attributes['gpu.nvidia.com'].productName ==
'NVIDIA H100 80GB HBM3' &&
device.capacity['gpu.nvidia.com'].memory >= 80
constraints:
- requests: ["gpu"]
matchAttribute: "gpu.nvidia.com/nvlinkDomain"
ResourceClaimTemplate 是用于自动创建 ResourceClaim 的模板,每个 Pod 实例获得独立的 Claim:
yaml
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: single-gpu-template
spec:
spec:
devices:
requests:
- name: gpu
deviceClassName: gpu.nvidia.com
selectors:
- cel:
expression: "device.capacity['gpu.nvidia.com'].memory >= 24"
CEL 表达式过滤
DRA 使用 Kubernetes 通用表达式语言(CEL)进行设备属性过滤,这是 Device Plugin 完全不具备的能力:
| 过滤维度 | CEL 表达式示例 | 说明 |
|---|---|---|
| 产品型号 | device.attributes['gpu.nvidia.com'].productName == 'NVIDIA H100' |
精确匹配 GPU 型号 |
| 显存容量 | device.capacity['gpu.nvidia.com'].memory >= 80 |
筛选显存 >= 80GB 的设备 |
| 计算能力 | device.attributes['gpu.nvidia.com'].computeCapability >= 9.0 |
筛选 SM 9.0+ 架构 |
| MIG 配置 | device.attributes['gpu.nvidia.com'].mig.profile == '1g.10gb' |
匹配 MIG 分区配置 |
| 厂商标签 | device.attributes['gpu.nvidia.com'].labels['team'] == 'ml-research' |
自定义标签过滤 |
拓扑约束
DRA 的 constraints 字段允许声明设备间的拓扑关系,这是分布式训练的关键需求:
yaml
# 要求 8 张 GPU 必须在同一个 NVLink 域内
spec:
devices:
requests:
- name: gpus
deviceClassName: gpu.nvidia.com
count: 8
constraints:
- requests: ["gpus"]
matchAttribute: "gpu.nvidia.com/nvlinkDomain"
matchAttribute 约束确保所有请求的设备共享相同的属性值------在上例中,8 张 GPU 必须属于同一个 NVLink 域,避免跨 socket 的带宽瓶颈。
DRA 与 Device Plugin 共存策略
DRA 和 Device Plugin 可以在同一集群中共存,但同一节点上的同一资源类型不能同时由两者管理。NVIDIA 推荐的迁移策略是:
[迁移路径]
阶段 1(当前):
│
├── 旧节点 ──→ Device Plugin(nvidia.com/gpu 扩展资源)
│
└── 新节点 ──→ DRA Driver(ResourceClaim 模型)
│
└── 通过 node label 区分调度目标
阶段 2(逐步迁移):
│
├── 全部节点 ──→ DRA Driver
│
└── Device Plugin DaemonSet 降级为可选(仅用于旧版客户端兼容)
阶段 3(完全迁移):
│
└── 移除 Device Plugin,全面使用 DRA
NVIDIA GPU Operator:GPU 全栈管理引擎
组件架构
NVIDIA GPU Operator 是管理 Kubernetes GPU 节点全栈软件的 Helm Chart,它自动化了从驱动安装到监控导出的完整生命周期:
[GPU Operator 组件架构]
GPU Operator Helm Chart
│
├── NVIDIA Driver Manager
│ │
│ └── 自动安装/升级 GPU 内核模块(nvidia.ko, nvidia-uvm.ko)
│ 版本:550.127.05(当前稳定)
│
├── NVIDIA Container Toolkit
│ │
│ └── 配置 containerd/CRI-O 的 NVIDIA 运行时
│ 使容器能够访问 /dev/nvidia* 设备节点
│
├── NVIDIA Device Plugin(可选,DRA 迁移后可禁用)
│ │
│ └── 向 kubelet 注册 nvidia.com/gpu 扩展资源
│
├── NVIDIA DRA Driver(独立 Helm Chart)
│ │
│ └── 注册 gpu.nvidia.com DeviceClass
│ 处理 ResourceClaim 的绑定与释放
│
├── NVIDIA MIG Manager
│ │
│ └── 管理 MIG 分区配置的创建、切换、销毁
│ 处理 drain-reconfigure-uncordon 流程
│
├── NVIDIA DCGM Exporter
│ │
│ └── 导出 GPU 指标到 Prometheus
│ GPU 利用率 / 显存使用 / 温度 / 功耗
│
└── NVIDIA vGPU Manager(可选)
│
└── 管理 vGPU 许可证与虚拟化实例
关键版本信息(2026 年 6 月)
| 组件 | 版本 | 说明 |
|---|---|---|
| Kubernetes | 1.34 | DRA GA 版本 |
| NVIDIA GPU Operator | v24.9.2 | 驱动 + 容器工具 + MIG + DCGM |
| NVIDIA DRA Driver | v25.3.0 | ResourceClaim 支持 |
| NVIDIA 驱动 | 550.127.05 | 支持 CUDA 12.6 |
| KAI Scheduler | v0.5.1 | Gang 调度 + 队列管理 |
| CUDA | 12.6 | 最新稳定版 |
DCGM 监控指标体系
DCGM(Data Center GPU Manager)Exporter 暴露的关键 Prometheus 指标:
| 指标名称 | 含义 | 告警阈值建议 |
|---|---|---|
DCGM_FI_DEV_GPU_UTIL |
GPU 计算利用率(%) | 训练期间 < 70% 需排查 |
DCGM_FI_DEV_FB_USED |
显存使用量(MiB) | 接近总显存需关注 OOM |
DCGM_FI_DEV_GPU_TEMP |
GPU 温度(°C) | > 85°C 告警 |
DCGM_FI_DEV_POWER_USAGE |
功耗(W) | 接近 TDP 需关注散热 |
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL |
NVLink 总带宽(字节) | 低于预期需排查拓扑 |
DCGM_FI_PROF_SM_ACTIVE |
SM 活跃比例(%) | 低于 50% 表示未充分利用 |
GPU 共享模式:MIG、MPS 与 Time-slicing
三种共享模式对比
| 模式 | 隔离级别 | 支持 GPU | 最大实例数 | 适用场景 |
|---|---|---|---|---|
| MIG(Multi-Instance GPU) | 硬件隔离:独立显存、SM 分区、故障域 | A100, H100, H200, B100 | 7 / GPU | 生产多租户推理、SaaS |
| MPS(Multi-Process Service) | 软件隔离:共享 SM、共享显存总线 | 任意 CUDA GPU | ~48 客户端 | 并发小模型推理、低延迟批处理 |
| Time-slicing | 无隔离:CUDA 上下文切换 | 任意 CUDA GPU | 无限(可超售) | 开发集群、Jupyter Notebook、CI |
| 整卡独占 | 完全隔离 | 任意 | 1 | 大模型训练、延迟敏感推理 |
MIG:硬件级分区
MIG(Multi-Instance GPU)是 NVIDIA A100 及以后架构引入的硬件分区技术。它将一张物理 GPU 划分为最多 7 个独立实例,每个实例拥有:
- 独立的 SM(Streaming Multiprocessor)分区
- 独立的 L2 缓存分区
- 独立的显存通道
- 独立的故障域(一个实例故障不影响其他实例)
H100 80GB 的典型 MIG 配置:
| 配置方案 | 实例数 | 每实例显存 | 每实例 SM | 适用场景 |
|---|---|---|---|---|
| 7x 1g.10gb | 7 | 10 GB | 1/7 | 小模型推理(8B 以下) |
| 4x 2g.20gb | 4 | 20 GB | 2/7 | 中等模型推理(13B 量化) |
| 3x 3g.40gb | 3 | 40 GB | 3/7 | 大模型推理(70B 量化) |
| 1x 7g.80gb | 1 | 80 GB | 7/7 | 等同整卡(禁用 MIG) |
| 混合配置 | 2+1+... | 可变 | 可变 | 多种模型混合部署 |
MIG 的关键限制:分区配置需要 GPU 重置,这意味着必须先驱逐节点上所有使用该 GPU 的 Pod。生产环境中,应通过 GPU Operator 的 MIG Manager 自动化 drain-reconfigure-uncordon 流程,并在维护窗口执行配置变更。
MPS:软件级共享
MPS(Multi-Process Service)通过一个中心化的服务器进程,将多个 CUDA 客户端的 kernel 调度到同一 GPU 的 SM 上并发执行。与 MIG 的硬件分区不同,MPS 是软件层面的多路复用:
[MPS 架构]
[MPS Control Daemon]
│
└── MPS Server(每个 GPU 一个)
│
├── Client 1 ──→ CUDA Stream 1 ──→ SM 分区
├── Client 2 ──→ CUDA Stream 2 ──→ SM 分区
├── Client 3 ──→ CUDA Stream 3 ──→ SM 分区
└── ...
│
└── 共享显存总线,硬件调度器分配 SM 时间
MPS 的优势在于:对于 kernel 启动频繁的小型推理任务,MPS 的并发执行比 Time-slicing 的上下文切换效率更高。但 MPS 的隔离性较弱------一个客户端的 CUDA 错误可能导致 MPS Server 崩溃,影响所有客户端。
Time-slicing:时间片共享
Time-slicing 是最简单的 GPU 共享方式:将一张物理 GPU 暴露为 N 个虚拟设备,通过 CUDA 上下文切换在不同客户端之间轮流执行。
[Time-slicing 工作原理]
物理 GPU
│
├── 虚拟设备 0 ──→ Pod A(推理中)
├── 虚拟设备 1 ──→ Pod B(等待上下文切换)
├── 虚拟设备 2 ──→ Pod C(等待上下文切换)
└── 虚拟设备 3 ──→ Pod D(等待上下文切换)
│
└── CUDA 驱动按时间片轮转调度
无性能保证,无隔离
Time-slicing 适用于开发和测试环境,其中 GPU 大部分时间处于空闲状态,让多个工程师共享同一张 GPU 可以显著降低成本。但绝不应在生产 SLO 环境中依赖 Time-slicing,因为一个高负载的 Notebook 可以饿死同 GPU 上的所有其他客户端。
KAI Scheduler:AI 工作负载感知调度
为什么默认调度器不够用
Kubernetes 默认的 kube-scheduler 采用"逐 Pod 调度 + Filter-Score"模型。这种模型对于分布式训练任务存在根本性缺陷:
[默认调度器的问题]
16 卡训练任务(PodGroup: 16 个 Pod)
│
├── kube-scheduler 逐个调度
│ │
│ ├── Pod 1-8 ──→ 调度到 Node A(有 8 张空闲 GPU)✅
│ ├── Pod 9-14 ──→ 调度到 Node B(有 6 张空闲 GPU)✅
│ └── Pod 15-16 ──→ Pending(无可用 GPU)❌
│
└── 结果:
├── Node A 上 8 张 GPU 空转等待 Pod 15-16
├── Node B 上 6 张 GPU 空转等待 Pod 15-16
└── 每小时浪费 $600+(按 H100 云上价格估算)
这就是"部分调度"问题:分布式训练要么全部 Pod 同时启动,要么全部等待------调度 14 个 Pod 而留下 2 个 Pending,比不调度更浪费。
KAI Scheduler 核心机制
KAI Scheduler 是 NVIDIA 开源的 Kubernetes 调度器,专为 AI/ML 工作负载设计。它作为第二个调度器与 kube-scheduler 并行运行,Pod 通过 schedulerName: kai-scheduler 显式选择。
Gang Scheduling(组调度) 是 KAI 的核心能力。通过 PodGroup 对象,KAI 确保一个训练任务的所有 Pod 原子性地被调度------要么全部成功,要么全部等待:
[Gang Scheduling 流程]
[提交 Job: parallelism=8]
│
├── KAI 读取 PodGroup: minMember=8
│
├── 检查集群可用 GPU 资源
│ │
│ ├── 可用 >= 8 张 ──→ 原子调度全部 8 个 Pod ✅
│ │
│ └── 可用 < 8 张 ──→ 全部 Pending,等待释放 ✅
│ (不浪费任何 GPU)
│
└── 调度完成后,PodGroup 状态变为 Running
Hierarchical Queues(层级队列) 允许按团队/项目组织 GPU 配额:
[队列层级示例]
root
│
├── team-training(quota: 32 GPU, limit: 48 GPU)
│ │
│ ├── project-llm(quota: 16, priority: 100)
│ │
│ └── project-vision(quota: 16, priority: 80)
│
└── team-inference(quota: 16 GPU, limit: 24 GPU)
│
├── project-serving(quota: 8, priority: 90)
│
└── project-staging(quota: 8, priority: 50)
- quota:保证配额,其他队列不可抢占
- limit:上限配额,可借用空闲资源
- overQuotaWeight:借用空闲资源时的权重
- priority:抢占优先级,高优先级队列可驱逐低优先级的借用资源
Priority-based Preemption(优先级抢占) 确保关键任务优先获得资源。当一个高优先级训练任务提交时,KAI 可以驱逐低优先级队列中借用的 burst 容量,但不会触碰其他队列的保证配额。
KAI vs Kueue vs Volcano
| 特性 | KAI Scheduler | Kueue | Volcano |
|---|---|---|---|
| 定位 | GPU AI/ML 专用调度 | 通用批处理队列层 | HPC 批处理调度 |
| Gang 调度 | ✅ 原生支持 | ✅ 通过 PodGroup | ✅ 原生支持 |
| 拓扑感知 | ✅ 与 DRA 深度集成 | ❌ 委托给 kube-scheduler | ❌ 无 |
| GPU 共享感知 | ✅ MIG/MPS/Time-slicing | ❌ 无 | ❌ 无 |
| 队列层级 | ✅ 层级队列 + 抢占 | ✅ ClusterQueue + LocalQueue | ✅ Queue + Queue |
| DRA 集成 | ✅ 原生支持 | 🔄 开发中 | ❌ 不支持 |
| 适用场景 | GPU 密集型 AI 集群 | CPU/GPU 混合批处理 | MPI/HPC 传统作业 |
技术优缺点与适用场景
技术优势
| 维度 | 优势 | 量化收益 |
|---|---|---|
| 拓扑感知 | DRA + Topology Manager 确保 GPU 通信路径最优 | 训练吞吐量提升 20-35% |
| 资源利用率 | MIG 分区 + 共享模式减少 GPU 空闲 | 推理成本降低 60-70% |
| 调度效率 | KAI Gang 调度消除部分调度浪费 | GPU 利用率从 60% 提升至 85% |
| 声明式管理 | ResourceClaim + DeviceClass 声明式设备管理 | 运维复杂度降低 50% |
| 可观测性 | DCGM + PodResources 构建细粒度监控 | 故障定位时间从小时级降至分钟级 |
现存局限
| 局限 | 影响 | 缓解方案 |
|---|---|---|
| DRA 仅 K8s 1.34+ | 旧集群无法使用 | 分阶段迁移,Device Plugin 共存 |
| MIG 重配置需 GPU 重置 | 配置变更需驱逐 Pod | 维护窗口执行,GPU Operator 自动化 |
| KAI 与 kube-scheduler 分裂 | 需显式指定 schedulerName | 统一标注规范,CI 检查 |
| 驱动版本碎片化 | 节点间驱动不一致导致 CUDA 错误 | GPU Operator 统一管理驱动版本 |
| MIG 分区数上限 7 | 大规模多租户场景不够 | 结合 MPS 或 Time-slicing 分层 |
生产适用场景
[场景选择决策树]
[AI 工作负载类型]
│
├── 大模型训练(70B+)
│ │
│ └── 整卡独占 + KAI Gang 调度 + NVLink 拓扑约束
│
├── 微调任务(7-13B)
│ │
│ └── 整卡独占 + KAI 队列调度
│
├── 生产推理(小模型 < 8B)
│ │
│ └── MIG 1g.10gb 分区 + kube-scheduler
│
├── 生产推理(大模型 70B+)
│ │
│ └── 整卡或 MIG 3g.40gb + kube-scheduler
│
├── 低延迟批处理推理
│ │
│ └── MPS 共享 + kube-scheduler
│
├── 开发 Notebook
│ │
│ └── Time-slicing(4-8 副本)+ kube-scheduler
│
└── CI / 模型验证
│
└── Time-slicing 或 MIG + 短 TTL
实战落地:生产级 GPU 集群部署
环境准备
集群要求:
- Kubernetes v1.34+(DRA GA 版本)
- containerd 作为容器运行时
- GPU 节点已安装 NVIDIA 物理驱动(或由 GPU Operator 自动安装)
- Helm v3.x
节点标签与污点:
bash
# 标签 GPU 节点
kubectl label nodes gpu-node-01 node-type=gpu
kubectl label nodes gpu-node-01 gpu-product=nvidia-h100
# 添加污点,确保非 GPU 工作负载不会调度到 GPU 节点
kubectl taint nodes gpu-node-01 nvidia.com/gpu=present:NoSchedule
Step 1:安装 NVIDIA GPU Operator
bash
# 添加 NVIDIA Helm 仓库
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
# 安装 GPU Operator
helm install --wait \n -n gpu-operator --create-namespace \n gpu-operator nvidia/gpu-operator \n --version v24.9.2 \n --set driver.version=550.127.05 \n --set toolkit.enabled=true \n --set migManager.enabled=true \n --set dcgmExporter.enabled=true
验证安装:
bash
# 检查所有 DaemonSet 就绪
kubectl get daemonsets -n gpu-operator
# 预期输出:
# nvidia-driver-daemonset 3/3
# nvidia-container-toolkit 3/3
# nvidia-mig-manager 3/3
# nvidia-dcgm-exporter 3/3
常见问题排查:
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| Driver DaemonSet CrashLoop | 内核版本与驱动不兼容 | 检查 NVIDIA 兼容性矩阵 |
| Container Toolkit 启动失败 | containerd 配置冲突 | 检查 /etc/containerd/config.toml |
| MIG Manager 不响应 | GPU 固件版本过低 | 升级 GPU VBIOS |
Step 2:安装 NVIDIA DRA Driver
bash
# 安装 DRA Driver(独立于 GPU Operator 的 Helm Chart)
helm install --wait \n -n nvidia-dra-driver --create-namespace \n nvidia-dra-driver nvidia/nvidia-dra-driver \n --version v25.3.0 \n --set resources.gpus.enabled=true \n --set resources.computeDomains.enabled=true
验证 DeviceClass 注册:
bash
# 查看已注册的 DeviceClass
kubectl get deviceclasses.resource.k8s.io
# 预期输出:
# NAME AGE
# gpu.nvidia.com 5m
# 查看节点上的设备
kubectl get devices.resource.k8s.io -o wide
# 预期输出(每个物理 GPU 一行):
# NAME DRIVER NODE CLASS
# gpu-node-01-0 gpu.nvidia.com gpu-node-01 gpu.nvidia.com
# gpu-node-01-1 gpu.nvidia.com gpu-node-01 gpu.nvidia.com
# ...
Step 3:安装 KAI Scheduler
bash
# 安装 KAI Scheduler
helm install --wait \n -n kai-scheduler --create-namespace \n kai-scheduler nvidia/kai-scheduler \n --version v0.5.1 \n --set global.registry=nvcr.io/nvidia/kai \n --set queueController.enabled=true \n --set podGrouper.enabled=true
创建队列层级:
yaml
# queue-hierarchy.yaml
apiVersion: kai.scheduler/v1alpha1
kind: Queue
metadata:
name: team-ml
spec:
parent: root
resources:
gpu:
quota: 32
limit: 48
overQuotaWeight: 2
priority: 100
---
apiVersion: kai.scheduler/v1alpha1
kind: Queue
metadata:
name: project-training
spec:
parent: team-ml
resources:
gpu:
quota: 16
limit: 32
priority: 90
---
apiVersion: kai.scheduler/v1alpha1
kind: Queue
metadata:
name: project-inference
spec:
parent: team-ml
resources:
gpu:
quota: 16
limit: 16
priority: 80
Step 4:提交 Gang 调度训练任务
yaml
# training-job.yaml
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: h100-nvlink-claim
spec:
spec:
devices:
requests:
- name: gpus
deviceClassName: gpu.nvidia.com
count: 8
selectors:
- cel:
expression: >
device.attributes['gpu.nvidia.com'].productName
.startsWith('NVIDIA H100')
constraints:
- requests: ["gpus"]
matchAttribute: "gpu.nvidia.com/nvlinkDomain"
---
apiVersion: scheduling.run.ai/v1
kind: PodGroup
metadata:
name: llama-finetune
spec:
minMember: 8
queue: project-training
priorityClassName: train-high
---
apiVersion: batch/v1
kind: Job
metadata:
name: llama-finetune
spec:
parallelism: 8
completions: 8
template:
metadata:
labels:
pod-group-name: llama-finetune
spec:
schedulerName: kai-scheduler
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: trainer
image: myorg/llama-trainer:v2
resources:
limits:
cpu: "8"
memory: "64Gi"
claims:
- name: gpu
resourceClaims:
- name: gpu
resourceClaimTemplateName: h100-nvlink-claim
部署流程:
bash
kubectl apply -f queue-hierarchy.yaml
kubectl apply -f training-job.yaml
# 监控调度状态
kubectl get podgroup llama-finetune -w
kubectl get pods -l pod-group-name=llama-finetune
Step 5:配置 GPU 监控
yaml
# ServiceMonitor for DCGM Exporter
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: nvidia-dcgm-exporter
namespace: gpu-operator
spec:
selector:
matchLabels:
app: nvidia-dcgm-exporter
endpoints:
- port: metrics
interval: 15s
Grafana Dashboard 核心面板:
| 面板 | PromQL 查询 | 用途 |
|---|---|---|
| GPU 利用率 | DCGM_FI_DEV_GPU_UTIL{node=~"$node"} |
监控计算负载 |
| 显存使用 | DCGM_FI_DEV_FB_USED{node=~"$node"} |
排查 OOM 风险 |
| GPU 温度 | DCGM_FI_DEV_GPU_TEMP{node=~"$node"} |
散热监控 |
| NVLink 带宽 | rate(DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL[5m]) |
拓扑健康检查 |
| Pod 级 GPU | DCGM_FI_DEV_GPU_UTIL * on(pod) group_left() kube_pod_info |
Pod 维度利用率 |
生产避坑经验
坑 1:驱动版本漂移
多节点集群中,不同节点的 NVIDIA 驱动版本不一致是 CUDA 错误的首要原因。症状是:同一个 Container 镜像在 Node A 上正常运行,在 Node B 上报 CUDA driver version is insufficient。
解决方案 :由 GPU Operator 统一管理驱动版本,禁止手动安装。使用 nodeSelector 将对驱动版本敏感的工作负载锁定到特定节点池。
坑 2:MIG 重配置竞态
MIG 分区切换需要 GPU 重置,这会导致所有使用该 GPU 的 Pod 被终止。如果 MIG Manager 的 drain-reconfigure-uncordon 流程与 Deployment Controller 的快速重调度发生竞态,可能出现 Pod 被重新调度到正在重配置的 GPU 上的情况。
解决方案 :在维护窗口执行 MIG 重配置。使用 nvidia.com/mig.config.state.pending annotation 触发 GPU Operator 的自动化流程,并确保节点 cordon 在 drain 完成前生效。
坑 3:KAI 队列饥饿
在多团队共享集群中,如果高优先级队列持续借用低优先级队列的 burst 容量,低优先级队列的任务可能长时间无法调度。
解决方案 :合理设置 quota(保证配额)和 limit(上限配额),确保每个团队有足够的保证资源。使用 overQuotaWeight 控制借用权重,避免单一队列垄断空闲资源。
坑 4:ResourceClaim CEL 表达式静默失败
DRA 的 CEL 表达式在属性名拼写错误时不会报错,而是返回空结果,导致 ResourceClaim 无法匹配到任何设备,Pod 永久 Pending。
解决方案 :在编写 CEL 表达式前,先用 kubectl get devices.resource.k8s.io -o yaml 查看完整的设备属性列表,确认属性名拼写正确。
全文总结
2026 年的 Kubernetes GPU 调度已从"不透明的整数资源"演进为"声明式的设备管理"。核心架构变化是三层分离:
- 设备抽象层 (DRA):ResourceClaim + DeviceClass + CEL 过滤,替代 Device Plugin 的
nvidia.com/gpu: 1模型,支持拓扑约束、参数化选择、设备共享 - 工作负载调度层(KAI):Gang 调度 + 层级队列 + 优先级抢占,解决分布式训练的原子调度需求,与 DRA 深度集成
- 硬件管理层(GPU Operator + MIG/MPS/Time-slicing):驱动自动化 + 分区管理 + 监控导出,将 GPU 节点的运维复杂度降至最低
生产级 GPU 集群的关键实践:
- DRA 替代 Device Plugin:Kubernetes 1.34+ 集群应优先采用 DRA,获得拓扑感知和声明式设备管理能力
- MIG 用于生产推理:将大 GPU 切分为多个隔离实例,推理成本可降低 60-70%
- KAI Gang 调度用于分布式训练:消除部分调度浪费,GPU 利用率从 60% 提升至 85%
- GPU Operator 统一管理:驱动版本、容器工具、MIG 配置、监控导出一站式管理
- DCGM + Prometheus + Grafana 构建可观测性:Pod 维度的 GPU 指标是性能调优和故障排查的基础
参考资料
- Kubernetes Device Plugins 官方文档
- Kubernetes Dynamic Resource Allocation 官方文档
- NVIDIA GPU Operator 官方文档
- NVIDIA DRA Driver GitHub
- KAI Scheduler GitHub
- NVIDIA MIG 用户指南
- NVIDIA DCGM 文档
- Kubernetes GPU Scheduling: DRA, KAI, MIG (2026)
- Kubernetes GPU Orchestration in 2026
本期专栏更新说明
本文为《AI 工程与安全深度实战》订阅专栏持续迭代内容,专栏按初/中/高阶递进规划,长期更新 AI 云原生架构、GPU 算力工程、LLMOps 运维智能化、模型安全攻防、供应链安全、安全治理与合规实践,一次订阅,永久持续更新。