kubernetes scheduler抢占调度和优先级队列

本文重点分析Kubernetes 1.10版本抢占式调度和优先级队列。在Kubernetes 1.8 以前,scheduler中是没有抢占式调度的,调度流程也很简单,从FIFO queue中Pop出一个pod,进行调度,失败了就重新加入queue(为了减轻scheduler的压力,有back off的机制),等待下次调度。

这样就导致一下几个问题:

  • 即使有back off的机制,但是在大规模集群中,Pending容器较多的时候,会影响新容器的调度速度。并且pod重新加入queue的逻辑过于简单,试想当集群资源已经满负荷时,是否有必要再调度失败后反复的重新加入queue

  • 没有抢占调度会导致集群满负荷时,重要的高优先级的应用无法调度成功,只能人工去释放资源。

调度流程

调度的主要流程在kubernetes\cmd\kube-scheduler\scheduler.go中

  • 从FIFO queue或者priority queue中Pop出一个pod
pod := sched.config.NextPod()
  • 调度该pod

    suggestedHost, err := sched.schedule(pod)
    
  • 如果调度失败,进入抢占式调度流程

    if err != nil {
        ****
          if fitError, ok := err.(*core.FitError); ok {
              preemptionStartTime := time.Now()
              sched.preempt(pod, fitError)
      ****
          }
          return
      }
    
  • 假定调度,scheduler加速调度速度的机制,这里不再赘述

    err = sched.assume(assumedPod, suggestedHost)
    
  • 将该pod和选定的node绑定

    err := sched.bind(assumedPod, &v1.Binding{
              ObjectMeta: metav1.ObjectMeta{Namespace: assumedPod.Namespace, Name: assumedPod.Name, UID: assumedPod.UID},
              Target: v1.ObjectReference{
                  Kind: "Node",
                  Name: suggestedHost,
              },
          })
    

抢占调度关键流程

在上面的调度流程中,如果调度失败,进入抢占流程

  • 首先判断该pod有资格进行抢占

    func podEligibleToPreemptOthers()
    

    如果该pod已经有NominatedNodeName 了,并且这个Node上有比该pod优先级更低的pod正在Terminating,则流程结束。

  • 筛选出可能可以进行抢占的node节点

    for _, failedPredicate := range failedPredicates {
              switch failedPredicate {
              case
                  predicates.ErrNodeSelectorNotMatch,
                  predicates.ErrPodNotMatchHostName,
                  predicates.ErrTaintsTolerationsNotMatch,
                  predicates.ErrNodeLabelPresenceViolated,
                  predicates.ErrNodeNotReady,
                  predicates.ErrNodeNetworkUnavailable,
                  predicates.ErrNodeUnschedulable,
                  predicates.ErrNodeUnknownCondition,
                  predicates.ErrVolumeZoneConflict,
                  predicates.ErrVolumeNodeConflict,
                  predicates.ErrVolumeBindConflict:
                  unresolvableReasonExist = true
                  break
                  // TODO(bsalamat): Please add affinity failure cases once we have specific affinity failure errors.
              }
          }
    

    只要不是以上这些情况的node,均可以

  • 计算出每台node上的victims

    • 遍历每台node节点上比改pod优先级低的pod,加入potentialVictims,并且移除他们所占用的资源(仅在内存中移除)
    • 将potentialVictims按优先级从高到低排序
    • 在这种状态下对pod进行podFitsOnNode,如果不通过,说明即使把所有低优先级的pod都驱逐掉,该pod也调度不上来,那么说明该node不适合,返回,否则继续。
    • 将potentialVictims根据PDB筛选成violatingVictims和nonViolatingVictims(PDB是kubernetes中保证应用高可用的一种资源)
    • 分别遍历violatingVictims和nonViolatingVictims(其中要记录numViolatingVictim,下一步选择最终node时会作为依据)。因为之前的排序,所以从高优先级pod开始,将victim占用的资源加回去,然后用podFitsOnNode检查该pod是否可以调度上来,如果不可调度了,就将该pod正式加入victims。因为排序,也尽量保留了高优先级的pod
  • 从上一步的nodes中选出一个node,具体规则如下。

    1. node上numViolatingVictim数最低的那个,也就是尽量较少的影响PDB
    2. 选择victims中最高pod优先级最低的那个Node
    3. 选择优先级之和最小的node
    4. 选择victims pod数最少的Node
    5. 如果这个时候还有不止一个node满足,那么random大法
  • 获取该node的nominatedPods中比该pod低优先级的pods作为nominatedPodsToClear,设置该pod的NominatedNodeName,删除victims,清除nominatedPodsToClear中pod的NominatedNodeName字段,更新到etcd,因为这些之前抢占的pod已经不适合在调到该node上了

优先级队列

在1.10版本中,初始化podQueue的时候会根据配置选择创建FIFO queue还是PriorityQueue

func NewSchedulingQueue() SchedulingQueue {
    if util.PodPriorityEnabled() {
        return NewPriorityQueue()
    }
    return NewFIFO()
}

我们这里只介绍PriorityQueue,下面是PriorityQueue的数据结构

