一、Volcano 简介

Volcano 是一个 Kubernetes 原生的批调度系统,主要面向 AI 训练、大数据计算、HPC、高性能批处理等场景。

相比 Kubernetes 默认调度器,Volcano 更关注“成组任务”的调度语义。例如分布式训练中的多个 worker、parameter server、MPI 任务等,这类任务往往不是单个 Pod 能独立完成的,而是需要一组 Pod 同时满足资源条件后才具备运行意义。

在普通 Kubernetes 调度中,Pod 通常是以单个对象为单位进行调度;而在分布式训练场景中,如果只启动其中一部分 Pod,任务可能无法正常运行,同时还会占用集群资源。Volcano 的 Gang Scheduling 就是为了解决这类问题。

Volcano 的核心能力主要包括:

  • VolcanoJob:Volcano 提供的批任务 CRD;

  • PodGroup:一组成组调度 Pod 的抽象;

  • Gang Scheduling:成组调度能力;

  • Queue:队列资源管理能力;

  • 多种调度插件:如 gang、priority、drf、predicates、proportion、capacity、nodeorder、binpack 等。

Volcano 不会替换 Kubernetes 默认调度器。只有在 Pod 模板中显式指定:

schedulerName: volcano

该 Pod 才会交给 Volcano Scheduler 调度。没有指定该字段的普通工作负载,仍然会使用 Kubernetes 默认调度器。


二、Volcano 部署

Volcano 官方文档提供了 YAML、源码和 Helm 等部署方式。实验环境中建议使用 Helm 部署,后续升级和卸载都比较方便。

1. 添加 Helm 仓库

helm repo add volcano-sh https://volcano-sh.github.io/helm-charts
helm repo update

2. 安装 Volcano

官方 Helm 安装命令如下:

helm install volcano volcano-sh/volcano \
  -n volcano-system \
  --create-namespace

如果希望避免镜像被频繁拉取,也可以增加镜像拉取策略参数:

helm install volcano volcano-sh/volcano \
  -n volcano-system \
  --create-namespace \
  --set basic.image_pull_policy=IfNotPresent

其中:

--set basic.image_pull_policy=IfNotPresent

不是必需参数,主要用于实验环境中减少重复拉取镜像。

3. 查看 Volcano 组件状态

kubectl get pod -n volcano-system

实际输出如下:

NAME                                   READY   STATUS    RESTARTS      AGE
volcano-admission-cf4d8dd5c-dshz9      1/1     Running   1 (11m ago)   22h
volcano-controllers-5b976c6d4b-6s92b   1/1     Running   1 (11m ago)   22h
volcano-scheduler-549f77db69-whptp     1/1     Running   1 (11m ago)   21h

这三个核心组件的作用如下:

组件 作用
volcano-scheduler Volcano 调度器,负责 PodGroup、VolcanoJob 等资源的调度
volcano-controllers Volcano 控制器,负责 VolcanoJob、PodGroup、Queue 等资源的生命周期管理
volcano-admission Admission 组件,负责资源校验、默认值处理等

只要这几个组件正常 Running,说明 Volcano 基础组件已经启动成功。


三、测试一:VolcanoJob 基础运行

1. 测试目标

该测试用于验证 Volcano 的基础功能是否正常,包括:

  • VolcanoJob CRD 是否可用;

  • Volcano Controller 是否能够创建对应 Pod;

  • Volcano Scheduler 是否能够调度 Pod;

  • VolcanoJob 是否会自动关联 PodGroup;

  • TaskCompleted -> CompleteJob 生命周期策略是否生效。

VolcanoJob 是 Volcano 提供的 CRD,和 Kubernetes 原生 Job 不是同一个资源。VolcanoJob 更适合批处理、AI 训练、MPI、大数据计算等场景,因为它天然集成了 Queue、PodGroup、Gang Scheduling 等能力。

2. YAML 示例

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: vcjob-basic
  namespace: volcano-demo
