痛点:AI 训练任务抢 GPU,集群资源乱成一锅粥
跑过 AI/ML 训练任务的运维都知道这个痛:团队 A 提交了一个 8 卡 GPU 的大模型微调任务,团队 B 同时提交了 20 个数据预处理 Job,结果所有任务一起抢资源,谁都跑不动。更头疼的是:
- 原生 Job 没有排队机制:
kubectl apply之后 Pod 直接创建,不管资源够不够,Pending 一堆 - 多团队资源配额靠 ResourceQuota 太粗糙:只能限制上限,不能做公平调度和借用
- GPU 等昂贵资源没有优先级抢占:低优先级任务占着 GPU 不释放,高优先级任务干等
- 批处理和在线服务混部时互相干扰:没有统一的准入控制
Kubernetes 原生的 kube-scheduler 擅长 Pod 到 Node 的调度,但对"这个 Job 该不该现在运行"这个问题,它管不了。这正是 Kueue 要解决的。
方案:Kueue — Kubernetes SIG 官方的作业队列管理器
Kueue 是 Kubernetes SIG Scheduling 下的官方子项目,不替换任何 K8s 原生组件,而是在 Job 创建和 Pod 调度之间加了一层"准入门控":
- Job 挂起 → 提交到 Kueue 队列 → Kueue 判断配额和优先级 → 放行(unsuspend)→ Pod 创建 → kube-scheduler 正常调度
核心概念只有三个:
| 概念 | 作用 | 类比 |
|---|---|---|
| ResourceFlavor | 定义资源类型(如 gpu-a100、gpu-t4、spot-cpu) |
不同规格的服务器池 |
| ClusterQueue | 集群级队列,绑定资源配额和抢占策略 | 资源池 + 调度规则 |
| LocalQueue | 命名空间级队列,用户提交 Job 的入口 | 团队的提交窗口 |
工作流:用户在自己命名空间的 LocalQueue 提交 Job → LocalQueue 关联到 ClusterQueue → Kueue 按配额、优先级、公平分享策略决定准入顺序。
实操:4 步部署 Kueue 并管理 GPU 训练队列
第 1 步:安装 Kueue
# 推荐使用官方 release manifest,当前稳定版 v0.10.x
KUEUE_VERSION=v0.10.0
kubectl apply --server-side -f \
https://github.com/kubernetes-sigs/kueue/releases/download/${KUEUE_VERSION}/manifests.yaml
# 验证安装
kubectl -n kueue-system get pods
# 应看到 kueue-controller-manager Running
第 2 步:定义 ResourceFlavor 和 ClusterQueue
# resource-flavors.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: gpu-a100
spec:
nodeLabels:
cloud.google.com/gke-accelerator: nvidia-tesla-a100
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: default-cpu
spec: {} # 匹配所有 CPU 节点
# cluster-queue.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: ai-training-queue
spec:
queueingStrategy: BestEffortFIFO # 允许小任务插队
preemption:
reclaimWithinCohort: Any
withinClusterQueue: LowerPriority
resourceGroups:
- coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
flavors:
- name: gpu-a100
resources:
- name: "nvidia.com/gpu"
nominalQuota: 16 # 常规配额 16 卡
borrowingLimit: 8 # 最多再借 8 卡
- name: "cpu"
nominalQuota: 64
- name: "memory"
nominalQuota: 256Gi
- name: default-cpu # GPU 不够时退化到 CPU
resources:
- name: "cpu"
nominalQuota: 128
- name: "memory"
nominalQuota: 512Gi
关键配置说明:
- BestEffortFIFO:严格 FIFO 可能导致大任务阻塞整个队列,BestEffort 允许资源足够时小任务先跑
- borrowingLimit:配合 Cohort(队列组)实现跨团队资源借用
- Flavor 顺序有优先级:先尝试 gpu-a100,配额满了自动尝试 default-cpu(Flavor Fungibility)
第 3 步:创建 LocalQueue 并提交任务
# local-queue.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
namespace: team-ml
name: ml-jobs
spec:
clusterQueue: ai-training-queue
# training-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
namespace: team-ml
name: llm-finetune-0919
labels:
kueue.x-k8s.io/queue-name: ml-jobs # 指定队列
spec:
suspend: true # 关键:必须以挂起状态提交,交给 Kueue 管理
parallelism: 4
completions: 4
template:
spec:
containers:
- name: trainer
image: nvcr.io/nvidia/pytorch:24.05-py3
command: ["torchrun", "--nproc_per_node=1", "train.py"]
resources:
requests:
nvidia.com/gpu: 1
cpu: "8"
memory: "32Gi"
limits:
nvidia.com/gpu: 1
restartPolicy: Never
kubectl apply -f resource-flavors.yaml
kubectl apply -f cluster-queue.yaml
kubectl apply -f local-queue.yaml
kubectl apply -f training-job.yaml
# 查看队列状态
kubectl get clusterqueue ai-training-queue -o wide
# 查看工作负载准入情况
kubectl get workloads -n team-ml
第 4 步:配置优先级抢占
# workload-priority.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: WorkloadPriorityClass
metadata:
name: production-training
value: 1000
description: "生产级模型训练,可抢占低优先级任务"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: WorkloadPriorityClass
metadata:
name: experiment
value: 100
description: "实验性训练,可被抢占"
在 Job 中通过 label 指定优先级:
metadata:
labels:
kueue.x-k8s.io/queue-name: ml-jobs
kueue.x-k8s.io/priority-class: production-training
当高优先级任务到达但 GPU 配额已满时,Kueue 会自动挂起低优先级任务(delete pods),腾出资源给高优先级任务。被抢占的任务回到队列等待重新准入。
3 个常见坑
坑 1:Job 忘记设置 suspend: true
这是最常见的错误。如果 Job 提交时没有 suspend: true,Pod 会直接创建,完全绕过 Kueue 的准入控制,配额形同虚设。
解决方案:用 Kyverno 或 ValidatingAdmissionPolicy 强制检查:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-kueue-suspend
spec:
validationFailureAction: Enforce
rules:
- name: check-suspend
match:
resources:
kinds: ["Job"]
namespaceSelector:
matchLabels:
kueue-managed: "true"
validate:
message: "带 kueue queue-name label 的 Job 必须设置 suspend: true"
pattern:
spec:
suspend: true
坑 2:Cohort 借用导致资源超卖
多个 ClusterQueue 放在同一个 Cohort 中可以互相借用空闲配额,但如果 borrowingLimit 没设上限,一个团队的突发任务可能吃光整个 Cohort 的资源。
解决方案:每个 ClusterQueue 必须设置 borrowingLimit,同时配合 lendingLimit 控制出借上限:
# 最多借入 8 卡 GPU,最多出借 4 卡
- name: gpu-a100
resources:
- name: "nvidia.com/gpu"
nominalQuota: 16
borrowingLimit: 8
lendingLimit: 4
坑 3:Cluster Autoscaler 扩容慢导致 Workload 超时
Kueue 准入放行后,如果节点不够,Pod 还是 Pending 等 Cluster Autoscaler 扩容。对于 GPU 节点,扩容可能需要 5-10 分钟。
解决方案:启用 ProvisioningRequest AdmissionCheck,让 Kueue 在准入前就触发预扩容:
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
name: provision-gpu-nodes
spec:
controllerName: kueue.x-k8s.io/provisioning-request
parameters:
apiGroup: kueue.x-k8s.io
kind: ProvisioningRequestConfig
name: gpu-provision-config
配合 Karpenter 的 NodePool,可以将 GPU 节点预热时间从分钟级缩短到秒级。
总结
Kueue 解决了 Kubernetes 原生 Job 管理的核心缺失——作业级准入控制和队列调度。适用场景:
- AI/ML 团队共享 GPU 集群:配额管理 + 优先级抢占 + 公平分享
- 批处理密集型工作负载:数据 ETL、CI/CD 批量构建、科学计算
- 混合工作负载集群:训练任务和推理服务在同一集群,Kueue 管批处理,原生 HPA 管在线服务
选型建议:如果你的 K8s 集群跑的是简单 CronJob,不需要 Kueue;但只要涉及 多团队共享 + GPU/昂贵资源 + 批量并行任务,Kueue 是目前 Kubernetes 生态里最正统的方案——SIG 官方维护,兼容所有主流 AI 训练框架(PyTorch、Kubeflow、Ray),生产可用。