概述
在 Kubernetes 上运行批处理作业时,常面临资源争抢和异构资源管理粗放的痛点。Kueue 作为一个工作负载排队管理器,在 Kubernetes 中负责管理 Job 等批处理任务的排队、准入和资源分配,确保集群资源公平、高效的被利用。它的核心工作原理是:当用户提交作业后,Kueue 会暂存(Suspend)该作业,而非立即创建 Pod。随后,Kueue 会根据预设的配额和调度策略,检查集群中的资源情况。一旦有足够的资源,Kueue 便会恢复(Resume)作业,使其得以正常执行。
工作原理
通过下面这个流程图,可以直观地理解 Kueue 中几个核心资源对象如何协同工作,以管理 Kubernetes 集群中的作业队列和资源分配。整个流程清晰地展示了一个作业从提交到执行的完整生命周期,以及各个对象在其中扮演的角色。
核心资源解析
1. ResourceFlavor
作用:定义“资源风味”,描述了集群中节点所能提供的资源类型和属性(如“高 CPU 型”、“带 GPU 型”、“抢占式实例”)。它通过标签选择器匹配具有特定属性的节点,是资源分配的基础。
角色定位:相当于资源的“菜单”,描述了集群里有什么“口味”的资源可供分配。ClusterQueue 的配额正是基于 ResourceFlavor 来定义的。
2. ClusterQueue
作用:这是一个集群级别的资源池。它聚合了一种或多种 ResourceFlavor 所描述的资源,并设置总体的资源配额、排队策略、公平共享及抢占规则。它是 Kueue 进行全局资源仲裁和决策的核心。
角色定位:相当于一个部门的“总预算”,规定了整个资源池的容量和分配规则。
3. LocalQueue
作用:这是一个命名空间级别的资源,作为用户提交工作的入口。每个 LocalQueue 都必须归属于一个特定的 ClusterQueue。它的主要功能是将来自其命名空间的作业请求转发到对应的 ClusterQueue 中。
角色定位:相当于部门内各个“项目组的预算申请入口”。它实现了多租户隔离(不同团队有自己的队列)和资源共享(所有队列消耗同一个 ClusterQueue 的预算)的统一。
4. Job 和 Workload
Job:这是用户直接提交的 Kubernetes 工作负载,例如 Kubernetes Job。
Workload:当 Kueue 发现一个 Job 带有特定的队列标签(如 kueue.x-k8s.io/queue-name: sample-queue)时,它会自动为这个 Job 创建一个对应的 Workload 对象 。这个 Workload 对象代表了在 Kueue 视野中进行排队、等待准入的工作单元。您可以通过查看 Workload 的状态来了解作业的排队情况。
安装 Kueue
前提条件
已创建了 TKE 集群(集群版本 >= 1.29),并且对集群有操作权限。
执行安装
2. 在应用市场中,应用场景选择调度。
3. 选择 kueue 进行创建,创建详情请参见 应用市场创建应用。如下图所示: 检查安装结果
1. 在应用管理中,查看应用状态是否为“正常”。如下图所示:
2. 单击应用名,进入应用详情页。如下图所示:
3. 查看工作负载状态是否为“正常”。如下图所示:
通过组件管理修改全局配置
TKE 组件管理现已支持 Kueue 作业队列管理器。安装 Kueue 后,可在组件管理列表中查看其状态,并通过修改全局配置入口对 Kueue 的关键参数进行调整。
前提条件
操作步骤
2. 在左侧导航栏选择组件管理,在列表中找到 Kueue 作业队列管理器。
3. 单击操作列的修改全局配置。
4. 在配置编辑页面,以 YAML 格式修改 Kueue 的全局配置参数,修改完成后单击完成。
常用配置项
|
waitForPodsReady.enable
| 是否开启 All-or-Nothing 调度。开启后,Kueue 在放行作业后会持续监控 Pod 就绪状态。 |
waitForPodsReady.timeout
| 等待 Pod 就绪的超时时间(如 2m)。超时后作业将被重新挂起,已创建的 Pod 将被删除,资源得以释放。 |
waitForPodsReady.blockAdmission
| 是否在等待 Pod 就绪期间阻止新作业准入,以防止多个作业互相阻塞导致死锁。 |
说明:
通过修改全局配置更新参数后,Kueue 的 controller-manager 将滚动重启以应用新配置,请在业务低峰期操作并注意对在途作业的影响。
实践场景
场景一:批调度(All or Nothing)
1. 场景描述
在 AI 模型训练、大数据分析等分布式计算场景中,一个作业(Job)通常包含多个需要协同工作的 Pod 任务。这些任务是一个整体,所有 Pod 必须同时获得资源才能开始有效计算。如果只有部分 Pod 被调度成功(即“部分调度”),会导致以下问题:
已调度的 Pod 空占资源却无法进行有效工作。
作业整体无法完成,造成集群资源的浪费。
需要手动干预来清理停滞的作业。
Kueue 确保了作业的 “All or Nothing” 调度:要么作业要求的所有 Pod 都被成功调度并开始运行,要么 Kueue 会阻止作业启动,或在一段时间后自动清理已创建但未能完全就绪的 Pod(需要进行 waitForPodsReady 配置),从而保障了集群资源的有效利用和作业的原子性。下面的序列图清晰地展示了一个需要4个 Pod 的 Job,从提交到最终实现 “All or Nothing” 调度的完整工作流程,特别是 waitForPodsReady 机制如何发挥作用:
2. 核心配置:启用 waitForPodsReady
这是实现 “All-or-Nothing” 语义的核心配置。它确保 Kueue 在放行一个 Job 后,会持续监控其 Pod 的创建和就绪状态。
检查与配置方法
1. 检查当前配置
查看 Kueue 配置,看是否有配置 waitForPodsReady,且没有被注释掉:
kubectl get cm -n kueue-system kueue-manager-config -o yaml | grep waitForPodsReady
2. 启用配置(如果尚未启用)
编辑 Kueue 的配置,在 data["controller_manager_config.yaml"] 下添加或修改以下配置:
apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
kind: ControllerManagerConfiguration
...
waitForPodsReady:
enable: true
timeout: 2m
blockAdmission: true
enable: true,启用该配置。这是前提。
timeout: 2m,关键设置。定义了 Kueue 放行 Job 后,等待其所有 Pod 变为 Ready 状态的最大时长。如果超时,Kueue 会将该 Job 重新挂起(suspend: true),并删除已创建的所有 Pod,释放被占用的资源。
blockAdmission: true,防止多个作业互相阻塞导致死锁,死锁场景可通过 此处 了解。 3. Kubernetes 对象创建
3.1 创建 ResourceFlavor
ResourceFlavor 是 Kueue 中定义的自定义资源,它定义了集群中可用的资源类型(如 CPU、GPU 机型)。
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: "default-flavor"
3.2 创建 ClusterQueue
ClusterQueue 是 Kueue 中定义的自定义资源,它定义了一个集群级别的资源池。通过 nominalQuota 来管理资源配额,是资源分配的核心。
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: cluster-queue
spec:
namespaceSelector: {}
resourceGroups:
- coveredResources: ["cpu"]
flavors:
- name: "default-flavor"
resources:
- name: "cpu"
nominalQuota: 2
3.3 创建 LocalQueue
LocalQueue 是 Kueue 中定义的自定义资源,作为在特定命名空间内用户提交工作的入口。它本身不持有配额,而是将工作负载转发到其绑定的 ClusterQueue 进行资源分配。
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
namespace: default
name: user-queue
spec:
clusterQueue: cluster-queue
3.4 提交作业
这是一个 Job 示例,它要求同时启动4个 Pod。通过在 Job 的标签中指定 kueue.x-k8s.io/queue-name,将其关联到特定的 LocalQueue,从而进入 Kueue 的调度队列。
apiVersion: batch/v1
kind: Job
metadata:
name: all-or-nothing-job
namespace: default
labels:
kueue.x-k8s.io/queue-name: user-queue
spec:
parallelism: 4
completions: 4
suspend: true
template:
spec:
containers:
- name: test
image: alpine:latest
command: [ "/bin/sh" ]
args: [ "-c", "sleep 36000000" ]
resources:
requests:
cpu: 1
restartPolicy: Never
该示例中,只有当指定的 LocalQueue 绑定的 ClusterQueue 中定义的配额能同时满足4个 Pod 的资源需求时,该 Job 才会被真正执行。
kueue.x-k8s.io/queue-name: user-queue:会使用该队列所属 ClusterQueue 中管理的配额进行资源判断。
parallelism 等于 completions 代表 “All or Nothing” 语义。
3.5 验证效果
查看作业状态:
kubectl get jobs all-or-nothing-job
当集群资源不够时,状态如下:
NAME STATUS COMPLETIONS DURATION AGE
all-or-nothing-job Suspended 0/4 37s
查看 Workload 状态(Workload 为 Kueue 创建的调度单元):
状态如下:
NAME QUEUE RESERVED IN ADMITTED FINISHED AGE
job-all-or-nothing-job-48f8a user-queue 62s
查看 ClusterQueue 状态:
kubectl get clusterqueue cluster-queue
状态如下:
NAME COHORT PENDING WORKLOADS
cluster-queue 1
场景二:资源异构场景下优化资源调度
1. 场景描述
当集群中存在多种专用硬件(如两种不同的 nvidia.com/gpu 资源,即 T4 和 A100 GPU)时,会有一个限制:系统可能无法区分它们,Pod 可能会被调度到任意一种硬件上。通过 Kueue 的 ResourceFlavor 和 ClusterQueue 机制,可以实现:优先使用 T4 GPU,在 T4 GPU 资源不足时,自动降级使用 A100,其工作流程如下:
2. 创建 ResourceFlavor
首先,定义两种 GPU 资源对应的 ResourceFlavor,它们通过 nodeSelector 匹配到对应标签的节点上。
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: gpu-t4
nodeSelector:
gpu-type: nvidia-t4
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: gpu-a100
nodeSelector:
gpu-type: nvidia-a100
3. 配置 ClusterQueue
这是实现优先级策略的核心。flavors 列表的顺序直接决定了优先级,Kueue 将严格按照从前到后的顺序尝试分配资源。
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: clusterqueue-gpu-shared
spec:
namespaceSelector: {}
resourceGroups:
- coveredResources: ["nvidia.com/gpu"]
flavors:
- name: gpu-t4
resources:
- name: "nvidia.com/gpu"
nominalQuota: 8
- name: gpu-a100
resources:
- name: "nvidia.com/gpu"
nominalQuota: 4
4. 创建 LocalQueue
LocalQueue 作为用户提交工作的入口,指向我们上面创建的 ClusterQueue。
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
namespace: default
name: gpu-workloads
spec:
clusterQueue: clusterqueue-gpu-shared
5. 提交 GPU 作业
apiVersion: batch/v1
kind: Job
metadata:
namespace: default
name: gpu-inference-job
labels:
kueue.x-k8s.io/queue-name: gpu-workloads
spec:
parallelism: 1
completions: 1
template:
spec:
containers:
- name: inference-container
image: nvidia/cuda:12.0-runtime-ubuntu20.04
command: ["python", "model-inference.py"]
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
restartPolicy: Never
6. 运行效果与优势
1. 可以细粒度地管理 GPU 资源的调度和使用。
2. 当一个 Job 被调度时,Kueue 会优先检查 clusterqueue-gpu-shared 中 gpu-t4 的配额。只要配额足够,Job 就会被调度到 T4 节点上,如果 T4 的配额已用尽,但 gpu-a100 尚有配额,新提交的 Job 将会被 Kueue 准入并调度到 A100 节点上。
3. 管理员只需要维护一个 ClusterQueue,通过调整 flavors 的顺序和配额即可轻松改变资源分配策略,无需用户修改应用配置。
场景三:跨租户资源共享(移除白名单限制)
1. 场景描述
在多租户 Kubernetes 集群中,不同团队(租户)通常独立管理各自的 ClusterQueue 和资源配额。在之前的版本中,ClusterQueue 之间的资源借用(通过 cohort 机制)受白名单限制——只有预先配置在白名单中的租户才能跨租户借用资源,增加了运维复杂性。
现已移除跨租户白名单限制,任意属于同一 cohort 的 ClusterQueue 均可相互借用空闲资源,无需额外白名单配置,多租户资源调度更加灵活。
2. 工作机制
Kueue 通过 cohort 字段将多个 ClusterQueue 关联为一个资源共享组:
同一 cohort 内的 ClusterQueue 可借用彼此的空闲配额。
移除白名单限制后,加入相同 cohort 即可自动获得跨租户借用能力,无需管理员维护白名单。
3. 配置示例
以下示例展示了两个团队的 ClusterQueue 共享同一 cohort 的配置:
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: team-a-queue
spec:
cohort: shared-pool
namespaceSelector:
matchLabels:
team: a
resourceGroups:
- coveredResources: ["cpu", "memory"]
flavors:
- name: "default-flavor"
resources:
- name: "cpu"
nominalQuota: 10
- name: "memory"
nominalQuota: 20Gi
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: team-b-queue
spec:
cohort: shared-pool
namespaceSelector:
matchLabels:
team: b
resourceGroups:
- coveredResources: ["cpu", "memory"]
flavors:
- name: "default-flavor"
resources:
- name: "cpu"
nominalQuota: 10
- name: "memory"
nominalQuota: 20Gi
效果:当团队 A 的队列有空闲 CPU 时,团队 B 的作业可自动借用,无需白名单授权,有效提升集群整体资源利用率。
4. 注意事项
跨租户资源借用仅在源 ClusterQueue 有空闲配额时生效;一旦源队列自身有新作业提交,借用的资源会被抢回。
如需限制特定队列不被借用,可在 ClusterQueue 的 flavorFungibility 或 preemption 字段中配置相应策略。