本文详细介绍Volcano
中Queue
和Job
核心对象的关键设计以及使用。关于这两个对象的基础介绍,请参考Volcano
基本介绍章节 Volcano介绍。
Queue
Queue
是Volcano
调度系统中的核心概念,用于管理和分配集群资源。
它充当了资源池的角色,允许管理员将集群资源划分给不同的用户组或应用场景。该自定义资源可以很好地用于多租户场景下的资源隔离。
该对象的数据结构如下:
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(权重)
定义:队列的权重,用于在优先级相同的队列之间按比例分配资源。
特点:
- 权重是相对的,只在优先级相同的队列之间起作用
- 权重决定了队列获得资源的比例,而不是绝对优先顺序
- 权重值越大,在同优先级队列中获得的资源比例越高
- 权重是队列间资源竞争的第二决定因素(优先级相同时才考虑权重)
使用场景:
- 在同一部门的多个项目队列之间分配资源
- 实现资源的按比例分配策略
- 在非抢占场景下微调资源分配
两者关系:
- 决策顺序:调度器首先根据优先级(
priority
)排序队列,然后才考虑权重(weight
) - 抢占机制:只有优先级(
priority
)会触发抢占,权重(weight
)不会导致抢占 - 资源分配:
- 不同优先级:高优先级队列可以抢占低优先级队列的资源
- 相同优先级:根据权重按比例分配资源
示例:
- 如果队列A的优先级为
100
,队列B的优先级为50
,那么即使队列B的权重远高于队列A,队列A仍然会优先获得资源 - 如果队列C和队列D的优先级都是
100
,权重分别是2
和1
,那么在资源分配时,队列C会获得约2/3
的资源,队列D获得约1/3
的资源
资源配置机制
Volcano
的Queue
资源对象采用了一个灵活的三级资源配置机制,这三个级别分别是:capability
(资源上限)、deserved
(应得资源)和guarantee
(保障资源)。这种设计允许集群管理员精细控制资源分配,特别适合多租户环境下的资源管理。
注意:要使
Queue
中的capability
、deserved
和guarantee
这三项资源配置属性生效,需要确保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
,即使集群资源非常紧张,该队列也能保证获得50
核CPU
资源用于运行关键业务。
三者之间的关系
- 资源层级关系:guarantee ≤ deserved ≤ capability
guarantee
是最低保障deserved
是正常情况下(资源不是极度紧张情况下)应得的资源capability
是最高上限
- 资源分配优先级:
- 首先保证所有队列的
guarantee
资源 - 然后按照
deserved
值和权重分配剩余资源 - 最后允许队列使用空闲资源,但不超过
capability
-
资源回收顺序:
- 首先回收超出
capability
的资源
-
这种情况通常不会发生,因为调度器会确保队列使用的资源不超过
capability
上限
- 然后回收超出
deserved
但未超出capability
的资源
-
当资源紧张时,队列使用的超过
deserved
值的资源可能被回收 -
这部分资源被视为"借用"的资源,在资源紧张时需要"归还"
- 最后考虑回收超出
guarantee
但未超出deserved
的资源
-
只有在极度资源紧张的情况下,且有更高优先级的队列需要资源时
-
这种回收通常通过抢占(
preemption
)机制实现,而不是简单的资源回收
guarantee
资源永远不会被回收
guarantee
资源是队列的最低保障,即使在极度资源紧张的情况下也不会被回收- 这是保证关键业务稳定运行的基础
- 首先回收超出
队列状态
Volcano Queue
有四种状态,用于控制队列的行为和作业调度。这些状态在集群维护和资源管理中非常重要。
状态状态类型
-
Open(开放):
- 队列处于正常工作状态
- 可以接受新的作业提交
- 队列中的作业可以被正常调度执行
- 这是队列的默认状态
-
Closing(关闭中):
- 队列正在关闭的过渡状态
- 不再接受新的作业提交
- 已有作业仍然可以继续运行
- 当队列中所有作业都完成后,队列会转变为Closed状态
-
Closed(已关闭):
- 队列完全关闭状态
- 不接受新的作业提交
- 队列中的作业不会被调度(即使有足够的资源)
- 通常用于系统维护或资源重新分配
-
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
的抢占。
注意事项:
要使queue
的reclaimable
配置真正生效,必须在Volcano
调度器配置中同时启用preempt
动作(action
)和reclaim
动作(action
)。
调度器配置示例:
actions: "enqueue, allocate, preempt, reclaim, backfill"
# 其他配置...
actions
字段中必须包含reclaim
tiers
结构中的某个插件层级中必须包含name: reclaim
的插件配置
如果未启用preempt
或reclaim
action
,即使配置了reclaimable: true
,也不会有实际效果。
reclaim action
负责识别可回收的资源并执行回收操作,但它依赖于preempt
插件提供的抢占机制来实际终止(kill
)低优先级队列中的任务。- 在
Volcano
的实现中,reclaim action
会调用preempt
插件注册的回调函数来判断哪些任务可以被回收,如果没有启用preempt action
,这个过程就无法完成。 - 资源回收的完整流程需要两个
action
协同工作:
reclaim action
负责识别资源紧张的情况并触发回收流程preempt action
负责实际执行抢占操作,包括终止低优先级任务
1. 基于 Queue 属性的抢占
这种抢占机制主要基于队列的weight
和reclaimable
属性组合实现。
工作原理:
- 当集群资源紧张时,
Volcano
可能会从低weight
队列中回收资源,以满足高weight
队列的需求 - 这种回收可能包括终止低
weight
队列中的任务 - 当
reclaimable
设置为true
时,该队列的资源可被回收或抢占 - 当发生队列级别的资源抢占时,被回收队列中的部分任务(
Pod
)会被调度器直接终止(kill
),以释放资源给高优先级队列使用。
抢占条件:
- 被抢占队列的
reclaimable
属性必须设置为true
- 抢占通常只发生在资源极度紧张且无法满足高
weight
队列的guarantee
资源需求时
2. 基于 PriorityClass 的抢占
虽然Queue
对象本身没有priorityClassName
属性,但Volcano
与Kubernetes
的PriorityClass
机制集成,在PodGroup
(作业组)级别支持基于优先级的抢占。
注意事项:
- 要使
PodGroup
基于PriorityClass
的强占生效,必须要Volcano
调度器启用preempt
动作(action
),因为PodGroup
是Volcano
的自定义资源。 - 如果是其他
Kubernetes
原生资源如Pod
,则不需要Volcano
调度器,Kubernetes
原生能力已支持。当高优先级Pod
无法调度时,kube-scheduler
会尝试抢占低优先级Pod
。
工作原理:
PodGroup
可以设置priorityClassName
属性,关联到Kubernetes
的PriorityClass
资源- 每个
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
属性抢占主要基于weight
和reclaimable
属性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)
Volcano
的Queue
对象提供了强大的亲和性配置功能,允许管理员控制队列中的任务应该调度到哪些节点上。这一功能特别适用于需要特定硬件资源(如GPU
、高内存节点)的工作负载。
注意:
Queue
的affinity
配置必须启用nodegroup
插件才能生效。该插件负责解析队列的亲和性配置并在调度过程中应用这些规则。如果未启用nodegroup
插件,即使在Queue
对象中配置了affinity
,这些配置也不会影响调度决策。
亲和性类型
Queue
的affinity
配置支持两种主要类型:
-
节点组亲和性(
nodeGroupAffinity
):指定队列中的任务应该调度到的节点组 -
节点组反亲和性(
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
配置进行过滤:
-
必需亲和性(
Required Affinity
):- 如果配置了
nodeGroupAffinity.requiredDuringSchedulingIgnoredDuringExecution
,任务只能调度到指定的节点组 - 如果配置了
nodeGroupAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution
,任务不能调度到指定的节点组
- 如果配置了
-
首选亲和性(
Preferred Affinity
):- 在满足必需亲和性的前提下,调度器会优先选择满足
preferredDuringSchedulingIgnoredDuringExecution
规则的节点
- 在满足必需亲和性的前提下,调度器会优先选择满足
-
与Pod亲和性的关系:
Queue
的亲和性配置不会直接修改Pod
的.spec.affinity
字段- 而是在调度决策过程中通过
nodegroup
插件应用这些规则 - 调度器会首先查看要调度的
Pod
所属的PodGroup
,然后确定其所属的Queue
- 在节点筛选阶段,同时考虑
Pod
自身的亲和性和Queue
的亲和性规则
-
优先级顺序:
Pod
自身的亲和性规则优先级最高Queue
的亲和性规则是额外的约束条件- 两者都必须满足才能完成调度
扩展集群(extendClusters)
Volcano
的Queue
对象提供了extendClusters
配置,允许将队列中的作业调度到多个集群中。这一功能在多集群管理和混合云场景中特别有用。
注意:要使
Queue
的extendClusters
配置生效,需要启用Volcano
的多集群调度功能,并配置相应的集群连接信息。
配置结构
extendClusters
字段是一个数组,每个元素定义了一个扩展集群及其相关属性:
extendClusters:
- name: "cluster-1" # 集群名称,必须与多集群配置中的名称匹配
weight: 5 # 集群权重,影响作业分配到该集群的概率
capacity: # 集群容量限制
cpu: 1000 # CPU容量
memory: 1000Gi # 内存容量
- name: "cluster-2"
weight: 3
capacity:
cpu: 500
memory: 500Gi
主要属性说明:
-
name:指定扩展集群的名称,必须与
Volcano
多集群配置中定义的集群名称相匹配 -
weight:集群的权重,决定了在多集群调度场景下,作业被调度到该集群的优先级
- 权重越高,该集群被选中的概率越大
- 当多个集群都满足作业调度要求时,权重起到决定性作用
-
capacity:定义该队列在指定集群中可以使用的资源上限
- 可以指定多种资源类型,如CPU、内存、GPU等
- 队列中的作业在该集群上使用的资源总和不能超过这个限制
工作原理
当启用多集群调度功能时,Volcano
调度器会根据以下流程处理队列的extendClusters
配置:
-
集群选择:
- 调度器首先检查作业所属队列的
extendClusters
配置 - 根据各集群的
weight
和当前资源状态,选择最合适的集群
- 调度器首先检查作业所属队列的
-
资源限制检查:
- 检查选中集群的
capacity
配置 - 确保队列在该集群上的资源使用不超过限制
- 检查选中集群的
-
跨集群调度:
- 将作业调度到选中的集群中执行
- 维护作业与集群的映射关系
应用场景
-
资源池扩展:当主集群资源不足时,可以将作业调度到其他集群,扩展资源池
-
混合云管理:允许将作业分配到不同的云环境(公有云、私有云、混合云)
-
地理分布式部署:将作业分配到不同地理位置的集群,实现全球资源调度
-
特定资源类型的分配:将需要特定硬件(如GPU、FPGA)的作业调度到配备这些资源的集群
配置示例
下面是一个完整的使用extendClusters
的Queue
配置示例:
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
集群 - 当本地集群资源不足或不满足作业要求时,会考虑调度到公有云集群
- 每个集群都有各自的资源限制,确保队列不会过度使用某一集群的资源
使用注意事项
-
多集群配置:使用
extendClusters
前,需要先在Volcano
调度器中配置多集群环境 -
集群连通性:确保各集群之间的网络连通性,特别是对于跨地域部署的集群
-
资源类型兼容性:确保不同集群的资源类型定义一致,否则可能导致调度失败
-
性能影响:跨集群调度可能引入额外的网络延迟和性能开销,需要考虑这些因素
队列使用方式
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
PodGroup
是Volcano
提供的一种自定义资源,用于将多个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
注意事项
-
调度器指定:无论使用哪种方式,都必须将
schedulerName
设置为volcano
,否则Pod
不会被Volcano
调度器处理 -
队列存在性:指定的队列必须已经创建,否则
Pod
将无法被成功调度 -
权限控制:在多租户环境中,通常需要配置
RBAC
权限,限制用户只能使用特定的队列 -
默认队列:如果未指定队列,
Pod
将被分配到默认队列(通常名为default
)
Job
Volcano Job
是Volcano
调度系统中的核心工作负载对象,用于定义和管理复杂的分布式、批处理和高性能计算任务。与原生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
同时启动才能正常工作。
工作原理
-
调度保证:
- 当设置
minAvailable=N
时,Volcano
调度器会确保至少有N
个Pod
同时被调度 - 如果集群资源不足以满足这一要求,所有
Pod
将保持在Pending
状态,而不是部分调度
- 当设置
-
PodGroup集成:
Volcano
会为每个Job
创建一个PodGroup
对象minAvailable
值会设置到PodGroup
中,用于指导调度决策
-
资源等待机制:
- 当资源不足时,作业将进入等待状态
- 一旦有足够资源可以满足
minAvailable
要求,作业将被调度
应用场景示例
-
分布式机器学习:
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 -
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
提供了灵活的重试机制,允许用户配置在作业失败时的处理方式。这一机制对于提高分布式任务的可靠性和容错能力至关重要。
重试控制参数
-
maxRetry:
- 定义作业失败时的最大重试次数
- 当作业失败次数超过该值时,作业将被标记为最终失败状态
- 默认值为
3
-
policies:
- 通过
event
和action
对定义特定事件发生时的处理方式 - 可以在作业级别或任务级别配置
- 通过
支持的事件类型(event)
Volcano
支持以下事件类型来触发重试策略:
事件 | 描述 | 备注 |
---|---|---|
PodFailed | 当Pod 失败时触发 | 适用于容器崩溃、内存溢出等异常情况 |
PodEvicted | 当Pod 被驱逐时触发 | 适用于资源抢占、节点维护等情况 |
PodPending | 当Pod 处于等待状态时触发 | 通常与timeout 参数配合使用,适用于检测调度卡住的情况 |
PodRunning | 当Pod 进入运行状态时触发 | 适用于监控Pod 状态变化,可用于取消其他延迟操作 |
JobUnknown (Unknown ) | 当作业状态未知时触发 | 适用于处理异常情况,如部分Pod无法调度而其他已运行 |
TaskCompleted | 当任务中所有Pod成功完成时触发 | 适用于一个任务完成后触发作业级别的动作 |
TaskFailed | 当任务意外失败时触发 | 适用于检测任务级别的失败 |
OutOfSync | 当Pod 或Job 状态更新时触发 | 系统内部事件,用于处理添加/更新/删除操作 |
CommandIssued | 当用户发出命令时触发 | 系统内部事件,用于响应外部命令 |
JobUpdated | 当Job 被更新时触发 | 系统内部事件,主要用于扩容/缩容操作 |
* (AnyEvent ) | 匹配任何事件 | 通配符,可用于捕获所有类型的事件 |
支持的动作类型(action)
Volcano
支持以下动作类型来处理重试:
动作 | 描述 | 备注 |
---|---|---|
AbortJob | 中止作业,但不清理资源 | 作业可以恢复 |
RestartJob | 重启整个作业 | 所有Pod 将被终止并重新创建 |
RestartTask | 只重启特定任务 | 只能用于任务级别的策略 |
RestartPod | 只重启特定的Pod | 提供更精细的重启控制 |
TerminateJob | 终止作业并清理所有资源 | 作业将无法恢复 |
CompleteJob | 将作业标记为完成 | 适用于关键任务完成时结束整个作业 |
重试策略配置示例
-
基本重试配置:
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: job-retry-example
spec:
minAvailable: 3
maxRetry: 5 # 最多重试五次
policies:
- event: PodFailed # 当Pod失败时
action: RestartJob # 重启整个作业 -
任务级别重试策略:
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 # 只重启该任务 -
带超时的重试策略:
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: timeout-retry-example
spec:
minAvailable: 3
policies:
- event: PodPending # 当Pod长时间处于等待状态
action: AbortJob # 中止作业
timeout: 1h # 超过1小时后触发
重试策略最佳实践
-
区分关键任务和非关键任务:
- 对于关键任务(如主节点)的失败,应触发
RestartJob
- 对于非关键任务(如工作节点)的失败,可以使用
RestartTask
或RestartPod
- 对于关键任务(如主节点)的失败,应触发
-
考虑超时设置:
- 对于可能长时间卡住的情况,添加
timeout
参数 - 超时时间应根据任务的复杂度和资源需求合理设置
- 对于可能长时间卡住的情况,添加
-
限制最大重试次数:
maxRetry
值不应设置过大,避免资源浪费- 对于大型作业,建议设置为
3-5
次
-
与队列策略的协调:
- 考虑队列的
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:
# ...
工作原理
-
依赖解析:
Volcano Job
控制器在创建作业时解析任务之间的依赖关系- 构建一个有向无环图(DAG)来表示任务执行顺序
-
执行顺序:
- 只有当所有依赖的任务完成后,一个任务才会启动
- 没有依赖关系的任务可以并行启动
-
状态传递:
- 当一个任务失败时,依赖于它的任务不会启动
- 这确保了工作流程的完整性
应用场景
- 数据处理管道:
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:
# ...
- 复杂的模型训练流程:
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:
# ...
注意事项与最佳实践
-
避免循环依赖:
- 不能创建循环依赖,如A依赖B,B依赖C,C依赖A
- 这将导致作业创建失败
-
考虑资源需求:
- 当使用依赖关系时,要考虑
minAvailable
的设置 - 如果同时运行的任务很少,可以将
minAvailable
设置得较小
- 当使用依赖关系时,要考虑
-
与重试策略的结合:
- 当使用依赖关系时,要为关键任务配置适当的重试策略
- 关键任务的失败可能会导致整个工作流程卡住
-
使用有意义的任务名称:
- 任务名称应清晰地反映其功能和在工作流程中的位置
- 这有助于理解和维护复杂的依赖关系