Skip to main content

本文详细介绍VolcanoQueueJob核心对象的关键设计以及使用。关于这两个对象的基础介绍,请参考Volcano基本介绍章节 Volcano介绍

Queue

QueueVolcano调度系统中的核心概念,用于管理和分配集群资源。 它充当了资源池的角色,允许管理员将集群资源划分给不同的用户组或应用场景。该自定义资源可以很好地用于多租户场景下的资源隔离。

该对象的数据结构如下:

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: default-queue # 队列名称
annotations: # 注释,可选
description: "Production workloads queue" # 自定义注释
spec:
weight: 1 # 队列权重,在优先级相同时影响资源分配比例,数值越大分配比例越高
priority: 100 # 队列优先级,决定队列间资源抢占顺序,数值越大优先级越高
capability: # 队列资源容量限制(上限)
cpu: 100 # CPU资源上限,单位为核心数
memory: 100Gi # 内存资源上限
nvidia.com/gpu: 4 # GPU资源上限
deserved: # 应得资源量(多队列竞争时的资源分配基准)
cpu: 80 # 应得的CPU资源量
memory: 80Gi # 应得的内存资源量
nvidia.com/gpu: 3 # 应得的GPU资源量
guarantee: # 队列保障资源,即使在资源紧张情况下也会保证分配
resource: # 保障的资源量
cpu: 50 # 保障的CPU资源量
memory: 50Gi # 保障的内存资源量
nvidia.com/gpu: 2 # 保障的GPU资源量
reclaimable: true # 是否允许回收资源,设为true时允许其他队列在空闲时借用该队列资源
parent: "root.eng" # 父队列,用于层级队列结构
affinity: # 队列亲和性配置,控制队列中的Pod调度到哪些节点
nodeGroupAffinity: # 节点组亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 必须满足的亲和性规则
- "gpu-nodes" # 节点组名称
preferredDuringSchedulingIgnoredDuringExecution: # 优先满足的亲和性规则
- "high-memory-nodes" # 节点组名称
nodeGroupAntiAffinity: # 节点组反亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 必须满足的反亲和性规则
- "test-nodes" # 节点组名称
extendClusters: # 扩展集群配置,指示队列中的作业将被调度到这些集群
- name: "cluster-1" # 集群名称
weight: 5 # 集群权重
capacity: # 集群容量
cpu: 1000 # CPU容量
memory: 1000Gi # 内存容量

在该队列对象上,我去掉了没有太大意义的一些配置项,以便更好理解队列功能。去掉的配置项如下:

  • 注解上实现的层级队列配置,如:
    annotations:
    "volcano.sh/hierarchy": "root/eng/prod"
    "volcano.sh/hierarchy-weights": "1/2/8"
  • 仅作队列类型标记使用的spec.type配置项,没有实际功能作用。

优先级与权重

Volcano队列系统中的priority(优先级)和weight(权重)是两个不同的概念,它们在资源分配和抢占机制中扮演不同的角色:

1. priority(优先级)

定义:队列的优先级,用于确定队列间资源分配和抢占的顺序。

特点

  • 优先级是绝对的,高优先级队列总是比低优先级队列先获得资源
  • 在资源紧张时,高优先级队列可以抢占(reclaim)低优先级队列的资源
  • 优先级值越大,队列优先级越高
  • 优先级是队列间资源竞争的第一决定因素

使用场景

  • 区分生产环境和开发环境队列
  • 确保关键业务队列优先获得资源
  • 实现多租户环境中的资源优先级策略

2. weight(权重)

定义:队列的权重,用于在优先级相同的队列之间按比例分配资源。

特点

  • 权重是相对的,只在优先级相同的队列之间起作用
  • 权重决定了队列获得资源的比例,而不是绝对优先顺序
  • 权重值越大,在同优先级队列中获得的资源比例越高
  • 权重是队列间资源竞争的第二决定因素(优先级相同时才考虑权重)

使用场景

  • 在同一部门的多个项目队列之间分配资源
  • 实现资源的按比例分配策略
  • 在非抢占场景下微调资源分配

