AI 训练作业有个与在线服务截然不同的脾气:它不怕等,但必须「整组资源到齐才开工」。一个要求 8 张 GPU 的分布式训练任务,在裸 Kubernetes 上可能 5 张先起来的 Pod 干等剩下 3 张,把 5 张卡的显存和调度槽位全部占死——这就是 gang scheduling 缺位导致的死锁。Kubernetes 官方的批调度系统 Kueue(kubernetes-sigs 项目,文档最新为 v0.19/v0.20 系列)专门解决这个问题:它不替 kube-scheduler 调度 Pod,而是先回答「这个作业现在够不够格开工」。
核心设计:suspend 一字段,撬动整个排队系统
Kueue 最聪明的地方在于没有改调度器。工作流是这样的:
- 用户提交 Job(或 RayJob、JobSet、Kubeflow 训练作业等),打上队列标签
kueue.x-k8s.io/queue-name; - Kueue 为作业创建一个 Workload 对象——配额管理的最小单元,代表「这个作业声称要多少资源」;
- Kueue 立即把 Job 的
suspend字段置为true:Pod 一个都不启动,作业在队列里排队; - 轮到它且配额够时,Kueue 预留配额并把
suspend置回false,控制器才创建 Pod,真正的 Pod 摆放仍由原生 kube-scheduler 完成; - 作业结束,配额释放,队列推进。
这个设计把「准不准」与「怎么摆」解耦:排队、配额、抢占这些批处理语义由 Kueue 管理,而自动扩容、Pod 调度、作业生命周期仍然交给 cluster-autoscaler、kube-scheduler 和 kube-controller-manager——官方 Overview 明确说了设计原则是「不重复 Kubernetes 成熟组件的功能」。
配额模型:三层结构
Kueue 的配额体系是三层树:
- LocalQueue:命名空间内的提交入口。用户只能往本命名空间的 LocalQueue 提交,它把作业导向某个 ClusterQueue;
- ClusterQueue:集群级的资源池,按 ResourceFlavor(资源「风味」——比如
gpu-a100与gpu-h100是两种 flavor)划分配额。同一种资源可以配置多个 flavor 并设置可替代性(Flavor Fungibility):A100 用尽时允许用 H100 准入; - Cohort:把多个 ClusterQueue 组成「借贷圈子」,成员在配额紧张时可以互相借用,闲置配额自动让给忙的队列。
一个 ClusterQueue 的大致形态(示意,字段以所用版本文档为准):
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: research-cq
spec:
cohort: gpu-cohort
resourceGroups:
- coveredResources:
- nvidia.com/gpu
flavors:
- name: gpu-a100
resources:
- name: nvidia.com/gpu
nominalQuota: 8
这套结构对应了公司的真实组织:平台方定 ClusterQueue 总盘子,各团队拿 LocalQueue,Cohort 表达「兄弟团队闲时可以借」的弹性。需要硬隔离就把 Cohort 拆小,需要高利用率就组大圈——利用率与隔离性的平衡钮。
公平共享与抢占:忙时不饿死,闲时不浪费
平时各队用各自的配额相安无事,麻烦都在资源紧张时。Kueue 的两个机制管这个:
公平共享(Fair Sharing):Cohort 内部按各 ClusterQueue 的名义配额比例瓜分借来的共享容量,谁平时「被借得多」在紧张时优先收回。Kueue 给每个 ClusterQueue 算一个 share 值(对资源的占用超过名义配额越多、share 越大)。
抢占(Preemption):资源不够时,高优先级作业可以抢走低优先级作业的配额。官方文档明确了两条规则——优先抢占借来的配额(把别人的闲置还回去),再动自己的名义配额;多个候选时,优先抢占 share 值最高的队列,也就是平时占共享容量最多、最「肥」的那个。这样抢占自然朝着「恢复公平」的方向走。
优先级用 WorkloadPriorityClass 定义——它独立于 Pod 的 PriorityClass,管的是「准入顺序与被抢顺序」,而不是 Pod 间调度优先级,两套体系不打架。
值得知道的其他能力
- 部分准入(partial admission):允许作业以缩小并行度的方式跑起来——8 卡的作业在只有 6 卡库存时可以按 6 卡启动,官方描述为「基于可用配额以降低并行度运行作业」;代价是训练时间变长或结果变化,需要作业本身支持;
- AdmissionChecks:准入钩子。典型用法是对接 cluster-autoscaler 的 ProvisioningRequest——配额够了还不够,先让 autoscaler 把节点扩出来,节点就绪再放行;
- MultiKueue:多集群分发,Kueue 自己找有容量的集群把作业送过去;
- 拓扑感知调度:让一个作业的 Pod 尽量落进同拓扑域(同机架/同交换机),减少分布式训练的通信开销;
- 生态集成:Job、JobSet(ML 训练的标准打包方式)、RayJob/RayCluster、Kubeflow 训练算子、AppWrapper 都有内建集成,普通 Pod 与 Pod Group 也能纳管;
- DRA 配额(alpha):对通过 DRA 请求的 GPU 设备做配额管理(Consumable capacity),与上一篇《Kubernetes 怎么把 GPU 分给容器》里的 DRA GA 趋势相呼应。
落地建议
- 先定配额模型再装软件:ClusterQueue 怎么划、Cohort 圈多大,本质是组织协作问题。建议先画「名义配额 + 借贷上限」表,再翻译成 YAML;
- 训练与推理可以同管:Kueue 也能纳管 Deployment/StatefulSet 形态的服务负载,让推理的常驻资源与训练的弹性需求在同一本账上打架,避免「训练夜里抢光推理的卡」靠人肉协调;
- gang 语义靠 Workload 整体准入保证:不要绕过 Kueue 直接改 suspend,抢跑的 Pod 会破坏整组到齐的假设;
- 版本升级看 release notes:Kueue 迭代快(v0.19/v0.20 系列),行为修复频繁(如状态写盘去重、孤儿 Workload 清理),升级前过一遍 GitHub Releases。
Kueue 解决的是「AI 批任务的门卫」问题:谁先进、进多少、什么时候请出去。它和 GPU 共享(时间片/MIG)与 DRA 并不重叠——一个管排队记账,一个管卡上怎么切。三件套配齐,Kubernetes 上的 AI 算力管理才算成型。
读者留言
COMMENTS 暂无还没有留言,来说第一句?