Kubernetes v1.36:PodGroup 让调度从"逐个审批"变成"整组放行"

2026-05-14 30 预计阅读时间:1 分钟
来源:kubernetes.io AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:15 分钟

AI/ML 训练和批处理任务有一个共同诉求:要么所有 Pod 同时跑起来,要么谁也别跑。Kubernetes 一直按 Pod 逐个调度,遇到这种"全有或全无"的场景就容易卡死——3 个 Pod 占了资源,第 4 个没位置,前 3 个白占着等,别人也用不上。v1.35 引入了 Workload API 和初步的 gang scheduling,但把运行状态塞进了 Workload 对象里,耦合重、扩展难。v1.36 做了一次架构拆分:Workload 只做静态模板,PodGroup 独立管运行状态,调度器有了专门的 PodGroup 调度周期,一口气把整组 Pod 的放置算完再原子提交。同时拓扑感知调度、工作负载感知抢占、DRA ResourceClaim 与 PodGroup 的打通也首次亮相。下面逐层拆解。

模板与运行态分离:Workload + PodGroup

v1.35 的 Workload 对象把 PodGroup 的 spec 和 status 都嵌在一起。一个 1000 Pod 的训练任务,每次状态更新都要改同一个对象,性能和扩展性都受限。v1.36 把两者拆开:

  • Workloadscheduling.k8s.io/v1alpha2):纯模板,定义 PodGroup 的 spec 模板,调度器不需要 watch 它。
  • PodGroupscheduling.k8s.io/v1alpha2):运行态对象,持有实际调度策略和状态,按 replica 分片更新 status。

调度器只读 PodGroup 就够了,逻辑更干净。v1alpha1 整个被替换,不再兼容。

一个最小示例——定义一个要求 4 Pod 同时启动的 gang 调度 Workload 模板:

apiVersion: scheduling.k8s.io/v1alpha2
kind: Workload
metadata:
  name: training-job-workload
  namespace: some-ns
spec:
  podGroupTemplates:
    - name: workers
      schedulingPolicy:
        gang:
          minCount: 4

控制器根据模板 stamp 出运行态 PodGroup:

apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
  name: training-job-workers-pg
  namespace: some-ns
spec:
  podGroupTemplateRef:
    workload:
      workloadName: training-job-workload
      podGroupTemplateName: workers
  schedulingPolicy:
    gang:
      minCount: 4
status:
  conditions:
    - type: PodGroupScheduled
      status: "True"
      lastTransitionTime: "2026-04-03T00:00:00Z"

Pod 通过新的 schedulingGroup 字段(替代了旧的 workloadRef)挂到 PodGroup:

apiVersion: v1
kind: Pod
metadata:
  name: worker-0
  namespace: some-ns
spec:
  schedulingGroup:
    podGroupName: training-job-workers-pg
  containers:
    - name: trainer
      image: registry.example/trainer:latest

这套拆分让大规模工作负载的状态更新不再打在同一个 Workload 对象上,分片写入 PodGroup status,性能和可扩展性都更好。

PodGroup 调度周期:原子决策,不再逐 Pod 碰运气

旧流程是调度器从队列里逐个 pop Pod,逐个找节点、逐个 reserve。如果 4 个 Pod 属于同一个 gang,第 1 个 reserve 了节点 A 的资源,第 2 个 reserve 了节点 B,到第 4 个发现资源不够——前 3 个已经占着位置,谁也跑不了,谁也释放不了。

v1.36 的 PodGroup 调度周期改了这件事:

  1. 调度器从队列 pop 出一个 PodGroup 成员时,把同组所有排队 Pod 一并取出,按确定性顺序排序。
  2. 取一次集群状态快照,避免中间被别人改了。
  3. 用 PodGroup 调度算法(仍走标准的 filter + score 流程)为整组找放置方案。
  4. 原子提交:满足 minCount → 可调度 Pod 一起进入 binding;不满足 → 全组返回队列,等资源释放后重试。

关键细节:已经绑定节点的 Pod 不会被撤销或驱逐,即使后续周期里组没凑齐。新加入的 Pod 会在调度时考虑已就位成员,但不会把已就位成员踢掉。

当前限制

  • 同质 PodGroup(所有 Pod 资源需求相同、无亲和性/反亲和性/拓扑约束)——算法能找到解就一定会找到。
  • 异质 PodGroup 或有 Pod 间依赖的组——不保证一定能找到解,即使解看起来"显而易见"。
  • 组内亲和性依赖——由于确定性处理顺序,可能无论集群状态如何都找不到放置。

