1. 资源抢占的背景和用途
1.1 什么是资源抢占
在Kubernetes集群中,资源抢占(Preemption)是指当高优先级的工作负载需要资源但集群资源不足时,调度系统会终止低优先级的工作负载以释放资源,从而确保高优先级工作负载能够正常运行的机制。
资源抢占是解决资源竞争问题的关键技术,特别是在以下场景中尤为重要:
- 资源紧张环境:在计算资源有限的集群中,需要确保关键业务获得足够资源
- 混合负载场景:同一集群中同时运行在线服务和批处理作业时,需要保证在线服务的资源优先级
- 弹性计算:在需求波动较大的环境中,通过优先级机制实现资源的动态分配
- 多租户环境:在多用户共享集群的情况下,通过优先级和抢占机制实现资源隔离和公平分配
1.2 Kubernetes原生抢占机制的局限性
Kubernetes原生的抢占机制主要基于Pod级别的PriorityClass实现,虽然提供了基本的资源抢占能力,但在复杂的高性能计算和AI/ML工作负载场景中存在以下局限性:
-
层级抢占机制不完善:
- 虽然原生的
Job可以通过PriorityClass设置优先级,但缺乏队列级别的资源管理 - 无法实现多层级、多维度的资源抢占策略
- 虽然原生的
-
缺乏作业感知:
- 无法识别同一作业中不同
Pod之间的关联关系 - 在资源紧张时,可能导致需要同时运行的
Pod无法同时获得资源(资源死锁,缺乏Gang调度) - 造成资源碎片化,降低资源利用效率
- 无法识别同一作业中不同
-
抢占策略单一:
- 仅支持基于优先级的简单抢占
- 无法根据不同场景和业务需求定制复杂的抢占策略
-
多租户支持有限:
- 缺乏队列级资源保障和隔离机制
- 难以为不同部门或团队提供差异化的资源配额和服务质量保障
1.3 Volcano抢占机制的优势
Volcano作为一个面向高性能计算和AI/ML工作负载的调度系统,提供了更加完善的资源抢占机制:
- 多层级抢占:支持
Queue(队列)、Job(作业)和Pod(容器组)三个层级的资源抢占 - 作业感知:理解批处理作业的特性,支持
Gang调度等高级特性 - 丰富的抢占策略:提供多种可配置的抢占策略,满足不同场景需求
- 队列级资源管理:支持队列级别的资源分配和抢占,适合多租户环境
- 公平性保障:通过
DRF(主导资源公平)算法等机制,确保资源分配的公平性
2. Volcano资源抢占的设计与实现
Volcano的资源抢占机制设计为三个层级:Queue(队列)、Job(作业)和Pod(容器组),形成了一个层级分明的抢占体系。
2.1 Queue级别抢占
2.1.1 设计原理
Queue是Volcano中最高层级的资源管理单位,比如在业务上可以代表一个租户或一个业务线。Queue级别的抢占主要基于以下原则:
- 优先级机制:每个
Queue可以设置优先级(priority),高优先级Queue可以抢占低优先级Queue的资源。 - 资源配额:
Queue可以设置最小保障资源(guarantee)和最大资源上限(capacity) - 权重分配:当多个
Queue优先级相同时,可以通过权重(weight)决定资源分配比例和抢占顺序
注意事项:
Queue级别的抢占机制从Volcano v1.10.0版本开始支持。Queue并不支持PriorityClass,而是通过priority属性来设置优先级。- 低优先级的
Queue如果显式设置了reclaimable: false,那么该Queue不能被高优先级Queue抢占(reclaimable默认值为true)。 - 不同资源管理插件(
proportion和capacity)会影响资源的分配和抢占:proportion插件会根据Queue的weight属性决定资源分配比例和抢占顺序。每个队列的deserved资源 = 剩余资源 × (队列weight/ 总weight)capacity插件会根据Queue的deserved属性决定资源分配比例和抢占顺序。当priority相同时,按照allocated/deserved的比率排序。
2.1.2 实现方式
Volcano主要通过以下核心组件实现Queue级别的资源抢占:
-
reclaim action:资源回收动作,是实现不同队列间资源抢占的核心机制 -
队列
priority属性:在Queue定义中设置的优先级值,决定了队列间的抢占顺序下面是
Volcano调度器的配置示例,展示了资源抢占相关的动作:# volcano-scheduler-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: volcano-scheduler-config
namespace: volcano-system
data:
volcano-scheduler.conf: |
# 调度器执行的动作序列,包含资源回收和抢占动作
actions: "enqueue, allocate, preempt, reclaim, backfill"
# 其他插件配置...上述配置中,资源抢占相关的核心动作是
reclaim。使用示例如下:apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: high-priority-queue
spec:
weight: 10
capability:
cpu: 100
memory: 100Gi
reclaimable: true # 允许其他Queue抢占此Queue的资源
priority: 100 # 队列优先级,值越大优先级越高
2.2 Job级别抢占
2.2.1 设计原理
Job代表一个完整的工作单元,如一个AI训练任务或批处理作业。Job级别的抢占主要基于以下原则:
-
优先级机制:
Job通过priorityClassName字段关联到Kubernetes的PriorityClass资源- 高优先级
Job可以抢占同一Queue中低优先级Job的资源 Job的优先级会传递给其创建的所有Pod,除非Pod模板中明确指定了不同的priorityClassName
-
最小资源保障:
Job可以设置minResources确保获得最小资源保障- 结合
minAvailable参数确保关键任务的资源需求
-
Gang调度:
- 支持
All-or-Nothing的调度模式 - 确保作业所有关键任务同时调度,避免资源碎片化
- 支持
2.2.2 实现方式
Volcano主要通过以下核心组件实现Job级别的抢占:
-
preempt action:资源抢占动作,是实现同一队列内不同作业间资源抢占的核心机制 -
priorityClassName:与Job关联的优先级类,决定了作业间的抢占顺序apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: high-priority-job
spec:
minAvailable: 3
priorityClassName: high-priority # 关联PriorityClass设置作业优先级
queue: research # 指定所属队列
tasks:
- replicas: 3
name: worker
template:
spec:
containers:
- image: training-image
name: worker
2.3 Pod级别抢占
2.3.1 设计原理
Pod是Kubernetes中最小的调度单位,也是Volcano调度的基本单位。Pod级别的抢占主要基于以下原则:
- 优先级机制:
Pod可以通过PriorityClass或task-priority注解设置优先级 - 可抢占性:
Pod可以通过volcano.sh/preemptable注解标记是否可被抢占 - 资源需求:
Pod的资源请求(requests)决定了需要多少资源
2.3.2 实现方式
Volcano主要通过以下核心组件实现Pod级别的抢占:
-
priorityClassName属性:当Pod使用Volcano调度器时,priorityClassName的优先级影响范围取决于Pod所属的Job和Queue:- 如果
Pod属于某个Volcano Job,其priorityClassName优先级仅在同一个Queue内的不同Job之间生效。 - 如果
Pod不属于任何Volcano Job,其priorityClassName优先级在整个集群范围内生效(不管该Pod是否属于某个Queue)。 - 优先级值越大,
Pod的优先级越高,在资源竞争时优先获得资源。
- 如果
-
volcano.sh/task-priority注解:设置同一Job内不同Pod的优先级,是实现Pod间抢占的核心 -
volcano.sh/preemptable注解:标记Pod是否可被抢占,控制Pod的可抢占性,即便Pod的priorityClassName优先级较低,只要没有设置volcano.sh/preemptable注解为true,就不能被高优先级的Pod抢占。apiVersion: v1
kind: Pod
metadata:
name: high-priority-pod
annotations:
volcano.sh/task-priority: "10" # 设置Pod在Job内的优先级
volcano.sh/preemptable: "true" # 标记Pod可被抢占
spec:
priorityClassName: high-priority # 设置Pod的全局优先级
schedulerName: volcano # 使用Volcano调度器
3. 资源抢占的层级关系
在Volcano的抢占体系中,Queue、Job和Pod三个层级之间存在明确的优先级关系,形成了一个层级化的抢占机制。
3.1 Queue与Queue之间的抢占
当集群资源紧张时,Queue之间的抢占遵循以下规则:
- 优先级决定性:高优先级
Queue可以抢占低优先级Queue的资源(priority属性) - 权重影响:当优先级相同时,权重(
weight属性)较高的Queue可以获得更多资源 - 最小保障:每个
Queue的guarantee资源是受保护的,不会被抢占 - 资源回收:当高优先级
Queue不需要资源时,被抢占的Queue可以重新获得资源 - 可回收标识:设置了
reclaimable: true或者没有设置reclaimable属性(默认值为true)的Queue才能被高优先级Queue抢占资源。如果Queue的reclaimable属性设置为false,即使其优先级较低,也不会被高优先级Queue抢占资源。
抢占过程:
Volcano调度器检测到高优先级Queue资源不足- 根据优先级和权重策略选择低优先级的
Queue - 检查低优先级
Queue的reclaimable属性,只有设置为true的Queue才能被抢占 - 从可抢占的低优先级
Queue中选择可抢占的Job和Pod - 驱逐选中的
Pod,释放资源给高优先级Queue
3.2 Job与Job之间的抢占
在同一Queue内,Job之间的抢占遵循以下规则:
- 优先级决定性:高优先级
Job可以抢占低优先级Job的资源 - 创建时间:当优先级相同时,通常先创建的
Job优先级更高 - 最小资源:每个
Job的minAvailable资源需求会影响抢占决策 - Gang调度:支持
All-or-Nothing的调度模式,确保作业完整性
抢占过程:
Volcano检测到高优先级Job资源不足- 根据
PriorityClass和创建时间选择同一Queue中优先级较低的Job - 从低优先级
Job中选择可抢占的Pod - 驱逐选中的
Pod,释放资源给高优先级Job
3.3 Pod与Pod之间的抢占
在同一Job内,Pod之间的抢占遵循以下规则:
- task-priority决定性:高
task-priority的Pod可以抢占低task-priority的Pod - 可抢占性:只有标记为可抢占(
volcano.sh/preemptable: "true"注解)的Pod才能被抢占 - BestEffort限制:
BestEffort Pod不能抢占非BestEffort Pod(BestEffort Pod是没有明确设置资源请求requests和限制limits的Pod,在资源紧张时是最先被终止的)。 - 状态限制:只有处于
Running状态的Pod才能被抢占
抢占过程:
Volcano检测到高优先级Pod无法调度- 根据任务优先级找到同一
Job中优先级较低的Pod - 检查
Pod的可抢占性和其他约束条件 - 驱逐选中的
Pod,释放资源给高优先级Pod
3.4 跨层级抢占的优先顺序
当涉及跨层级的资源抢占时,Volcano遵循以下优先顺序:
- Queue优先级最高:不同
Queue之间的优先级高于Queue内部的Job优先级 - Job优先级次之:同一
Queue内不同Job之间的优先级高于Job内部的Pod优先级 - Pod优先级最低:同一
Job内不同Pod之间的优先级最低
这意味着:
- 即使是低优先级
Queue中的高优先级Job,也无法抢占高优先级Queue中的低优先级Job - 即使是低优先级
Job中的高优先级Pod,也无法抢占高优先级Job中的低优先级Pod - 这种设计确保了资源抢占的层次性和可预测性
这种分层优先级设计的优势:
- 资源隔离:确保高优先级
Queue的资源不会被低优先级Queue抢占 - 可预测性:资源抢占行为更加可预测,便于资源规划
- 业务保障:重要业务可以通过
Queue优先级得到保障 - 灵活性:在保证优先级层次的同时,仍然允许同层级的资源抢占
使用建议:
- 根据业务重要性设置合适的
Queue优先级 - 在
Queue内部,根据任务紧急程度设置Job优先级 - 在
Job内部,根据任务依赖关系设置Pod优先级 - 合理使用
Queue reclaimable属性和Pod volcano.sh/preemptable注解,进一步控制资源抢占行为