更新时间:2026-05-21 GMT+08:00
分享

多维组调度(Gang)

背景信息

组调度(Gang)满足了调度过程中“All or nothing”的调度需求,避免Pod的任意调度导致集群资源的浪费,主要应用于AI、大数据等多任务协作场景。启用该能力后,可以解决分布式训练任务之间的资源忙等待和死锁等痛点问题,大幅度提升整体训练性能。

随着AI任务的演进,AI任务可以进一步细分为不同维度的子任务,例如分布式训练任务中的多个分片或分布式推理任务中的不同Role。子任务内部及子任务之间出现了新的多维度Pod组调度(Gang)需求。子任务内的多个Pod需在最小资源得到满足后才能启动,以防止子任务内部只有部分Pod启动而导致任务无效运行。当存在多个子任务时,需确保一定数量或一定比例的子任务启动后,任务才能正常运行。为此,Volcano提供了多维组调度(Gang)能力策略,在调度时为不同维度的任务提供组调度(Gang)能力,以满足不同维度任务对资源整体调度的需求。

功能介绍

多维组调度(Gang)支持以下能力:

  • 任务维度组调度(Gang)能力

    指在调度过程中检查任务下的Pod已调度数量是否达到了最小运行数量。只有当任务内的最小Pod运行数量得到满足时,该任务下的Pod才会被调度到集群中,否则任务调度将不会执行。

  • 子任务维度组调度(Gang)能力

    每个子任务需遵循组调度(Gang)原则。只有当一个子任务内的所有Pod均满足Gang调度条件时,该子任务才会被调度,否则该子任务的调度将不会执行。在集群资源不足的情况下,确保子任务能够整体调度,以保持子任务的完整性,避免因Pod数量不足而导致子任务无效运行,从而浪费资源。

  • 子任务之间组调度(Gang)能力

    在调度过程中,监控任务中不同类型的子任务可调度数量,当最小可调度子任务数量要求得到满足时,该任务下的Pod才会被调度;否则,任务调度将不会执行。当集群资源不足时,允许以子任务为单位进行弹性调度,确保任务内启动的不同类型子任务满足最低部署数量和比例要求,以保障任务的有效运行。

Volcano通过PodGroup提供了对任务内部Pod的分组定义,通过配置字段将一个PodGroup内的Pod分成多个SubGroup。在此过程中,可以定义每个SubGroup进行组调度(Gang)的Pod数量以及不同类型SubGroup的最小数量要求。在调度时,每个SubGroup将会遵循各自的组调度条件约束。任务负载可以通过设置关联的PodGroup,利用Volcano对负载进行多维Gang调度。

apiVersion: scheduling.volcano.sh/v1beta1
kind: PodGroup
metadata:
  name: pg-test1
  namespace: default
spec:
  subGroupPolicy:                          
  - name: subgroup-test
    subGroupSize: 2                          # 指示subGroup内至少需要几个Pods可被调度
    minSubGroups: 4                          # 指示至少需要多少subGroup可被调度                 
    labelSelector:                           # 分组Pod需要匹配的标签
        matchLabels:
          task-type: test  
    matchLabelKeys:                          # Pod的分组依据,Label value值相同的Pod分到同一个SubGroup中
    - group                                             
  minMember: 8                    #PodGroup内至少需要几个Pods可被调度

涉及参数解释如下:

  • minMember:指定工作负载最小Pod数量。
  • subGroupPolicy: 指定分组策略,将任务中的Pod按照策略分为不同的分组。
    • name:分组策略名字。
    • subGroupSize: 指定一个分组里面最小Pod的数量。
    • minSubGroups:指定当前分组策略内最小的分组数量。
    • labelSelector:被分组的Pod需要匹配的标签。
    • matchLabelKeys:Pod的分组依据,根据Pod标签所对应的value值进行分组,value值相同的Pod分到同一个SubGroup中。

Volcano Job在Job定义中提供了对应的分组定义和组调度(Gang)约束定义,通过Volcano自动创建和管理PodGroup,并在调度时根据多维Gang调度策略对Volcano Job进行调度。

前提条件

集群中已安装Volcano调度器插件,且插件版本在1.21.1及以上。

