Kubernetes 官方出品:一个 Controller 搞定 Job 排队和资源配额

kueue-p1-intro.jpg

多个团队共用一个 Kubernetes 集群,A 团队提交了一批训练任务,几十张 GPU 很快就被占满;B 团队新提交的 Job 只能一直 Pending。 因为,而是 Kubernetes 原生采用"先到先得"的调度方式,没有 Job 队列,也没有多租户配额管理。

Kueue 正是 Kubernetes 官方为此提供的解决方案。它不替换 kube-scheduler,只负责 Job 的排队和准入,在此基础上实现资源配额管理和公平调度。

1. Kueue 简介

Kueue 是什么?

Kueue 是 Kubernetes SIGs 维护的官方 Job 级队列管理系统,负责决定 Job 何时准入(Admit)、何时被驱逐(Preempt),核心目标是管理资源配额和多租户公平调度。

和 Volcano 最大的区别:Kueue 不替换 kube-scheduler,它只管"排队和准入",调度还是交给原生调度器。

1
2
Volcano = 自定义调度器 + 队列管理 + 作业管理
Kueue   = 队列管理 + 配额管理(调度交给 kube-scheduler)

Kueue 能做什么?

  • 多租户配额管理:通过 ClusterQueue 为不同团队划分资源配额

  • 公平调度:基于 Dominant Resource Shares(DRS)的公平共享算法,防止资源被单一团队长期独占

  • 队列排队:Job 提交后按优先级排队,配额不足时挂起等待

  • Cohort 借调:同一 Cohort 内的 ClusterQueue 可以借用彼此空闲配额,用完归还

  • 弹性配额:支持 nominalQuota(保底)+ borrowingLimit(借用上限)+ lendingLimit(出借上限)三层额度控制

  • 标准兼容:原生支持 K8s Job / JobSet / PyTorchJob / TFJob / RayJob 等,无需改 Job YAML

为什么需要 Kueue?

或者说 Kueue 解决了什么原生 K8s 解决不了的问题?

原生 Kubernetes 的资源管理是"先到先得",没有队列的概念:

场景原生 KubernetesKueue优势
配额管理ResourceQuota 粗粒度控制ClusterQueue 细粒度配额(按 GPU 型号/节点池划分)精确到资源类型和 Flavor
多租户公平先到先得,无法保障公平Fair Sharing 基于 DRS 的公平分配防止资源独占
Job 排队Pending 后只能等支持优先级队列、FIFO 策略按优先级有序调度
跨团队借调不支持Cohort 机制,空闲配额自动借出、按需归还提高集群利用率

举一个典型场景:公司有两个 AI 团队共享一个 GPU 集群,各有 50% 的 A100 配额。团队 A 没任务时,团队 B 可以借用全部 GPU;团队 A 提交任务后,Kueue 会通过 preemption 让团队 B 释放借用的资源,回到公平状态。

原生 K8s 做不到这一点。

Kueue vs Volcano 快速对比

维度KueueVolcano
定位队列 + 配额管理(轻量)批处理调度平台(重量)
架构不替换调度器,旁路管理自定义调度器,替换 kube-scheduler
归属Kubernetes SIGs 官方CNCF(华为发起)
部署复杂度一个 ControllerScheduler + Controller + Admission 三个组件
Gang Scheduling通过 All-or-Nothing with Ready Pods(超时机制)原生 Gang Scheduling
Job API标准 K8s Job(自动创建 Workload)自定义 VolcanoJob(vcjob)

更多参考:Kueue 官方文档Volcano 系列文章

2. 核心概念