spec:
  schedulerName: volcano
  queue: default
  minAvailable: 1
  maxRetry: 1
  tasks:
    - name: basic-task
      replicas: 1
      policies:
        - event: TaskCompleted
          action: CompleteJob
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: busybox
              image: busybox:latest
              imagePullPolicy: IfNotPresent
              command:
                - sh
                - -c
                - |
                  echo "Volcano job is running"
                  sleep 20
                  echo "Volcano job completed"
              resources:
                requests:
                  cpu: "100m"
                  memory: "64Mi"
                limits:
                  cpu: "100m"
                  memory: "64Mi"

3. 验证结果

查看 VolcanoJob:

kubectl -n volcano-demo get vcjob

输出如下:

NAME          STATUS    MINAVAILABLE   RUNNINGS   AGE
vcjob-basic   Running   1              1          13s

查看 PodGroup:

kubectl -n volcano-demo get podgroup

输出如下:

NAME                                               STATUS    MINMEMBER   RUNNINGS   AGE
vcjob-basic-90ac694a-9eae-4cfb-aef2-e14c7ef7cd5a   Running   1           1          20s

查看 Pod:

kubectl -n volcano-demo get pod

输出如下:

NAME                       READY   STATUS      RESTARTS   AGE
vcjob-basic-basic-task-0   0/1     Completed   0          23s

4. 结果分析

这个测试中,vcjob-basicminAvailable 为 1,task 副本数也是 1,因此只要这个 Pod 能够被调度,整个 Job 就满足最小运行条件。从 PodGroup 可以看到:

STATUS      Running
MINMEMBER   1
RUNNINGS    1

说明 Volcano 已经为该 VolcanoJob 创建了对应的 PodGroup,并且 PodGroup 当前满足最小成员数要求。Pod 最终变成:

STATUS      Completed

这是正常现象。因为 busybox 容器执行完 echosleep 20 后会正常退出,restartPolicy: Never 表示容器退出后不会重启,所以 Pod 状态会变成 Completed。

需要注意的是,kubectl get vcjob 中 Job 状态从 Running 变为 Completed 可能会有一个短暂的 controller 同步延迟。只要 Pod 已经 Completed,并且配置了:

policies:
  - event: TaskCompleted
    action: CompleteJob

Volcano Controller 后续会根据 task 完成状态把 VolcanoJob 标记为 Completed。

5. 字段说明

schedulerName: volcano

表示该 Job 创建出来的 Pod 使用 Volcano Scheduler 调度。

queue: default

表示该 Job 使用 Volcano 默认队列。Volcano 启动后会自动创建 default queue,未显式指定队列的 VolcanoJob 会进入默认队列。

minAvailable: 1

表示该 Job 至少需要 1 个 Pod 满足调度条件,Job 才能进入运行状态。

tasks:
  - name: basic-task
    replicas: 1

表示该 Job 中包含一个名为 basic-task 的 task,副本数量为 1。

policies:
  - event: TaskCompleted
    action: CompleteJob

表示当该 task 内的 Pod 成功完成后,将整个 VolcanoJob 标记为完成。TaskCompleted 用于判断某个 task 是否完成,CompleteJob 用于将 Job 置为完成状态,并处理未完成的 Pod。


四、测试二:Gang Scheduling 成功场景

1. 测试目标

该测试用于验证 Volcano 的 Gang Scheduling 能力。

Gang Scheduling 的核心语义是:一组 Pod 必须作为整体满足调度条件。如果最小成员数不满足,则该组 Pod 不应被部分调度。

在分布式训练场景中,假设一个任务需要 2 个 worker 同时启动,如果资源只允许启动其中 1 个 worker,那么任务实际上无法正常工作。Gang Scheduling 的作用就是避免这种“部分启动、整体不可用”的情况。

2. YAML 示例

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: vcjob-gang-success
  namespace: volcano-demo
spec:
  schedulerName: volcano
  queue: default
  minAvailable: 2
  maxRetry: 1
  tasks:
    - name: worker
      replicas: 2
      policies:
        - event: TaskCompleted
          action: CompleteJob
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: busybox
              image: busybox:latest
              imagePullPolicy: IfNotPresent
              command:
                - sh
                - -c
                - |
                  echo "gang worker started: $(hostname)"
                  sleep 60
                  echo "gang worker finished"
              resources:
                requests:
                  cpu: "100m"
                  memory: "64Mi"
                limits:
                  cpu: "100m"
                  memory: "64Mi"

3. 验证结果