配置组调度策略

安装Volcano后,您可通过“配置中心 > 调度配置”选择开启或关闭Gang调度能力,默认开启。

  1. 登录CCE控制台,单击集群名称进入集群。
  2. 在左侧选择“配置中心”,在右侧选择“调度配置”页签。
  3. 选择Volcano调度器,在“AI任务性能增强调度”配置中,选择是否开启“组调度 (Gang) ”

    启用该能力后,可增强集群业务的吞吐量,提高业务运行性能。

  4. 修改完成后,单击“确认配置”

    配置完成后,可以在工作负载或Volcano Job中使用Gang调度能力。

部署负载使用多维组调度(Gang)能力

本文通过以下场景示例为您介绍如何使用多维组调度(Gang)能力。

创建Volcano Job

通过Volcano Job创建训练任务负载时,可以通过Volcano Job定义不同的训练分片Gang调度策略,实现不同维度的弹性部署。在指定训练任务整体Pods数量的基础上,通过partitionPolicy指定任务内不同分片的分组策略及分片的最少启动数量,确保调度时分片Pod和分片的整体调度。创建Volcano Job时,只需指定minAvailable数量和partitionPolicy即可,Volcano调度器会根据partitionPolicy自动创建并管理PodGroup的分组,示例如下。

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: sample
spec:
  schedulerName: volcano
  minAvailable: 5           # 可选,表示Job内最小Pod数量(Gang调度条件)
  tasks:
    - name: "ps"
      replicas: 2           # 必选,表示一个任务内有多少Pod
      partitionPolicy:      # 可选,表示对当前任务内的所有Pod进行分组
        totalPartitions: 2  # 必选,对任务内的一组Pod分为多少组
        partitionSize: 1    # 必选,每组中包含的Pod数量(Gang调度条件),totalPartitions和partitionSize相乘的结果应与replicas相等 
        minPartitions: 1    # 可选,表示分组数量的Gang调度条件
      template:
        spec:
          containers:
            - image: busybox
              command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
              imagePullPolicy: IfNotPresent
              name: ps
              resources:
                requests:
                  cpu: "500m"
          restartPolicy: OnFailure
    - name: "worker"
      replicas: 8           # 必选,表示一个任务内有多少Pod
      partitionPolicy:      # 可选,表示对当前任务内的所有Pod进行分组,按照分组设置网络拓扑亲和的调度能力
        totalPartitions: 4  # 必选,对task内的一组Pod分为多少组       
        partitionSize: 2    # 必选,每组中包含的pod数量,totalPartitions和partitionSize相乘的结果应与replicas相等 
        minPartitions: 2    # 可选,表示分组数量的gang调度条件
      template:
        spec:
          containers:
            - image: busybox
              command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
              imagePullPolicy: IfNotPresent
              name: worker
              resources:
                requests:
                  cpu: "500m"
          restartPolicy: OnFailure

示例中组调度(Gang)需要满足以下三个维度:

  • partitionSize:Parameter Server(PS)任务中每个分组至少需要1个Pod可调度后该分组可调度,Worker任务中每个分组至少需要2个Pod可调度后该分组可调度。
  • minPartitions:每个任务内最小的分组数量,PS任务中至少需要一个分组可调度,Worker任务中至少需要2个分组可调度。
  • minAvailable:Job内最小的Pod数量。Job需要至少5个Pod可调度。

当集群资源不足仅满足5个Pod可以被调度时,优先按照多维Gang调度的约束进行Pod调度,确保调度的Pod满足各个Partition的Pod数量要求和Partition的数量要求。PS任务启动了一个完整分组,而Worker任务启动了2个完整分组,从而满足了整体任务的最小启动要求。

执行kubectl get pods命令查看负载信息,返回类似信息如下。

NAME              READY   STATUS    RESTARTS   AGE
sample-ps-0       1/1     Running   0          34s
sample-ps-1       0/1     Pending   0          34s
sample-worker-0   1/1     Running   0          34s
sample-worker-1   1/1     Running   0          34s
sample-worker-2   1/1     Running   0          34s
sample-worker-3   1/1     Running   0          34s
sample-worker-4   0/1     Pending   0          61s
sample-worker-5   0/1     Pending   0          61s
sample-worker-6   0/1     Pending   0          61s
sample-worker-7   0/1     Pending   0          61s

