欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

104 - kube-scheduler源码分析 - predicate整体流程

程序员文章站 2022-06-05 16:07:30
(注:从微信公众:CloudGeek复制过来,格式略微错乱,更好阅读体验请移步公众号,二维码在文末) 今天我们来跟一下predicates的整个过程;predicate这个词应该是“断言、断定”的意思,在这里我们姑且翻译为“预选”,虽然不符合这个单词的本意,但是在schedule过程中predica ......

(注:从微信公众:cloudgeek复制过来,格式略微错乱,更好阅读体验请移步公众号,二维码在文末)

104 - kube-scheduler源码分析 - predicate整体流程

 

今天我们来跟一下predicates的整个过程;predicate这个词应该是“断言、断定”的意思,在这里我们姑且翻译为“预选”,虽然不符合这个单词的本意,但是在schedule过程中predicate过程做的事情确实还是叫“预选”比较好理解!

上一讲我们提到predicate过程的入口在findnodesthatfit这个函数,所以今天我们从这个函数入手,看看这里面有哪些玄机。这个函数在:pkg/scheduler/core/generic_scheduler.go:289,声明如下:

104 - kube-scheduler源码分析 - predicate整体流程

 

可以看到有不少参数,我们理一下这些参数都是什么:

  1. pod *v1.pod,
    //表示一个pod
  2. nodenametoinfo map[string]*schedulercache.nodeinfo,
    //nodeinfonode级别的信息集合,里面包含v1.nodepodsusedportsnode上的信息;nodenametoinfo也就是一个nodenamenodeinfo的映射
  3. nodes []*v1.node,
    //node列表,可用的node集合
  4. predicatefuncs map[string]algorithm.fitpredicate,
    //predicate函数的别名到具体函数的映射,这里的string类似:podfitshostports;后面的fitpredicate类型是一个func类型:type fitpredicate func(pod *v1.pod, meta predicatemetadata, nodeinfo *schedulercache.nodeinfo) (bool, []predicatefailurereason, error);这个函数类型判断一个pod能否跑在一个node
  5. extenders []algorithm.schedulerextender,
    //schedulerextender是一个接口类型,表示的是一个外部的处理过程,主要用于某些资源不是直接由k8s管理的场景下,调度决策需要外部介入时调用
  6. metadataproducer algorithm.predicatemetadataproducer,
    //predicatemetadataproducer是一个函数类型,入参是podnodenametoinfo,返回值是predicatemetadatapredicatemetadata是一个interface类型,这个类型表示predicate metadata支持的所有access操作,包含3个函数:shallowcopy()/addpod()/removepod();这个interface的实现是structpredicatemetadata,这个struct包含podpodportsserviceaffinityinuse等属性
  7. ecache *equivalencecache,
    //结构体equivalencecache主要包含1:一个以node namekeyalgorithmcachevaluemap2:一个获取equivalence pod的函数。algorithmcache这个结构体存储了一个lru.cache类型的属性,lru是最近最少使用的意思,groupcache里实现的这个cache
  8. schedulingqueue schedulingqueue,
    //这个interface保存一个等待被调度的pods队列,有add()pop()等函数
  9. alwayscheckallpredicates bool,
    //是否检查所有的predicate

 

咋看你肯定感觉迷糊,略抓狂,这么多东西咋个理解呢,,,别急,咱再看一下一个关键类型,然后静下心来往后看完,再回过头看是不是理解了这里的所有参数:

1、上面的fitpredicate类型源码里解释如下:

// fitpredicate is a function that indicates(标示) if a pod fits into an existing node. the failure information is given by the error.入参有3个,分别是:

  • pod *v1.pod
  • meta predicatemetadata
  • nodeinfo *schedulercache.nodeinfo

返回值是:

  • bool
  • []predicatefailurereason
  • error

也就是说给定一个pod和一个node,这个函数需要判断这个pod能否跑在这个node上,能否体现在返回值bool类型上;然后如果失败了,也就是不能的情况,需要返回predicatefailurereason集合,也就是失败的原因们。这个predicatefailurereason是个interface,看一眼定义就很清晰了,特别简单:

104 - kube-scheduler源码分析 - predicate整体流程 

ok,我们接着看findnodesthatfit函数的返回值:

1.[]*v1.node, 

2.failedpredicatemap, 

//这个返回值是map[string][]algorithm.predicatefailurereason类型,这个类型就是上面截图中那个

3.error

 

到这里我们可以初步判断findnodesthatfit函数的输入是一个pod和一堆nodes和xxx,返回值是可以跑这个pod的node集合和xxx,xxx先不考虑,我们专注一下这里的一个pod和n个node,返回值是m个node,m<=n.

这个函数的逻辑并不复杂,我们撇开里面主要的子函数podfitsonnode后过程大致如下图:

 104 - kube-scheduler源码分析 - predicate整体流程

 

这里我们稍微看一下这里的checknode函数是怎么被并发调用的:

104 - kube-scheduler源码分析 - predicate整体流程