查看 VolcanoJob:

kubectl -n volcano-demo get vcjob

输出如下:

NAME                 STATUS      MINAVAILABLE   RUNNINGS   AGE
vcjob-gang-success   Running     2              2          15s

查看 Pod:

kubectl -n volcano-demo get pod

输出如下:

NAME                          READY   STATUS      RESTARTS   AGE
vcjob-gang-success-worker-0   1/1     Running     0          17s
vcjob-gang-success-worker-1   1/1     Running     0          17s

查看 PodGroup:

kubectl -n volcano-demo get podgroup

输出如下:

NAME                                                      STATUS    MINMEMBER   RUNNINGS   AGE
vcjob-gang-success-b731581b-ea2f-4d6b-b936-35bbb842b5f5   Running   2           2          23s

4. 结果分析

该 Job 的核心配置是:

minAvailable: 2
tasks:
  - name: worker
    replicas: 2

这表示该 Job 需要创建 2 个 worker Pod,并且至少要有 2 个 Pod 同时满足调度条件后,Volcano 才会放行。

从实验结果可以看到:

MINAVAILABLE   2
RUNNINGS       2

同时 PodGroup 中也显示:

MINMEMBER      2
RUNNINGS       2

这说明 Volcano 已经将该 Job 转换为一个 PodGroup 进行调度,并且当前集群资源满足该 PodGroup 的最小运行要求。因此两个 worker Pod 被一起调度成功。

这里的重点不是“创建了两个 Pod”,而是 Volcano 在调度前确认了这两个 Pod 作为一个整体满足最小运行条件。


五、测试三:Gang Scheduling 失败场景

1. 测试目标

该测试用于验证资源不足时,Volcano 是否会阻止 PodGroup 被部分调度。

为了便于观察效果,下面示例故意让每个 Pod 申请较大的内存。我的测试环境是 32Gi 内存的单节点 Kubernetes 集群,每个 Pod 申请 20Gi 内存,两个 Pod 总共需要 40Gi 内存,因此无法同时满足 minAvailable: 2 的要求。

如果测试节点内存更大,可以将 20Gi 调整为更高值,例如 100Gi200Gi,确保资源不足。

2. YAML 示例

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: vcjob-gang-pending
  namespace: volcano-demo
spec:
  schedulerName: volcano
  queue: default
  minAvailable: 2
  maxRetry: 1
  tasks:
    - name: worker
      replicas: 2
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: busybox
              image: busybox:latest
              imagePullPolicy: IfNotPresent
              command:
                - sh
                - -c
                - |
                  echo "should-not-start"
                  sleep 3600
              resources:
                requests:
                  cpu: "100m"
                  memory: "20Gi"
                limits:
                  cpu: "100m"
                  memory: "20Gi"

3. 验证结果

查看 VolcanoJob:

kubectl -n volcano-demo get vcjob

输出如下:

NAME                 STATUS      MINAVAILABLE   RUNNINGS   AGE
vcjob-gang-pending   Pending     2                         13s

查看 PodGroup:

kubectl -n volcano-demo get podgroup

输出如下:

NAME                                                      STATUS    MINMEMBER   RUNNINGS   AGE
vcjob-gang-pending-5fbf33e1-39c5-4938-af57-88bb2c9f8b2e   Pending   2                      17s

查看 PodGroup 事件:

kubectl -n volcano-demo describe podgroup vcjob-gang-pending-5fbf33e1-39c5-4938-af57-88bb2c9f8b2e

输出如下:

Status:
  Phase:  Pending
Events:
  Type     Reason         Age                From     Message
  ----     ------         ----               ----     -------
  Normal   Unschedulable  8s (x25 over 33s)  volcano  resource in cluster is overused: overused memory
  Warning  Unschedulable  8s (x25 over 33s)  volcano  0/0 tasks in gang unschedulable: pod group is not ready, 2 minAvailable

4. 结果分析

该测试的核心配置是:

minAvailable: 2
replicas: 2

同时,每个 Pod 的内存请求为:

requests:
  memory: "20Gi"

两个 Pod 总共需要 40Gi 内存,而测试节点只有 32Gi 内存,无法同时满足两个 Pod 的资源请求。因此 Volcano 将该 Job 保持在 Pending 状态。

PodGroup 事件中最关键的信息是:

resource in cluster is overused: overused memory

这说明当前集群内存资源无法满足该 PodGroup 的最小运行需求。

另一条事件:

pod group is not ready, 2 minAvailable

说明 Volcano 当前没有满足 minAvailable=2 的条件,因此不会放行这组 Pod。

这里需要注意:不要只看 0/0 tasks in gang unschedulable 这一段。真正的根因要结合上面的事件一起看。本例中根因是内存资源不足,也就是:

overused memory

这正是 Gang Scheduling 的核心效果:当两个 Pod 不能同时满足调度条件时,Volcano 不会只启动其中一个 Pod,而是让整个 PodGroup 等待资源满足。


六、测试四:Queue 队列资源限制

1. 测试目标

该测试用于验证 Volcano Queue 的资源管理能力。

Queue 是 Volcano 中用于组织 PodGroup 的队列资源,可以作为多租户、团队隔离、资源分配的基础对象。本测试重点验证 Queue 的 capability 字段。

capability 表示该 Queue 可使用资源的上限,属于硬约束。也就是说,如果一个 PodGroup 的资源需求超过 Queue 的 capability,该 PodGroup 不应被正常调度。

需要注意:Queue 的资源控制依赖 Volcano Scheduler 中启用的队列相关插件,例如 proportioncapacity。如果手动修改过 volcano-scheduler-configmap,需要确认队列插件没有被关闭。

2. 创建 small-queue

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
  name: small-queue
spec:
  weight: 1
  reclaimable: false
  capability:
    cpu: "500m"
    memory: "512Mi"

查看 Queue:

kubectl get queue

输出如下:

NAME          PARENT
default       root
root          
small-queue   root

查看 small-queue 详情:

kubectl describe queue small-queue

输出如下:

Name:         small-queue
API Version:  scheduling.volcano.sh/v1beta1
Kind:         Queue
Spec:
  Capability:
    Cpu:             500m
    Memory:          512Mi
  Dequeue Strategy:  traverse
  Parent:            root
  Reclaimable:       false
  Weight:            1
Status:
  Allocated:
    Cpu:     0
    Memory:  0
  Reservation:
  State:  Open
Events:   <none>

3. Queue 结果分析

从输出可以看到:

State: Open

表示 small-queue 当前处于可用状态,可以接收新的 PodGroup。

Allocated:
  Cpu:     0
  Memory:  0

表示当前该 Queue 已经分配出去的资源为 0。也就是说,此时还没有运行中的 PodGroup 占用该 Queue 的资源。

Capability:
  Cpu:     500m
  Memory:  512Mi

表示该 Queue 最多只能使用 500m CPU 和 512Mi 内存。后续提交到该 Queue 的 Job,资源使用量不能超过这个上限。

4. 创建未超过 Queue capability 的 Job

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: vcjob-small-queue-ok
  namespace: volcano-demo
spec:
  schedulerName: volcano
  queue: small-queue
  minAvailable: 1
  tasks:
    - name: worker
      replicas: 1
      policies:
        - event: TaskCompleted
          action: CompleteJob
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: busybox
              image: busybox:latest
              imagePullPolicy: IfNotPresent
              command:
                - sh
                - -c
                - |
                  echo "queue-ok"
                  sleep 6000
              resources:
                requests:
                  cpu: "200m"
                  memory: "128Mi"
                limits:
                  cpu: "200m"
                  memory: "128Mi"

该 Job 申请的资源为:

cpu: "200m"
memory: "128Mi"

small-queue 的资源上限为:

cpu: "500m"
memory: "512Mi"

因此该 Job 没有超过 Queue 的 capability,应能够正常调度。验证结果如下:

kubectl get pod -n volcano-demo
NAME                            READY   STATUS      RESTARTS   AGE
vcjob-small-queue-ok-worker-0   1/1     Running     0          7s

查看 PodGroup:

kubectl get podgroup -n volcano-demo
NAME                                                        STATUS    MINMEMBER   RUNNINGS
vcjob-small-queue-ok-b0b82002-a693-4c89-98a6-23d1fa43e0ba   Running   1           1

查看 VolcanoJob:

kubectl get vcjob -n volcano-demo
NAME                   STATUS      MINAVAILABLE   RUNNINGS   AGE
vcjob-small-queue-ok   Running     1              1          29s

5. 结果分析

该 Job 使用的是:

queue: small-queue

并且 minAvailable 为 1,task 副本数也为 1。

从实验结果可以看到,PodGroup 状态为 Running,MINMEMBER=1RUNNINGS=1,说明该 PodGroup 已经满足最小运行条件,并且没有超过 Queue 的 capability 限制。

这个实验说明:当 Job 的资源请求没有超过 Queue 的资源上限时,Volcano 可以正常调度该 Job。

6. 创建超过 Queue capability 的 Job

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: vcjob-small-queue-over
  namespace: volcano-demo
spec:
  schedulerName: volcano
  queue: small-queue
  minAvailable: 1
  tasks:
    - name: worker
      replicas: 1
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: busybox
              image: busybox:latest
              imagePullPolicy: IfNotPresent
              command:
                - sh
                - -c
                - |
                  echo "queue-over"
                  sleep 3600
              resources:
                requests:
                  cpu: "1"
                  memory: "128Mi"
                limits:
                  cpu: "1"
                  memory: "128Mi"

该 Job 申请的资源为:

cpu: "1"
memory: "128Mi"

small-queue 的 CPU 上限只有:

cpu: "500m"

因此该 Job 的 CPU 请求超过了 Queue 的 capability。验证结果如下:

kubectl get vcjob -n volcano-demo
NAME                     STATUS      MINAVAILABLE   RUNNINGS   AGE
vcjob-small-queue-over   Pending     1                         5s

查看 PodGroup:

kubectl get podgroup -n volcano-demo
NAME                                                          STATUS    MINMEMBER   RUNNINGS   AGE
vcjob-small-queue-over-89a74ecf-2cca-4eff-afec-2d5570a77db7   Pending   1                      9s

查看 PodGroup 事件:

kubectl describe podgroup vcjob-small-queue-over-89a74ecf-2cca-4eff-afec-2d5570a77db7 -n volcano-demo

输出如下:

Status:
  Phase:  Pending
Events:
  Type     Reason         Age                 From     Message
  ----     ------         ----                ----     -------
  Normal   Unschedulable  21s (x25 over 45s)  volcano  queue resource quota insufficient: insufficient cpu
  Warning  Unschedulable  21s (x25 over 45s)  volcano  0/0 tasks in gang unschedulable: pod group is not ready, 1 minAvailable

7. 结果分析

该实验中最关键的事件是:

queue resource quota insufficient: insufficient cpu

这句话表示不是节点 CPU 一定不足,而是 Queue 的资源额度不足。

也就是说,当前 PodGroup 所属的 small-queue 只有 500m CPU capability,而 Job 申请了 1 核 CPU,超过了该 Queue 的资源上限。因此 Volcano 不会调度该 PodGroup。

这里要区分两类资源不足:

第一类是集群资源不足,例如前面 Gang Scheduling 失败场景中的:

resource in cluster is overused: overused memory

这表示集群维度资源不够。

第二类是队列资源额度不足,例如本测试中的:

queue resource quota insufficient: insufficient cpu

这表示 Queue 维度资源额度不够,即使集群整体可能还有资源,也不能突破该 Queue 的 capability 上限。

8. Queue 字段说明

weight: 1

表示队列权重。使用 proportion 插件时,Volcano 会根据所有队列的 weight 计算队列应得资源比例。weight 是软约束,不是硬限制。

reclaimable: false

表示当该队列使用了超过自身应得资源的部分时,是否允许其他队列回收这部分超额资源。

设置为 false 后,其他队列不能回收该队列已经占用的超额资源。实验环境中设置为 false,主要是为了减少资源回收带来的干扰。

capability:
  cpu: "500m"
  memory: "512Mi"

表示该队列可使用资源的上限。capability 是硬约束,超过该上限的 PodGroup 不应被调度运行。


七、测试五:普通 Deployment 使用 Volcano 调度

1. 测试目标

Volcano 不只支持 VolcanoJob,也可以调度 Kubernetes 原生工作负载,例如 Deployment、StatefulSet、Kubernetes Job 等。

对于普通工作负载,如果希望使用 Volcano 的 Gang Scheduling,需要同时满足两个条件:

第一,在工作负载上层资源中配置:

scheduling.volcano.sh/group-min-member: "2"

第二,在 Pod 模板中指定:

schedulerName: volcano

这两个配置缺一不可。

group-min-member 用于告诉 Volcano:这个工作负载最少需要多少个 Pod 作为一组满足调度条件。

schedulerName: volcano 用于告诉 Kubernetes:这个工作负载创建出来的 Pod 要交给 Volcano Scheduler 调度。

如果只写 group-min-member,但没有写 schedulerName: volcano,Pod 仍然会走默认调度器,不会真正使用 Volcano 的调度能力。

如果只写 schedulerName: volcano,但没有写 group-min-member,Pod 可以交给 Volcano 调度,但普通工作负载不会体现指定最小成员数的 Gang Scheduling 效果。

2. YAML 示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: volcano-deploy-gang
  namespace: volcano-demo
  annotations:
    scheduling.volcano.sh/group-min-member: "2"
spec:
  replicas: 2
  selector:
    matchLabels:
      app: volcano-deploy-gang
  template:
    metadata:
      labels:
        app: volcano-deploy-gang
      annotations:
        scheduling.volcano.sh/queue-name: "default"
    spec:
      schedulerName: volcano
      containers:
        - name: busybox
          image: busybox:latest
          imagePullPolicy: IfNotPresent
          command:
            - sh
            - -c
            - |
              echo "deployment gang pod started: $(hostname)"
              sleep 3600
          resources:
            requests:
              cpu: "100m"
              memory: "20Gi"
            limits:
              cpu: "100m"
              memory: "20Gi"

3. 验证结果

查看 Pod:

kubectl -n volcano-demo get pod

输出如下:

NAME                                   READY   STATUS      RESTARTS   AGE
volcano-deploy-gang-5dc6b66cfc-4b597   0/1     Pending     0          7s
volcano-deploy-gang-5dc6b66cfc-tsfnh   0/1     Pending     0          7s

查看 PodGroup:

kubectl -n volcano-demo get podgroup

输出如下:

NAME                                                      STATUS    MINMEMBER   RUNNINGS   AGE
podgroup-89bcd154-6137-49f5-9eb6-aaa0f39023fc             Pending   2                      15s

查看其中一个 Pod:

kubectl -n volcano-demo describe pod volcano-deploy-gang-5dc6b66cfc-4b597

关键输出如下:

Name:             volcano-deploy-gang-5dc6b66cfc-4b597
Namespace:        volcano-demo
Node:             <none>
Labels:           app=volcano-deploy-gang
                  pod-template-hash=5dc6b66cfc
Annotations:      scheduling.k8s.io/group-name: podgroup-89bcd154-6137-49f5-9eb6-aaa0f39023fc
Status:           Pending
Controlled By:    ReplicaSet/volcano-deploy-gang-5dc6b66cfc

Containers:
  busybox:
    Image:      busybox:latest
    Limits:
      cpu:     100m
      memory:  20Gi
    Requests:
      cpu:        100m
      memory:     20Gi

Conditions:
  Type           Status
  PodScheduled   False

Events:
  Type     Reason            Age   From     Message
  ----     ------            ----  ----     -------
  Warning  FailedScheduling  58s   volcano  pod group is not ready, 2 Pending, 2 minAvailable; Pending: 2 Unschedulable

4. 结果分析

从 Pod 输出可以看到,两个 Deployment Pod 都处于 Pending:

STATUS   Pending

该 Deployment 配置了 2 个副本,每个 Pod 申请 20Gi 内存,总共需要 40Gi 内存,而测试环境是 32Gi 单节点集群,无法同时满足两个 Pod 的资源需求。从 PodGroup 输出可以看到:

STATUS      Pending
MINMEMBER   2
RUNNINGS

这说明 Volcano 已经为这个 Deployment 自动创建了 PodGroup,并且该 PodGroup 的最小成员数是 2。但是当前没有 Pod 被成功调度,所以 RUNNINGS 为空。

Pod 的 annotation 中有:

