DRA:Kubernetes 设备资源分配的下一代 API
DRA 的核心价值不是“替代 Device Plugin”,而是把设备分配从节点本地的 Allocate 黑盒推进到控制面可见、可审计、可组合的资源 API。这个转变对 GPU 调度体系的影响是结构性的。
在前面 Kubernetes 设备资源模型中已经看到,Device Plugin 的 Advertise→Bind→Allocate 模型把设备分配的全部决策权下沉到了 kubelet 本地,调度器只知道“这个节点有 N 块设备”,不知道设备的具体属性、参数和分配策略。DRA(Dynamic Resource Allocation,动态资源分配)正是为解决这一结构性缺陷而生的,它已在 Kubernetes v1.35 进入 GA(Stable)阶段。
本文不重复 DRA 的快速入门,而是聚焦三个层面:Device Plugin 的根本局限在哪里、DRA 的 API 设计做了哪些取舍、以及它对 GPU 调度体系的长远影响。
Device Plugin 的三个结构性问题
Device Plugin 的设计初衷是“让厂商能接入异构设备”,这个目标它完成得很好。但随着 GPU 平台化程度加深,三个结构性问题逐渐暴露。
参数化请求无法表达。 Pod spec 里 nvidia.com/gpu: 1 只能声明“我要一块 GPU”,无法表达“我要一块带 40GB 显存的 H100,启用 MIG profile 4g.40gb”。调度器在选节点时,只能判断“节点上有没有至少一块 GPU”,不能判断“有没有满足我具体参数的 GPU”。所有参数化逻辑都被推迟到 kubelet 的 Allocate 阶段,这意味着调度器可能把 Pod 放到一个设备参数不匹配的节点上,分配失败后 Pod 才会 CrashLoopBackOff。
分配过程对控制面不透明。 Device Plugin 的 Allocate 调用发生在 kubelet 本地,不走 API Server。调度器、准入控制器、审计日志都看不到“具体哪块设备分配给了哪个 Pod”。这对多租户平台来说是治理盲区——你无法在控制面回答“这台 H100 上的 4 个 Pod 分别占了哪几个 MIG 实例”这类问题,只能 SSH 到节点上查。
设备生命周期与 Pod 耦合。 Device Plugin 没有独立的“设备分配”对象。分配随着 Pod 创建而发生,随着 Pod 删除而释放。如果你需要预先分配设备(比如在训练任务启动前先锁住 GPU)、或者让多个 Pod 共享同一块设备的特定资源份额,Device Plugin 的模型不直接支持,只能靠厂商自行实现私有方案。
这三个问题的根源是同一个:Device Plugin 把“设备选择与分配”做成了节点侧的本地实现,而不是控制面的一等资源对象。
DRA 的核心设计:四个 API 对象
DRA 引入了四个 resource.k8s.io/v1 API 组下的核心对象,各自承担不同的职责。
ResourceSlice:设备信息的发布通道。 DRA 驱动在集群中创建和管理 ResourceSlice,向调度器暴露节点上的设备信息——包括设备属性(型号、显存、拓扑)、可用容量以及哪些节点可以访问这些设备。调度器通过扫描 ResourceSlice 来回答“哪个节点有满足条件的设备”这个问题。ResourceSlice 替代了 Device Plugin 中“通过 gRPC 向 kubelet 广告设备”的那一步,把设备信息从节点本地提升到了集群可见的 API 对象。
DeviceClass:管理员定义的设备类别。 DeviceClass 类似 StorageClass 的设计思路——管理员或驱动定义“一类设备的分配策略”,包括匹配哪些设备、使用哪个驱动、分配模式(独占还是共享)等。Pod 不直接指定“我要某某型号的 GPU”,而是引用一个 DeviceClass,由 DeviceClass 的规则来决定具体匹配哪些设备。这把“设备选择策略”从 Pod spec 中解耦出来,集中到了集群治理层面。
ResourceClaim:设备分配的一等对象。 这是 DRA 与 Device Plugin 最根本的区别。ResourceClaim 是一个标准的 Kubernetes 资源对象,代表“对特定设备的分配请求”。当调度器找到一个匹配的设备时,它会更新 ResourceClaim 的 status 字段,记录分配结果。整个分配过程经过 API Server,可以被审计、被观测、被其他控制器引用。ResourceClaim 支持两种使用方式:直接创建让多个 Pod 共享同一份设备分配,或者通过 ResourceClaimTemplate 为每个 Pod 自动生成独立的 Claim。
ResourceClaimTemplate:自动化生成的模板。 当每个 Pod 需要独立的设备分配时(这是大多数情况),用 ResourceClaimTemplate 可以自动为每个 Pod 生成 ResourceClaim,其生命周期与 Pod 绑定。Pod 删除时自动清理对应的 Claim,避免资源泄漏。
这四个对象的设计意图很清晰:把设备分配从"节点本地的 gRPC 调用"提升到"控制面的标准 API 对象"。Device Plugin 的 Allocate 黑盒变成了可审计的 ResourceClaim 状态变更。
调度流程对比:Device Plugin vs DRA
理解两个模型的差异,最直接的方式是对比调度流程。
Device Plugin 的流程是三步闭环:驱动通过 gRPC 向 kubelet 广告设备(Advertise),调度器根据整数计数做节点选择(Bind),kubelet 调用驱动的 Allocate 完成交付。调度器在 Bind 阶段只能看到“节点上有 N 块设备”这个标量信息,具体分配决策完全在 Allocate 阶段才发生。
DRA 的流程在此基础上增加了两个关键步骤。驱动先通过 ResourceSlice 发布设备信息(包括属性和容量),调度器在选节点时就可以基于设备属性做过滤——比如“这个节点的 GPU 显存够不够”、“是不是 H100”、“MIG profile 是否匹配”。找到匹配节点后,调度器更新 ResourceClaim 的 allocation 状态,再完成 Pod 绑定。分配结果通过 API Server 持久化,而不是在 kubelet 本地一闪而过。
这个差异看起来只是多走了几步 API,但对平台治理的影响是根本性的。调度器第一次有了设备属性层面的决策能力,而不是只能"赌 Allocate 阶段不会失败"。分配结果第一次变成了可审计的 API 对象,而不是节点本地的瞬时状态。
| 维度 | Device Plugin | DRA |
|---|---|---|
| 调度器能看到的设备信息 | 整数计数(nvidia.com/gpu: 8) | 多维属性(显存、型号、拓扑、容量) |
| 分配可见性 | kubelet 本地 gRPC,不可审计 | 经 API Server,ResourceClaim 可审计 |
| 设备生命周期 | 与 Pod 耦合,Pod 删除即释放 | ResourceClaim 独立,可预先分配和共享 |
| 参数化能力 | 无,只能声明整数数量 | CEL 表达式过滤设备属性 |
| 设备共享 | 厂商私有实现 | ResourceClaim 天然支持多 Pod 引用 |
| 设计类比 | 类似静态 PV | 类似 StorageClass + PVC 的声明式模型 |
架构演变:从 Classic DRA 到 Structured Parameters
DRA 的架构本身也经历了一次重要的角色反转,这一点直接影响你理解 DRA 的方式。
最初 KEP-3063 定义了“Classic DRA”,引入了控制面控制器和 ResourceClaim/DeviceClass 的基本框架。后来 KEP-4381 引入了 Structured Parameters(结构化参数),允许用 CEL 表达式对设备属性做精细过滤。随着 Structured Parameters 的成熟,两个 KEP 的角色发生了反转:KEP-4381 现在定义基础功能,KEP-3063 变成了扩展。也就是说,结构化调度是 DRA 的主流路径,Classic DRA 的控制面控制器反而成了可选的向后兼容层。
这个反转的含义是:DRA 的设计重心从“让 ResourceClaim 在控制面流转”变成了“让调度器基于结构化参数做更智能的设备选择”。CEL 表达式可以表达“我要显存大于 40GB 的 GPU”、“我要支持 NVLink 的双卡配置”这类条件,调度器在选节点时就能精确匹配,而不是事后补救。
DRA 没有解决什么
DRA 解决的是“设备分配的可表达性和可观测性”,但有几件明确不做的事情。
DRA 不定义隔离机制。 MIG 的硬件级隔离、vGPU 的虚拟化切分、时间片复用——这些仍然是数据平面的责任。DRA 让你更容易声明“我要一个 MIG 4g.40gb 的实例”,但隔离本身仍然由底层硬件和驱动提供。在前面的 数据平面谱系一章中已经讨论过,隔离强度和资源单位是数据平面的核心交付,DRA 只是把“请求这些能力”的过程标准化了。
DRA 不替代调度策略。 队列管理、优先级抢占、配额控制——这些仍然是 Volcano/Kueue 的领地。DRA 和 Kueue 有天然的合作点(Kueue 可以基于 ResourceClaim 做队列感知的设备分配),但 DRA 本身不包含调度策略语义。
DRA 不支持抢占。 截至 Kubernetes v1.35,DRA 资源不支持 Pod 预占。一个低优先级 Pod 占用的设备不会被高优先级 Pod 抢走,高优先级 Pod 只能等待设备释放。这对严格 SLA 场景是一个需要注意的限制。
与 Device Plugin 的过渡期共存。 DRA 和 Device Plugin 在同一个集群中可以共存。同一个节点上的设备可以一部分通过 Device Plugin 暴露(作为 Extended Resource),另一部分通过 DRA 的 ResourceSlice 暴露。但这种共存增加了运维复杂度,平台需要决定迁移节奏和边界。
对 GPU 调度体系的长远影响
DRA GA 之后,它对 GPU 基础设施的影响主要体现在三个方向。
参数化调度成为可能。 这是 DRA 最直接的价值。在 Device Plugin 时代,调度器选节点的依据是“这个节点有几块 GPU”;DRA 时代,调度器可以基于显存容量、MIG profile、互联拓扑、设备健康状态等多维属性做选择。结合 决策轴一章的框架,DRA 直接强化了“粒度”和“兼容性”两个维度的表达能力。
设备共享有了标准的 API 层。 DRA 的 ResourceClaim 天然支持设备共享——多个 Pod 可以引用同一个 ResourceClaim。结合 v1.34 引入的 Consumable Capacity(可消费容量)特性,驱动可以声明设备的可分配容量(比如一块 80GB 的 GPU 可以被多个 Claim 按需切分),调度器负责确保所有 Claim 的容量之和不超过总量。这为 GPU 共享提供了一条标准化的路径,而不依赖各家厂商的私有实现。
异构芯片的统一接入层。 GPU、TPU、NPU、DPU 的接入在 Device Plugin 时代各自为政——每个厂商实现自己的 plugin,API 语义不统一。DRA 通过 ResourceSlice + DeviceClass + CEL 表达式提供了一套统一的设备描述和匹配框架。异构芯片的厂商只需要实现 DRA 驱动、发布 ResourceSlice,就可以接入 Kubernetes 的标准调度流程。这对前面 异构加速器全景一章讨论的多芯片协同架构是一个重要的基础设施支撑。
从 Device Plugin 迁移到 DRA
对于已经在生产环境使用 Device Plugin 的团队,迁移不需要一步到位。
短期(当前):DRA 和 Device Plugin 可以共存。可以在部分节点上启用 DRA 驱动做灰度验证,同时保持 Device Plugin 在其余节点上运行。
中期(验证期):选择合适的场景先迁移。参数化需求强的场景(MIG profile 选择、显存容量匹配、异构芯片混合部署)是 DRA 最能体现价值的切入点。纯整卡独占的场景,Device Plugin 仍然够用。
长期(全面迁移):当 DRA 驱动的成熟度和生态覆盖度足够时,Device Plugin 会逐步退化为向后兼容层。各主流 GPU 厂商已经在推进 DRA 驱动的实现(NVIDIA 的 CDI-based DRA driver 已进入 beta 阶段,AMD 的 ROCm DRA driver 在 alpha 阶段),生态追赶速度取决于厂商投入和社区 adoption。
迁移的核心判断标准很简单:你是否需要在调度阶段表达“我要什么样的设备”,而不仅仅是“我要几块设备”。如果答案是“是”,DRA 就值得认真评估。
总结
DRA 的本质是把 Kubernetes 的设备资源分配从“节点本地黑盒”推进到“控制面可见的一等 API”。它解决了 Device Plugin 的三个结构性问题——参数化请求、分配透明度、生命周期解耦——同时明确地把隔离机制和调度策略留给数据平面和控制面各自处理。对 GPU 平台来说,DRA 不是锦上添花,而是让“异构、参数化、可治理”的设备调度从可能变为可行的基础设施前提。
参考文献
- Dynamic Resource Allocation - kubernetes.io
- KEP-3063: Dynamic Resource Allocation - github.com
- KEP-4381: DRA Structured Parameters - github.com
- Kubernetes v1.34: DRA Consumable Capacity - kubernetes.io
- Delve into DRA, devices, and drivers on Kubernetes - Azure AKS Blog
- Kubernetes Device Management with DRA - Google Cloud Blog