Queue Quota 深度解析:从 Volcano 三级模型到 MiniMax 级 AI 集群的调度治理实践
Queue Quota 深度解析:从 Volcano 三级模型到 MiniMax 级 AI 集群的调度治理实践
Queue Quota 的本质一句话就能说清:它是队列级别的资源记账单元,声明"这个队列能用多少资源、必须保多少资源、最多借多少资源、愿意借出多少资源"——区别于 namespace 级的 ResourceQuota,它的作用域是队列而不是命名空间,并且支持跨队列的借用与回收。
而 MiniMax 这类大模型公司的实际做法,是基于 Volcano 二次开发批调度器,把 Quota 建模从单纯的 CPU/内存扩展到 GPU 卡数、卡型、NVLink 域、IB 域、CPU 沙盒等多个维度,队列树直接对齐业务组织结构,配合夜间潮汐复用把 ETTR(有效训练时间比例)做到 97% 这个业界最高档位。
以下分两个层面展开:先把 Queue Quota 的理论语义讲透,再切到一线调度器研发的真实工作。
一、Queue Quota 的完整语义
1.1 为什么不能只用 ResourceQuota
原生 Kubernetes 的 ResourceQuota 工作在 Admission 层,按 namespace 限制资源申请总量。它有三个致命局限:
- 静态切分:每个 namespace 被分配固定配额,资源空闲时也不能借给别人,集群整体利用率会被人为压低;
- 没有队列语义:Pod 无序竞争,先到先得,缺少"排队、准入、优先级"的作业治理能力;
- 没有记账与回收:资源"借出去"之后没有"还回来"的机制,高优任务出现时无法追讨。
队列 Quota 是把这三个缺口补齐的核心抽象。它出现在多个主流调度器里,语义略有差异:
| 调度器 | 核心对象 | 关键字段 | 借用机制 |
|---|---|---|---|
| Volcano | Queue | guarantee / deserved / capability / reclaimable / weight | reclaim action 按 deserved 做归还 |
| Kueue | ClusterQueue + Cohort | nominalQuota / borrowingLimit / lendingLimit | Cohort 内共享未用 nominal 配额 |
| YuniKorn | hierarchical Queue | guaranteed / max | 层级树内自上而下分配 |
| 字节 GRO | Queue CRD | min / max / weight | 弹性队列,Quota 管控 |
| NVIDIA KAI | Queue | quota / overQuotaWeight / limit | 层级队列 + 超额权重 |
1.2 Volcano 三级模型:保底、应得、封顶
这是最值得精读的模型,因为它把"资源保障"和"资源共享"这对矛盾用三个字段表达得最干净:
- guarantee:独占预留,其他队列碰不到,用于保护核心业务。即使集群再紧张,这部分资源也是这个队列的私产;
- deserved:应得份额,空闲时可以被别的队列借走,忙时可以 reclaim 回来。这是弹性共享的关键;
- capability:硬上限,无论集群多空闲,本队列都不能超过。这是防止单一团队吃光整个集群的护栏。
约束条件非常明确:guarantee ≤ deserved ≤ capability。同时还有两个易被忽略的软规则:
- 如果队列的 allocated 已经超过自己的 deserved,它就失去了从其他队列 reclaim 资源的资格——“借出去要先还回来”;
reclaimable: true(默认)表示允许其他队列从这个队列回收超额资源,把它关掉可以让关键队列彻底免于被回收,代价是集群整体利用率下降。
1.3 一个生产级的 Queue YAML
1 | apiVersion: scheduling.volcano.sh/v1beta1 |
层级队列场景下,子队列的 deserved 总和必须 ≤ 父队列的 deserved,子队列的 capability 不能超过父队列的 capability——这保证 Quota 树的守恒性。
1.4 Kueue 的三字段与 Volcano 的对应关系
Kueue 用另一套词汇表达同样的思想:
| Volcano | Kueue | 语义差异 |
|---|---|---|
| guarantee | nominalQuota(下限部分) | Kueue 的 nominalQuota 是"名义配额",本身既是应得也是可被借出的额度 |
| deserved | nominalQuota | Kueue 直接用 nominalQuota 做"应得"语义 |
| capability | nominalQuota + borrowingLimit | Kueue 的上限 = nominalQuota + borrowingLimit(借入上限) |
| reclaimable: true | lendingLimit | Kueue 可以精细控制"愿意借出多少" |
关键差异在于:Volcano 的 reclaim 是运行时机制——队列真实占用了超过 deserved 的资源后,别的队列来抢时要还回去;Kueue 的 borrow/lend 是准入时机制——Workload 被 Admission 时就要判断"借用后是否违反 borrowingLimit / lendingLimit"。
Kueue 还有一个 Cohort 概念,与 Volcano 的层级队列是两套组织方式:
- Cohort:ClusterQueue 可以加入 Cohort,共享 Cohort 级的 nominalQuota 池;Cohort 本身也可以组成层级树(Hierarchical Cohort);
- Cohort 级 nominalQuota 是"额外共享池",不占 ClusterQueue 自己的份额;
- ClusterQueue 想借用 Cohort 资源,必须显式声明该资源的 nominalQuota,哪怕值是 0——这个"必须显式声明"的约束保证了借用关系是可审计的。
1.5 Quota 与 DRF 的关系
一个常见误区是把 Queue Quota 和 DRF 混为一谈。Quota 是静态配置的治理规则,DRF 是动态运行的公平算法,二者是叠加而非替代:
- 队列与队列之间,按 capability/deserved/weight 做资源切分(治理层);
- 同一队列内部,多个作业竞争时按 DRF 的 dominant share 排序(公平层);
- 抢占与回收时,DRF 还会参与 PreemptableFn 的判断——只允许朝公平方向抢占。
Volcano 的 proportion 插件用 weight 自动计算 deserved:deserved = (weight / total-weight) × total-resource,适合扁平结构;capacity 插件则显式配置 deserved,适合层级结构——二者互斥,不能同时启用。
二、假如你是 MiniMax 的调度器研发
基于公开信息还原一下 MiniMax 的基础设施现状,以及在这个现状下,调度器研发人员每天真正在解决的问题长什么样。
2.1 真实的基础设施底座
MiniMax 技术总监在 2024 ML-Summit 上明确讲过他们的调度架构选型:
- 统一平台入口层:队列控制和分发,作为所有算力请求的统一入口;
- 集群层:基于 Volcano 二次开发的批调度器作为资源分配组件;
- 物理资源层:只用设备插件(非虚拟化),Kubernetes 管理裸金属 GPU;
- 网络:FatTree 结构,IB/RoCE,NCCL 统一封装,针对网络特点和故障做优化;
- 混合算力供给:自建核心集群(保障 3T 级预训练/后训练/RL 长时间连续运行)+ 云厂商深度合作(训练与在线推理弹性)+ 通用厂商(推理算力补充、削峰)。
2026 年的业绩会又披露了几个关键数字:自建集群 ETTR(有效训练时间比例)97%,是业界已知的最高档位之一;夜间在线推理低谷的算力自动调度给评测、算法验证与 RL rollout;多模态退役集群的大量 CPU 为 Agent 沙盒提供混合调度空间。
这决定了调度器研发人员面对的真实场景:不是管理一个集群,而是管理一个由自建 IDC + 多家云组成的混合算力池;不是只调度 GPU 训练,而是同时调度 GPU 训练、GPU 推理、CPU 密集的 Agent 沙盒、多模态批处理四类异构负载。
2.2 队列树长什么样
实际设计中,Queue Quota 不会只按"组织架构"划分,而会按业务类型 + 卡型 + 拓扑域三个维度做切分。一个接近真实的队列树大概长这样:
1 | root (集群总量:比如 8000 H800 + 4000 H20 + 若干国产卡 + CPU 池) |
每个叶队列都有完整的 guarantee / deserved / capability / reclaimable / priority 五元组配置,而不是一个笼统的数字。
2.3 Quota 建模的真实维度
这是原生 Volcano 与实际需求差距最大的地方。开源 Volcano 的 Queue 默认只支持 CPU / memory / 扩展资源(如 nvidia.com/gpu)的总量计数,但真实的大模型集群需要把 Quota 建模到更细的资源维度:
| 资源维度 | 为什么要单独建模 | 实现方式 |
|---|---|---|
| GPU 总数 | 基础记账 | 原生扩展资源 |
| 卡型 | H800 / H20 / 国产卡不能互换,必须分池 | ResourceFlavor(Kueue)或按节点池分 Queue(Volcano) |
| NVLink 域 / 超节点 | GB200 NVL72 的 72 卡是一个紧耦合域,作业必须对齐域边界 | HyperNode CRD(Volcano 1.12+) |
| IB/RoCE 域 | TP 跨 IB 会慢 7-50 倍,不能让一个 job 跨 spine | HyperNode 层级表达胖树拓扑 |
| CPU 沙盒份额 | Agent RL 需要 CPU 密集的沙盒,与 GPU 训练争 CPU | 独立 CPU 池 + 独立调度器(Agentuler)或 NodeShard 切分 |
| 存储 IO / 缓存命中率 | checkpoint 写入和数据集读取是隐性瓶颈 | 数据本地性评分 + 缓存亲和 |
HyperNode 是关键补充。Volcano 从 v1.11 开始引入 HyperNode CRD,用树形结构统一表达网络拓扑——叶子是节点,中间层是交换机,根是整个 FatTree 顶端。与传统的 node label 方式相比,HyperNode 有三个优势:
- 统一语义:标准化的 API 接口,避免不同团队用 label 表达拓扑时的语义不一致;
- 层级结构:树形结构精确反映实际 FatTree 的分层;
- 易于管理:可以手动维护,也可以用拓扑自动发现工具。
Job / PodGroup 通过 networkTopology 字段声明拓扑约束:
mode: hard:作业必须全部落在同一个 HyperNode 内(TP 域内必须满足);mode: soft:尽量落在同一 HyperNode,失败时允许放宽;highestTierAllowed:hard 模式下允许作业跨到的最高层级——tier 1 表示同一 leaf switch 下,tier 2 表示可以跨 leaf 但不能跨 spine。
v1.14 又在 HyperNode 之上叠加了 HyperNode 级 Binpack 和 SubGroup 级精细化拓扑感知,让"8 卡必须同 NVLink 域"和"64 卡可以跨 rack 但不能跨 spine"这类混合约束可以被同时表达。
2.4 潮汐复用的调度策略
“夜间把推理算力让给评测和 RL rollout”,这句话在工程上要拆成四个动作:
① 时段感知的 Quota 动态调整。 推理队列的 capability 在白天是 1200 卡,夜间降到 400 卡;评测队列的 capability 白天 300 卡,夜间提升到 2000 卡。Volcano 支持运行时调整 Queue 的 deserved/capability,但真正的生产实现通常是外挂一个 Quota Controller,按 CRON 或流量指标自动调整 Queue YAML,而不是让调度器自己感知时间。
② 推理副本的优雅缩容。 推理服务不是"说杀就杀"——要先做流量排水,等 in-flight 请求完成,再走模型卸载。整条链路可能要几分钟,所以 Quota Controller 需要提前 10-15 分钟发出"准备缩容"信号,触发 HPA / Ray Serve 缩容,等实际 Pod 释放完毕后再正式归还 Quota。
③ 训练任务的 backfill 窗口。 夜间腾出来的 GPU 不是瞬间就能被预训练任务用上——预训练是 gang 严格型作业,需要凑齐几百张卡、且拓扑对齐才能启动。所以实际流程是:夜间推理缩容 → 空闲资源进入公共池 → 评测 / RL rollout(可弹性扩缩的作业)先 backfill 上去 → 如果还有剩余再留给下一次预训练 restart。
④ 早晨的反向潮汐。 白天开始前要把资源还给推理——这时候评测/RL rollout 的作业要能被优雅抢占。这意味着这类作业必须支持 checkpoint / 重放,不能是无状态的裸跑。RL rollout 通常天然支持(每条 trajectory 独立),评测任务也支持(按 case 粒度重试),所以把它们放在"可让路"的 QoS 层。
2.5 抢占的真实代价
理论上"preempt action 让高优任务可以抢占低优任务",但 MiniMax 这种规模的训练集群上,一次抢占的代价是实实在在的 GPU 小时数:
- 一个千卡 MoE 训练任务被抢占,需要写几十 GB 到上百 GB 的 checkpoint 到并行文件系统,可能要 2-5 分钟;
- 恢复时要重新拉起全部 worker、重建 NCCL 通信组、重放数据 pipeline,又需要几分钟;
- 如果抢占发生在 NVLink 域对齐的作业上,重建后的拓扑可能对不齐,导致 MFU(模型算力利用率)永久性下降。
所以实际的 Quota 治理里,关键训练队列通常把 reclaimable 设为 false——预训练队列借出去的资源,不还就不还了,但不能抢回来。同时配合"最短受保护运行时长":一个训练任务启动后前 N 小时不可被抢占,避免刚启动就被抢走,让 checkpoint 写入成本打了水漂。
2.6 Agentic RL 的 CPU 沙盒调度
这是 2025 年下半年新增的最棘手问题。MiniMax 的 Agent 架构团队在腾讯云峰会上披露了真实的困境:
- Agentic RL 每个 query 要展开上百条 trajectory,每条都要一个独立的沙箱环境;
- 面对十万级并发沙箱,K8s 原生调度"完全起不来";
- 每个 Pod 的启动要百毫秒级,否则 GPU 集群等沙箱的每一秒都在空转烧钱。
这意味着调度器研发人员要处理的不是"GPU 队列 Quota 怎么分",而是"如何在 K8s 之上,支撑十万级 CPU Pod 的秒级启动"。
Volcano v1.14 给出的参考答案是双调度器架构:
- Batch Scheduler:沿用原有 session-based 机制,约 1 秒一个调度周期,擅长 gang、拓扑、抢占、回收等复杂决策;
- Agent Scheduler(Agentuler):专为延迟敏感型 Agent 任务设计,采用多 Worker 并行消费调度队列 + 乐观并发控制 + 冲突感知绑定,共享 batch scheduler 的 predicate / node-order / bin-pack 插件;
- NodeShard CRD 作为两个调度器之间的契约:动态划分节点池,典型配置是 batch 侧占 CPU 利用率 0-60% 区间的节点,agent 侧占 70-100% 区间的节点,
shard-sync-period=60s让分片随负载动态调整; - Sharding Controller 根据集群实时状态动态计算每个调度器的候选节点池,而不是静态硬隔离。
对 Quota 来说,这引入了第四个维度——同一物理节点的 CPU 要在 batch scheduler 和 agent scheduler 之间动态分账,Quota 不再是单一调度器内部的记账,而是跨调度器的契约。
2.7 真实的痛点清单
假设你真入职做调度器研发,每天打交道的问题不是"Quota 是什么",而是下面这些:
| 痛点 | 具体表现 | 对应的调度器动作 |
|---|---|---|
| 碎片化 | 集群总量够,但凑不齐一个 8 卡 NVLink 域 | Binpack 评分 + HyperNode 对齐 + 反碎片重调度 |
| Gang 死锁 | 两个 64 卡作业各拿到 32 卡,互相等对方释放 | PodGroup enqueue 准入:资源不够不入队 |
| 优先级通胀 | 业务团队都把自己的任务标 P0 | ResourceQuota 限高优 + 预算制 + 审计 |
| 抢占代价 | 千卡任务被抢,checkpoint + NCCL 重建浪费小时级 | reclaimable=false + 最短受保护时长 |
| Quota 虚占 | 队列 Quota 占了但没跑满,别人借不走 | 动态调整 deserved + 定期审计 + fairshare 衰减 |
| 多集群孤岛 | 自建 IDC + 多家云,各自有 Quota,资源不互通 | 统一入口层 + Volcano Global 多集群 Quota 治理 |
| 拓扑对齐漂移 | 故障重启后作业落在不同 NVLink 域,MFU 下降 | HyperNode 亲和 + 重启时优先回原域 |
| 观测盲区 | 队列账面占用高,但实际计算效率低 | ETTR / MFU / 有效运行时长作为一级指标 |
2.8 一天的典型工作
把这些落到实处,调度器研发的一天大概是这样:
- 上午:看 ETTR 看板,发现 pretrain-3t 队列的 MFU 从 48% 掉到 41%,排查发现昨晚 RL rollout 扩容时,把一个 NVLink 域里的 8 张卡抢走了 2 张,今天预训练任务 restart 后对不齐拓扑;修 reclaim 策略,在 HyperNode 层面加"域内原子性"约束;
- 下午:业务方提需求,要给新成立的 post-train 子团队开一个新队列,配 300 张 H20 的 deserved;核对集群 H20 总量、与 inference 队列的 guarantee 冲突、层级 parent 下的 deserved 守恒,写 Queue YAML 提 PR 走评审;
- 晚上:夜间潮汐触发,监控推理缩容是否顺利完成,评测任务是否如预期 backfill 上去;发现 sandbox 队列的 CPU Pod 启动 P99 从 800ms 恶化到 3s,查 NodeShard 边界,调整 Agentuler 与 Batch 的分界。
真正做调度器研发,80% 的时间不是写新调度算法,而是治理 Quota、修抢占策略、调拓扑约束、排查"为什么任务还没跑起来"——这些问题没有一个能靠背理论回答,全部要落到对自家集群负载特征的深度理解上。
三、把两层打通:理论是配置,实践是治理
Queue Quota 的理论模型(Volcano / Kueue / YuniKorn)提供了一个抽象框架,但真正落地时,Quota 不是配置出来,而是治理出来:
| 层面 | 理论上 | 实践中 |
|---|---|---|
| 字段约束 | guarantee ≤ deserved ≤ capability 是一条简单约束 | 每个字段背后都是一次与业务方的谈判——保底多少、可以借多少、上限卡在哪儿、被抢时是优雅退出还是硬驱逐 |
| 公平性 | DRF 保证了公平 | 公平还要叠加历史消耗衰减、时间预算(最短受保护时长)、审计与配额高优限制,才能避免"人人都是 P0"的博弈坍塌 |
| 状态归属 | Quota 是调度器内部状态 | Quota 是多集群、多调度器、多业务团队之间的契约,需要 CRD 化、有审计、有变更流程 |
| 资源维度 | CPU / memory / GPU 总数 | 卡型、NVLink 域、IB 域、CPU 沙盒份额、存储 IO 都要单独建模 |
| 借用语义 | borrowing / lending 是准入时判断 | 借出去的"什么时候还、被谁还、还了之后谁先用"是运行时治理问题 |
MiniMax 这种规模的 AI 公司,调度器研发的核心价值不是发明新算法,而是把开源调度器的抽象,精准映射到自家"混合算力 + 多类型负载 + 强拓扑约束 + 潮汐复用"的真实业务形态上——这一步没有捷径,只能靠对负载特征的持续观测和与业务团队的反复磨合。
四、结语
Queue Quota 这个话题,从面试题的角度看是"Volcano 的 guarantee / deserved / capability 是什么";从工程师的角度看,是一个横跨资源建模、拓扑感知、多调度器契约、组织博弈治理的复合问题。
把这条认知链路完整走下来,有三个分水岭级别的判断:
- 第一层:能不能分清 ResourceQuota 和 Queue Quota 的作用域差异;
- 第二层:能不能把 Quota 从"数字记账"扩展到"卡型 × 拓扑 × 沙盒"多维建模;
- 第三层:能不能理解 Quota 的本质是治理契约而非配置,理解抢占代价、Quota 虚占、优先级通胀这些"人的问题"。
准备 AI Infra 面试时,与其背 YAML 字段,不如把每个字段背后对应的业务谈判场景和失效模式各想一遍——这条链路能完整走下来的候选人,在任何一轮调度器相关的技术面里都很难被问倒。
延伸资源
- 相关阅读:《从零搞懂 GPU 集群调度》——本文的理论前置:Volcano/Kueue 选型、Gang Scheduling、拓扑感知调度的入门篇
- 相关阅读:《拆解那 11.9%:大规模训练集群的生产力损失度量与自动化治理》——ETTR 指标的来源与归因方法,本文潮汐复用目标(97% ETTR)的度量基础
- 相关阅读:《浅谈大规模集群的高性能网络容器化建设》——HyperNode 建模的物理对象:胖树拓扑、IB/RoCE 域与 NCCL
- 相关阅读:《VPC 与 Cilium ClusterMesh:多集群组网的地基与上层建筑》——"多集群孤岛"痛点的组网侧解法