Kubernetes v1.36:工作负载感知调度再进一步
AI/ML 和批处理工作负载带来了独特的调度挑战,已经超出了简单逐个 Pod 调度的范畴。 在 Kubernetes v1.35 中,我们引入了首批工作负载感知调度改进, 其中包括基础性的 Workload API、基于 Pod 框架构建的基本编组调度支持, 以及用于高效处理相同 Pod 的机会性批处理特性。
Kubernetes v1.36 通过清晰分离 API 关注点,引入了一项重要的架构演进:
Workload API 充当静态模板,而新的 PodGroup API 负责处理运行时状态。
为了支持这一点,kube-scheduler 提供了新的 PodGroup 调度周期,
支持以原子方式处理工作负载,并为未来增强铺平道路。
此版本还首次推出了拓扑感知调度和工作负载感知抢占的初始迭代,以推进调度能力。
此外,工作负载的 ResourceClaim 支持为 PodGroup 解锁了动态资源分配
(DRA)。
最后,为了展示其面向真实场景的就绪程度,v1.36 交付了 Job 控制器与新 API 集成的第一阶段。
Workload 和 PodGroup API 更新
Workload API 现在用作静态模板,而新的 PodGroup API 描述运行时对象。
Kubernetes v1.36 将 Workload 和 PodGroup API 作为
scheduling.k8s.io/v1alpha2 API 组的一部分引入,
完全替代此前的 v1alpha1 API 版本。
在 v1.35 中,Pod 组及其运行时状态嵌入在 Workload 资源中。 新模型解耦了这些概念:Workload 现在充当静态模板对象, 而 PodGroup 负责管理运行时状态。 这种分离也提升了性能和可扩缩性,因为 PodGroup API 允许按副本对状态更新进行分片。
由于 Workload API 仅作为模板存在,kube-scheduler 的逻辑得以简化。
调度器可以直接读取 PodGroup;PodGroup 包含调度器所需的全部信息,
因此调度器无需监视或解析 Workload 对象本身。
更新后的配置如下所示。工作负载控制器(例如 Job 控制器) 定义 Workload 对象,而这个对象现在充当 Pod 组的静态模板:
apiVersion: scheduling.k8s.io/v1alpha2
kind: Workload
metadata:
name: training-job-workload
namespace: some-ns
spec:
# Pod 组现在定义为模板,
# 其中包含 PodGroup 对象的 spec 字段。
podGroupTemplates:
- name: workers
schedulingPolicy:
gang:
# 仅当 4 个 Pod 可以同时运行时,此 gang 才可调度
minCount: 4
随后,控制器基于这些模板生成运行时 PodGroup 实例。 PodGroup 运行时对象保存实际的调度策略,并引用创建它所用的模板。 它还包含一个状态字段,其中的状况会映射各个 Pod 的状态, 反映这个组的整体调度状态:
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
name: training-job-workers-pg
namespace: some-ns
spec:
# PodGroup 引用其来源 Workload 模板。
# 相比之下,.metadata.ownerReferences 指向“真正的”工作负载对象,
# 例如 Job。
podGroupTemplateRef:
workload:
workloadName: training-job-workload
podGroupTemplateName: workers
# 实际的调度策略放在运行时 PodGroup 中
schedulingPolicy:
gang:
minCount: 4
status:
# status 包含映射各个 Pod 状况的状况信息。
conditions:
- type: PodGroupScheduled
status: "True"
lastTransitionTime: 2026-04-03T00:00:00Z
最后,为了将这一新架构与各个 Pod 衔接起来,Pod API 中的 workloadRef 字段已被
schedulingGroup 字段替代。创建 Pod 时,你可以将它们直接关联到运行时 PodGroup:
apiVersion: v1
kind: Pod
metadata:
name: worker-0
namespace: some-ns
spec:
# workloadRef 字段已被 schedulingGroup 替代
schedulingGroup:
podGroupName: training-job-workers-pg
...
通过将 Workload 保持为静态模板,并将 PodGroup 提升为一等、独立的 API, 我们为在未来 Kubernetes 版本中构建高级工作负载调度能力奠定了坚实基础。
PodGroup 调度周期和编组调度
为了高效管理这些工作负载,kube-scheduler 现在提供了专用的 PodGroup 调度周期。 调度器不再逐个 Pod 顺序评估和预留资源,因为这种方式存在调度死锁风险; 它会把整个组作为一个统一操作来评估。
当调度器从调度队列中弹出某个 PodGroup 成员时,无论该组使用哪种具体策略, 它都会获取该组中其余已排队的 Pod,以确定性方式对它们排序, 并按如下方式执行一个原子的调度周期:
-
调度器对集群状态获取一次快照,以避免竞态条件,并确保在评估整个组时保持一致性。
-
随后,调度器尝试使用 PodGroup 调度算法为组内所有 Pod 找到有效的 Node 放置方案; 该算法会利用标准的基于 Pod 的过滤和评分阶段。
-
根据算法结果,调度决策会以原子方式应用到整个 PodGroup。
-
成功:如果找到了放置方案且满足组约束,可调度的成员 Pod 会一起直接进入绑定阶段。 其余仍不可调度的 Pod 会返回调度队列,等待可用资源,以便之后加入已调度的 Pod。
(注意:如果在某些 Pod 已经调度后,又有新的 Pod 添加到 PodGroup, 这个周期会在考虑已有 Pod 的同时评估新 Pod。 关键在于,已经分配到 Node 的 Pod 会继续运行。 即使该组在后续周期中未能满足其要求,调度器也不会取消分配或驱逐这些 Pod。)
-
失败:如果该组未能满足其要求,整个组会被视为不可调度。 不会绑定任何 Pod,这些 Pod 会返回调度队列,并在退避期之后稍后重试。
-
这个周期是编组调度的基础。
当你的工作负载需要严格的全有或全无放置时,
gang 策略会利用这个周期,防止部分部署造成资源浪费和潜在死锁。
虽然调度器仍会在满足 minCount 要求之前将 Pod 保留在 PreEnqueue 中,
但实际调度阶段现在完全依赖新的 PodGroup 周期。
具体而言,在算法执行期间,调度器会验证可调度 Pod 的数量是否满足 minCount。
如果集群无法容纳所需的最小数量,则不会绑定任何 Pod。
该组调度失败,并等待释放出足够资源。
局限性
PodGroup 调度周期的首个版本存在一些局限性:
-
对于基本的同构 Pod 组(即所有 Pod 具有相同调度要求, 且不存在亲和性、反亲和性或拓扑分布约束等 Pod 间依赖的 Pod 组), 如果存在可行的放置方案,算法预计能够找到它。
-
对于异构 Pod 组,即使存在有效的放置方案,也不保证一定能找到, 哪怕这个方案看起来很简单。
-
对于存在 Pod 间依赖的 Pod 组,即使存在有效的放置方案,也不保证一定能找到。
除上述情况外,对于涉及组内依赖的场景 (例如某个 Pod 的可调度性通过 Pod 间亲和性依赖于另一个组成员), 由于该算法采用确定性的处理顺序,无论集群状态如何,都可能无法找到放置方案。
拓扑感知调度
对于 AI/ML 训练或批处理这类复杂分布式工作负载,将 Pod 随机放置到整个集群中 可能引入显著的网络延迟,并成为整体性能瓶颈。
拓扑感知调度通过允许你直接在 PodGroup 上定义拓扑约束来解决这个问题, 确保其 Pod 被共同放置在特定的物理或逻辑域内:
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
name: topology-aware-workers-pg
spec:
schedulingPolicy:
gang:
minCount: 4
# 强制 Pod 根据机架拓扑共同放置
schedulingConstraints:
topology:
- key: topology.kubernetes.io/rack
在这个示例中,kube-scheduler 会尝试将 Pod 调度到符合 rack 拓扑约束的多种 Node 组合上。
随后,它会根据 PodGroup 利用资源的效率,以及在该域内可以成功调度多少 Pod,
选择最优放置方案。
为实现这一点,调度器用一个专用的、基于放置方案的算法扩展了 PodGroup 调度周期; 该算法包含三个阶段:
-
根据组的调度约束生成候选放置方案 (理论上适合分配给 PodGroup 的 Node 子集)。 拓扑感知调度插件使用新的
PlacementGenerate扩展点创建这些放置方案。 -
评估每个候选放置方案,确认整个 PodGroup 是否实际能够放入其中。
-
对所有可行的放置方案打分,为 PodGroup 选择最合适的方案。 拓扑感知调度插件使用新的
PlacementScore扩展点对这些放置方案进行打分。
目前,拓扑感知调度不会触发 Pod 抢占来满足约束。 不过,我们计划在即将到来的版本中,将工作负载感知抢占与拓扑约束集成起来。
虽然 Kubernetes v1.36 交付了这一基础性的拓扑感知调度能力,
Kubernetes 项目计划很快扩展其能力。
未来更新将引入对多级拓扑、软约束(偏好)的支持,
与动态资源分配(DRA)的更深度集成,
以及在与 basic 调度策略配合使用时更稳健的行为。
工作负载感知抢占
为了支持新的 PodGroup 调度周期,Kubernetes v1.36 引入了一种新的抢占机制, 称为工作负载感知抢占。 当某个 PodGroup 无法调度时,调度器会利用这一机制尝试让该 PodGroup 的调度成为可能。
与标准逐个 Pod 调度周期中使用的默认抢占相比,这种新机制会将整个 PodGroup 视为单个抢占者单元。 它不会在每个 Node 上单独评估抢占牺牲者,而是在整个集群范围内搜索。 这允许调度器同时从多个 Node 抢占 Pod, 从而为随后调度整个 PodGroup 腾出足够空间。
工作负载感知抢占还直接向 PodGroup API 引入了两个额外概念:
-
PodGroup
priority:覆盖构成 PodGroup 的各个 Pod 的优先级。 -
PodGroup
disruptionMode:指定 PodGroup 中的 Pod 是否可以独立被抢占, 或者是否必须以全有或全无的方式一起被抢占。
在 Kubernetes v1.36 中,只有工作负载感知抢占机制会遵从这些字段。 负责这组特性的人员希望在未来版本中, 将这些字段的支持扩展到其他中断来源, 包括逐个 Pod 调度周期中使用的默认抢占。
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
name: victim-pg
spec:
priorityClassName: high-priority
priority: 1000
disruptionMode: PodGroup
在这个示例中,当调度器在工作负载感知抢占周期中将 victim-pg
作为潜在抢占牺牲者进行评估时,
它会使用 1000 作为其优先级,并以严格的全有或全无方式抢占该 PodGroup。
工作负载的 DRA ResourceClaim 支持
自 Kubernetes v1.34 正式可用以来,DRA 已经让 Pod 能够对 GPU、TPU 和 NIC 等设备 提出详细请求。所请求的设备可以由多个按名称请求同一个 ResourceClaim 的 Pod 共享。 其他请求可以通过 ResourceClaimTemplate 进行复制; 在这种情况下,Kubernetes 会为每个引用该模板的 Pod 生成一个名称非确定性的 ResourceClaim。 然而,对于需要让某些 Pod 共享某些设备的大规模工作负载, 目前仍需要自行管理各个 ResourceClaim 的创建。
现在,除了 Pod 之外,PodGroup 也可以表示 ResourceClaimTemplate 的可复制单元。
对于 PodGroup 的某个 spec.resourceClaims 所引用的 ResourceClaimTemplate,
无论组内有多少 Pod,Kubernetes 都会为整个 PodGroup 生成一个 ResourceClaim。
当 Pod 中用于 ResourceClaimTemplate 的某个 spec.resourceClaims
与其 PodGroup 中的某个 spec.resourceClaims 匹配时,
该 Pod 的申领会解析为为 PodGroup 生成的 ResourceClaim,
并且不会为这个单独的 Pod 生成 ResourceClaim。
Workload 对象中的单个 PodGroupTemplate 可以表达一种资源请求:
这种请求既会为每个不同的 PodGroup 复制,
又可由每个组内的 Pod 共享。
下面的示例展示了两个 Pod 请求同一个 ResourceClaim; 这个 ResourceClaim 是从其 PodGroup 的 ResourceClaimTemplate 生成的:
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
name: training-job-workers-pg
spec:
...
resourceClaims:
- name: pg-claim
resourceClaimTemplateName: my-claim-template
---
apiVersion: v1
kind: Pod
metadata:
name: topology-aware-workers-pg-pod-1
spec:
...
schedulingGroup:
podGroupName: training-job-workers-pg
resourceClaims:
- name: pg-claim
resourceClaimTemplateName: my-claim-template
---
apiVersion: v1
kind: Pod
metadata:
name: topology-aware-workers-pg-pod-2
spec:
...
schedulingGroup:
podGroupName: training-job-workers-pg
resourceClaims:
- name: pg-claim
resourceClaimTemplateName: my-claim-template
此外,PodGroup 通过 resourceClaimName 引用的 ResourceClaim,
或通过 resourceClaimTemplateName 生成的申领,
都会为整个 PodGroup 预留。
此前,kube-scheduler 只能在 ResourceClaim 的 status.reservedFor 字段中
列出各个 Pod,而该字段限制为 256 个条目。
现在,status.reservedFor 中的单个 PodGroup 引用可以表示远多于 256 个 Pod,
从而允许设备的高基数共享。
这些变更结合起来,使具有复杂拓扑的大规模工作负载能够利用 DRA 实现可扩缩的设备管理。
与 Job 控制器集成
在 Kubernetes v1.36 中,Job 控制器可以代表你创建和管理 Workload 与 PodGroup 对象, 因此表示紧耦合并行应用(例如分布式 AI 训练)的 Job 无需任何额外工具即可进行编组调度。 如果没有这种集成,你需要自行创建 Workload 和 PodGroup, 并将它们的引用连接到 Pod 模板中。 现在,Job 控制器以原生方式自动化了这一过程。
启用 EnableWorkloadWithJob
特性门控后,Job 控制器会自动:
-
为每个符合条件的 Job 创建一个 Workload 和对应的运行时 PodGroup;
-
在 Job 创建的每个 Pod 上设置
.spec.schedulingGroup, 使调度器将它们视为一个 gang; -
将 Job 设置为所生成对象的属主, 因此这些对象会在 Job 被删除时被垃圾收集。
什么时候会触发集成?
为了让首个特性迭代保持可预测,只有当 Job 具有定义明确且固定的形态时, Job 控制器才会创建 Workload 和 PodGroup:
-
.spec.parallelism大于 1 -
.spec.completionMode设置为Indexed -
.spec.completions等于.spec.parallelism -
Pod 模板上尚未设置
schedulingGroup
这些条件描述了编组调度可以推理的一类 Job:
每个 Pod 都具有稳定身份(Indexed),gang 的规模在准入时已知且固定
(parallelism == completions),并且没有其他控制器已经声明调度责任
(schedulingGroup 字段未设置)。
不满足这些条件的 Job 会像以前一样逐个 Pod 调度。
如果你自行在 Pod 模板上设置 schedulingGroup
(例如因为有更高层的控制器正在管理该工作负载),
Job 控制器会保持 Pod 模板不变,
并且不会创建自己的 Workload 或 PodGroup。
这使得该特性可以安全地在已经使用外部批处理系统的集群中启用。
下面是一个符合编组调度条件的 Job 示例:
apiVersion: batch/v1
kind: Job
metadata:
name: training-job
namespace: job-ns
spec:
completionMode: Indexed
parallelism: 4
completions: 4
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: registry.example/trainer:latest
Job 控制器会创建归此 Job 所有的 Workload 和 PodGroup,
并且它创建的每个 Pod 都会携带指向所生成 PodGroup 的 .spec.schedulingGroup。
随后,一旦可以使用本文前面描述的 PodGroup 调度周期同时放置全部四个 Pod,
这些 Pod 就会一起调度。
尚未覆盖的内容
当前约束将这一集成限制在静态、索引化、完全并行的 Job 上。 对其他工作负载形态的支持,包括弹性 Job 和其他内置控制器, 由 KEP-5547 跟踪。
在未来 Kubernetes 版本中,这一集成将扩展为支持更多工作负载控制器, 而当前针对 Job 的约束也可能放宽。
后续计划
工作负载感知调度的旅程并不会止步于此。 面向 v1.37,社区正在积极推进:
-
将 Workload 和 PodGroup API 晋升到 Beta: 我们的主要目标是让 Workload 和 PodGroup API 成熟到 Beta 阶段, 巩固它们在 Kubernetes 生态系统中的基础地位。 作为晋升过程的一部分,我们还计划引入
minCount可变性, 以解锁弹性 Job,并允许动态工作负载高效扩缩。 -
多级 Workload 层次结构: 为了支持 JobSet 或通过 LeaderWorkerSet(LWS)实现的解聚推理等复杂现代 AI 工作负载, 我们正在扩展架构以支持多级层次结构。 我们计划引入一个新的 API, 允许将多个 PodGroup 组织成层次结构, 直接反映真实工作负载控制器的组织方式。
-
推进高级调度特性进阶: 我们专注于推动更广泛的工作负载感知调度生态系统走向成熟。 这包括将拓扑感知调度和工作负载感知抢占等现有特性推进到 Beta 阶段。
-
统一的控制器集成 API: 为简化采用过程,我们正在开发控制器集成 API。 这将为真实世界中的工作负载控制器提供一种统一、标准化的方法, 用于消费工作负载感知调度能力。
这些重点领域的优先级和实现顺序可能会发生变化。敬请关注后续更新。
入门
以下所有工作负载感知调度改进在 v1.36 中均作为 Alpha 特性提供。 要试用它们,你必须进行以下配置:
- 前提条件:Workload 和 PodGroup API 支持:
在
kube-apiserver和kube-scheduler上启用GenericWorkload特性门控,并确保启用scheduling.k8s.io/v1alpha2API 组。
满足前提条件后,你可以启用具体特性:
- 编组调度:在
kube-scheduler上启用GangScheduling特性门控。 - 拓扑感知调度:在
kube-scheduler上启用TopologyAwareWorkloadScheduling特性门控。 - 工作负载感知抢占:在
kube-scheduler上启用WorkloadAwarePreemption特性门控(还需要启用GangScheduling)。 - 工作负载的 DRA ResourceClaim 支持:在
kube-apiserver、kube-controller-manager、kube-scheduler和kubelet上启用DRAWorkloadResourceClaims特性门控。 - Job 控制器的 Workload API 集成:在
kube-apiserver和kube-controller-manager上启用WorkloadWithJob特性门控。
我们鼓励你在测试集群中试用工作负载感知调度, 并分享你的经验,以帮助塑造 Kubernetes 调度的未来。 你可以通过以下方式发送反馈:
- 通过 Slack(#workload-aware-scheduling)联系我们。
- 参加 SIG Scheduling 会议。
- 在 Kubernetes 仓库中提交新的 issue。
了解更多
要深入了解这些特性的架构和设计,请阅读以下 KEP: