k8s调度器优先级和抢占机制

优先级(Priority)和抢占(Preemption)机制

优先级和抢占机制,解决的是Pod调度失败时该怎么办的问题

正常情况下,当一个Pod调度失败后,它就会被暂时“搁置”起来,直到Pod被更新,或者集群状态发生变化,调度器才会对这个Pod进行重新调度

特殊要求的场景:

当一个高优先级的Pod调度失败后,该Pod并不会被“搁置”,而是会“挤走”某个Node上的一些低优先级的Pod.这样就保证这个高优先级Pod的调度成功

优先级的实现

需要在Kubernetes里提交一个PriorityClass资源的定义

Kubernetes规定优先级是一个32bit的整数,最大值不超过1000000000(10 亿,1 billion),并且值越大代表优先级越高.而超出10亿的值,其实是被Kubernetes保留下来分配给系统 Pod 使用的.这样做的目的,就是保证系统Pod不会被用户抢占掉

YAML文件里的globalDefault被设置为true的话,那就意味着这个PriorityClass的值会成为系统的默认值.而如果这个值是false就表示我们只希望声明使用该PriorityClass的Pod拥有值为1000000的优先级,对于没有声明PriorityClass的Pod来说它们的优先级就是0

在创建了PriorityClass对象之后,Pod 就可以声明使用它.Kubernetes的PriorityAdmissionController就会自动将这个Pod的spec.priority字段设置为PriorityClass的值

优先级的体现:

当Pod拥了优先级之后,高优先级的Pod就可能会比低优先级的Pod提前出队,从而尽早完成调度过程.这个过程,就是“优先级”这个概念在 Kubernetes里的主要体现

抢占的体现:

当一个高优先级的Pod调度失败的时候,调度器的抢占能力就会被触发.这时,调度器就会试图从当前集群里寻找一个节点,使得当这个节点上的一个或者多个低优先级 Pod 被删除后,待调度的高优先级Pod 就可以被调度到这个节点上.这个过程,就是“抢占”这个概念在Kubernetes里的主要体现

当上述抢占过程发生时,抢占者并不会立刻被调度到被抢占的Node上.调度器只会将抢占者的spec.nominatedNodeName字段,设置为被抢占的Node的名字.然后,抢占者会重新进入下一个调度周期,然后在新的调度周期里来决定是不是要运行在被抢占的节点上.即使在下一个调度周期,调度器也不会保证抢占者一定会运行在被抢占的节点上.原因如下:

1.调度器只会通过标准的DELETE API来删除被抢占的Pod,所以这些Pod必然是有一定的“优雅退出”时间(默认是 30s)的,而在这段时间里,其他的节点也是有可能变成可调度的或者直接有新的节点被添加到这个集群中来.所以,鉴于优雅退出期间,集群的可调度性可能会发生的变化

2.在抢占者等待被调度的过程中,如果有其他更高优先级的Pod也要抢占同一个节点,那么调度器就会清空原抢占者的spec.nominatedNodeName字段,从而允许更高优先级的抢占者执行抢占,并且这也就使得原抢占者也有机会去重新抢占其他节点

抢占机制的设计和实现

抢占发生的原因,一定是一个高优先级的Pod调度失败

抢占算法的一个最重要的设计,就是在调度队列的实现里,使用了两个不同的队列

第一个队列activeQ:

凡是在activeQ里的Pod,都是下一个调度周期需要调度的对象.所以,当你在 Kubernetes 集群里新创建一个Pod的时候,调度器会将这个Pod入队到activeQ里面.而我在前面提到过的,调度器不断从队列里出队(Pop)一个Pod 进行调度,实际上都是从activeQ里出队的

第二个队列unschedulableQ:

专门用来存放调度失败的Pod,而这里的一个关键点就在于,当一个unschedulableQ里的Pod被更新之后,调度器会自动把这个Pod移动到activeQ里

抢占者调度失败后执行两个操作:

1.抢占者就会被放进unschedulableQ里面

2.调度失败事件会触发调度器为抢占者寻找牺牲者的流程

抢占的具体流程

1.调度器会检查这次失败事件的原因,来确认抢占是不是可以帮助抢占者找到一个新节点.这是因为有很多Predicates的失败是不能通过抢占来解决的.比如,PodFitsHost 算法(负责的是,检查Pod的nodeSelector与Node的名字是否匹配)这种情况下,除非Node 的名字发生变化,否则你即使删除再多的Pod,抢占者也不可能调度成功

2.如果确定抢占可以发生,那么调度器就会把自己缓存的所有节点信息复制一份,然后使用这个副本来模拟抢占过程.

调度器会检查缓存副本里的每一个节点,然后从该节点上最低优先级的Pod开始,逐一“删除”这些Pod.而每删除一个低优先级Pod,调度器都会检查一下抢占者是否能够运行在该 Node 上.一旦可以运行,调度器就记录下这个Node的名字和被删除Pod的列表,这就是一次抢占过程的结果了.

当遍历完所有的节点之后,调度器会在上述模拟产生的所有抢占结果里做一个选择,找出最佳结果.而这一步的判断原则,就是尽量减少抢占对整个系统的影响.比如,需要抢占的 Pod 越少越好,需要抢占的Pod的优先级越低越好

在得到了最佳的抢占结果之后,这个结果里的Node,就是即将被抢占的Node.被删除的Pod列表,就是牺牲者

3.调度器开始执行真正的抢占操作

第一步:  调度器会检查牺牲者列表,清理这些Pod所携带的nominatedNodeName字段

第二步:  调度器会把抢占者的nominatedNodeName,设置为被抢占的Node的名字

抢占者Pod的更新操作,就会触发调度器自动把这个Pod从unschedulableQ移动到activeQ里,让抢占者在下一个调度周期重新进入调度流程

接下来,调度器就会通过正常的调度流程来调度抢占者,但是在任何一次正常调度流程中任何一个Pod都有可能被其它新的抢占者抢占,这其中包括了刚unschedulableQ中转成activeQ里的pod 这就是为什么调度器并不保证抢占者一定会运行在当初选定的被抢占的Node上

第三步:  调度器会开启一个Goroutine同步地删除牺牲者

4.对于任何一个待调度的Pod在调度的时候需要处理的特殊额外情况

为某一对Pod和Node执行Predicates算法的时候,如果待检查的Node是一个即将被抢占的节点,即:调度队列里有nominatedNodeName字段值是该Node名字的Pod存在(可以称之为:“潜在的抢占者”)那么调度器就会对这个Node将同样的Predicates算法运行两遍

如果没有潜在抢占者调度一个Pod就只需执行一遍Predicates算法

第一遍: 调度器会假设上述“潜在的抢占者”已经运行在这个节点上,然后执行Predicates算法

由于InterPodAntiAffinity规则的存在.由于InterPodAntiAffinity规则关心待考察节点上所有Pod之间的互斥关系,所以在执行调度算法时必须考虑,如果抢占者已经存在于待考察Node上时,待调度Pod还能不能调度成功

在这里只要考虑那些优先级等于或者大于待调度Pod的抢占者.对于其他较低优先级Pod来说,待调度Pod总是可以通过抢占运行在待考察Node上

第二遍: 调度器会正常执行Predicates算法,即不考虑任何“潜在的抢占者"

因为“潜在的抢占者”最后不一定会运行在待考察的Node上,Kubernetes调度器并不保证抢占者一定会运行在当初选定的被抢占的Node上

只有这两遍Predicates算法都能通过时,这个Pod 和Node才会被认为是可以绑定(bind)的

5.当整个集群发生可能会影响调度结果的变化(添加或者更新Node添加和更新 PV、Service 等)时,调度器会执行MoveAllToActiveQueue的操作,把所调度失败的Pod从unscheduelableQ移动到activeQ里面

6.当一个已经调度成功的Pod被更新时,调度器则会将unschedulableQ里所有跟这个Pod有 Affinity/Anti-affinity关系的Pod,移动到 activeQ 里面

原文地址:https://www.cnblogs.com/yxh168/p/12253513.html

时间: 2024-08-04 15:06:43

k8s调度器优先级和抢占机制的相关文章

7.k8s.调度器scheduler 亲和性、污点

#k8s. 调度器scheduler 亲和性.污点 默认调度过程:预选 Predicates (过滤节点) --> 优选 Priorities(优先级排序) --> 优先级最高节点 实际使用,根据需求控制Pod调度,需要用到如下: 指定节点.nodeAffinity(节点亲和性).podAffinity(pod 亲和性). podAntiAffinity(pod 反亲和性) #指定调度节点 # Pod.spec.nodeName 强制匹配,直接指定Node 节点,跳过 Scheduler 调度

从零开始入门 K8s | 调度器的调度流程和算法介绍

导读:Kubernetes 作为当下最流行的容器自动化运维平台,以声明式实现了灵活的容器编排,本文以 v1.16 版本为基础详细介绍了 K8s 的基本调度框架.流程,以及主要的过滤器.Score 算法实现等,并介绍了两种方式用于实现自定义调度能力. 调度流程 调度流程概览 Kubernetes 作为当下最主流的容器自动化运维平台,作为 K8s 的容器编排的核心组件 kube-scheduler 将是我今天介绍的主角,如下介绍的版本都是以 release-1.16 为基础,下图是 kube-sch

k8s的调度器架构和策略

调度器功能 默认调度器的主要职责,就是为一个新创建出来的Pod寻找一个最合适的节点(Node) 调度器对一个 Pod 调度成功,实际上就是将它的 spec.nodeName 字段填上调度结果的节点名字 预选节点 从集群所有的节点中,根据调度算法挑选出所有可以运行该 Pod 的节点默认调度器会首先调用一组叫作 Predicate 的调度算法,来检查每个Node 优选节点 从预选的结果中,再根据调度算法挑选一个最符合条件的节点作为最终结果.再调用一组叫作 Priority 的调度算法,来给上一步得到

朴素的UNIX之-调度器细节

0.多进程调度的本质 我们都知道UNIX上有一个著名的nice调用,何谓nice,当然是"好"了,常规的想法是nice值越大越好,实际上,nice值越好,自己的优先级越低,那么为何不用badness呢? 事实上,如果我们理解了操作系统多进程调度系统是一个"利他"系统,这个问题就不是个问题了.nice当然还是好,不是对自己好,而是对别人好.利他系统 是一个人人为我我为人人的系统,类似还有TCP流量控制和拥塞控制,人类的宗教社会组织等等,利他系统都有一个负反馈机制,让波

Yarn 调度器Scheduler详解

理想情况下,我们应用对Yarn资源的请求应该立刻得到满足,但现实情况资源往往是有限的,特别是在一个很繁忙的集群,一个应用资源的请求经常需要等待一段时间才能的到相应的资源.在Yarn中,负责给应用分配资源的就是Scheduler.其实调度本身就是一个难题,很难找到一个完美的策略可以解决所有的应用场景.为此,Yarn提供了多种调度器和可配置的策略供我们选择. 一.调度器的选择 在Yarn中有三种调度器可以选择:FIFO Scheduler ,Capacity Scheduler,FairS ched

go调度: 第二部分-go调度器

前言 这个博客是三部分中提供go调度器的语义和机制的部分. 博客三部分的顺序: 1) go调度: 第一部分-操作系统调度 2) go调度: 第二部分-go调度器 3) go调度: 第三部分-并发 介绍 在博客三部分中的第一部分, 我解释了操作系统调度器中对理解go调度器机制重要的方面. 在博客的这部分, 我将在语义上解释go调度器如何工作, 并且关注于上层行为. go调度器是一个复杂的系统, 那些小的机制的细节不是很重要. 重要的是对事情是如何运行和表现有个好的模型. 这将允许你做出更好的决定.

MRv2 工作机制 、 公平调度器、MR压缩、边数据

对于节点数超过 4000 的大型集群,前一节描述的 MapReduce 系统开始面临着扩展的瓶颈. 2010 年 Yahoo 的团队开始设计下一代的 MapReduce. (Yet Another Resource Negotiator.YARN Application Resource Nefotiator). YARN 将 JobTracker 的只能划分为多个独立的实体,从而改善了 MR1 面临的扩展瓶颈问题. JobTracker 负责作业调度和任务进度监视,追踪任务.重启失败或过慢的任

k8s中的默认调度器

默认调度器中的调度算法分为预选和优选 预选策略1.基础的检查项(GeneralPredicates)PodFitsResources node剩余可分配资源(Allocatable-sum(request))是否足够pod调度PodFitsHost node的名称是否跟pod的spec.nodeName字段一致PodFitsHostPorts pod指定的spec.nodePort端口在待考察node上是否已被占用PodMatchNodeSelector pod的nodeSelector或者no

图解kubernetes调度器ScheduleAlgorithm核心实现学习框架设计

ScheduleAlgorithm是一个接口负责为pod选择一个合适的node节点,本节主要解析如何实现一个可扩展.可配置的通用算法框架来实现通用调度,如何进行算法的统一注册和构建,如何进行metadata和调度流程上下文数据的传递 1. 设计思考 1.1 调度设计 1.1.1 调度与抢占 当接收到pod需要被调度后,默认首先调用schedule来进行正常的业务调度尝试从当前集群中选择一个合适的node 如果调度失败则尝试抢占调度,根据优先级抢占低优先级的pod运行高优先级pod 1.1.2 调