第 8 篇|Volcano 资源竞争:队列份额与作业调度

本文最后更新于:2 天前

前言

上一篇跟踪了一个 Volcano 作业的准入、试分配和绑定。这一篇加入第二个作业,集中回答三个问题:每个 Queue 可以继续使用多少资源,调度器先看谁,以及资源已经占满时怎样让等待成员继续运行。

主案例使用两个 Queue 和 12 核 CPU,先计算 Queue 份额,再看同一 Queue 内的作业怎样按 CPU、GPU 占用排序。默认流程是 enqueue, allocate, backfill,主动释放资源需要另外启用 preempt 或 reclaim。

Queue 表达什么

Queue 保存一组作业共享资源时使用的策略,并不对应固定的一组节点。要知道一个 Queue 还能否继续获得资源,需要把它的需求、当前占用和算出的份额放在一起看:

  • request:Queue 中已经分配和仍在等待的成员总共请求多少资源。
  • allocated:已经进入 Allocated、Binding、Bound、Running 等状态的成员占了多少资源。
  • weight:多个 Queue 都有未满足需求时,各自按多大比例参与份额计算。
  • capability:这个 Queue 的份额上限。
  • deserved:proportion 插件根据需求、权重和上限算出的本次份额。

guarantee 还可以为 Queue 设置保障量。它参与份额下限和可用上限的计算;主案例中 A 的保障量是 2 核,而按权重算出的份额是 4 核,所以它不会改变最终结果。

这些数字使用 Pod 声明的资源请求。一个 Pod 请求 2 核 CPU,即使进程此刻只用了 0.2 核,allocated 仍按 2 核计算。

用两个 Queue 计算一次份额

这里单独设定一组资源数量,不沿用第六篇的等大资源槽:集群共有 12 核 CPU,demo-job 属于 Queue A,demo-job-b 属于 Queue B。A 已占用 2 核,另有一个请求 2 核的成员等待;B 已占用 10 核。当前状态如下:

Queue weight guarantee capability request allocated
A:ray-batch 1 2 核 6 核 4 核 2 核
B:ray-batch-b 2 0 10 核 10 核 10 核
两个作业分别进入两个 Queue,共享集群中的节点资源。Queue 可以汇集多个命名空间的工作负载;图中沿用 default。
两个作业分别进入两个 Queue,共享集群中的节点资源。Queue 可以汇集多个命名空间的工作负载;图中沿用 default。 查看原图

weight 怎样得到 4 核和 8 核

proportion 从集群总量 12 核开始计算份额。此时这 12 核已经全部被 Pod 占用,计算要回答的是接下来各个 Queue 应分得多少。

A、B 的权重合计为 3。每三个等份中,A 按一份、B 按两份参与计算:

  • A 增加的份额:12 × 1/3 = 4 核。
  • B 增加的份额:12 × 2/3 = 8 核。

两边都没有超过各自的需求和上限,因此 deserved 最终是 4 核和 8 核。weight 影响的是份额计算,不会立即移动正在运行的 Pod,也不会直接绑定新的 Pod。

这段计算先按权重增加份额,再依次受可用上限和需求约束,最后考虑保障量。源码里的 realCapability 结合集群资源与各 Queue 的保障量计算可用上限,再受本 Queue 的 capability 限制。本例总保障量是 2 核,扣除后再加回本 Queue 的保障量:A 的上限为 min(12−2+2, 6)=6 核,B 为 min(12−2+0, 10)=10 核,恰好与表中的上限相同:

1
2
3
4
5
6
7
8
9
oldDeserved := attr.deserved.Clone()
attr.deserved.Add(remaining.Clone().Multi(float64(attr.weight) / float64(totalWeight)))

if attr.realCapability != nil {
attr.deserved.MinDimensionResource(attr.realCapability, api.Infinity)
}
attr.deserved.MinDimensionResource(attr.request, api.Zero)

attr.deserved = helpers.Max(attr.deserved, attr.guarantee)

如果 A 只需要 2 核,按权重得到的 4 核会被 request 截到 2 核,余下 2 核继续给仍有需求的 B,结果变成 2 核和 10 核。若 B 的上限又只有 7 核,最终是 2 核和 7 核,还有 3 核没有进入这两个 Queue 的 deserved。

同一组权重在不同需求与上限下得到不同份额。图中按 CPU 计算,灰色部分是尚未计入两队列 deserved 的份额余量。
同一组权重在不同需求与上限下得到不同份额。图中按 CPU 计算,灰色部分是尚未计入两队列 deserved 的份额余量。 查看原图

这些余量分配都在同一次 proportion 计算中完成。

