Skip to main content

以下笔记是在工作使用中,通过分析volcano源码,并且实践得到的经验。volcano源码版本为 commit: 80eea1df4b922773c47ac4f8e483b48a3ccc7090,最新提交时间:Wed Aug 13 10:17:58 2025 +0800

默认root队列问题

  1. volcano默认情况下会给创建的一级队列设置默认的父级队列为root队列。
  2. volcano资源队列默认的root队列资源配额是集群的所有资源的总和(nodes allocatable),并且会随着节点变化或节点资源的变化而自动改变。
  3. 当队列已分配的额度超过root队列时,volcano调度器将不在继续工作,出现系统性故障。
  4. 具体请参考问题排查:Volcano层级队列配置引发的调度器系统性故障问题

层级队列的插件配置

层级队列的特性需要启用capacity插件,并且设置enableHierarchy: true

volcano-scheduler-configmap.yaml
apiVersion: v1
data:
volcano-scheduler.conf: |
actions: "enqueue, allocate, backfill"
tiers:
- plugins:
- name: priority
- name: gang
enablePreemptable: false
- name: conformance
- plugins:
- name: overcommit
- name: drf
enablePreemptable: false
- name: predicates
- name: capacity
enableHierarchy: true
- name: nodeorder
- name: binpack
kind: ConfigMap
metadata:
name: volcano-scheduler-configmap
namespace: volcano-system

capability、deserved与guarantee

  1. 大部分使用场景下,不需要对队列设置deserved,只需要设置guaranteecapability即可。
  2. guarantee是给队列的预留资源量,不管队列用不用,volcano调度器都会为该队列预留guarantee的资源量。
  3. capability是队列的资源配额,当队列的资源使用量超过capability时,volcano调度器会拒绝该队列的资源申请。
  4. 队列可以通过guarantee预留某一种资源,并且通过capability限制对该资源的最大使用量。在队列的capabilityguarantee中不设置其他资源的配额时,表示该队列对集群中的其他资源使用集群中空余的资源。
    test-queue-card.yaml
    apiVersion: scheduling.volcano.sh/v1beta1
    kind: Queue
    metadata:
    name: test-queue-card
    spec:
    capability:
    nvidia.com/gpu: 2
    guarantee:
    resource:
    nvidia.com/gpu: 2

层级队列的guarantee与capability配置

  1. Volcano Job或者Pod只能创建到队列的叶子节点上,否则任务无法创建成功,并且会引发调度器系统性无法工作。调度器日志中会出现类似如下的报错:

    E0826 09:01:29.031005       1 capacity.go:551] The Queue <test-queue> of Job <volcano-system/podgroup-b82f796a-6767-46b9-94ad-b94ae7f7b695> is not leaf queue
  2. 当子级队列设置有guarantee配额时,父级队列必须设置guarantee配额,并且父级队列的guarantee配额必须大于等于子级队列的guarantee配额。

  3. 当子级队列设置有capability配额时,父级队列可以不设置capability配额,但如果父级队列设置了capability配额,那么父级队列的capability配额必须大于等于子级队列的capability配额。

    使用示例:

    test-queue.yaml
    apiVersion: scheduling.volcano.sh/v1beta1
    kind: Queue
    metadata:
    name: test-queue
    spec:
    guarantee:
    resource:
    cpu: 2
    memory: 2Gi
    test-queue2.yaml
    apiVersion: scheduling.volcano.sh/v1beta1
    kind: Queue
    metadata:
    name: test-queue2
    spec:
    parent: test-queue
    capability:
    cpu: 2
    memory: 2Gi
    guarantee:
    resource:
    cpu: 2
    memory: 2Gi
  4. 子级可以设置capabilityguarantee,表示对该队列预留了资源并且设置了资源上限。父级可以只设置guarantee,不需要设置capability。并且父级队列的guarantee配额必须大于等于所有子级队列的guarantee配额。

    使用示例:

    层级关系:

    test-queue
    ├── test-queue-card
    └── test-queue-cpu
    test-queue.yaml
    apiVersion: scheduling.volcano.sh/v1beta1
    kind: Queue
    metadata:
    name: test-queue
    spec:
    guarantee:
    resource:
    cpu: 2
    memory: 2Gi
    nvidia.com/gpu: 2
    test-queue-card.yaml
    apiVersion: scheduling.volcano.sh/v1beta1
    kind: Queue
    metadata:
    name: test-queue-card
    spec:
    capability:
    nvidia.com/gpu: 2
    guarantee:
    resource:
    nvidia.com/gpu: 2
    test-queue-cpu.yaml
    apiVersion: scheduling.volcano.sh/v1beta1
    kind: Queue
    metadata:
    name: test-queue-cpu
    spec:
    capability:
    cpu: 2
    memory: 2Gi
    guarantee:
    resource:
    cpu: 2
    memory: 2Gi

