
Kubernetes DRA:GPU 调度从“数卡”走向“选卡”
Kubernetes 官方文档已经将 Dynamic Resource Allocation(DRA,动态资源分配)标记为 Kubernetes v1.35 Stable,并默认启用,核心 API 也稳定为 resource.k8s.io/v1。
这件事对 AI 基础设施的意义,不是让申请 GPU 的 YAML 变短,而是让 Kubernetes 对异构设备的理解从:
这个节点还有几张卡?
进一步走向:
哪张卡符合任务要求?它在哪个节点可访问?分配前需要怎样准备?任务结束后如何释放?
传统 Device Plugin 并没有因此失效。对设备型号统一、只关心数量的集群,它仍然简单而成熟。DRA 真正补上的,是型号、显存、拓扑、共享和设备配置逐渐复杂之后,标准调度模型缺失的那一层。
Device Plugin 解决了“能用卡”,但主要表达数量
Kubernetes 管理 GPU,长期以来主要依赖厂商 Device Plugin。典型 Pod 会这样申请设备:
apiVersion: v1
kind: Pod
metadata:
name: cuda-worker
spec:
containers:
- name: worker
image: nvidia/cuda:12.8.0-base-ubuntu22.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
它表达得很清楚:给这个容器一张 NVIDIA GPU。
Device Plugin 在节点侧发现设备,通过 kubelet 报告 nvidia.com/gpu 这样的扩展资源,并在容器启动前完成设备文件、环境变量等注入。对于硬件同质、任务只关心卡数的资源池,这套机制已经足够稳定。
问题在于,扩展资源主要是一个整数计数模型。只写 nvidia.com/gpu: 1,很难在标准资源请求中直接表达:
- 必须是 A100、H100,还是任意型号都可以;
- 显存至少需要多少;
- 两张卡是否需要处于合适的互联或 NUMA 拓扑;
- 请求的是整卡、切分实例,还是可共享设备;
- 首选高端卡,资源不足时能否退到另一种设备;
- 分配后是否需要执行厂商特定的准备动作。
实践中当然可以组合节点标签、亲和性、污点、准入 Webhook、不同扩展资源名和厂商调度插件。但硬件类型越多,选择逻辑越容易散落在多个组件里。

数量够,不等于设备合适。 DRA 要解决的正是从“资源计数”到“具体设备选择”的表达问题。
DRA 的核心:把设备变成可描述、可筛选的对象
DRA 不再只让工作负载提交一个设备计数,而是把设备需求抽象为声明,让调度器结合驱动发布的设备信息,同时决定具体设备和可访问它的节点。
理解 DRA,先抓住四个对象。
DeviceClass:平台提供什么类型的设备
DeviceClass 由集群管理员或设备驱动定义,表示一类可申请设备及其公共选择规则。
它有点像存储中的 StorageClass:应用通常不必知道某张物理卡位于哪台机器,只需要引用平台提供的类别,例如“训练型 GPU”或“高速网络设备”。
ResourceClaim:工作负载具体需要什么
ResourceClaim 是一份设备资源声明。它可以表达设备类别、数量和筛选条件;Pod 再引用这份声明。
如果多个 Pod 需要共享同一个已知 Claim,可以直接引用它;若每个 Pod 都需要独立但配置相似的设备,则可以用 ResourceClaimTemplate 让 Kubernetes 为 Pod 生成对应 Claim。
ResourceSlice:集群里实际有哪些设备
ResourceSlice 通常由 DRA 驱动创建和维护,用来发布设备清单、属性以及节点可访问范围。
应用开发者一般不会手工维护它。它是设备驱动告诉 Kubernetes“现在有哪些设备可供选择”的标准接口。
调度器:同时匹配设备与节点
调度器根据 Claim 的条件,在 ResourceSlice 发布的候选设备中完成匹配,并把 Pod 放到能够访问被分配设备的节点。
这与“先选一个还有 GPU 计数的节点,再由节点侧决定具体设备”不同:设备选择和节点选择被纳入同一个调度决策。