份额怎样影响下一次分配

proportion 用 allocated / deserved 计算 Queue 的份额使用率,源码中称为 share。主案例只看 CPU:

Queue allocated deserved share
A 2 核 4 核 50%
B 10 核 8 核 125%

Queue 自身的调度优先级(priority)相同时,share 较小的 A 先被检查。B 的 125% 表示它当前占用超过了新算出的份额;重新计算 deserved 不会直接结束 B 中的 Pod。

A 中有一个等待成员请求 2 核。调度器会计算分配后的占用量是否超过 A 的 deserved:

1
2
futureUsed := attr.allocated.Clone().Add(totalReq)
allocatable, _ := futureUsed.LessEqualWithDimensionAndResourcesName(attr.deserved, totalReq)

A 当前占 2 核,再增加 2 核正好达到 4 核,所以 Queue 额度允许继续。随后还要为这个成员寻找节点。由于集群的 12 核已经全部占用,它此时仍然无法直接绑定。

同一 Queue 内还有多个作业时

选中 Queue 后,如果里面有多个待调度作业,当前配置先比较 PriorityClass 所表达的作业优先级;优先级相同时,DRF(Dominant Resource Fairness,主导资源公平性)再比较各作业对 CPU、GPU 等资源的占用比例。它取各资源比例中的最大值作为主导份额,数值较小的作业排在前面。下图另用同一 Queue 内的两个作业说明这个比较,与前面的跨 Queue 份额计算分开。

同一个 Queue 内的两个作业分别计算 CPU 和 GPU 占用比例,DRF 取较大的比例作为主导份额。
同一个 Queue 内的两个作业分别计算 CPU 和 GPU 占用比例,DRF 取较大的比例作为主导份额。 查看原图

因此,调度器先通过 Queue 规则选中 A,再在 A 内比较作业。DRF 参与后一层选择,A、B 的 4 核和 8 核份额仍由前面的 proportion 计算。

资源占满后怎样继续

默认的 enqueue, allocate, backfill 不会主动结束运行中的 Pod。需要释放资源时,Volcano 提供两条传统路径:

Action 范围 作用
preempt 同一个 Queue 内 高优先级作业从同 Queue 的其他作业中选择可以退出的成员
reclaim 不同 Queue 之间 份额不足的 Queue 从超过 deserved 且允许回收的 Queue 中选择成员

preempt 的跨作业候选首先要求双方属于同一个 Queue:

1
return job.Queue == preemptorJob.Queue && preemptor.Job != task.Job

回到主案例,A 的 deserved=4、allocated=2,还差 2 核;B 的 deserved=8、allocated=10,正好多占 2 核。如果启用 reclaim,并且 B 中有一个请求 2 核的成员通过优先级、可抢占性和 Gang 检查,调度器会按下面的顺序处理:

  1. 在 Statement 中记录要退出的 B 成员。
  2. 把 A 的等待成员标为 Pipelined,表示计划使用即将释放的资源,此时还没有绑定。
  3. 提交 Statement 后,B 的 Pod 开始终止,对应资源进入 Releasing。
  4. kubelet 完成终止,SchedulerCache 观察到资源释放后,A 的成员才会重新分配并绑定。
传统驱逐路径先推演、再提交。候选不满足条件时撤销本轮尝试;提交后仍需等待被选中的 Pod 退出,未来可用资源才能成为当前可用资源。
传统驱逐路径先推演、再提交。候选不满足条件时撤销本轮尝试;提交后仍需等待被选中的 Pod 退出,未来可用资源才能成为当前可用资源。 查看原图

Gang 约束仍然参与成员筛选。如果移走一个成员会让 B 的作业低于 MinAvailable,这个成员不能用于本次回收。

小结:从 Queue 份额到 Pod 运行

资源竞争可以沿三步理解:proportion 根据需求、权重和上限计算 Queue 的 deserved;Queue 排序和额度检查决定先尝试谁;资源已经占满时,preempt 或 reclaim 才可能让等待成员获得未来资源。

份额计算、Pod 当前占用和节点空闲量是三件事。主案例中 A 有 2 核份额可用,但节点没有空闲 CPU;只有等待资源自然释放,或者完成一次符合条件的 reclaim,A 的成员才可能进入绑定。

下一篇回到 KubeRay,比较不开启批调度和接入 Volcano 后的两条创建流程。

参考资料


第 8 篇|Volcano 资源竞争:队列份额与作业调度
https://tanxinyu.work/kuberay-volcano-08-volcano-queues/
作者
谭新宇
发布于
2026年9月15日
许可协议