scheduling.k8s.io/group-name: podgroup-89bcd154-6137-49f5-9eb6-aaa0f39023fc

这说明该 Pod 已经被 Volcano 关联到了对应的 PodGroup。也就是说,这个 Deployment 创建出来的 Pod 已经不再是单独按普通 Pod 进行调度,而是被纳入 PodGroup 的 Gang Scheduling 逻辑中。

5. 关键字段说明

metadata:
  annotations:
    scheduling.volcano.sh/group-min-member: "2"

该注解写在 Deployment 的 metadata.annotations 下,用于声明该 Deployment 至少需要 2 个 Pod 作为一组满足调度条件。

普通 Deployment 本身不是 VolcanoJob,因此需要通过这个注解告诉 Volcano:这个普通工作负载也需要按 PodGroup 方式进行成组调度。

spec:
  template:
    metadata:
      annotations:
        scheduling.volcano.sh/queue-name: "default"

该注解写在 Pod template 下,用于指定 PodGroup 所属队列。最终创建出来的 Pod 会携带该注解,Volcano 在创建 PodGroup 时会根据该信息设置 queue。

spec:
  template:
    spec:
      schedulerName: volcano

该字段必须写在 Pod 模板中。Deployment 本身不会直接被调度,真正被调度的是 Deployment 创建出来的 Pod。因此,schedulerName: volcano 必须出现在 spec.template.spec 下。

6. 普通 Deployment 的 PodGroup 创建逻辑

普通 Deployment 使用 Volcano Gang Scheduling 的大致流程如下:

Deployment
    |
    v
ReplicaSet
    |
    v
Pod
    |
    v
Volcano PodGroup Controller
    |
    v
自动创建或更新 PodGroup
    |
    v
Volcano Scheduler 按 PodGroup 调度

Deployment 并不会直接变成 VolcanoJob。它仍然是 Kubernetes 原生 Deployment。

真正发生的是:

  1. Deployment 创建 ReplicaSet;

  2. ReplicaSet 创建 Pod;

  3. Pod 模板中指定了 schedulerName: volcano

  4. Deployment 上配置了 scheduling.volcano.sh/group-min-member

  5. Volcano Controller 根据这些信息自动创建 PodGroup;

  6. Volcano Scheduler 根据 PodGroup 的 minMember 和资源需求进行调度;

  7. 调度成功后,Pod 会绑定到节点;资源不足时,PodGroup 保持 Pending。

从实验中的 Pod annotation 可以看到:

scheduling.k8s.io/group-name: podgroup-89bcd154-6137-49f5-9eb6-aaa0f39023fc

这就是 Volcano 将 Pod 关联到 PodGroup 的结果。

同时,Pod 的 Controlled By 字段显示:

Controlled By: ReplicaSet/volcano-deploy-gang-5dc6b66cfc

说明这个 Pod 的上层控制器仍然是 ReplicaSet。也就是说,Deployment 的工作负载控制逻辑仍然由 Kubernetes 原生控制器负责,Volcano 只负责调度层面的成组调度。


八、核心概念补充

1. VolcanoJob 与 Kubernetes Job 的区别

VolcanoJob 是 Volcano 自定义资源:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job

Kubernetes 原生 Job 是:

apiVersion: batch/v1
kind: Job

两者不是同一种资源。

查看 VolcanoJob:

kubectl get vcjob -n volcano-demo

查看 Kubernetes 原生 Job:

kubectl get job -n volcano-demo

VolcanoJob 更适合批计算、AI 训练、MPI、分布式任务等场景,因为它天然集成了 PodGroup、Queue、Gang Scheduling 等能力。

2. minAvailable 与 replicas 的关系

replicas 表示 task 需要创建多少个 Pod。

minAvailable 表示整个 VolcanoJob 至少需要多少个 Pod 同时满足调度条件。

例如:

tasks:
  - replicas: 3
minAvailable: 2

表示该 Job 总共期望创建 3 个 Pod,但至少有 2 个 Pod 可以调度时,Job 就可以进入运行状态。

如果希望所有 Pod 都必须同时满足调度条件,应设置:

minAvailable = 所有 task 的 replicas 总数

例如:

tasks:
  - replicas: 2
minAvailable: 2

这表示两个 Pod 必须同时满足调度条件。