一个简化的 DRA 例子
下面只展示核心关系。假设集群已经安装 gpu.example.com DRA 驱动,并且该驱动会发布 GPU 的型号属性。示例中的驱动名、属性名和型号都是占位值,实际配置必须以厂商驱动文档为准。
平台先定义一个设备类别:
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: training-gpu
spec:
selectors:
- cel:
expression: >-
device.driver == 'gpu.example.com' &&
device.attributes['gpu.example.com'].type == 'gpu'
工作负载再创建一份声明,请求一张指定型号的训练卡:
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: model-training-gpu
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: training-gpu
count: 1
selectors:
- cel:
expression: >-
device.attributes['gpu.example.com'].model == 'ExampleGPU-X100'
---
apiVersion: v1
kind: Pod
metadata:
name: model-training
spec:
resourceClaims:
- name: accelerator
resourceClaimName: model-training-gpu
containers:
- name: trainer
image: registry.example.com/ml/trainer:1.0
resources:
claims:
- name: accelerator
这不是一份可以直接复制到任意 GPU 集群的部署文件。DRA 只规定统一框架,具体设备属性及准备方式仍由驱动定义。
真正的变化也不是“用更多 YAML 替换 nvidia.com/gpu: 1”,而是工作负载现在提交了一份带条件的设备声明。调度器要找到的不再是任意一张可用卡,而是满足声明的具体设备。
一次 DRA 分配如何发生
完整过程可以压缩成五步:
- 发现设备:节点上的 DRA 驱动发现 GPU 或其他硬件;
- 发布信息:驱动通过 ResourceSlice 发布设备属性与节点可访问范围;
- 提交声明:工作负载创建 ResourceClaim,或通过 ResourceClaimTemplate 生成 Claim;
- 联合调度:调度器匹配具体设备,并选择能够访问它的节点;
- 准备与回收:Pod 到达节点后,节点侧 DRA 驱动准备设备;使用结束后取消准备,并按 Claim 生命周期释放分配。
设备准备可能涉及设备配置、挂载、运行时参数注入等动作。Kubernetes 负责协调声明、调度和生命周期,驱动负责理解具体硬件。