type PriorityQueue struct {
    lock sync.RWMutex
    cond sync.Cond

    // activeQ is heap structure that scheduler actively looks at to find pods to
    // schedule. Head of heap is the highest priority pod.
    activeQ *Heap
    // unschedulableQ holds pods that have been tried and determined unschedulable.
    unschedulableQ *UnschedulablePodsMap
    // nominatedPods is a map keyed by a node name and the value is a list of
    // pods which are nominated to run on the node. These are pods which can be in
    // the activeQ or unschedulableQ.
    nominatedPods map[string][]*v1.Pod
    // receivedMoveRequest is set to true whenever we receive a request to move a
    // pod from the unschedulableQ to the activeQ, and is set to false, when we pop
    // a pod from the activeQ. It indicates if we received a move request when a
    // pod was in flight (we were trying to schedule it). In such a case, we put
    // the pod back into the activeQ if it is determined unschedulable.
    receivedMoveRequest bool
}

type UnschedulablePodsMap struct {
    // pods is a map key by a pod's full-name and the value is a pointer to the pod.
    pods    map[string]*v1.Pod
    keyFunc func(*v1.Pod) string
}

activeQ

activeQ是一个有序的Heap结构,Pop时会弹出最高优先级的Pending Pod。

unscheduableQ

主要是一个Map,key为pod.Name + "_" + pod.Namespace,value为那些已经尝试调度并且调度失败的UnSchedulable的Pod。

nominatedPods

nominatedPods表示已经被该node提名的,期望调度在该node上的,但是又还没最终成功调度过来的Pods。

nominatedPods的作用是防止高优先级的Pods进行抢占调度时删除了低优先级Pods--->再次调度,在这段时间内,抢占的资源又被低优先级的Pods占用了。

  • podFitsOnNode的时候,会有2次predicate。
  • 第一次的时候,如果发现该node上有nominatedPods,则将nominatedPods中大于等于该pod优先级的pod的资源加入node上,使用修改过的nodeinfo进行一次predicate,作用就是保证高优先级的pod在上面所述的这段时间里资源不被占用
  • 第二次的时候,会用原始的nodeinfo进行一次predicate。

receivedMoveRequest

  • 从activeQ中Pop出pod时,会将receivedMoveRequest置为false。

  • 某些事件发生时,会将pod从unschedulableQ -> activeQ,此时会将receivedMoveRequest置为true。

    当调度发生Error时,会尝试将UnSchedulable Pod重新加入unSchedulableQ 或者activeQ中。判断的依据就是receivedMoveRequest 。

    当receivedMoveRequest为false并且该Pod Condition Status为False或者Unschedulable时,会将该Pod Add或者Update到unschedulableQ。否则加到activeQ中。

    if !p.receivedMoveRequest && isPodUnschedulable(pod) {
        p.unschedulableQ.addOrUpdate(pod)
        p.addNominatedPodIfNeeded(pod)
        return nil
    }
    err := p.activeQ.Add(pod)
    if err == nil {
        p.addNominatedPodIfNeeded(pod)
        p.cond.Broadcast()
    }

其实目的就是当pod从unschedulableQ -> activeQ时,意味着集群可能有资源了,然后就直接加入activeQ,不用先进unSchedulableQ 了,算是一个优化

各类event涉及的queue操作

scheduled pod cache

  • add pod

    将unschedulableQ中和该pod有affinity的pods加入activeQ

  • update pod

    和add pod类似

  • delete pod

    将unschedulableQ中的pod全部加到activeQ中

unscheduled pod queue

  • add pod

    将pod加入activeQ

  • update pod

    首先判断是否需要更新:

    • pod已经assumed

    • pod只有ResourceVersion, Spec.NodeName, Annotations发生了更新

    以上情况下都不需要更新。

    如果需要更新:

    • 如果pod已经在activeQ里了,更新。
    • 如果pod在unschedulableQ中,首先查看该pod是不是做了“有效的”更新,如果是,加入activeQ。(这种pod加入activeQ中还有希望被调度成功)。如果不是,直接更新unschedulableQ中的pod。
    • 如果这两个队列里都没有,加入activeQ
  • delete pod

    NominatedPod删除

    从activeQ和unschedulableQ中找到,删除。

node event

  • add node

    说明有资源加进来了,将所有unschedulableQ中的pod加入activeQ

  • update node

    和上面一样

  • delete node

    没操作

service event

  • service的所有事件都会触发所有unschedulableQ中的pod加入activeQ

pvc event

  • pvc的add和update事件都会触发所有unschedulableQ中的pod加入activeQ
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容

  • 调度方案综述 进程调度问题概述 在多道程序环境下,进程的数目往往多于处理机数目。这就要求系统能按照某种算法,动态的...
    炸克伯格阅读 7,295评论 0 5
  • 进程调度:在可运行态进程之间分配有限处理器时间资源的内核子系统。通俗来说就是,各个进程之间如何有规律的使用CPU。...
    batbattle阅读 3,129评论 0 9
  • 写邮件和管邮件,几乎可以用一本书来说明,在互联网的大环境下,电话、短信、微信让文字沟通越来越方便,电子邮件用的越来...
    新仔走天涯阅读 359评论 0 0
  • 儿子走了,我就像被掏空了一样,再没有以前的风风火火,急着下班给儿子做饭;儿子走了,我再也不用给他洗衣服,那难洗的白...
    雪之莹阅读 209评论 0 0
  • 这段时间《前任3》很火,几乎刷爆了我的朋友圈。 很多人都说这部电影太扎心,太像拍给自己了。 为此我特意去了影院,看...
    英吹斯听ing自媒体工作室阅读 240评论 0 0