3. PodGroup 是 Volcano 调度的关键对象

PodGroup 是 Volcano 成组调度的核心对象,用于描述一组 Pod 的最小运行条件。

核心字段包括:

spec:
  minMember: 2
  queue: default
  minResources:
    cpu: "200m"
    memory: "128Mi"

字段含义如下:

字段 说明
minMember PodGroup 中至少需要多少个 Pod 或 task 运行
queue PodGroup 所属 Queue
minResources PodGroup 满足最小运行条件所需的资源
priorityClassName PodGroup 的优先级
status.phase PodGroup 当前状态

PodGroup 的常见状态如下:

状态 含义
Pending 已被 Volcano 接收,但资源条件尚未满足
Inqueue 已通过队列校验,等待调度
Running 至少已有 minMember 数量的 Pod 正在运行
Unknown 部分 Pod 运行,部分 Pod 未被调度,通常与资源不足有关

4. Gang Scheduling 的本质

Gang Scheduling 不是简单地创建多个 Pod,也不是让多个 Pod 的创建时间尽量接近。

它的核心是调度约束:

达到最小成员数,则整体调度;
达不到最小成员数,则整体等待。

例如,一个训练任务需要 2 个 worker:

replicas: 2
minAvailable: 2

如果资源只够启动 1 个 worker,Volcano 不会只启动其中一个,而是让整个 PodGroup 等待资源满足。

5. Queue capability 与 weight 的区别

capability 是硬约束,表示队列最多可以使用多少资源。

capability:
  cpu: "500m"
  memory: "512Mi"

超过该上限的任务不应被正常调度。

weight 是软约束,主要用于 proportion 插件计算队列之间的资源分配比例。

weight: 1

当集群资源空闲时,某个队列可能临时使用超过其应得值的资源;当其他队列需要资源时,超额使用的资源可能被回收或重新分配。

6. 普通工作负载使用 Volcano 的必要条件

普通 Deployment、StatefulSet、Kubernetes Job 想使用 Volcano 调度,需要满足以下条件:

第一,Pod 模板中必须指定 Volcano 调度器:

spec:
  template:
    spec:
      schedulerName: volcano

第二,如果希望使用 Gang Scheduling,需要在上层工作负载中配置:

metadata:
  annotations:
    scheduling.volcano.sh/group-min-member: "2"

第三,如果希望指定 Queue,可以在 Pod 模板中配置:

spec:
  template:
    metadata:
      annotations:
        scheduling.volcano.sh/queue-name: "default"

其中最容易遗漏的是 schedulerName: volcano。如果不写这个字段,Pod 不会交给 Volcano Scheduler 调度,group-min-member 也就无法真正体现 Volcano 的 Gang Scheduling 效果。


九、总结

本文主要验证了 Volcano 的几个基础能力。

第一,Volcano 可以通过 Helm 快速部署,核心组件包括 volcano-schedulervolcano-controllersvolcano-admission

第二,VolcanoJob 使用的是 Volcano 自定义 CRD:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job

它与 Kubernetes 原生 Job 不是同一个资源。

第三,Gang Scheduling 的核心是 PodGroup。对于 VolcanoJob,通常通过 minAvailable 控制最小可运行 Pod 数;对于 PodGroup,则对应 minMember

第四,Queue 可以用于队列级资源管理。其中 capability 是资源上限,属于硬约束;weight 用于资源比例划分,属于软约束。

第五,普通 Deployment、StatefulSet、Kubernetes Job 也可以使用 Volcano 调度。如果希望普通工作负载使用 Volcano Gang Scheduling,需要同时配置:

scheduling.volcano.sh/group-min-member: "2"

以及:

schedulerName: volcano

其中,group-min-member 用于声明最小成组调度数量,schedulerName: volcano 用于确保 Pod 交给 Volcano Scheduler 调度。两者缺一不可。

整体来看,Volcano 的核心价值不是简单地提供一个新的 Job 类型,而是为 Kubernetes 增强了批处理、成组调度和队列资源管理能力。对于 AI 训练、大数据计算、HPC 等场景,Volcano 能够更好地表达“任务组整体运行”的调度语义,避免只启动部分 Pod 导致任务无法运行和资源浪费。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