Pod使用volcano队列

  1. 如果Pod想要使用volcano队列:
    • 需要通过注解的形式注入队列名称,并且设置PodschedulerNamevolcano
    • 需要注意注解的键名为scheduling.volcano.sh/queue-name而不是volcano.sh/queue-name
    • 队列的识别是由volcano controller实现的,volcano controller只识别通过注解方式注入的队列名称,不支持标签注入队列名称。
  2. volcano controller会通过Informer机制监听所有Pod的创建,并判断schedulerNamevolcano时才会纳入自身管理。volcano controller会为每个Pod创建对应的PodGroup,随后通过PodGroup的逻辑使用到volcano队列机制。
  3. 通过volcano job创建的Pod中,volcano会将队列名称注入到Pod的标签和注解中,键名为volcano.sh/queue-name。注意该键名和手动为Pod注入volcano队列注解的键名不同。

哪些状态的Pod会被计入队列使用量

  1. volcanoPod包装为了Task,同时扩展了Pod的状态,叫做TaskStatusTaskStatus的枚举值如下:

    const (
    // Pending means the task is pending in the apiserver.
    Pending TaskStatus = 1 << iota

    // Allocated means the scheduler assigns a host to it.
    Allocated

    // Pipelined means the scheduler assigns a host to wait for releasing resource.
    Pipelined

    // Binding means the scheduler send Bind request to apiserver.
    Binding

    // Bound means the task/Pod bounds to a host.
    Bound

    // Running means a task is running on the host.
    Running

    // Releasing means a task/pod is deleted.
    Releasing

    // Succeeded means that all containers in the pod have voluntarily terminated
    // with a container exit code of 0, and the system is not going to restart any of these containers.
    Succeeded

    // Failed means that all containers in the pod have terminated, and at least one container has
    // terminated in a failure (exited with a non-zero exit code or was stopped by the system).
    Failed

    // Unknown means the status of task/pod is unknown to the scheduler.
    Unknown
    )
  2. 只有Bound, Binding, Running, Allocated四种TaskStatusPod才会被计入队列使用量。关键的源码如下:

    // 根据Pod生成TaskStatus
    func getTaskStatus(pod *v1.Pod) TaskStatus {
    switch pod.Status.Phase {
    case v1.PodRunning:
    if pod.DeletionTimestamp != nil {
    return Releasing
    }

    return Running
    case v1.PodPending:
    if pod.DeletionTimestamp != nil {
    return Releasing
    }

    if len(pod.Spec.NodeName) == 0 {
    return Pending
    }
    return Bound
    case v1.PodUnknown:
    return Unknown
    case v1.PodSucceeded:
    return Succeeded
    case v1.PodFailed:
    return Failed
    }

    return Unknown
    }

    // AllocatedStatus判断Pod是否计入队列使用量
    func AllocatedStatus(status TaskStatus) bool {
    switch status {
    case Bound, Binding, Running, Allocated:
    return true
    default:
    return false
    }
    }
  3. 其中BindingAllocated的状态计算较复杂,如果需要粗略计算,可以只关注BoundRunning状态即可。

常见调度问题排查

  1. 队列配额设置不合理。如:

    • 父级队列有设置guarantee配额,并且该配额值小于所有子级队列的guarantee配额值之和
    • 父级队列有设置capability配额,并且该配额值小于所有子级队列的capability配额值之和
    • 这个时候可以查看volcano调度器日志,并通过grep关键字than来查看是否出现该问题,以及出现配额设置不正确的队列名称
  2. 调度器系统性不工作。这个时候查看volcano调度器日志即可,默认每隔1秒钟就会执行Session的创建和关闭,会有大量的错误日志输出。

  3. 队列资源确实不够用,Pod无法调度。如:

    • 其他队列使用了大量的guarantee配置,占用了集群资源,导致其他队列没有配置guarantee时,无法调度Pod
    • 队列配置的capability资源确实不够用了,导致无法调度Pod
    • 这种场景的问题不是很好排查,volcano调度器的日志不是很友好,只有自己编写工具排查并确定资源问题。