需要注意:Unprepare 取消的是节点侧准备状态,不等于删除 DeviceClass 或 ResourceSlice;资源何时完全释放,还取决于 Claim 的生命周期和使用关系。
DRA 对 GPU 调度真正改变了什么
从“有一张卡”变成“有一张合适的卡”
混合 GPU 集群中,不同代际、显存和性能特征的设备可能共存。如果全部暴露成同一种扩展资源,调度结果也许满足数量,却不一定满足任务的容量和性能要求。
DRA 允许驱动发布结构化属性,允许 Claim 提交筛选条件,为精细选卡提供了标准入口。
让设备与节点进入同一个决策
多卡训练不仅需要卡数,还可能关心 GPU 之间、GPU 与 CPU 之间的拓扑。DRA 为“选择具体设备,并把 Pod 放到能够访问它的节点”提供了统一基础。
但能表达多复杂的拓扑,仍取决于 Kubernetes 对应能力和厂商驱动实现,不能因为 API 稳定就假定所有拓扑问题已经解决。
为共享、切分和可配置设备提供更合理的抽象
GPU 未必始终是一张物理卡对应一个工作负载。切分实例、共享容量和分配前配置,都比整数计数复杂。
DRA 将“申请什么”与“驱动如何准备”分开,使这些能力不必全部压缩进固定扩展资源名称。厂商可以表达自己的设备模型,Kubernetes 则提供声明、调度和生命周期框架。
支持按优先级声明备选设备
DRA 的设备请求可以提供按顺序尝试的候选方案。例如首选某种高性能设备,无法分配时再申请另一类设备或不同数量的设备。
这让“首选方案 + 可接受的降级方案”有机会进入标准声明,而不必全部由外部控制器改写 Pod。具体可用字段仍应以目标 Kubernetes 版本与驱动支持为准。
Stable、默认启用,不等于升级后自动生效
Kubernetes v1.35 中 DRA 进入 Stable,意味着核心 API 获得稳定性承诺,集群组件默认具备对应框架能力。
它不意味着:
- 升级控制平面后,GPU 会自动改用 DRA 发布;
- 现有
nvidia.com/gpu请求会自动转换为 ResourceClaim; - 所有 GPU 厂商、设备型号和驱动版本都已支持 DRA;
- Device Plugin 会立即退出;
- 型号、显存、拓扑和共享能力在不同驱动中完全一致;
- 原有监控、配额、计费和故障排查体系无需调整。
“默认启用”描述的是 Kubernetes 框架能力。DRA 真正落地,仍需要匹配的厂商驱动、节点组件、资源类别、权限、策略和可观测性。
DRA 不会立刻取代 Device Plugin
Device Plugin 生态成熟、模型简单。对于同型号 GPU 集群,以及只需要整数设备的工作负载,继续使用扩展资源完全合理。
DRA 更适合这些需求已经明确的场景:
- 一个集群存在多种 GPU 型号或显存规格;
- 任务需要按设备属性选择硬件;
- 多卡任务对互联或 NUMA 拓扑敏感;
- 平台要管理切分、共享或可配置设备;
- 希望把散落在标签、Webhook 和自定义控制器中的选卡逻辑迁移到标准 API;
- 需要用统一方式管理 GPU、FPGA、网卡等不同设备。
更现实的演进路径是两套机制并存:简单工作负载继续使用 Device Plugin,需要精细设备选择的工作负载逐步采用 DRA。如何并存、是否会重复管理同一设备,必须遵循厂商部署说明。
生产落地前,至少检查这些问题
版本与驱动
- 控制平面、kubelet、容器运行时是否满足目标驱动要求;
- 是否使用稳定的
resource.k8s.io/v1API; - 厂商 DRA 驱动支持哪些 GPU、固件和内核模块;
- 驱动发布哪些属性,它们的名称、类型和语义是什么;
- DRA 与传统 Device Plugin 能否并存,如何避免重复管理。
调度与生命周期
- 单卡、多卡、混合型号节点能否得到预期分配;
- 设备不足时,Pod 的 Pending 原因是否清晰;
- CPU、内存、存储、节点亲和性与设备 Claim 同时存在时是否正确调度;
- Pod 删除、Job 重试、节点排空或驱动重启后,设备能否正确释放;
- ResourceClaim 的创建者和清理责任是否明确。
治理与可观测性
- DeviceClass 的命名、用途和维护者是否明确;
- 用户应直接创建 Claim,还是主要使用平台模板;
- RBAC、准入策略、命名空间隔离和配额如何设计;
- 能否从 Pod 追踪到 Claim、节点和具体设备;
- 是否监控 ResourceSlice 更新、声明长期 Pending、设备准备失败和驱动异常;
- 原有容量报表、利用率、成本核算和审计是否需要调整。
最后的判断
DRA 稳定化的价值,不在于让 GPU Pod 的配置更短。它真正解决的是:当设备不再同质、不可分割、只需计数时,Kubernetes 需要一种标准方式描述设备、提出需求,并让调度器同时选择设备和节点。
如果集群只有少量同型号 GPU,任务只关心卡数,Device Plugin 依然简单有效。
如果硬件异构程度越来越高,任务开始关心型号、显存、拓扑、切分或共享,DRA 就提供了比扩展资源整数更合适的基础模型。
所以,Kubernetes v1.35 中 DRA 进入 Stable 是一个重要节点,但不是迁移完成的标志。接下来真正决定生产价值的,是厂商驱动成熟度、调度验证、运维适配和平台治理。
GPU 调度正在从“数卡”走向“选卡”,但走完这一步,不能只升级 Kubernetes。
参考资料
Kubernetes DRA:GPU 调度从“数卡”走向“选卡”
AI 时代,CI/CD 不是可选项