DRA 与 Device Plugin
约 6680 字大约 22 分钟
2026-05-22
Kubernetes 管理 GPU、FPGA、RDMA 网卡、智能网卡、MIG 实例、vGPU 等特殊硬件资源时,历史上主要依赖 Device Plugin。Device Plugin 把硬件抽象成一个个扩展资源,例如 nvidia.com/gpu: 1,让调度器像处理 CPU、Memory 一样按数量调度。
DRA 是 Dynamic Resource Allocation,动态资源分配。它把“我要什么设备”从一个简单的整数资源名,升级成一组 Kubernetes 原生 API 对象:DeviceClass、ResourceSlice、ResourceClaim、ResourceClaimTemplate。DRA 的目标不是让 Pod 运行中随时热插拔 GPU,而是在调度和启动 Pod 之前,用更结构化的方式描述、选择、分配和准备设备。
一句话总结:
- Device Plugin:简单、成熟、生态广,适合按整数申请设备,例如申请 1 张 GPU。
- DRA:表达力更强,适合按型号、显存、拓扑、分区、共享能力等条件选择设备,并把设备分配过程暴露成 Kubernetes API。
背景
Kubernetes 原生资源模型最早主要面向 CPU、Memory、Storage 这类通用资源。GPU 和高速网卡这类硬件有几个特殊点:
- 需要厂商驱动、runtime、设备文件、环境变量、mount、CDI 配置等初始化逻辑。
- 资源通常不能像 CPU 那样天然超卖,很多场景只能整数分配。
- 一个节点上可能有多块型号不同的设备,或者同一块设备被切成多个 MIG / vGPU / VF。
- 设备有健康状态、NUMA 拓扑、PCIe 拓扑、显存容量、带宽、驱动版本等属性。
- AI 训练和推理场景希望表达“要 A100 80G”、“要同一 NUMA 下的 GPU 和 RDMA 网卡”、“优先 H100,不行就 A100”、“允许多容器共享同一资源”等需求。
Device Plugin 解决了“如何让 Kubernetes 看到并分配这类硬件”的第一阶段问题。DRA 解决的是第二阶段问题:如何让 Kubernetes 以原生、结构化、可调度的方式理解设备能力和分配状态。
历史
Device Plugin 的历史
Device Plugin 诞生的核心背景是:Kubernetes 不可能为每种硬件都内置厂商逻辑。GPU、FPGA、RDMA、QAT、DPU、NPU 都有不同的驱动和初始化方式,如果每种硬件都改 kubelet 或 scheduler,Kubernetes 核心会很难维护。
Device Plugin 的演进大致如下:
| 时间 | 阶段 | 说明 |
|---|---|---|
| Kubernetes v1.8 | Alpha | Device Plugin framework 引入,用厂商无关的方式发现、上报、分配外部设备。 |
| Kubernetes v1.10 | Beta | Device Plugin 功能进入 Beta,逐步成为 GPU 等特殊硬件的主流接入方式。 |
| Kubernetes v1.26 | Stable | kubelet 内部的 Device Manager 进入 GA;但 Device Plugin API 本身仍然不是 stable API,当前仍需关注 API 版本兼容。 |
Device Plugin 让厂商不需要修改 Kubernetes 核心代码,只需要在节点上运行一个插件进程。插件通过 kubelet 暴露的 gRPC 注册接口把资源名、设备列表和健康状态告诉 kubelet,kubelet 再把这些资源作为 Node 的 status.capacity / status.allocatable 上报给 API Server。
典型资源名是扩展资源名:
nvidia.com/gpu
amd.com/gpu
intel.com/qat
rdma/shared_hca
example.com/fooDevice Plugin 的成功点在于足够简单:
- 应用侧只需要在 Pod 里写
resources.limits。 - 调度器只需要判断某个节点有没有足够数量的扩展资源。
- kubelet 在创建容器时调用插件的
Allocate接口,让插件返回设备文件、环境变量、mount、CDI 设备名等运行时配置。
它的局限也来自这个简单模型:
- 调度器主要看到的是
resourceName -> integer count,对设备属性理解有限。 - 资源只能以整数形式暴露,不能直接表达显存、带宽、分区容量这类细粒度需求。
- Kubernetes 核心不支持通过 Device Plugin 在多个 Pod / 容器之间共享同一个设备;厂商可以通过虚拟资源、MIG、time-slicing 等方式绕开,但语义不在 Kubernetes 核心 API 中。
- 设备选择通常依赖节点标签、annotation、插件私有配置或调度扩展,不是统一的 Pod API。
- 对“某个具体设备”的分配结果、健康状态、共享关系、分区关系,历史上很难通过 Kubernetes 标准对象清晰表达。
DRA 的历史
DRA 的设计目标是把设备资源分配变成 Kubernetes 原生 API,而不只是 kubelet 本地插件协议。
大致演进如下:
| 时间 | 阶段 | 说明 |
|---|---|---|
| Kubernetes v1.26 | Alpha | 引入 DRA alpha API。最早版本更像 PVC/PV 模型的泛化,用于请求和共享通用资源。 |
| Kubernetes v1.30 | 重构方向 | 引入 structured parameters,资源请求从厂商私有参数逐步转向调度器可理解的结构化设备属性。 |
| Kubernetes v1.32 | Beta | 新 DRA 模型进入 Beta,但默认关闭;v1.26 到 v1.31 的 classic DRA 已不再是后续方向。 |
| Kubernetes v1.34+ | Stable / 默认启用 | DRA 核心进入 GA 并默认启用,稳定 API 使用 resource.k8s.io/v1;当前官方任务文档通常要求集群版本不低于 v1.34。 |
| Kubernetes v1.36 | 能力扩展 | 优先级候选请求进入 stable;扩展资源兼容、可分区设备、设备 taint、绑定条件、资源健康等能力继续成熟。 |
DRA 的关键变化是:资源不再只是 Node 上的一个整数,而是由 driver 发布的 ResourceSlice 描述设备池,由管理员或 driver 定义 DeviceClass,应用通过 ResourceClaim 声明自己需要什么设备。
这使 scheduler 可以在调度阶段直接参与设备选择,而不是先选节点、再让 kubelet 在本地挑设备。
Device Plugin 工作原理
组件关系
Device Plugin 的核心组件在节点本地:
Device Plugin DaemonSet
|
| gRPC over Unix socket
v
kubelet Device Manager
|
| Node status: capacity / allocatable
v
API Server
|
| scheduler sees extended resources
v
kube-scheduler插件通常以 DaemonSet 运行在有设备的节点上,也可以通过操作系统包或手工方式运行。因为它需要访问 kubelet 的 device plugin 目录和宿主机设备,一般需要 privileged 权限。
关键目录:
/var/lib/kubelet/device-plugins/
/var/lib/kubelet/device-plugins/kubelet.sockkubelet.sock 是 kubelet 的注册 socket。设备插件启动自己的 gRPC server 后,通过这个 socket 向 kubelet 注册。
注册流程
Device Plugin 启动后大致执行这些步骤:
- 初始化厂商设备 ,例如发现 GPU、检查驱动、读取健康状态。
- 在
/var/lib/kubelet/device-plugins/下创建自己的 Unix socket。 - 实现 Device Plugin gRPC API,核心方法包括:
ListAndWatch:持续上报设备列表和健康状态。Allocate:容器创建时返回设备文件、环境变量、mount、annotation、CDI 设备名等。GetPreferredAllocation:可选,向 kubelet 建议更合适的设备组合。PreStartContainer:可选,在容器启动前做设备重置等动作。
- 通过 kubelet 的
Registration服务注册资源名,例如nvidia.com/gpu。 - kubelet 把健康设备数量写入 Node 状态。
注册成功后,节点状态会出现类似资源:
status:
capacity:
nvidia.com/gpu: "8"
allocatable:
nvidia.com/gpu: "8"如果某个设备故障,插件通过 ListAndWatch 报告 unhealthy。kubelet 会减少 allocatable,防止新 Pod 继续调度到这块设备;已经绑定到故障设备的 Pod 通常不会自动迁移,只会在应用或容器运行时层面表现为错误。
应用如何使用 Device Plugin
应用只需要在容器资源里声明扩展资源。以 GPU 为例:
apiVersion: v1
kind: Pod
metadata:
name: gpu-demo
spec:
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:12.4.1-base-ubuntu22.04
command: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1注意点:
- GPU 等设备资源通常只写在
limits下。 - 扩展资源必须是整数,不能写
0.5。 - 扩展资源不能像 CPU 那样 overcommit。
- 调度器只按资源名和数量做过滤,具体哪张卡由 kubelet Device Manager 和插件在节点本地处理。
如果要按 GPU 型号调度,传统做法一般依赖节点标签,例如 NVIDIA GPU Feature Discovery / Node Feature Discovery 打出的标签:
apiVersion: v1
kind: Pod
metadata:
name: a100-demo
spec:
nodeSelector:
nvidia.com/gpu.product: NVIDIA-A100-SXM4-80GB
containers:
- name: app
image: nvidia/cuda:12.4.1-base-ubuntu22.04
command: ["sleep", "3600"]
resources:
limits:
nvidia.com/gpu: 1这个方式可用,但它本质上是“节点级过滤 + 设备数量申请”。如果一个节点上混有多种设备、多个 MIG profile、多个 NUMA/PCIe 拓扑,单纯节点标签很难精确表达“容器最终拿到哪一个设备”。
运维常用检查命令
查看节点是否已经上报设备资源:
kubectl describe node <node-name> | grep -A5 -E "Capacity|Allocatable|nvidia.com/gpu"查看节点的完整 allocatable:
kubectl get node <node-name> -o jsonpath='{.status.allocatable}' | jq查看插件 DaemonSet:
kubectl get ds -A | grep -i device
kubectl logs -n <namespace> ds/<device-plugin-daemonset>查看某个 Pod 是否因为资源不足无法调度:
kubectl describe pod <pod-name>
kubectl get events --sort-by=.lastTimestampDRA 工作原理
核心对象
DRA 使用 resource.k8s.io/v1 API 组,核心对象如下:
| 对象 | 谁创建 | 作用 |
|---|---|---|
ResourceSlice | DRA driver | 描述某个设备池里有哪些设备、在哪些节点、有什么属性和容量。 |
DeviceClass | 集群管理员或 driver | 定义一类可被申请的设备,以及如何筛选设备。类似“设备类别”。 |
ResourceClaim | workload operator 或控制器 | 声明某个工作负载要使用什么设备。可以被多个 Pod / 容器引用以共享同一资源。 |
ResourceClaimTemplate | workload operator | 模板对象,通常用于 Deployment / Job / StatefulSet,让每个 Pod 自动生成独立的 ResourceClaim。 |
可以把它类比成存储里的 PVC / StorageClass,但不要完全等同:
DeviceClass像StorageClass,定义可申请资源类别。ResourceClaim像PersistentVolumeClaim,表达工作负载的资源需求。ResourceSlice是 driver 发布给 scheduler 的设备库存和属性。
DRA 调度流程
典型流程如下:
DRA driver publishes ResourceSlices
|
v
Admin creates DeviceClasses
|
v
User creates ResourceClaim / ResourceClaimTemplate
|
v
Pod references claims in spec.resourceClaims
|
v
kube-scheduler allocates matching devices and chooses a node
|
v
kubelet calls DRA driver NodePrepareResources
|
v
container starts with prepared devices
|
v
Pod deletion -> NodeUnprepareResources -> cleanup和 Device Plugin 最大的区别在于:DRA 的资源选择发生在 Kubernetes 调度模型里。scheduler 能看到 ResourceSlice 里的设备属性和容量,可以把设备分配和节点选择一起考虑。
集群如何启用和验证 DRA
当前 Kubernetes 中 DRA 核心功能已经是默认启用的稳定能力,但仍需要注意版本:
- 建议 Kubernetes v1.34 或更高版本。
resource.k8s.io/v1是稳定 API。- 一些高级能力仍然是 beta 或 alpha,需要检查对应 feature gate 和 API group。
- 老 driver 可能还依赖
resource.k8s.io/v1beta1或resource.k8s.io/v1beta2。
验证集群是否支持 DRA:
kubectl get deviceclasses
kubectl api-resources --api-group=resource.k8s.io如果不支持,常见错误是:
error: the server doesn't have a resource type "deviceclasses"安装 DRA driver 后,检查 driver 是否发布了设备:
kubectl get resourceslices
kubectl get resourceslice <name> -o yamlDeviceClass 示例
DeviceClass 用于定义一类可申请设备。下面示例定义一个由 gpu.example.com driver 管理的 GPU 类:
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: gpu.example.com
spec:
selectors:
- cel:
expression: |-
device.driver == "gpu.example.com" &&
device.attributes["gpu.example.com"].type == "gpu"这里的 selectors 使用 CEL 表达式。实际可用字段取决于 driver 在 ResourceSlice 中发布了哪些 attributes 和 capacity。
管理员可以先查看 ResourceSlice:
kubectl get resourceslice <resourceslice-name> -o yaml一个简化后的 ResourceSlice 可能长这样:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: node-a-gpu-slice
spec:
driver: gpu.example.com
nodeName: node-a
pool:
name: node-a
generation: 1
resourceSliceCount: 1
devices:
- name: gpu-0
attributes:
gpu.example.com/type:
string: gpu
gpu.example.com/model:
string: A100
capacity:
gpu.example.com/memory:
value: 80Gi不同 driver 的 attributes 命名方式可能不同,应该以 driver 文档和实际 ResourceSlice 为准。
ResourceClaimTemplate 示例
如果希望每个 Pod 都拿到一份独立但配置相同的设备,用 ResourceClaimTemplate。
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: a100-claim-template
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.example.com
allocationMode: ExactCount
count: 1
selectors:
- cel:
expression: |-
device.attributes["gpu.example.com"].model == "A100" &&
device.capacity["gpu.example.com"].memory == quantity("80Gi")含义:
- 从
gpu.example.com这个DeviceClass里选设备。 - 要求精确申请 1 个设备。
- 设备属性必须是 A100。
- 设备容量必须满足 80Gi 显存。
Pod 引用 ResourceClaimTemplate
Pod 通过两个位置引用 claim:
spec.resourceClaims:把 claim 或 claim template 暴露给这个 Pod。containers[].resources.claims:某个容器实际使用哪个 claim。
示例:
apiVersion: batch/v1
kind: Job
metadata:
name: dra-gpu-job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: trainer
image: ubuntu:24.04
command: ["sleep", "3600"]
resources:
claims:
- name: gpu
resourceClaims:
- name: gpu
resourceClaimTemplateName: a100-claim-template当 Job 创建 Pod 时,Kubernetes 会根据 a100-claim-template 为每个 Pod 生成独立的 ResourceClaim。调度器会为 claim 选择匹配设备,并把 Pod 调度到能访问该设备的节点。
ResourceClaim 示例
如果希望多个容器或多个 Pod 共享同一个设备资源,可以手工创建 ResourceClaim,再让 Pod 引用它。
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: shared-gpu-claim
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.example.com
allocationMode: ExactCount
count: 1
selectors:
- cel:
expression: device.attributes["gpu.example.com"].model == "A100"Pod 引用这个 claim:
apiVersion: v1
kind: Pod
metadata:
name: two-container-shared-gpu
spec:
containers:
- name: worker-a
image: ubuntu:24.04
command: ["sleep", "3600"]
resources:
claims:
- name: shared-gpu
- name: worker-b
image: ubuntu:24.04
command: ["sleep", "3600"]
resources:
claims:
- name: shared-gpu
resourceClaims:
- name: shared-gpu
resourceClaimName: shared-gpu-claim这表达的是两个容器引用同一个 ResourceClaim。实际是否能安全共享,还取决于设备、driver、隔离机制和 workload 本身。
DRA 支持非整卡申请吗
结论是:DRA 的 API 支持非整卡建模和调度,但真正能不能申请半张卡、8Gi 显存或一个 GPU slice,取决于 DRA driver 和底层硬件 / runtime 是否实现了对应能力。
先排除一个常见误解:传统扩展资源不支持小数申请。下面这种写法不是 Kubernetes 支持的 GPU 申请方式:
resources:
limits:
nvidia.com/gpu: 0.5扩展资源仍然是整数语义。DRA 要解决的是另一件事:把“一个设备”从“物理整卡”扩展成 driver 定义的“逻辑设备”或“可消耗容量”。
方式一:申请一个逻辑设备
DRA 里的 count: 1 表示申请 1 个满足条件的设备,但这个设备不一定等于一张物理整卡。它可以是 DRA driver 暴露出来的逻辑设备,例如:
- 一个 MIG 实例。
- 一个 vGPU slice。
- 一个由 driver 预先切好的 GPU profile。
- 一个抽象的设备组合。
例如 driver 在 ResourceSlice 里发布了不同 MIG profile,用户可以通过 selector 申请某个 profile:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: mig-1g-claim-template
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.example.com
allocationMode: ExactCount
count: 1
selectors:
- cel:
expression: device.attributes["gpu.example.com"].profile == "1g.5gb"这个 YAML 仍然是 count: 1,但它申请的是 1 个 1g.5gb 逻辑设备,而不是 1 张完整物理 GPU。
方式二:Partitionable devices
partitionable devices 用来描述“多个逻辑设备共享同一个底层物理资源”的关系。典型场景是 GPU 被切成多个逻辑实例,这些实例会消耗同一张物理卡上的显存、算力或其他计数器。
DRA driver 可以在 ResourceSlice 中发布共享计数器和逻辑设备,让 scheduler 知道这些逻辑设备不能无限并发分配。例如底层只有 8Gi 显存,两个逻辑设备都要消耗 6Gi,那么 scheduler 最多只能分配其中一个。
简化示例:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-counterset
spec:
driver: gpu.example.com
nodeName: node-a
pool:
name: node-a
generation: 1
resourceSliceCount: 2
sharedCounters:
- name: gpu-0-counters
counters:
memory:
value: 8Gi
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-logical-devices
spec:
driver: gpu.example.com
nodeName: node-a
pool:
name: node-a
generation: 1
resourceSliceCount: 2
devices:
- name: gpu-0-slice-a
consumesCounters:
- counterSet: gpu-0-counters
counters:
memory:
value: 6Gi
- name: gpu-0-slice-b
consumesCounters:
- counterSet: gpu-0-counters
counters:
memory:
value: 6Gi这类能力让 Kubernetes 可以理解“逻辑设备之间存在底层资源冲突”,而不是只看到一堆互不相关的扩展资源整数。
方式三:Consumable capacity
consumable capacity 更接近“从同一个设备上按需扣减容量”。DRA driver 可以声明某个设备允许多次分配:
allowMultipleAllocations: true然后在设备上声明可消耗容量,例如显存或带宽:
capacity:
memory:
value: 40Gi用户在 ResourceClaim 或 ResourceClaimTemplate 里申请其中一部分:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-memory-claim-template
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.example.com
capacity:
requests:
memory: 8Gischeduler 会把这个 8Gi 从设备的总 capacity 中扣掉,并确保所有 claim 消耗的总量不超过设备总量。分配结果里也会记录实际消耗的 capacity 和 share ID。
但这里有一个非常重要的边界:DRA 只负责声明、调度、记账和把分配结果交给 driver;它本身不负责 GPU 显存隔离或 CUDA 算力隔离。
也就是说:
- scheduler 可以知道“这个 Pod 申请了 8Gi 显存”。
- driver 需要真的能把 8Gi 显存限制住。
- 底层硬件、runtime 或用户态组件需要提供隔离能力。
- 如果 driver 只会分配整卡,那么 DRA YAML 写得再细,最终也只能得到整卡或逻辑整设备。
所以更准确的判断方式是:
| 问题 | 答案 |
|---|---|
nvidia.com/gpu: 0.5 支持吗? | 不支持,传统扩展资源是整数。 |
DRA count: 1 一定等于 1 张物理卡吗? | 不一定,取决于 driver 暴露的是物理整卡还是逻辑设备。 |
| DRA 能申请 MIG / vGPU slice 吗? | 可以建模,前提是 DRA driver 暴露这些逻辑设备。 |
| DRA 能按 8Gi 显存这类容量申请吗? | 可以通过 consumable capacity 建模,前提是 driver 和 feature gate 支持。 |
| DRA 自己会做显存隔离吗? | 不会,隔离由 driver、硬件、runtime 或类似 HAMi 的组件实现。 |
DRA 的扩展资源兼容模式
DRA 也提供了和旧扩展资源模型兼容的迁移路径。管理员可以在 DeviceClass 上声明 extendedResourceName:
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: gpu.example.com
spec:
selectors:
- cel:
expression: device.driver == "gpu.example.com"
extendedResourceName: example.com/gpu之后应用仍然可以用旧写法:
resources:
limits:
example.com/gpu: 1scheduler 会把这个扩展资源请求映射到 DRA 设备分配。这个能力适合迁移:
- 平台侧逐步从 Device Plugin 切到 DRA driver。
- 应用侧暂时不改 YAML。
- 同一个集群可以逐步迁移节点或资源类型。
此外,DRA 还支持特殊前缀形式:
resources:
limits:
deviceclass.resource.kubernetes.io/gpu.example.com: 1这种写法表示“直接从某个 DeviceClass 申请指定数量设备”,Kubernetes 会生成对应的 DRA claim。
使用区别
最直观的 YAML 区别
Device Plugin:
resources:
limits:
nvidia.com/gpu: 1DRA:
resources:
claims:
- name: gpu
resourceClaims:
- name: gpu
resourceClaimTemplateName: a100-claim-templateDevice Plugin 把设备请求放在容器 resources.limits 里;DRA 把设备请求放进独立的 claim 对象,再由 Pod / 容器引用。
详细对比
| 维度 | Device Plugin | DRA |
|---|---|---|
| Kubernetes API 形态 | 扩展资源,表现为 vendor.com/resource: count。 | 原生 API 对象:DeviceClass、ResourceSlice、ResourceClaim、ResourceClaimTemplate。 |
| 用户声明方式 | 在容器 resources.limits 里写整数数量。 | 先声明 claim,再在 Pod / container 中引用 claim。 |
| 调度器看到的信息 | 主要是节点上某个扩展资源的可用整数数量。 | 可以看到设备类、设备属性、容量、claim 需求和 driver 发布的 slice。 |
| 设备筛选 | 通常靠节点标签、nodeSelector、affinity、插件私有机制。 | 使用 CEL 对设备属性和容量做结构化筛选。 |
| 资源单位 | 整数,不支持小数和原生容量语义。 | 可以表达设备、容量、共享、分区等更复杂语义,具体取决于 driver 和 feature gate。 |
| 共享能力 | Kubernetes 核心语义中不支持设备被多个容器共享;厂商可通过虚拟资源或私有机制实现。 | 可以通过多个容器 / Pod 引用同一个 ResourceClaim 表达共享;v1.36 中 consumable capacity 等能力进一步增强细粒度共享。 |
| 生命周期 | kubelet 在容器创建时调用 Allocate。 | scheduler 分配 claim,kubelet 调用 DRA driver prepare / unprepare。 |
| 设备分配结果 | 主要在 kubelet 本地和 PodResources API 中查看。 | ResourceClaim.status 能记录分配结果;ResourceSlice 和 claim 是集群级对象。 |
| 运维复杂度 | 低,生态成熟,NVIDIA / AMD / Intel / RDMA 等都有成熟插件。 | 更高,需要 Kubernetes 新版本和 DRA-compatible driver。 |
| 迁移成本 | 应用写法简单,存量广。 | 原生写法更复杂;可以用 DRA extended resource 兼容旧写法。 |
| 适合场景 | 整卡、静态 MIG、简单 GPU 调度、存量生产集群。 | 复杂设备选择、动态分区、细粒度共享、拓扑感知、多设备协同、未来 GPU 虚拟化平台。 |
调度语义区别
Device Plugin 下,scheduler 的判断类似:
Pod wants nvidia.com/gpu = 1
Node A allocatable nvidia.com/gpu = 4
Node A passes至于 Node A 上到底是哪张 GPU、显存多少、PCIe 拓扑如何、是否同一个 NUMA、是否已被切分,scheduler 本身没有统一结构化视角。它可以结合节点标签做粗粒度判断,但不能原生理解单个设备的属性。
DRA 下,scheduler 的判断更接近:
Pod references ResourceClaim
ResourceClaim wants DeviceClass gpu.example.com
selectors require model=A100 and memory=80Gi
ResourceSlices say node-a has gpu-0 satisfying the claim
scheduler reserves that allocation and binds Pod to node-a也就是说,DRA 把“选择哪个设备”前移到了调度阶段,让设备分配和 Pod 调度成为同一个决策的一部分。
共享语义区别
Device Plugin 默认语义是独占整数设备。例如一个 Pod 申请 nvidia.com/gpu: 1,Kubernetes 认为它占用了一个不可再分配的整数资源。
厂商可以用这些方式实现共享或虚拟化:
- 把一张物理卡切成多个 MIG 实例,再暴露不同扩展资源。
- 使用 time-slicing,把一个物理 GPU 暴露成多个逻辑副本。
- 使用 vGPU / MPS / 私有调度器 / webhook / annotation。
这些方案能工作,但 Kubernetes 核心只看到“若干个扩展资源整数”,不知道它们背后的共享关系和容量约束。
DRA 则把共享语义放进 API:
- 多个容器可以引用同一个
ResourceClaim。 - 多个 Pod 可以引用同一个手工创建的
ResourceClaim。 - consumable capacity 可以让一个设备被多个 claim 消耗不同容量。
- partitionable devices 可以表达一个物理设备切成多个逻辑设备时的共享底层资源约束。
这些能力是否可用,取决于 Kubernetes 版本、feature gate 和具体 DRA driver。
可观测性区别
Device Plugin 常用观测入口:
- Node
status.capacity/status.allocatable。 - Device Plugin DaemonSet 日志。
- kubelet PodResources API。
- 设备监控组件,例如 DCGM exporter。
- v1.36 开始,
allocatedResourcesStatus可以在 Pod status 中报告设备健康信息。
DRA 额外提供:
kubectl get deviceclasseskubectl get resourcesliceskubectl get resourceclaimsResourceClaim.status.allocation- DRA driver 的 prepare / unprepare 指标和日志。
- DRA 资源健康状态、device taint、resource pool status 等新能力。
什么时候用哪个
继续用 Device Plugin 的场景
如果满足下面条件,Device Plugin 仍然是务实选择:
- 当前只是申请整卡 GPU,例如
nvidia.com/gpu: 1。 - 集群已经稳定运行 NVIDIA GPU Operator / NVIDIA device plugin。
- 应用不需要按单卡属性做复杂选择。
- GPU 节点型号比较统一,节点标签足够表达调度需求。
- 生产稳定性优先,暂时不想引入 DRA driver 和新 API。
例如大多数基础 GPU 推理服务、普通训练 Job、单租户 GPU 集群,用 Device Plugin 已经够用。
优先评估 DRA 的场景
如果有下面需求,DRA 更值得评估:
- 希望按设备属性筛选,例如型号、显存、驱动能力、MIG profile、网卡带宽。
- 一个节点上有多种不同设备,需要精确分配具体设备。
- 需要多个容器或多个 Pod 共享同一个设备资源。
- 需要动态分区、可消耗容量、设备 taint / toleration。
- 需要把设备分配结果作为 Kubernetes 对象暴露给控制器、审计或平台层。
- 需要把 GPU、RDMA、DPU、存储加速器等多类资源放进统一调度模型。
- 希望从私有 annotation / webhook / scheduler extender 迁移到 Kubernetes 标准 API。
对 AI Infra 来说,DRA 更适合承载未来的 GPU 虚拟化和异构资源编排平台,因为它给 scheduler、autoscaler、operator 留出了更清晰的扩展点。
迁移建议
不要把 DRA 迁移理解成“一次性替换所有 device plugin”。更稳妥的路线是:
- 保持现有 Device Plugin 生产路径稳定。
- 在测试集群启用 Kubernetes v1.34+,安装具体厂商的 DRA driver。
- 先验证
DeviceClass、ResourceSlice、ResourceClaim的基本生命周期。 - 用一个非核心 Job 验证
ResourceClaimTemplate。 - 如果应用 YAML 改造成本高,评估 DRA extended resource 兼容模式,让应用继续写
resources.limits。 - 对共享、分区、设备 taint、health status 等高级能力单独做灰度,不要默认全部打开。
- 建立资源观测:ResourceClaim 分配结果、driver prepare/unprepare 延迟、Pod pending 原因、设备健康状态。
迁移时要特别注意:
- 同一个资源名不要在同一节点上被 Device Plugin 和 DRA driver 以冲突方式同时暴露,除非使用的是 Kubernetes 支持的 DRA extended resource 迁移路径。
- DRA driver 的成熟度比 Device Plugin 更关键。API 稳定不代表某个厂商 driver 已经适合生产。
- beta / alpha DRA 能力需要和集群升级策略绑定,不能只看单个 YAML 是否能 apply。
- 资源共享不等于隔离可靠。GPU memory、PCIe 带宽、MPS、MIG、vGPU 的隔离边界仍然由硬件和 driver 决定。
常见误区
DRA 的 dynamic 不是运行中热插拔
DRA 的 dynamic 主要指“资源可以通过 claim 在调度时动态选择和分配”。它不表示一个正在运行的 Pod 可以随时增加、减少或替换 GPU。运行中资源变更仍然受 Kubernetes、runtime、driver 和应用能力限制。
DRA 不替代厂商驱动
DRA 是 Kubernetes 资源分配 API,不是 GPU driver、CUDA runtime、container toolkit 或 RDMA userspace。节点上仍然需要正确安装硬件驱动和容器运行时集成。
Device Plugin 没有过时到不能用
Device Plugin 是成熟生产路径。DRA 是更强的下一代模型,但使用前要确认 Kubernetes 版本、driver、runtime、监控和运维流程都准备好。
DRA 的 YAML 更复杂是有原因的
Device Plugin 的写法很短:
nvidia.com/gpu: 1DRA 的写法更长,因为它把原先藏在节点标签、插件配置、webhook、调度扩展和厂商私有逻辑里的信息显式建模了。复杂度没有消失,而是从隐式逻辑迁移到了标准 API 对象里。
总结
Device Plugin 和 DRA 不是简单的新旧替代关系,而是两个抽象层级:
- Device Plugin 是 kubelet 设备接入机制,重点是“节点上有什么设备,如何交给容器使用”。
- DRA 是 Kubernetes 资源分配机制,重点是“工作负载声明什么设备需求,调度器如何选择、分配和跟踪这些设备”。
在简单 GPU 场景下,Device Plugin 仍然足够好;在 AI Infra、GPU 虚拟化、异构资源池、设备共享和精细调度场景下,DRA 是更长期的方向。
参考资料
- Kubernetes: Device Plugins
- Kubernetes: Dynamic Resource Allocation
- Kubernetes: Set Up DRA in a Cluster
- Kubernetes: Allocate Devices to Workloads with DRA
- Kubernetes Blog: Device Manager graduates to GA in v1.26
- Kubernetes Blog: DRA alpha API in v1.26
- Kubernetes Blog: DRA graduated to GA in v1.34
- Kubernetes Blog: DRA updates in v1.36
- KEP-5004: DRA Extended Resource
更新日志
ed202-add some docs于e64bf-add gpu basic于