在部署之前,先理解 Kueue 的 5 个核心对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
┌─────────────────────────────────────────────────────┐
│                     Cohort                           │
│  ┌─────────────────┐   ┌─────────────────┐          │
│  │  ClusterQueue A  │   │  ClusterQueue B  │         │
│  │  (nominal: 5GPU) │   │  (nominal: 5GPU) │         │
│  │  borrowLimit: 5  │   │  borrowLimit: 5  │         │
│  └────────┬─────────┘   └────────┬─────────┘         │
│           │                      │                   │
│     ResourceFlavor: A100 / T4 / CPU                  │
└─────────────────────────────────────────────────────┘
         │                      │
    LocalQueue A            LocalQueue B    ← Namespaced
      (team-a)               (team-b)
         │                      │
    Workload 1             Workload 2       ← 用户的 Job
  • ResourceFlavor:Flavor,代表不同类型的资源(如 A100 vs T4、Spot vs On-Demand),可以绑定 nodeLabels / taints

  • ClusterQueue:集群级队列,定义资源配额(nominalQuota / borrowingLimit / lendingLimit),是配额管理的核心

  • LocalQueue:命名空间级队列,用户直接和它打交道,指向一个 ClusterQueue

  • Cohort:ClusterQueue 的组,同组内可以互相借调空闲配额

  • Workload:Kueue 的调度单元,Kueue 会为每个 Job 自动创建对应的 Workload,用户一般不需要手动创建

调度流程

一个 Job 从提交到运行,经过 Kueue 的完整流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
用户提交 Job (带 kueue.x-k8s.io/queue-name 标签)
Kueue 自动创建 Workload 对象
Workload 进入 LocalQueue → 找到对应的 ClusterQueue
检查 ClusterQueue 是否有足够配额?(允许从 Cohort 借调)
  ├── 没有 → 询问 Cluster Autoscaler 能否扩容? → 等待扩容
  ↓ (有配额 或 扩容完成)
检查 ClusterQueue 是否配置了 AdmissionCheck?
  ├── 有 → 等待所有 Check 通过 → 最终准入
  └── 没有 → 直接最终准入
Kueue 注入 nodeAffinity/tolerations (锁定算力)
Job Controller 创建 Pod
Kube Scheduler 分配 Pod 到具体节点
节点资源不足? → 触发 Cluster Autoscaler 真正扩容节点 (Provision)

kueue-workflow.svg

关键点:Kueue 只负责"准入"决策,Pod 真正调度到哪个节点还是 kube-scheduler 决定。

3. Kueue 部署

Kueue 部署非常轻量,只需要一个 Controller。

版本兼容性

Kueue 要求 Kubernetes 1.29+,本文使用 Kubernetes v1.36.1 部署最新的 Kueue v0.18.1

安装

官方提供了两种安装方式,推荐 Helm:

方式一:kubectl 直接安装

1
kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.18.1/manifests.yaml

方式二:Helm 安装(推荐)

1
2
3
4
5
helm install kueue oci://registry.k8s.io/kueue/charts/kueue \
  --version=0.18.1 \
  --namespace kueue-system \
  --create-namespace \
  --wait --timeout 300s

无法访问 registry.k8s.io 可以从 GitHub 下载 chart 包安装:

1
2
3
4
helm install kueue https://github.com/kubernetes-sigs/kueue/releases/download/v0.18.1/kueue-0.18.1.tgz \
  --namespace kueue-system \
  --create-namespace \
  --wait --timeout 300s

卸载

1
helm uninstall kueue --namespace kueue-system

验证

1
kubectl -n kueue-system get po
1
2
NAME                                       READY   STATUS    RESTARTS   AGE
kueue-controller-manager-665fc6d58-whxcz   1/1     Running   0          60m

一个 Pod 就搞定了,这就是 Kueue 轻量的地方。

4. 快速上手:一个完整的 Demo

接下来我们跑一个最小化 Demo:创建 ResourceFlavor → ClusterQueue → LocalQueue → 提交 Job,完整走一遍 Kueue 的工作流程。

4.1 创建基础资源

Kueue 使用三种资源来管理作业排队:

  • ResourceFlavor:描述作业可用的硬件配置
  • ClusterQueue:定义资源池的配额(CPU、内存等)
  • LocalQueue:用户提交作业的命名空间级队列,映射到 ClusterQueue
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
metadata:
  name: "default-flavor"
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ClusterQueue
metadata:
  name: "cluster-queue"