两者关系

  1. 决策顺序:调度器首先根据优先级(priority)排序队列,然后才考虑权重(weight
  2. 抢占机制:只有优先级(priority)会触发抢占,权重(weight)不会导致抢占
  3. 资源分配
    • 不同优先级:高优先级队列可以抢占低优先级队列的资源
    • 相同优先级:根据权重按比例分配资源

示例

  • 如果队列A的优先级为100,队列B的优先级为50,那么即使队列B的权重远高于队列A,队列A仍然会优先获得资源
  • 如果队列C和队列D的优先级都是100,权重分别是21,那么在资源分配时,队列C会获得约2/3的资源,队列D获得约1/3的资源

资源配置机制

VolcanoQueue资源对象采用了一个灵活的三级资源配置机制,这三个级别分别是:capability(资源上限)、deserved(应得资源)和guarantee(保障资源)。这种设计允许集群管理员精细控制资源分配,特别适合多租户环境下的资源管理。

注意:要使Queue中的capabilitydeservedguarantee这三项资源配置属性生效,需要确保Volcano调度器启用了capacity插件。这个插件是处理这三级资源配置的核心组件。

1. capability(资源上限)

定义:队列能够使用的资源上限,队列中的所有作业使用的资源总和不能超过这个上限。

特点

  • 代表队列能够使用的最大资源量
  • 设置了资源使用的硬限制,防止单个队列过度消耗集群资源
  • 可以针对多种资源类型设置(CPU、内存、GPU等)

示例场景: 假设一个生产队列设置了capability.cpu=100,那么即使集群有更多空闲资源,该队列中的所有作业使用的CPU总和也不能超过100核。

2. deserved(应得资源)

定义:队列在资源竞争情况下应该获得的资源量,是资源分配和回收的基准线。

特点

  • 当集群资源充足时,队列可以使用超过deserved的资源(但不超过capability
  • 当集群资源紧张时,超过deserved的资源可能被回收给其他队列使用
  • 是资源借用和回收机制的核心参考值

配置建议

  • 在同级队列场景下,所有队列的deserved值总和应等于集群总资源
  • 在层级队列场景下,子队列的deserved值总和应等于父队列的deserved

示例场景: 假设队列A设置了deserved.cpu=80,当资源充足时,它可以使用多达100核(capability上限);但当资源紧张时,它只能保证获得80核,超出部分可能被回收。当资源极度紧张时,会保证后续介绍的队列guarantee资源,

3. guarantee(保障资源)

定义:队列被保证能够使用的最小资源量,即使在集群资源紧张的情况下也不会被回收。

特点

  • 提供了资源使用的最低保障
  • 这部分资源专属于该队列,不会被其他队列借用或抢占
  • 即使在资源紧张的情况下,调度器也会确保队列能获得这些资源

示例场景: 假设关键业务队列设置了guarantee.cpu=50,即使集群资源非常紧张,该队列也能保证获得50CPU资源用于运行关键业务。

三者之间的关系

  1. 资源层级关系:guarantee ≤ deserved ≤ capability
  • guarantee是最低保障
  • deserved是正常情况下(资源不是极度紧张情况下)应得的资源
  • capability是最高上限
  1. 资源分配优先级
  • 首先保证所有队列的guarantee资源
  • 然后按照deserved值和权重分配剩余资源
  • 最后允许队列使用空闲资源,但不超过capability
  1. 资源回收顺序

    1. 首先回收超出capability的资源
    • 这种情况通常不会发生,因为调度器会确保队列使用的资源不超过capability上限

    1. 然后回收超出deserved但未超出capability的资源
    • 当资源紧张时,队列使用的超过deserved值的资源可能被回收

    • 这部分资源被视为"借用"的资源,在资源紧张时需要"归还"

    1. 最后考虑回收超出guarantee但未超出deserved的资源
    • 只有在极度资源紧张的情况下,且有更高优先级的队列需要资源时

    • 这种回收通常通过抢占(preemption)机制实现,而不是简单的资源回收

    1. guarantee资源永远不会被回收
    • guarantee资源是队列的最低保障,即使在极度资源紧张的情况下也不会被回收
    • 这是保证关键业务稳定运行的基础

队列状态

Volcano Queue有四种状态,用于控制队列的行为和作业调度。这些状态在集群维护和资源管理中非常重要。

状态状态类型

  1. Open(开放)

    • 队列处于正常工作状态
    • 可以接受新的作业提交
    • 队列中的作业可以被正常调度执行
    • 这是队列的默认状态
  2. Closing(关闭中)

    • 队列正在关闭的过渡状态
    • 不再接受新的作业提交
    • 已有作业仍然可以继续运行
    • 当队列中所有作业都完成后,队列会转变为Closed状态
  3. Closed(已关闭)

    • 队列完全关闭状态
    • 不接受新的作业提交
    • 队列中的作业不会被调度(即使有足够的资源)
    • 通常用于系统维护或资源重新分配
  4. Unknown(未知)

    • 队列状态不明确
    • 通常是由于系统错误或通信问题导致
    • 控制器会尝试将队列恢复到已知状态

检查队列状态

要检查队列的当前状态,可以使用以下命令:

# 查看单个队列状态
kubectl get queue <队列名称> -o jsonpath='{.status.state}'

# 查看所有队列状态
kubectl get queue -o custom-columns=NAME:.metadata.name,STATE:.status.state

状态控制

Volcano使用命令(Command)机制来控制队列的状态,而不是通过直接修改Queue对象的字段。以下是控制队列状态的方法:

方式一:使用kubectl vc命令行工具(推荐)

Volcano提供了专门的命令行工具来操作队列:

# 关闭队列
kubectl vc queue operate --action close --name <队列名称>

# 开启队列
kubectl vc queue operate --action open --name <队列名称>
方式二:创建Command资源

如果需要以编程方式或在自动化脚本中控制队列状态,可以创建Command资源:

apiVersion: bus.volcano.sh/v1alpha1
kind: Command
metadata:
generateName: queue-name-close- # 会自动生成唯一名称
namespace: default
ownerReferences:
- apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
name: <队列名称>
uid: <队列UID> # 需要获取队列的实际UID
targetObject:
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
name: <队列名称>
uid: <队列UID>
action: CloseQueue # 或 OpenQueue
方式三:使用Volcano API客户端

Go程序中,可以使用Volcano的客户端库:

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"volcano.sh/apis/pkg/apis/bus/v1alpha1"
"volcano.sh/apis/pkg/client/clientset/versioned"
)

func closeQueue(client *versioned.Clientset, queueName string) error {
// 获取队列信息
queue, err := client.SchedulingV1beta1().Queues().Get(context.TODO(), queueName, metav1.GetOptions{})
if err != nil {
return err
}

// 创建控制器引用
ctrlRef := metav1.NewControllerRef(queue, v1beta1.SchemeGroupVersion.WithKind("Queue"))

// 创建命令
cmd := &v1alpha1.Command{
ObjectMeta: metav1.ObjectMeta{
GenerateName: fmt.Sprintf("%s-close-", queue.Name),
OwnerReferences: []metav1.OwnerReference{*ctrlRef},
},
TargetObject: ctrlRef,
Action: string(v1alpha1.CloseQueueAction),
}

// 提交命令
_, err = client.BusV1alpha1().Commands(queue.Namespace).Create(context.TODO(), cmd, metav1.CreateOptions{})
return err
}

资源分配机制

Volcano中,队列的资源分配主要基于weight属性配置,而资源抢占机制则是通过内置的调度策略实现的。

定义weight属性用于定义队列在资源分配过程中的权重。

特点

  • weight值越高,队列在资源分配中的优先级越高
  • 当多个队列竞争资源时,资源分配比例与队列的weight值成正比
  • 这主要应用于deserved资源的分配过程

工作原理

  • 当集群资源充足时,所有队列可以获得其deserved资源
  • 当集群资源不足以满足所有队列的deserved资源时,按照weight比例分配资源
  • 例如,如果队列A的weight是队列B的两倍,那么队列A获得的资源也将是队列B的两倍

资源抢占机制

Volcano中,资源抢占有两种主要实现方式:基于Queue属性的抢占和基于PriorityClass的抢占。

注意事项:

要使queuereclaimable配置真正生效,必须在Volcano调度器配置中同时启用preempt动作(action)和reclaim动作(action)。

调度器配置示例:

actions: "enqueue, allocate, preempt, reclaim, backfill"
# 其他配置...
  • actions字段中必须包含reclaim
  • tiers结构中的某个插件层级中必须包含name: reclaim的插件配置

如果未启用preemptreclaim action,即使配置了reclaimable: true,也不会有实际效果。

  1. reclaim action负责识别可回收的资源并执行回收操作,但它依赖于preempt插件提供的抢占机制来实际终止(kill)低优先级队列中的任务。
  2. Volcano的实现中,reclaim action会调用preempt插件注册的回调函数来判断哪些任务可以被回收,如果没有启用preempt action,这个过程就无法完成。
  3. 资源回收的完整流程需要两个action协同工作:
  • reclaim action负责识别资源紧张的情况并触发回收流程
  • preempt action负责实际执行抢占操作,包括终止低优先级任务

1. 基于 Queue 属性的抢占

这种抢占机制主要基于队列的weightreclaimable属性组合实现。

工作原理

  • 当集群资源紧张时,Volcano可能会从低weight队列中回收资源,以满足高weight队列的需求
  • 这种回收可能包括终止低weight队列中的任务
  • reclaimable设置为true时,该队列的资源可被回收或抢占
  • 当发生队列级别的资源抢占时,被回收队列中的部分任务(Pod)会被调度器直接终止(kill),以释放资源给高优先级队列使用。

抢占条件

  • 被抢占队列的reclaimable属性必须设置为true
  • 抢占通常只发生在资源极度紧张且无法满足高weight队列的guarantee资源需求时

2. 基于 PriorityClass 的抢占

虽然Queue对象本身没有priorityClassName属性,但VolcanoKubernetesPriorityClass机制集成,在PodGroup(作业组)级别支持基于优先级的抢占。

注意事项:

  • 要使PodGroup基于PriorityClass的强占生效,必须要Volcano调度器启用preempt动作(action),因为PodGroupVolcano的自定义资源。
  • 如果是其他Kubernetes原生资源如Pod,则不需要Volcano调度器,Kubernetes原生能力已支持。当高优先级Pod无法调度时,kube-scheduler会尝试抢占低优先级Pod

工作原理

  • PodGroup可以设置priorityClassName属性,关联到KubernetesPriorityClass资源
  • 每个PriorityClass对应一个整数值,表示优先级
  • 当集群资源紧张时,高优先级的PodGroup可以抢占低优先级的PodGroup资源
  • 这种抢占是通过终止(kill)低优先级PodGroup中的Pod来实现的

示例配置

# 在Kubernetes中定义PriorityClass
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for critical production jobs"
---
# 在PodGroup中使用PriorityClass
apiVersion: scheduling.volcano.sh/v1beta1
kind: PodGroup
metadata:
name: critical-job
spec:
minMember: 3
priorityClassName: high-priority # 关联到上面定义的PriorityClass

3. 两种抢占机制的区别与配合

作用范围不同

  • Queue属性抢占是队列级别的,影响整个队列的资源分配
  • PriorityClass抢占是PodGroup级别的,可以跨队列进行抢占

决策依据不同

  • Queue属性抢占主要基于weightreclaimable属性
  • PriorityClass抢占主要基于PriorityClass的优先级值

应用场景不同

  • Queue属性抢占适用于资源分组和多租户场景,实现队列间的资源隔离
  • PriorityClass抢占适用于同一队列内或跨队列的作业优先级管理

配合使用

  • 在实际使用中,这两种机制可以配合使用
  • 首先基于Queue属性进行队列级别的资源分配
  • 然后基于PriorityClass在队列内部或跨队列进行细粒度的作业优先级管理

层级队列

Volcano中,层级队列结构是一种特殊的资源管理方式,允许将队列组织成树形结构,实现更精细的资源分配和控制。Volcano支持层级队列结构,允许根据组织结构或业务需求创建多层次的队列体系。

层级队列的配置方式

Volcano中,通过spec.parent属性可以简单直接地配置层级队列关系:

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: dev-queue
spec:
weight: 2
reclaimable: true
parent: "root.eng" # 指定父队列,使用点号分隔层级

层级队列的使用示例

下面是一个使用parent属性创建三层队列结构的示例:

# 根队列
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: root
spec:
weight: 1
reclaimable: false
---
# 工程部队列(第二层)
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: eng
spec:
weight: 2
reclaimable: false
parent: "root" # 指定父队列为root
---
# 开发环境队列(第三层)
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: dev
spec:
weight: 2
reclaimable: true
parent: "root.eng" # 指定父队列为root.eng

层级队列的工作原理

层级队列的资源分配遵循以下原则:

  • 自上而下的资源分配:集群资源首先分配给根队列,然后根队列将资源分配给子队列,以此类推
  • 权重比例分配:同一父队列下的子队列根据其权重比例分配资源
  • 资源限制:子队列的deserved资源总和不能超过父队列的deserved资源
  • 资源继承:如果子队列没有设置capability,它会继承父队列的capability

层级队列的资源回收与抢占

层级队列中的资源回收遵循特定的规则:

  • 同级队列优先:当资源紧张时,会优先从同级的队列中回收资源
  • 层级传递:如果同级队列的资源回收不足,会向上传递资源需求,由父队列协调资源
  • 保障机制:即使在资源紧张时,也会保证队列的guarantee资源不被回收

层级队列的优势

  • 组织结构映射:可以直接映射企业的组织结构,如部门/团队/项目
  • 精细化资源管理:实现多层次的资源配额和限制
  • 灵活的资源共享:同一父队列下的子队列可以共享资源
  • 简化管理:可以在父队列级别设置策略,自动应用到所有子队列

队列亲和性(affinity)

VolcanoQueue对象提供了强大的亲和性配置功能,允许管理员控制队列中的任务应该调度到哪些节点上。这一功能特别适用于需要特定硬件资源(如GPU、高内存节点)的工作负载。

注意Queueaffinity配置必须启用nodegroup插件才能生效。该插件负责解析队列的亲和性配置并在调度过程中应用这些规则。如果未启用nodegroup插件,即使在Queue对象中配置了affinity,这些配置也不会影响调度决策。

亲和性类型

Queueaffinity配置支持两种主要类型:

  1. 节点组亲和性(nodeGroupAffinity:指定队列中的任务应该调度到的节点组

  2. 节点组反亲和性(nodeGroupAntiAffinity:指定队列中的任务不应该调度到的节点组

每种亲和性类型又分为两个级别:

  • requiredDuringSchedulingIgnoredDuringExecution:必须满足的亲和性规则,如果不满足,任务将不会被调度
  • preferredDuringSchedulingIgnoredDuringExecution:优先满足的亲和性规则,如果不满足,任务仍然可以被调度到其他节点

配置示例

下面是一个完整的Queue亲和性配置示例:

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: gpu-workloads
spec:
weight: 10
reclaimable: false
affinity:
# 节点组亲和性配置
nodeGroupAffinity:
# 必须满足的亲和性规则
requiredDuringSchedulingIgnoredDuringExecution:
- "gpu-nodes" # 必须调度到标记为 gpu-nodes 的节点组

# 优先满足的亲和性规则
preferredDuringSchedulingIgnoredDuringExecution:
- "high-memory-nodes" # 优先调度到标记为 high-memory-nodes 的节点组

# 节点组反亲和性配置
nodeGroupAntiAffinity:
# 必须满足的反亲和性规则
requiredDuringSchedulingIgnoredDuringExecution:
- "test-nodes" # 不能调度到标记为 test-nodes 的节点组

工作原理

Volcano调度器为队列中的任务选择节点时,会根据队列的affinity配置进行过滤:

  1. 必需亲和性(Required Affinity

    • 如果配置了nodeGroupAffinity.requiredDuringSchedulingIgnoredDuringExecution,任务只能调度到指定的节点组
    • 如果配置了nodeGroupAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution,任务不能调度到指定的节点组
  2. 首选亲和性(Preferred Affinity

    • 在满足必需亲和性的前提下,调度器会优先选择满足preferredDuringSchedulingIgnoredDuringExecution规则的节点
  3. 与Pod亲和性的关系

    • Queue的亲和性配置不会直接修改Pod.spec.affinity字段
    • 而是在调度决策过程中通过nodegroup插件应用这些规则
    • 调度器会首先查看要调度的Pod所属的PodGroup,然后确定其所属的Queue
    • 在节点筛选阶段,同时考虑Pod自身的亲和性和Queue的亲和性规则
  4. 优先级顺序

    • Pod自身的亲和性规则优先级最高
    • Queue的亲和性规则是额外的约束条件
    • 两者都必须满足才能完成调度

扩展集群(extendClusters)

VolcanoQueue对象提供了extendClusters配置,允许将队列中的作业调度到多个集群中。这一功能在多集群管理和混合云场景中特别有用。

注意:要使QueueextendClusters配置生效,需要启用Volcano的多集群调度功能,并配置相应的集群连接信息。

配置结构

extendClusters字段是一个数组,每个元素定义了一个扩展集群及其相关属性:

extendClusters:
- name: "cluster-1" # 集群名称,必须与多集群配置中的名称匹配
weight: 5 # 集群权重,影响作业分配到该集群的概率
capacity: # 集群容量限制
cpu: 1000 # CPU容量
memory: 1000Gi # 内存容量
- name: "cluster-2"
weight: 3
capacity:
cpu: 500
memory: 500Gi

主要属性说明:

  1. name:指定扩展集群的名称,必须与Volcano多集群配置中定义的集群名称相匹配

  2. weight:集群的权重,决定了在多集群调度场景下,作业被调度到该集群的优先级

    • 权重越高,该集群被选中的概率越大
    • 当多个集群都满足作业调度要求时,权重起到决定性作用
  3. capacity:定义该队列在指定集群中可以使用的资源上限

    • 可以指定多种资源类型,如CPU、内存、GPU等
    • 队列中的作业在该集群上使用的资源总和不能超过这个限制

工作原理

当启用多集群调度功能时,Volcano调度器会根据以下流程处理队列的extendClusters配置:

  1. 集群选择

    • 调度器首先检查作业所属队列的extendClusters配置
    • 根据各集群的weight和当前资源状态,选择最合适的集群
  2. 资源限制检查

    • 检查选中集群的capacity配置
    • 确保队列在该集群上的资源使用不超过限制
  3. 跨集群调度

    • 将作业调度到选中的集群中执行
    • 维护作业与集群的映射关系

应用场景

  1. 资源池扩展:当主集群资源不足时,可以将作业调度到其他集群,扩展资源池

  2. 混合云管理:允许将作业分配到不同的云环境(公有云、私有云、混合云)

  3. 地理分布式部署:将作业分配到不同地理位置的集群,实现全球资源调度

  4. 特定资源类型的分配:将需要特定硬件(如GPU、FPGA)的作业调度到配备这些资源的集群

配置示例

下面是一个完整的使用extendClustersQueue配置示例:

apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: multi-cluster-queue
spec:
weight: 10
capability:
cpu: 2000
memory: 4000Gi
reclaimable: true
# 扩展集群配置
extendClusters:
- name: "on-premise-cluster" # 本地数据中心集群
weight: 10 # 高权重,优先使用
capacity:
cpu: 1000
memory: 2000Gi
nvidia.com/gpu: 8
- name: "cloud-cluster-a" # 公有云集群A
weight: 5 # 中等权重
capacity:
cpu: 500
memory: 1000Gi
- name: "cloud-cluster-b" # 公有云集群B
weight: 3 # 低权重
capacity:
cpu: 500
memory: 1000Gi

在这个配置中:

  • 队列的作业会优先调度到权重为10的on-premise-cluster集群
  • 当本地集群资源不足或不满足作业要求时,会考虑调度到公有云集群
  • 每个集群都有各自的资源限制,确保队列不会过度使用某一集群的资源

使用注意事项

  1. 多集群配置:使用extendClusters前,需要先在Volcano调度器中配置多集群环境

  2. 集群连通性:确保各集群之间的网络连通性,特别是对于跨地域部署的集群

  3. 资源类型兼容性:确保不同集群的资源类型定义一致,否则可能导致调度失败

  4. 性能影响:跨集群调度可能引入额外的网络延迟和性能开销,需要考虑这些因素

队列使用方式

Volcano Queue可以与多种Kubernetes工作负载类型结合使用。以下是不同工作负载类型如何关联到指定队列的示例:

1. Volcano Job

Volcano Job可以直接在定义中指定队列名称:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: distributed-training
spec:
queue: ai-training # 指定队列名称
minAvailable: 3
tasks:
- replicas: 1
name: ps
template:
spec:
containers:
- name: tensorflow
image: tensorflow/tensorflow:latest-gpu

2. Kubernetes Pod

对于普通的Kubernetes Pod,可以通过添加特定注解来指定队列:

apiVersion: v1
kind: Pod
metadata:
name: ml-inference
annotations:
volcano.sh/queue-name: "inference-queue" # 指定队列名称
spec:
schedulerName: volcano # 必须指定使用volcano调度器
containers:
- name: inference-container
image: ml-model:v1

3. Kubernetes Deployment

对于Deployment,需要在Pod模板中添加相关注解:

apiVersion: apps/v1
kind: Deployment
metadata:
name: web-service
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
volcano.sh/queue-name: "web-queue" # 通过labels指定队列名称(推荐方式,便于查询)
# 也可以通过annotations指定队列名称
# annotations:
# volcano.sh/queue-name: "web-queue"
spec:
schedulerName: volcano # 必须指定使用volcano调度器
containers:
- name: nginx
image: nginx:latest

注意:使用labels方式关联队列有助于通过标签选择器快速查询属于特定队列的Pod,例如:

kubectl get pods -l volcano.sh/queue-name=web-queue

4. Kubernetes StatefulSet

StatefulSet的配置方式与Deployment类似:

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: database
spec:
serviceName: "db"
replicas: 3
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
annotations:
volcano.sh/queue-name: "db-queue" # 指定队列名称
spec:
schedulerName: volcano # 必须指定使用volcano调度器
containers:
- name: mysql
image: mysql:5.7

5. PodGroup

PodGroupVolcano提供的一种自定义资源,用于将多个Pod作为一个组进行调度:

apiVersion: scheduling.volcano.sh/v1beta1
kind: PodGroup
metadata:
name: ml-training-group
spec:
queue: high-priority # 指定队列名称
minMember: 3
---
apiVersion: v1
kind: Pod
metadata:
name: training-worker-1
labels:
podgroup: ml-training-group # 关联到PodGroup
spec:
schedulerName: volcano
containers:
- name: training
image: ml-training:v1

注意事项

  1. 调度器指定:无论使用哪种方式,都必须将schedulerName设置为volcano,否则Pod不会被Volcano调度器处理

  2. 队列存在性:指定的队列必须已经创建,否则Pod将无法被成功调度

  3. 权限控制:在多租户环境中,通常需要配置RBAC权限,限制用户只能使用特定的队列

  4. 默认队列:如果未指定队列,Pod将被分配到默认队列(通常名为default

Job

Volcano JobVolcano调度系统中的核心工作负载对象,用于定义和管理复杂的分布式、批处理和高性能计算任务。与原生Kubernetes Job相比,Volcano Job提供了更丰富的功能和更灵活的调度策略,特别适合机器学习、大数据分析、科学计算等领域。

基本结构

Volcano Job的数据结构如下:

apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: distributed-training # 作业名称
spec:
minAvailable: 3 # 最小可用Pod数量,作业启动所需的最小资源数量
minSuccess: 2 # 最小成功Pod数量,任务被认为完成所需的最小成功数量
schedulerName: volcano # 指定使用volcano调度器
priorityClassName: high # 作业优先级(可选)
queue: ai-training # 所属队列
maxRetry: 5 # 最大重试次数
ttlSecondsAfterFinished: 3600 # 作业完成后保留时间(秒)
plugins: # 使用的插件
ssh: [] # SSH插件配置
env: [] # 环境变量插件配置
svc: [] # 服务插件配置
policies: # 策略配置
- event: PodEvicted # 触发事件
action: RestartJob # 对应动作
tasks: # 任务定义,一个Job可以包含多个任务
- replicas: 1 # 副本数
name: ps # 任务名称
policies: # 任务级别策略
- event: TaskCompleted # 触发事件
action: CompleteJob # 对应动作
template: # Pod模板
metadata:
labels:
role: ps
spec:
containers:
- name: tensorflow
image: tensorflow/tensorflow:2.4.0-gpu
resources:
limits:
cpu: 4
memory: 8Gi
nvidia.com/gpu: 1
- replicas: 4 # 副本数
name: worker # 任务名称
template: # Pod模板
metadata:
labels:
role: worker
spec:
containers:
- name: tensorflow
image: tensorflow/tensorflow:2.4.0-gpu
resources:
limits:
cpu: 2
memory: 4Gi
nvidia.com/gpu: 1

Job状态

Volcano Job是一个更高级别的抽象,它包含一个或多个Task,每个Task可以有多个Pod副本。Job有以下几种状态:

状态说明描述
Pending等待中Job正在队列中等待,等待调度决策
Inqueue入队Job已入队,等待调度
Aborting中止中Job正在被中止,等待释放Pod
Aborted已中止Job已被中止,所有Pod已被释放
Running运行中Job中的Pod正在运行
Restarting重启中Job正在重启,等待Pod终止
Completing完成中Job正在完成,等待Pod终止
Completed已完成Job已成功完成,所有Pod都已成功运行并终止
Terminating终止中Job正在终止,等待Pod终止
Terminated已终止Job已终止,所有Pod已被终止
Failed失败Job已失败,无法继续运行

批量调度

minAvailable属性是Volcano Job中的核心功能之一,用于实现“批量调度”或“整体调度”机制。这一机制在分布式计算、机器学习等领域特别重要,因为这些应用通常需要多个Pod同时启动才能正常工作。

工作原理

  1. 调度保证

    • 当设置minAvailable=N时,Volcano调度器会确保至少有NPod同时被调度
    • 如果集群资源不足以满足这一要求,所有Pod将保持在Pending状态,而不是部分调度
  2. PodGroup集成

    • Volcano会为每个Job创建一个PodGroup对象
    • minAvailable值会设置到PodGroup中,用于指导调度决策
  3. 资源等待机制

    • 当资源不足时,作业将进入等待状态
    • 一旦有足够资源可以满足minAvailable要求,作业将被调度

应用场景示例

  1. 分布式机器学习

    apiVersion: batch.volcano.sh/v1alpha1
    kind: Job
    metadata:
    name: tf-training
    spec:
    minAvailable: 3 # 1个PS + 2个Worker最小要求
    tasks:
    - replicas: 1
    name: ps
    - replicas: 4
    name: worker
  2. MPI并行计算

    apiVersion: batch.volcano.sh/v1alpha1
    kind: Job
    metadata:
    name: mpi-job
    spec:
    minAvailable: 5 # 1个Launcher + 4个Worker
    tasks:
    - replicas: 1
    name: launcher
    - replicas: 4
    name: worker

与其他属性的配合

  • 与minSuccess的区别minAvailable关注的是调度启动时的最小要求,而minSuccess关注的是任务成功所需的最小成功Pod数量。这里的成功是指Pod处于Succeeded状态(即Pod内所有容器都成功终止,退出码为0

  • 与policies的结合:可以配合PodFailed策略,当Pod失败导致可用Pod数量小于minAvailable时触发重启或终止操作

重试策略

Volcano Job提供了灵活的重试机制,允许用户配置在作业失败时的处理方式。这一机制对于提高分布式任务的可靠性和容错能力至关重要。

重试控制参数

  1. maxRetry

    • 定义作业失败时的最大重试次数
    • 当作业失败次数超过该值时,作业将被标记为最终失败状态
    • 默认值为3
  2. policies

    • 通过eventaction对定义特定事件发生时的处理方式
    • 可以在作业级别或任务级别配置

支持的事件类型(event)

Volcano支持以下事件类型来触发重试策略:

事件描述备注
PodFailedPod失败时触发适用于容器崩溃、内存溢出等异常情况
PodEvictedPod被驱逐时触发适用于资源抢占、节点维护等情况
PodPendingPod处于等待状态时触发通常与timeout参数配合使用,适用于检测调度卡住的情况
PodRunningPod进入运行状态时触发适用于监控Pod状态变化,可用于取消其他延迟操作
JobUnknown (Unknown)当作业状态未知时触发适用于处理异常情况,如部分Pod无法调度而其他已运行
TaskCompleted当任务中所有Pod成功完成时触发适用于一个任务完成后触发作业级别的动作
TaskFailed当任务意外失败时触发适用于检测任务级别的失败
OutOfSyncPodJob状态更新时触发系统内部事件,用于处理添加/更新/删除操作
CommandIssued当用户发出命令时触发系统内部事件,用于响应外部命令
JobUpdatedJob被更新时触发系统内部事件,主要用于扩容/缩容操作
* (AnyEvent)匹配任何事件通配符,可用于捕获所有类型的事件

支持的动作类型(action)

Volcano支持以下动作类型来处理重试:

动作描述备注
AbortJob中止作业,但不清理资源作业可以恢复
RestartJob重启整个作业所有Pod将被终止并重新创建
RestartTask只重启特定任务只能用于任务级别的策略
RestartPod只重启特定的Pod提供更精细的重启控制
TerminateJob终止作业并清理所有资源作业将无法恢复
CompleteJob将作业标记为完成适用于关键任务完成时结束整个作业

重试策略配置示例

  1. 基本重试配置

    apiVersion: batch.volcano.sh/v1alpha1
    kind: Job
    metadata:
    name: job-retry-example
    spec:
    minAvailable: 3
    maxRetry: 5 # 最多重试五次
    policies:
    - event: PodFailed # 当Pod失败时
    action: RestartJob # 重启整个作业
  2. 任务级别重试策略

    apiVersion: batch.volcano.sh/v1alpha1
    kind: Job
    metadata:
    name: task-retry-example
    spec:
    minAvailable: 3
    maxRetry: 3
    tasks:
    - replicas: 1
    name: master
    policies:
    - event: PodFailed # 当master失败时
    action: RestartJob # 重启整个作业
    - replicas: 3
    name: worker
    policies:
    - event: PodFailed # 当worker失败时
    action: RestartTask # 只重启该任务
  3. 带超时的重试策略

    apiVersion: batch.volcano.sh/v1alpha1
    kind: Job
    metadata:
    name: timeout-retry-example
    spec:
    minAvailable: 3
    policies:
    - event: PodPending # 当Pod长时间处于等待状态
    action: AbortJob # 中止作业
    timeout: 1h # 超过1小时后触发

重试策略最佳实践

  1. 区分关键任务和非关键任务

    • 对于关键任务(如主节点)的失败,应触发RestartJob
    • 对于非关键任务(如工作节点)的失败,可以使用RestartTaskRestartPod
  2. 考虑超时设置

    • 对于可能长时间卡住的情况,添加timeout参数
    • 超时时间应根据任务的复杂度和资源需求合理设置
  3. 限制最大重试次数

    • maxRetry值不应设置过大,避免资源浪费
    • 对于大型作业,建议设置为3-5
  4. 与队列策略的协调

    • 考虑队列的reclaimable属性对重试策略的影响
    • 如果作业在可回收的队列中,可能需要更强大的重试策略

与其他特性的结合

  • 与minAvailable的结合:当Pod失败导致可用Pod数量小于minAvailable时,可以触发重试

  • 与minSuccess的结合:当成功的Pod数量无法达到minSuccess时,可以触发重试

  • 与插件的结合:重试时会重新应用插件配置,确保环境变量、SSH密钥等正确重新配置

任务依赖关系

Volcano Job支持在不同任务之间定义依赖关系,这一功能在复杂的工作流程中非常有用,如数据处理管道、模型训练和评估流程等。

依赖关系配置

Volcano Job使用dependsOn字段定义任务之间的依赖关系:

tasks:
- name: task-A
replicas: 1
template:
# ...
- name: task-B
replicas: 2
dependsOn:
name: ["task-A"] # task-B依赖于task-A
template:
# ...
- name: task-C
replicas: 3
dependsOn:
name: ["task-A", "task-B"] # task-C依赖于task-A和task-B
template:
# ...

工作原理

  1. 依赖解析

    • Volcano Job控制器在创建作业时解析任务之间的依赖关系
    • 构建一个有向无环图(DAG)来表示任务执行顺序
  2. 执行顺序

    • 只有当所有依赖的任务完成后,一个任务才会启动
    • 没有依赖关系的任务可以并行启动
  3. 状态传递

    • 当一个任务失败时,依赖于它的任务不会启动
    • 这确保了工作流程的完整性

应用场景

  1. 数据处理管道
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: data-pipeline
spec:
minAvailable: 1
tasks:
- name: data-collection
replicas: 1
template:
# ...
- name: data-preprocessing
replicas: 3
dependsOn:
name: ["data-collection"]
template:
# ...
- name: model-training
replicas: 2
dependsOn:
name: ["data-preprocessing"]
template:
# ...
  1. 复杂的模型训练流程
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: ml-workflow
spec:
minAvailable: 1
tasks:
- name: data-preparation
replicas: 1
template:
# ...
- name: feature-extraction
replicas: 2
dependsOn:
name: ["data-preparation"]
template:
# ...
- name: model-training
replicas: 1
dependsOn:
name: ["feature-extraction"]
template:
# ...
- name: model-evaluation
replicas: 1
dependsOn:
name: ["model-training"]
template:
# ...
- name: model-deployment
replicas: 1
dependsOn:
name: ["model-evaluation"]
template:
# ...

注意事项与最佳实践

  1. 避免循环依赖

    • 不能创建循环依赖,如A依赖B,B依赖C,C依赖A
    • 这将导致作业创建失败
  2. 考虑资源需求

    • 当使用依赖关系时,要考虑minAvailable的设置
    • 如果同时运行的任务很少,可以将minAvailable设置得较小
  3. 与重试策略的结合

    • 当使用依赖关系时,要为关键任务配置适当的重试策略
    • 关键任务的失败可能会导致整个工作流程卡住
  4. 使用有意义的任务名称

    • 任务名称应清晰地反映其功能和在工作流程中的位置
    • 这有助于理解和维护复杂的依赖关系