这些限制意味着第一版更适合"同质 gang"场景,复杂拓扑依赖的异质组还需要后续版本改进。

拓扑感知调度:把 Pod 按机架拢在一起

分布式训练对网络延迟敏感。Pod 随机散布在不同机架,AllReduce 的梯度同步会被跨机架带宽卡住。拓扑感知调度允许在 PodGroup 上直接声明拓扑约束:

apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
  name: topology-aware-workers-pg
spec:
  schedulingPolicy:
    gang:
      minCount: 4
  schedulingConstraints:
    topology:
      - key: topology.kubernetes.io/rack

调度器的工作流程:

  1. 生成候选放置——通过新的 PlacementGenerate 扩展点,按拓扑约束筛选出节点子集组合。
  2. 评估可行性——确认整组 PodGroup 能在某个放置里全部放下。
  3. 评分选优——通过新的 PlacementScore 扩展点,对可行放置按资源利用效率打分,选最优。

当前版本不支持为满足拓扑约束而触发抢占,后续版本会和工作负载感知抢占整合。多级拓扑、软约束偏好、与 DRA 的深度联动也在规划中。

工作负载感知抢占:整组抢、整组让

默认抢占是逐 Pod 评估每个节点上的受害者。对 PodGroup 来说这不够——可能需要在 3 个节点上各腾一点空间,才能把整组放进去。

v1.36 的工作负载感知抢占把 PodGroup 当一个抢占单位,跨全集群搜索受害者组合,可以同时从多个节点抢占 Pod,腾出足够空间后再整组放入。

两个新概念直接加在 PodGroup API 上:

  • PodGroup priority——覆盖组内各 Pod 的单独优先级,抢占评估时用组优先级。
  • PodGroup disruptionMode——控制组内 Pod 是否可以被独立抢占,还是必须"全有或全无"地一起被抢占。
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
  name: victim-pg
spec:
  priorityClassName: high-priority
  priority: 1000
  disruptionMode: PodGroup

disruptionMode: PodGroup 意味着抢占评估时,要么整组一起被驱逐,要么谁也不动。这防止了"只抢了组里一半 Pod,剩下的半残运行"的情况。目前这两个字段只在工作负载感知抢占中被尊重,后续会扩展到默认抢占等场景。

DRA ResourceClaim 与 PodGroup 打通

DRA(Dynamic Resource Allocation)从 v1.34 GA 后,Pod 可以通过 ResourceClaimResourceClaimTemplate 请求 GPU/TPU/NIC 等设备。ResourceClaimTemplate 会为每个 Pod 生成一个独立 ResourceClaim,但大规模工作负载里"某些 Pod 共享某些设备"的场景,之前只能手动管理。

v1.36 让 PodGroup 成为 ResourceClaimTemplate 的复制单元——一个 PodGroup 只生成一个 ResourceClaim,组内所有 Pod 共享它:

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

Pod 的 resourceClaimTemplateName 匹配 PodGroup 的声明时,Pod 直接引用 PodGroup 生成的那个 ResourceClaim,不再为 Pod 单独生成。

另一个关键改进:ResourceClaimstatus.reservedFor 之前只能列单个 Pod(上限 256 条目)。现在一条 PodGroup 引用就能代表远超 256 个 Pod,高基数设备共享不再是瓶颈。

Job 控制器原生集成:不用手写 Workload 了

v1.36 的 Job 控制器可以自动为符合条件的 Job 创建 Workload 和 PodGroup,Pod 自动带上 schedulingGroup,不再需要额外工具或手动接线。

启用 WorkloadWithJob feature gate 后,Job 控制器会:

  1. 为符合条件的 Job 创建 Workload + PodGroup。
  2. 在每个 Pod 上设置 .spec.schedulingGroup
  3. 设置 Job 为 owner,Job 删除时自动垃圾回收。

触发条件(必须全部满足):

  • spec.parallelism > 1
  • spec.completionMode = Indexed
  • spec.completions == spec.parallelism
  • Pod 模板上 schedulingGroup 未被设置

这些条件描述的是"形状固定、每个 Pod 有稳定身份、gang 大小在准入时已知"的 Job。不符合条件的 Job 按原来的逐 Pod 调度,行为不变。如果你已经在 Pod 模板上设了 schedulingGroup(比如有更上层控制器在管),Job 控制器不会覆盖,安全地与外部批处理系统共存。

一个符合条件的最小 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 控制器会自动创建 Workload 和 PodGroup,4 个 Pod 通过 PodGroup 调度周期一起放入——凑齐 4 个才绑定,凑不齐就全组等。