如上图,checknode是一个函数类型,明显predicatefuncs都在这个内嵌函数中执行了。这个内嵌函数的调用在截图的倒数第二行:workqueue.parallelize(16, len(nodes), checknode);这个函数的入参是16,nodes的数量,checknode这个函数,跟进去看一下可以知道这里的逻辑,不复杂不过挺有意思:

 104 - kube-scheduler源码分析 - predicate整体流程

上面的workers是16,pieces是node数量,doworkpiece就是checknode这个函数,这个函数的参数还记得吗?是一个int类型的i;parallelizeuntil这个函数中写入了pieces个数据到toprocess,也就是node的数量,然后就close掉了这个channal,也就是这个channal被读完就废了。然后判断如果node数量少于workers,也就是少于16的话,则workers=16;最后开了workers个goroutines, 也就是最多16个并发来消费toprocess,也就是最多16个并发来计算n个checknode任务,每个checknode任务处理一个node上的predicate functions计算过程。

好,下面看podfitsonnode了,先略看函数声明:

 104 - kube-scheduler源码分析 - predicate整体流程

可以看到这里的注释不少,大致翻译过来是这个意思:podfitsonnode检查一个以nodeinfo形式提供的node是否能够通过给定的predicate functions筛选;对于一个给定的pod,podfitsonnode会检查node上是否有已经存在的等价的pod,如果存在则尝试尽量重用这个pod缓存的predicate结果信息。这个函数会从2个不同的入口被调用:schedule and preempt,当从schedule进入时,本函数检测一个node在考虑所有已存在pods和被指定将跑到这个node上是所有更高优先级或者相同优先级(和当前要被调度的pod比较)的pod都跑起来的情况下能否跑现在这个被调度的pod.;;;就解释到这里,下面我们还是老规矩,看看入参和返回值:

入参:

  • pod *v1.pod,
  • meta algorithm.predicatemetadata,
  • info *schedulercache.nodeinfo,

//上一个函数的nodenametoinfo[nodename]获取到的nodeinfo

  • predicatefuncs map[string]algorithm.fitpredicate,
  • ecache *equivalencecache,
  • queue schedulingqueue,
  • alwayscheckallpredicates bool,
  • equivcacheinfo *equivalenceclassinfo,

 

返回值:

  • bool,
  • []algorithm.predicatefailurereason,

//上层函数返回值中的failedpredicatemapmap[string][]algorithm.predicatefailurereason类型

  • error

这里没有逐个解释,基本和上层函数的参数对应得上,findnodesthatfit解释哪些nodes能够跑给定的pod,而podfitsonnode解释给定node能否跑给定pod.我们先看一下这个过程的简要流程图(2次循环考虑第一次的情况):

104 - kube-scheduler源码分析 - predicate整体流程

104 - kube-scheduler源码分析 - predicate整体流程

104 - kube-scheduler源码分析 - predicate整体流程

 

看一下代码:predicateresults := make(map[string]hostpredicate)这一行的string标识predicate名称,hostpredicate类型是:

104 - kube-scheduler源码分析 - predicate整体流程 

这个i从0到1只跑2遍的for循环中分歧点只有如下这个if:

104 - kube-scheduler源码分析 - predicate整体流程 

如上,在i为0时调用到了addnominatedpods函数,这个函数把更高或者相同优先级的pod(待运行到本node上的)信息增加到meta和nodeinfo中,也就是对应考虑这些nominated都running的场景;后面i为1对应的就是不考虑这些pod的场景。对于这个2遍过程,注释里是这样解释的:

如果这个node上“指定”了更高或者相等优先级的pods(也就是优先级不低于本pod的一群pods将要跑在这个node上),我们运行predicates过程当这些pods信息全被加到meta和nodeinfo中的情况。如果所有的predicates过程成功了,我们再次运行这些predicates过程在这些pods信息没有被加到meta和nodeinfo的情况。这样第二次过程可能会因为一些pod间的亲和性策略过不了(因为这些计划要跑的pods没有跑,所以可能亲和性被破坏)。这里其实基于2点考虑:1、有亲和性要求的pod如果认为这些nominated pods在,则在这些nominated pods不在的情况下会异常;2、有反亲和性要求的pod如果认为这些nominated pods不在,则在这些nominated pods在的情况下会异常。

我们接着看predicate函数主要是怎样被调用的:

104 - kube-scheduler源码分析 - predicate整体流程 

如上,predicate是fitpredicate类型的一个对象,也就是对应具体的predicate函数,所以下面的predicate(pod, metatouse, nodeinfotouse)也就对应一个具体的predicate函数的执行。

最后我们瞄一眼具体的predicate函数是怎么定义的:

一个predicate函数类似这样:

 104 - kube-scheduler源码分析 - predicate整体流程

这样被注册:

104 - kube-scheduler源码分析 - predicate整体流程 

紫色部分的常量对应一个个字符串描述:

104 - kube-scheduler源码分析 - predicate整体流程

行,剩下的我们下回分解~

 104 - kube-scheduler源码分析 - predicate整体流程