Kueue 管理 AI 批任务:配额借用、准入与抢占边界
训练与批量推理不应靠创建大量 Pending Pod 抢 GPU。本文用 Kueue 的 LocalQueue、ClusterQueue 和 ResourceFlavor 建立租户准入、配额借用、抢占与回滚流程。
本文要点
- 主题聚焦:AI、大模型、DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
训练或离线推理直接创建大量 Pod,会把配额竞争藏进 Pending。Kueue 先挂起受管 Job,再按 Workload 准入整组资源;任务重试和幂等仍按 LLM 批量推理 设计。
先固定控制面的版本合同
Kueue API 必须服从已安装 CRD。当前官网示例使用 kueue.x-k8s.io/v1beta2,旧集群可能只有 v1beta1;CI 用目标集群做 discovery 与 server-side dry-run:
kubectl api-resources --api-group=kueue.x-k8s.io
kubectl explain clusterqueue.spec --api-version=kueue.x-k8s.io/v1beta2
kubectl explain localqueue.spec --api-version=kueue.x-k8s.io/v1beta2
kubectl apply --server-side --dry-run=server -f queues.yaml
不匹配时使用对应 schema,不能只改版本字符串;升级前确认 controller、CRD conversion 与 webhook 就绪。
用三层对象划清所有权
ResourceFlavor 表示一种可替代资源形态,例如 A100 节点池;ClusterQueue 是平台管理员维护的集群级配额池;LocalQueue 位于租户 namespace,只引用获准使用的 ClusterQueue。一个最小的当前版配置如下:
apiVersion: kueue.x-k8s.io/v1beta2
kind: ResourceFlavor
metadata:
name: a100
spec:
nodeLabels:
accelerator.example.com/model: a100
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: ClusterQueue
metadata:
name: team-a-cq
spec:
namespaceSelector:
matchLabels:
platform.example.com/kueue: enabled
cohortName: ai-shared
preemption:
reclaimWithinCohort: LowerPriority
borrowWithinCohort:
policy: LowerPriority
withinClusterQueue: LowerPriority
resourceGroups:
- coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
flavors:
- name: a100
resources:
- name: cpu
nominalQuota: "64"
- name: memory
nominalQuota: 512Gi
- name: nvidia.com/gpu
nominalQuota: 8
borrowingLimit: 2
---
apiVersion: kueue.x-k8s.io/v1beta2
kind: LocalQueue
metadata:
name: batch
namespace: team-a
spec:
clusterQueue: team-a-cq
Job 用标签 kueue.x-k8s.io/queue-name: batch 选择 LocalQueue,并明确 requests;Kueue 会创建 Workload 并管理 suspension。租户 RBAC 只允许提交 Job 和读取本 namespace 的队列状态,不能修改 ClusterQueue、ResourceFlavor 或优先级类,这与 AI 平台租户配额隔离 同样重要。
清单中的 selector 要求先执行 kubectl label namespace team-a platform.example.com/kueue=enabled --overwrite。未带标签的 namespace 必须保持 inadmissible;能修改 namespace 标签的主体等同拥有队列准入权。
借用不是永久扩容
nominalQuota 是名义份额,同 cohort 的队列可借空闲配额,borrowingLimit 限制借入量。GPU 型号和故障成本用不同 flavor 表达,任务只声明可容忍的 flavor。
上述 policy 只允许较高优先级回收低优先级 victim。平台创建受控 PriorityClass 或 WorkloadPriorityClass,租户只能引用获准等级;checkpoint 与输出幂等通过后才允许抢占,并记录 victim、原因与恢复点。后台任务队列 的业务优先级不能直接变成集群抢占权。
以准入状态做验收
测试名义配额、借用上限与超限排队,再由受控高优先级任务触发抢占,核对 victim 低于发起者、原因、checkpoint 和幂等恢复。另从无 selector 标签的 namespace 提交 Job,断言始终不准入且不创建计算 Pod。
kubectl get clusterqueues -o wide
kubectl get localqueues -A
kubectl get workloads -A
kubectl describe workload -n team-a <name>
kubectl get events -n team-a --sort-by=.lastTimestamp
告警区分 flavor、配额、admission check 与 webhook 故障;等待时长、借用率和驱逐率进入 LLM SLO 监控。
停止准入后再回滚
错误配额可能造成集中抢占时,先设 stopPolicy: Hold,停止新准入并让已准入任务结束:
kubectl patch clusterqueue team-a-cq --type=merge \
-p '{"spec":{"stopPolicy":"Hold"}}'
导出 Workload 状态,修正 flavor、quota、cohort 或 preemption 后 dry-run,再恢复准入。不要删除队列或 Pending Job。控制器只能回滚到兼容的 CRD 组合;已运行任务先按 LLM 灾难恢复 验证 checkpoint。
延伸阅读
Related