Kueue 与 AI 批任务调度:在 Kubernetes 上给训练作业排队、记账与抢占

AI 训练作业有个与在线服务截然不同的脾气:它不怕等,但必须「整组资源到齐才开工」。一个要求 8 张 GPU 的分布式训练任务,在裸 Kubernetes 上可能 5 张先起来的 Pod 干等剩下 3 张,把 5 张卡的显存和调度槽位全部占死——这就是 gang scheduling 缺位导致的死锁。Kubernetes 官方的批调度系统 Kueue(kubernetes-sigs 项目,文档最新为 v0.19/v0.20 系列)专门解决这个问题:它不替 kube-scheduler 调度 Pod,而是先回答「这个作业现在够不够格开工」。

核心设计:suspend 一字段,撬动整个排队系统

Kueue 最聪明的地方在于没有改调度器。工作流是这样的:

  1. 用户提交 Job(或 RayJob、JobSet、Kubeflow 训练作业等),打上队列标签 kueue.x-k8s.io/queue-name;
  2. Kueue 为作业创建一个 Workload 对象——配额管理的最小单元,代表「这个作业声称要多少资源」;
  3. Kueue 立即把 Job 的 suspend 字段置为 true:Pod 一个都不启动,作业在队列里排队;
  4. 轮到它且配额够时,Kueue 预留配额并把 suspend 置回 false,控制器才创建 Pod,真正的 Pod 摆放仍由原生 kube-scheduler 完成;
  5. 作业结束,配额释放,队列推进。

这个设计把「准不准」与「怎么摆」解耦:排队、配额、抢占这些批处理语义由 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 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?