spec:
  namespaceSelector: {}  # 允许所有命名空间
  resourceGroups:
  - coveredResources: ["cpu", "memory"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 9
      - name: "memory"
        nominalQuota: 9Gi
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: LocalQueue
metadata:
  namespace: "default"
  name: "user-queue"
spec:
  clusterQueue: "cluster-queue"
1
kubectl apply -f queue.yaml

4.2 验证队列状态

1
2
3
4
5
# ClusterQueue 是否激活
kubectl get clusterqueue cluster-queue -o wide

# LocalQueue 是否就绪
kubectl get localqueue user-queue -n default
1
2
3
4
5
6
7
root@lixd-dev-4:~# kubectl get clusterqueue cluster-queue -o wide
NAME            COHORT   STRATEGY         PENDING WORKLOADS   ADMITTED WORKLOADS
cluster-queue            BestEffortFIFO   0                   0

root@lixd-dev-4:~# kubectl get localqueue user-queue -n default
NAME         CLUSTERQUEUE    PENDING WORKLOADS   ADMITTED WORKLOADS
user-queue   cluster-queue   0                   0

队列创建成功,当前没有 Workload。

4.3 提交 Job

注意看,这里用的就是标准的 K8s Job,只需要加一个 label 就能接入 Kueue:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: user-queue  # 接入 Kueue 的关键
spec:
  parallelism: 3
  completions: 3
  template:
    spec:
      containers:
      - name: dummy-job
        image: registry.k8s.io/e2e-test-images/agnhost:2.53
        command: ["/bin/sh"]
        args: ["-c", "sleep 30"]
        resources:
          requests:
            cpu: "1"
            memory: "200Mi"
      restartPolicy: Never
1
kubectl apply -f job.yaml

4.4 验证

1
2
3
4
5
# 查看 Kueue 自动创建的 Workload
kubectl get workloads -n default

# 查看 Pod
kubectl get pods -n default
1
2
3
4
5
6
7
8
9
root@lixd-dev-4:~# kubectl get workloads -n default
NAME                   QUEUE        RESERVED IN     ADMITTED   FINISHED   AGE
job-sample-job-555d8   user-queue   cluster-queue   True                  9s

root@lixd-dev-4:~# kubectl get pods -n default
NAME               READY   STATUS    RESTARTS   AGE
sample-job-j6xdp   1/1     Running   0          14s
sample-job-k4mtm   1/1     Running   0          14s
sample-job-nvjkz   1/1     Running   0          14s

可以看到 Kueue 自动为 Job 创建了对应的 Workload,Workload 状态为 Admitted(已准入),3 个 Pod 正常运行。

4.5 等待任务完成

1
2
# 等待任务完成
kubectl get jobs -n default -w
1
2
3
root@lixd-dev-4:~# kubectl get jobs -n default -w
NAME         STATUS     COMPLETIONS   DURATION   AGE
sample-job   Complete   3/3           34s        44s

Job 完成后,Workload 也会被标记为 Finished:

1
kubectl get workloads -n default
1
2
3
root@lixd-dev-4:~# kubectl get workloads -n default
NAME                   QUEUE        RESERVED IN     ADMITTED   FINISHED   AGE
job-sample-job-555d8   user-queue   cluster-queue   True       True       49s

到这里,我们完整走了一遍 Kueue 的工作流程:创建队列 → 提交 Job → Kueue 自动准入 → Pod 运行 → 任务完成

整个过程中,我们用的是标准的 K8s Job,唯一的变化就是加了一个 kueue.x-k8s.io/queue-name 标签。这就是 Kueue “不替换调度器、旁路管理"设计哲学的体现。

5. 小结

Kueue 是 Kubernetes SIGs 官方出品的 Job 队列与配额管理系统,它的核心设计哲学是”只管排队和准入,不动调度器",这让它非常轻量且对现有集群侵入性极小。

本文介绍了 Kueue 的背景、核心概念和部署方式,并通过一个最小化 Demo 走完了"创建队列 → 提交 Job → 自动准入 → 任务完成"的完整流程。

下一篇我们将深入解析 Kueue 的五大核心对象(ResourceFlavor / ClusterQueue / LocalQueue / Cohort / Workload),搞清楚它们各自的职责和配置细节。

0%