Volcano Capacity 插件 DeviceClass 配额(DRA Quota)设计解析与实战指南
【免费下载链接】volcanoA Cloud Native Batch System (Project under CNCF)项目地址: https://gitcode.com/GitHub_Trending/vol/volcano
本文围绕 Volcano 开源仓库中 DeviceClass Quota Support in Capacity Plugin 设计文档,深入讲解如何让 Kubernetes Dynamic Resource Allocation(DRA)资源接入 Volcano 已有的队列配额模型,并结合 用户指南 与 capacity 插件源码 给出完整的配置示例、启用步骤与底层实现原理。读完本文,你将掌握在 Volcano 队列上配置整卡(Device Count)配额、可消耗容量(Consumable Capacity)配额、混合资源配额以及共享 ResourceClaim 计费语义的完整实战方案。
背景:DRA 引入后,队列配额的问题依然存在
Volcano 的 capacity 插件已经为 CPU、内存以及其他标准资源提供了成熟的队列配额管理,核心语义由队列上的capability、deserved、guarantee三个字段表达。Kubernetes DRA(Dynamic Resource Allocation)则带来了另一套资源模型,核心对象包括DeviceClass、ResourceClaim和ResourceSlice。
对使用者而言,业务诉求并没有因为资源模型变化而改变:一个团队希望明确声明“这个队列最多可以使用多少 DRA 设备”“这个队列可以借用多少软上限”“这个队列保底获得多少资源份额”。因此设计目标非常明确——让 DRA 资源直接参与现有队列配额模型,而不是为 DRA 单独引入一套平行的配额 API。相关背景可参考 Capacity 插件设计 与 层次化队列设计。
设计目标与非目标
设计目标
- 复用现有队列字段
capability、deserved、guarantee; - 同时支持整设备配额(whole-device quota)与可消耗容量配额(consumable-capacity quota);
- 保持 DRA 配额语义与现有 capacity 插件行为一致;
- 同时支持直接引用
ResourceClaim与引用ResourceClaimTemplate两种用法; - 让共享
ResourceClaim的计费方式对用户直观可理解。
非目标
- 不管理物理设备的放置或分配细节;
- 不替代 Kubernetes DRA 自身的调度逻辑;
- 不定义新的 DRA 专用队列 CRD 结构。
核心思想:用保留 Key 格式在队列 ResourceList 中表达 DRA 配额
DRA 配额不新增字段,而是直接使用队列ResourceList中的保留 Key 格式来表达:
| Key 格式 | 示例 | 含义 |
|---|---|---|
deviceclass/<DeviceClass> | deviceclass/gpu.nvidia.com: "8" | 按设备数量限制 |
<dim>.deviceclass/<DeviceClass> | cores.deviceclass/hami-core-gpu.project-hami.io: "800" | 按可消耗容量维度限制 |
这一设计使得 DRA 资源与cpu、memory以及设备插件资源处于同一个配置模型之中,用户无需学习第二套配额模型。
源码印证:Key 的解析逻辑
在 capacity.go 中定义了三个关键常量:
DeviceClassCountPrefix = "deviceclass/" DeviceClassCapacitySep = ".deviceclass/"parseDRAResourceList函数(capacity.go)对两种 Key 格式分别处理:
- 若资源名包含
.deviceclass/,则拆分为“维度名 + DeviceClass”,归入该 DeviceClass 的Capacity映射; - 若资源名以
deviceclass/开头,则截取 DeviceClass 名,作为设备数量Count记录。
解析结果被封装为 DRAResource 结构体,其中Count表示设备总数,Capacity表示维度名到请求量的映射。测试 TestParseDRAResourceListCombinesCountAndCapacity 验证了 Count 与 Capacity 能被同时解析合并。
队列语义不变:capability / deserved / guarantee
对于 DRA 资源,队列字段的含义与普通资源完全一致:
| 字段 | 含义 |
|---|---|
capability | 硬上限(hard upper bound) |
deserved | 软份额,可在其周围借出或回收(soft share) |
guarantee | 最低受保护份额(minimum protected share) |
这意味着用户不需要为 DRA 学习第二套配额模型,直接复用既有的 capacity 插件语义即可。从源码看,newDRAQuotaAttr(capacity.go)直接从队列的capability、deserved、guarantee三份 ResourceList 中解析出 DRA 配额属性draQuotaAttr,该结构分别维护每个 DeviceClass 的capability、deserved、guarantee、allocated(当前分配)与inqueue(会话内已准入)状态。
配额配置实战
1. 整卡配额(Whole-Device Quota)
当队列需要按设备数量限制时,使用deviceclass/<name>格式:
apiVersion: scheduling.volcano.sh/v1beta1 kind: Queue metadata: name: gpu-team spec: reclaimable: true capability: cpu: "64" memory: "256Gi" "deviceclass/gpu.nvidia.com": "8" deserved: "deviceclass/gpu.nvidia.com": "4" guarantee: "deviceclass/gpu.nvidia.com": "1"含义:gpu-team队列最多同时使用 8 张gpu.nvidia.com设备,软份额为 4 张,保底 1 张。
2. 可消耗容量配额(Consumable-Capacity Quota)
当队列需要按 vGPU 核数、显存等可消耗维度限制时,使用<dim>.deviceclass/<name>格式:
apiVersion: scheduling.volcano.sh/v1beta1 kind: Queue metadata: name: vgpu-team spec: reclaimable: true capability: "cores.deviceclass/hami-core-gpu.project-hami.io": "800" "memory.deviceclass/hami-core-gpu.project-hami.io": "320Gi" deserved: "cores.deviceclass/hami-core-gpu.project-hami.io": "400" guarantee: "cores.deviceclass/hami-core-gpu.project-hami.io": "100"3. 混合资源队列(Mixed Resource Queues)
一个队列可以同时包含传统资源、设备插件资源、整卡 DRA 配额和 DRA 容量维度:
apiVersion: scheduling.volcano.sh/v1beta1 kind: Queue metadata: name: ml-team spec: reclaimable: true capability: cpu: "100" memory: "200Gi" "nvidia.com/gpu": "4" "deviceclass/gpu.nvidia.com": "8" "cores.deviceclass/hami-core-gpu.project-hami.io": "800" "memory.deviceclass/hami-core-gpu.project-hami.io": "320Gi"启用 capacity 插件与 DRA 配额
使用前需要满足以下前提:
- Kubernetes 集群已启用 DRA;
- 集群中已安装支持 DRA 的驱动;
- Volcano 已安装并启用了
capacity插件。
编辑调度器配置:
kubectl edit cm -n volcano-system volcano-scheduler-configmap确保capacity插件已启用;若需要队列共享与回收(reclaim)行为,还需要启用reclaimaction。示例:
kind: ConfigMap apiVersion: v1 metadata: name: volcano-scheduler-configmap namespace: volcano-system data: volcano-scheduler.conf: | actions: "enqueue, allocate, backfill, reclaim" tiers: - plugins: - name: priority - name: gang - name: conformance - plugins: - name: drf - name: predicates - name: capacity arguments: capacity.DynamicResourceAllocationEnable: true capacity.DRAConsumableCapacityEnable: true - name: nodeorder两个插件参数均为可选。从 New 函数 可见,未设置时 Volcano 默认采用对应 Kubernetes feature gate 的值作为默认值:
capacity.DynamicResourceAllocationEnable:控制是否执行 DRA 配额检查,默认取 Kubernetes 的DynamicResourceAllocationfeature gate;capacity.DRAConsumableCapacityEnable:控制是否检查 DRA 内部的容量维度,默认取 Kubernetes 的DRAConsumableCapacityfeature gate。
在checkDRAAllocatable(capacity.go)中可以看到:设备数量检查使用饱和加法计算allocated + inqueue + request是否超过capability.Count;而容量维度检查仅在consumableCapacityEnabled为真时进行,若请求的维度未在 capability 中定义或累加后超过配额上限,则直接判定不可分配。
请求如何被解释:两种用户侧用法
从队列视角看,DRA 配额由ResourceClaim请求消耗,支持两种模式:
- Pod 直接引用一个
ResourceClaim; - Pod 引用
ResourceClaimTemplate,由 Kubernetes 为 Pod 创建实际的 claim。
两种情况下队列配额视图一致:Volcano 将请求计入对应的DeviceClass。
方式一:使用 ResourceClaimTemplate(推荐,每个 Pod 独立 claim)
apiVersion: resource.k8s.io/v1 kind: ResourceClaimTemplate metadata: name: gpu-template namespace: default spec: spec: devices: requests: - name: gpu-request exactly: deviceClassName: gpu.nvidia.com count: 2 allocationMode: ExactCount --- apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: gpu-job spec: schedulerName: volcano queue: gpu-team minAvailable: 1 tasks: - replicas: 1 name: worker template: spec: resourceClaims: - name: gpu resourceClaimTemplateName: gpu-template containers: - name: main image: nvidia/cuda:11.0-base command: ["nvidia-smi"] resources: requests: cpu: "2" memory: "4Gi" claims: - name: gpu方式二:直接引用 ResourceClaim(适合多 Pod 共享同一 claim)
apiVersion: resource.k8s.io/v1 kind: ResourceClaim metadata: name: shared-gpu namespace: default spec: devices: requests: - name: gpu-request exactly: deviceClassName: gpu.nvidia.com count: 2 allocationMode: ExactCount shareable: true --- apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: shared-gpu-job spec: schedulerName: volcano queue: gpu-team minAvailable: 2 tasks: - replicas: 2 name: worker template: spec: resourceClaims: - name: gpu resourceClaimName: shared-gpu containers: - name: main image: nvidia/cuda:11.0-base command: ["nvidia-smi"] resources: requests: cpu: "2" memory: "4Gi" claims: - name: gpu共享 ResourceClaim 语义:按 claim 计费一次
当多个 Pod 引用同一个可共享的ResourceClaim时,配额应按 claim 计数一次,而不是按 Pod 计数多次。这符合用户直觉:一个逻辑共享 claim 应只消耗一份队列配额,不能让队列用量因为多个 Pod 挂载同一共享 claim 而虚高。
源码印证:resourceClaimRefs 引用计数
在 capacity.go 的addTaskDRAAllocated中可以看到具体实现:每个队列维护resourceClaimRefs映射(claim 键到引用计数),只有当某个 claim 在该队列中的引用计数为 0(首次出现)时才真正累加 DRA 分配;随后递增引用计数。removeTaskDRAAllocated(capacity.go)在引用计数降为 1 时才扣减配额并删除记录,否则只做递减。
测试 TestSharedResourceClaimIsChargedOnce 明确验证了这一行为:两个任务共享同一个 claim 时,第二个任务不再额外消耗 DRA 配额;而两个不同的 claim 仍会分别计费,超限时会被拒绝。
与集群容量的关系:逻辑边界与物理分配的互补
队列配额是逻辑调度边界,物理设备可用性仍由 Kubernetes DRA 及其安装的驱动决定。两者职责互补而非重叠:
- Volcano 检查队列是否被允许消耗更多某个
DeviceClass的资源; - Kubernetes DRA 检查是否存在匹配的物理分配方案。
对于可消耗容量,Volcano 统计的是逻辑总量。例如一个请求 2 台设备、每台 8Gi 显存的申请,将消耗队列memory.deviceclass/<DeviceClass>配额中的 16Gi。但这不能解决物理碎片问题:即使集群有两台 8Gi 设备,一个需要在单台设备上 10Gi 的 Pod 仍可能通过队列配额检查,却在最终 DRA 分配阶段失败。测试 TestCheckDRAAllocatable_overflowRejected 也验证了设备数与 inqueue 累加溢出时会被拒绝的边界行为。
根队列与总资源视图
对于 DRA 资源,集群范围的总量既可以来自实际集群状态,也可以来自管理员显式的队列配置。核心的用户侧规则是:
- 当管理员需要更强的逻辑边界,或自动汇总的总量不足以满足其环境需求时,可以显式配置根队列的 DRA 总量。
在层次化队列场景下,源码通过getDRADelta(capacity.go)计算叶子队列 DRA 分配增量,并仅对祖先队列 capability 中配置过的 DeviceClass 进行增量传播(见 buildHierarchicalQueueAttrs),保证祖先队列的 DRA 视图正确汇总。
不支持或延后的场景
第一版设计刻意保持窄范围,以下场景不是主要目标:
allocationMode: All;FirstAvailable风格的优先级请求;- 依赖更特殊设备语义的可分区设备总量统计。
这些场景在 Kubernetes DRA 中仍然存在,但不是本文所述的主要配额计费模型。用户指南(how_to_use_dra_quota.md)同样提醒:若环境重度依赖这些模式,在将其视为严格的队列配额信号前需仔细评估行为。
观察队列用量
可以通过以下命令查看队列的分配状态:
kubectl get queue -o yamlDRA 用量出现在status.allocated中,例如:
status: allocated: "deviceclass/gpu.nvidia.com": "4" "cores.deviceclass/hami-core-gpu.project-hami.io": "400"从源码看,AllocateFunc与DeallocateFunc事件处理器(capacity.go)在任务分配/释放时同步维护attr.allocated与 DRA 分配计数,并更新队列指标,因此status.allocated反映的是实时配额占用。
队列共享示例
capability、deserved、guarantee模型同样适用于 DRA 资源,可以让多个队列按普通 capacity 语义共享同一个 DRA 资源池:
apiVersion: scheduling.volcano.sh/v1beta1 kind: Queue metadata: name: queue-a spec: reclaimable: true capability: "deviceclass/gpu.nvidia.com": "6" deserved: "deviceclass/gpu.nvidia.com": "3" guarantee: "deviceclass/gpu.nvidia.com": "1" --- apiVersion: scheduling.volcano.sh/v1beta1 kind: Queue metadata: name: queue-b spec: reclaimable: true capability: "deviceclass/gpu.nvidia.com": "6" deserved: "deviceclass/gpu.nvidia.com": "5" guarantee: "deviceclass/gpu.nvidia.com": "2"为什么这样设计
该设计的核心取舍是:保持 DRA 配额体验与既有 Volcano 队列管理融为一体,而不是另建一套仅针对 DRA 的配额系统。由此带来四个直接收益:
- 不新增 DRA 专用队列 API 面;
- 配额语义与现有 capacity 插件资源完全一致;
- 硬上限、软共享、保底三种能力的工作流不变;
- 一套配置模型同时覆盖 CPU、内存、设备插件与 DRA。
最佳实践
- 使用
ResourceClaimTemplate管理每个 Pod 独立的分配生命周期; - 仅在确实需要共享时才使用直接
ResourceClaim; - 让队列
capability与实际集群资源策略保持一致; - 使用
deserved与guarantee表达团队共享规则,而不只是硬上限; - 混合环境中,在同一个队列内同时配置 DRA Key 与普通资源 Key。
参考资料
- DeviceClass Quota 设计文档
- DeviceClass Quota 用户指南
- Capacity 插件用户指南
- Capacity 插件设计
- Capacity 插件上的层次化队列
- capacity 插件源码
- capacity 插件测试用例
- DRAResource 数据结构定义
【免费下载链接】volcanoA Cloud Native Batch System (Project under CNCF)项目地址: https://gitcode.com/GitHub_Trending/vol/volcano
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考