如何在测试集群里试用

所有功能在 v1.36 都是 Alpha,需要手动开启。以下是一份完整的 feature gate 配置清单:

前置条件——在 kube-apiserver 和 kube-scheduler 上启用:

# kube-apiserver 启动参数
--feature-gates=GenericWorkload=true
# 确保 scheduling.k8s.io/v1alpha2 API group 已启用

# kube-scheduler 启动参数
--feature-gates=GenericWorkload=true

各子功能——在对应组件上额外启用:

功能 Feature Gate 启用位置
Gang 调度 GangScheduling kube-scheduler
拓扑感知调度 TopologyAwareWorkloadScheduling kube-scheduler
工作负载感知抢占 WorkloadAwarePreemption kube-scheduler(需同时开 GangScheduling
DRA ResourceClaim 支持 DRAWorkloadResourceClaims kube-apiserver、kube-controller-manager、kube-scheduler、kubelet
Job 控制器集成 WorkloadWithJob kube-apiserver、kube-controller-manager

一个快速验证 gang 调度的完整流程——假设你已经有一个 v1.36 测试集群并开启了上述 gate:

# 1. 创建一个 4 Pod gang Workload
kubectl apply -f - <<EOF
apiVersion: scheduling.k8s.io/v1alpha2
kind: Workload
metadata:
  name: gang-demo-workload
  namespace: default
spec:
  podGroupTemplates:
    - name: workers
      schedulingPolicy:
        gang:
          minCount: 4
EOF

# 2. 手动创建对应 PodGroup(模拟控制器行为)
kubectl apply -f - <<EOF
apiVersion: scheduling.k8s.io/v1alpha2
kind: PodGroup
metadata:
  name: gang-demo-workers-pg
  namespace: default
spec:
  podGroupTemplateRef:
    workload:
      workloadName: gang-demo-workload
      podGroupTemplateName: workers
  schedulingPolicy:
    gang:
      minCount: 4
EOF

# 3. 创建 4 个 Pod,全部指向同一个 PodGroup
for i in 0 1 2 3; do
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: gang-demo-worker-$i
  namespace: default
spec:
  schedulingGroup:
    podGroupName: gang-demo-workers-pg
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.9
EOF
done

# 4. 观察:如果集群能同时放 4 个 Pod,它们会一起进入 Running;
#    如果资源不够,4 个都会 Pending,不会出现"2 个 Running、2 个 Pending"的半残状态
kubectl get pods -w

如果想用 Job 控制器自动集成,更简单——只要 Job 符合条件,开 WorkloadWithJob gate 后直接提交 Job 即可,控制器会自动创建 Workload 和 PodGroup。

采用建议与注意事项

  1. 先在测试集群验证——Alpha API 随版本可能变动,v1alpha2 替换了 v1alpha1,后续 Beta 阶段可能再调整字段。不要直接在生产环境开启。
  2. 优先用 Job 控制器集成——如果你的工作负载恰好是 Indexed + parallelism == completions 的固定形状 Job,这是最省力的路径,不需要手写 Workload 和 PodGroup。
  3. 同质 gang 是当前最稳妥的场景——异质 PodGroup 和有 Pod 间亲和性依赖的组,算法不保证找到解。初期建议把 gang 调度用在"所有 Pod 规格相同、无复杂亲和性"的训练或批处理任务上。
  4. 拓扑感知调度暂不支持抢占——如果机架里资源不够,不会为了满足拓扑约束去驱逐别人。需要确保目标拓扑域内有足够空闲资源。
  5. DRA + PodGroup 是大规模 GPU 共享的关键路径——如果你在跑多 Pod 共享同一 GPU 的训练任务,这个组合能大幅减少 ResourceClaim 数量和 reservedFor 条目压力。
  6. disruptionMode: PodGroup 对保护高优先级任务有用——设置后,抢占评估要么整组驱逐要么不动,避免训练任务被部分抢占后进入半残状态。但当前只在工作负载感知抢占中生效,默认抢占还不认这个字段。

v1.37 的路线图已经在推进:Workload 和 PodGroup API 升 Beta、minCount 可变性(弹性 Job)、多级 Workload 层级(支持 JobSet / LWS 等复杂结构)、拓扑感知调度和工作负载感知抢占升 Beta、统一控制器集成 API。如果你在跑分布式训练或大规模批处理,现在可以在测试集群里试水,把反馈送到 SIG Scheduling(Slack #workload-aware-scheduling 或每周例会),帮助这些功能在 Beta 阶段更贴合真实场景。


相关推荐