103 - 调度算法-寻找predicates和priorities
2019-11-05

 

 

scheduler的主要逻辑是predicate和priority,前者回答哪些节点可以运行pod的问题,后者回答哪个节点更合适运行pod的问题。今天我们的任务是:从主函数出发,寻找predicates和priorities的入口!

 

前面我们提到过Execute()其实是运行了这个Run方法,在cmd/kube-scheduler/app/server.go的337行。

顺着opts.Run()往里跟:

 

可以很清楚看到opts.Run()的逻辑,初始化一个server,然后执行server.Run()方法。这里的server类型是*SchedulerServer,这个类型的官方解释是:

SchedulerServer represents all the parameters required to start the kubernetes scheduler server. 也就是说运行scheduler server所需的所有参数集合:

 

 

我们顺着主干往下走,看一下server.Run()方法的定义:

 如上图,我们需要关注一下函数开头的注释,这个Run是要运行SchedulerServer,永远不退出!也就是说到这里就启动了一个server,开始无怨无悔永不停息地处理pod的scheduler流程!接着通过一个方法SchedulerConfig()获取到一个对象叫做schedulerConfig,我们也看一下这个对象的定义:Config is an implementation of the Scheduler"s configured input data.

 

 最后一个sched的创建代表着scheduler的daemon程序准备差不多了!sched的类型如下:

 

 从注释中我们可以得到很多信息,Scheduler监视者未调度的pods,尝试寻找合适的node,把pod和node的绑定关系告诉api server!Run函数继续往后看可以找到(server.go的602行):

 

 可以看到准备好了一个sched.Run(),但是没有立刻执行,626行有一个run(stop),就不贴截图了,我们直接跟到sched.Run()这个方法看一下里面写了啥:

 

 这个Run()方法开始watching and scheduling,最后面的红框需要注意几点,这是新开一个goroutine执行,然后立刻返回的。新开的goroutine是干嘛呢?每隔0秒就执行一次sched.scheduleOne方法!这里的0秒可能需要理解一下,我们看一下wait.Until()方法的定义:

 

 ok,其实是当f这个函数被调用完成后过0秒开始下一次调用,说白了就是前赴后继中间不休息!后面我们当然继续看scheduleOne()方法做了啥:

 

 可以看到scheduleOne()方法能够处理一个pod完整的schedulering工作流。第一步是获取一个pod,这个pod的获取方法是这样定义的:

 

 这里我们关注一下这个方法首先是阻塞的,也就是不返回一个结果就一直卡住。接着看一下suggestedHost是什么:

 

 可以看到,这个类型是string,string不就意味着这就是最后的结果吗?不然怎么着也是一个[]string是吧???所以这里的suggestedHost也就是最后调度算法所给出建议跑pod的host!!!ok,我们的路没有偏离主线,继续看schedule方法的逻辑(上图中可以看到host是通过方法:sched.config.Algorithm.Schedule()获取的,我们直接看Schedule()方法):

 

 

 

 这个方法的参数是pod信息和node信息(获取node信息的接口),返回值是string类型,也就是根据pod信息和nodes信息看pod能够跑在哪个node上,然后返回这个node的名字!

 

 上图从generic_scheduler.go的134行开始,这个msg信息很有意思,"Computing predicates",后面的findNodesThatFit()函数返回filteredNodes,也就是predicates过程的结果,返回的filteredNodes也就是可以运行pod的node集合!往下看150行处:

 

 可以看到priorities过程在这里,PrioritizeNodes()函数返回一个priorityList,这个priorityList是schedulerapi.HostPriorityList类型,也就是[]HostPriority类型,HostPriority类型的定义如下:

 

 可以看到,这个类型其实存的数据就是一个节点的名字和分数信息,也就是说PrioritizeNodes()函数完成了所有可以跑pod的node的分数计算!结尾的selectHost()方法就很简单是,选择一个分高的host返回:

 

 ok,总算跟完了,到这里我们就完成了整个调度过程的略读,下次开始我们可以看具体的predicates和priorities算法了!