创建Kthena ModelServing推理负载

当分布式推理负载存在Prefill和Decode两种角色时,每个角色内可以有多个Pod。单个Prefill或Decode角色内的多个Pod需要默认满足Gang调度能力,即一个角色实例中的Pod要么全部被调度,要么一个都不调度。Prefill和Decode角色可以有多个实例,存在不同的组合方式。例如,在4P2D的场景下,只有满足最低2P1D数量要求时,才能为负载Pod分配资源。

Kthena提供的模型服务编排(ModelServing)已支持通过Volcano调度推理负载。在ModelServing中,可以直接定义推理负载在不同维度的Gang调度要求,并通过Volcano实现负载的多维度Gang调度。创建ModelServing时,只需指定gangPolicy和Role,ModelServing控制器将根据gangPolicy和Role定义自动创建并管理PodGroup的分组,示例如下。

apiVersion: workload.serving.volcano.sh/v1alpha1
kind: ModelServing
metadata:
  name: sample
  namespace: default
spec:
  schedulerName: volcano
  replicas: 1
  template:
    restartGracePeriodSeconds: 60
    gangPolicy:
      minRoleReplicas:    # 可选,各Role最少Repilca数量的Gang调度条件
        prefill: 2       
        decode: 2
    roles:
      - name: prefill
        replicas: 4        # 必选,Role Replica数量 
        entryTemplate:
          spec:
            containers:
              - name: leader
                image: busybox
                command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
                resources:
                  limits:
                    cpu: "0.5"
                  requests:
                    cpu: "0.5"
        workerReplicas: 1    # 可选,Role Prefill内Worker Pod数量
        workerTemplate:
          spec:
            containers:
              - name: worker
                image: busybox
                command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
                resources:
                  limits:
                    cpu: "0.5"
                  requests:
                    cpu: "0.5"
      - name: decode
        replicas: 4  
        entryTemplate:
          spec:
            containers:
              - name: leader
                image: busybox
                command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
                resources:
                  limits:
                    cpu: "0.5"
                  requests:
                    cpu: "0.5"
        workerReplicas: 1    # 可选,Role Replica内Worker Pod数量
        workerTemplate:
          spec:
            containers:
              - name: worker
                image: busybox
                command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
                resources:
                  limits:
                    cpu: "0.5"
                  requests:
                    cpu: "0.5"

示例中组调度(Gang)需要满足是两个维度:

  • workerReplicas:每个Role Replica内最少Worker Pod数量。Role Replica内至少需要entryReplica (1) + workerReplicas个Pod可调度后该Role Replica可调度。
  • minRoleReplicas:各个Role最小的副本数量,示例中Prefill和Decode各自至少需要2个副本(2P2D)才能调度。

ModelServing工作负载的最少Pod数由最少Role replicas数量*Role Pod数量计算得到,您无需单独设置。

在资源不足时可以看到Prefill和Decode Role各自启动了2个Replica,每个Replica内2个Pod。

NAME                   READY   STATUS    RESTARTS   AGE
sample-0-decode-0-0    1/1     Running   0          8s
sample-0-decode-0-1    1/1     Running   0          8s
sample-0-decode-1-0    1/1     Running   0          8s
sample-0-decode-1-1    1/1     Running   0          7s
sample-0-decode-2-0    0/1     Pending   0          7s
sample-0-decode-2-1    0/1     Pending   0          7s
sample-0-decode-3-0    0/1     Pending   0          7s
sample-0-decode-3-1    0/1     Pending   0          7s
sample-0-prefill-0-0   1/1     Running   0          8s
sample-0-prefill-0-1   1/1     Running   0          8s
sample-0-prefill-1-0   1/1     Running   0          8s
sample-0-prefill-1-1   1/1     Running   0          8s
sample-0-prefill-2-0   0/1     Pending   0          8s
sample-0-prefill-2-1   0/1     Pending   0          8s
sample-0-prefill-3-0   0/1     Pending   0          8s
sample-0-prefill-3-1   0/1     Pending   0          8s

相关文档