(注:从微信公众:CloudGeek复制过来,格式略微错乱,更好阅读体验请移步公众号,二维码在文末)
今天我们来跟一下predicates的整个过程;predicate这个词应该是“断言、断定”的意思,在这里我们姑且翻译为“预选”,虽然不符合这个单词的本意,但是在schedule过程中predicate过程做的事情确实还是叫“预选”比较好理解!
上一讲我们提到predicate过程的入口在findNodesThatFit这个函数,所以今天我们从这个函数入手,看看这里面有哪些玄机。这个函数在:pkg/scheduler/core/generic_scheduler.go:289,声明如下:
可以看到有不少参数,我们理一下这些参数都是什么:
- pod *v1.Pod,
//表示一个pod - nodeNameToInfo map[string]*schedulercache.NodeInfo,
//NodeInfo是node级别的信息集合,里面包含v1.Node,pods,usedPorts等node上的信息;nodeNameToInfo也就是一个node的name到NodeInfo的映射 - nodes []*v1.Node,
//node列表,可用的node集合 - 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上 - extenders []algorithm.SchedulerExtender,
//SchedulerExtender是一个接口类型,表示的是一个外部的处理过程,主要用于某些资源不是直接由k8s管理的场景下,调度决策需要外部介入时调用 - metadataProducer algorithm.PredicateMetadataProducer,
//PredicateMetadataProducer是一个函数类型,入参是pod和nodeNameToInfo,返回值是PredicateMetadata,PredicateMetadata是一个interface类型,这个类型表示predicate metadata支持的所有access操作,包含3个函数:ShallowCopy()/AddPod()/RemovePod();这个interface的实现是struct:predicateMetadata,这个struct包含pod、podPorts、serviceAffinityInUse等属性 - ecache *EquivalenceCache,
//结构体EquivalenceCache主要包含1:一个以node name为key,AlgorithmCache为value的map;2:一个获取equivalence pod的函数。AlgorithmCache这个结构体存储了一个lru.Cache类型的属性,lru是最近最少使用的意思,groupcache里实现的这个Cache - schedulingQueue SchedulingQueue,
//这个interface保存一个等待被调度的pods队列,有Add()、Pop()等函数 - 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,看一眼定义就很清晰了,特别简单:
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后过程大致如下图:
这里我们稍微看一下这里的checkNode函数是怎么被并发调用的:
如上图,checkNode是一个函数类型,明显predicateFuncs都在这个内嵌函数中执行了。这个内嵌函数的调用在截图的倒数第二行:workqueue.Parallelize(16, len(nodes), checkNode);这个函数的入参是16,nodes的数量,checkNode这个函数,跟进去看一下可以知道这里的逻辑,不复杂不过挺有意思:
上面的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任务处理一个