以阅读k8s其中的一个模块,scheduler为例子,来说讲我是怎么读代码的java
scheduler是k8s的调度模块,作的事情就是拿到pod以后在node中寻找合适的进行适配这么一个单纯的功能。实际上,我已经屡次编译和构建这个程序并运行起来。在个人脑中,sheduler在整个系统中是这样的:node
scheduler做为一个客户端,从apiserver中读取到须要分配的pod,和拥有的node,而后进行过滤和算分,最后把这个匹配信息经过apiserver写入到etcd里面,供下一步的kubelet去拉起pod使用。这样,马上有几个问题浮现出来git
问1.scheduler读取到的数据结构是怎么样的?(输入)
问2.scheduler写出的的数据结构是怎么样的?(输出)
问3.在前面的测试中,scheduler成为了系统的瓶颈,为何?
问4.社区有人说增长缓存能有效提升scheduler的效率,他的思路是可行的吗?github
kubernetes\plugin\cmd\kube-scheduler\scheduler.go
这段代码比较短就全文贴出来了redis
package main import ( "runtime" "k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/version/verflag" "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app" "github.com/spf13/pflag" ) func init() { healthz.DefaultHealthz() //忽略…… } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) //忽略…… s := app.NewSchedulerServer() //关注,实际调用的初始化 s.AddFlags(pflag.CommandLine) //忽略,命令行解析 util.InitFlags() util.InitLogs() defer util.FlushLogs() //忽略,开日志等 verflag.PrintAndExitIfRequested() s.Run(pflag.CommandLine.Args()) //关注,实际跑的口子 }
能够看到,对于细枝末节我一律忽略掉,进入下一层,可是,我并非不提出问题,提出的问题会写在这里,而后从脑子里面“忘掉”,以减轻前进的负担算法
kubernetes\plugin\cmd\kube-scheduler\app\server.go
进入这个文件后,重点看的就是数据结构和方法:api
SchedulerServer
这个结构存放了一堆配置信息,裸的,能够看到里面几个成员变量都是基本类型,int, string等AlgorithmProvider
来建立具体算法的调度器。再下一层的入口在:缓存
sched := scheduler.New(config) sched.Run()
对于这层的问题是:
问5.几个限流是怎么实现的?QPS和Brust有什么区别?
问6.算法提供者AlgorithmProvider
是怎么被抽象出来的?须要完成什么事情?数据结构
答5.在翻了限流的代码后,发现来自于kubernetes\Godeps\_workspace\src\github.com\juju\ratelimit
,实现的是一个令牌桶的算法,burst指的是在n个请求内保持qps平均值的度量。详见这篇文章架构
kubernetes\plugin\pkg\scheduler\scheduler.go
答2:在这里我看到了输出的数据结构为:
b := &api.Binding{ ObjectMeta: api.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name}, Target: api.ObjectReference{ Kind: "Node", Name: dest, }, }
这个文件最重要的数据结构是:
type Config struct { // It is expected that changes made via modeler will be observed // by NodeLister and Algorithm. Modeler SystemModeler NodeLister algorithm.NodeLister Algorithm algorithm.ScheduleAlgorithm Binder Binder // Rate at which we can create pods // If this field is nil, we don't have any rate limit. BindPodsRateLimiter util.RateLimiter // NextPod should be a function that blocks until the next pod // is available. We don't use a channel for this, because scheduling // a pod may take some amount of time and we don't want pods to get // stale while they sit in a channel. NextPod func() *api.Pod // Error is called if there is an error. It is passed the pod in // question, and the error Error func(*api.Pod, error) // Recorder is the EventRecorder to use Recorder record.EventRecorder // Close this to shut down the scheduler. StopEverything chan struct{} }
数据结构是什么?数据结构就是舞台上的角色,而函数方法就是这些角色之间演出的一幕幕戏。对象是有生命的,从建立到数据流转,从产生到消亡。而做为开发者来讲,首先是搞懂这些人物设定,是关公仍是秦琼,是红脸仍是黑脸?看懂了人,就看懂了戏。
这段代码里面,结合下面的方法,我能够得出这么几个印象:
问7.结合观看了modeler.go
以后,发现这是在绑定后处理的,所谓的assuemPod,就是把绑定的pod放到一个队列里面去,不是很理解为何这个互斥操做是放在bind以后作?
问8.Binder是怎么去作绑定操做的?
下一层入口:
dest, err := s.config.Algorithm.Schedule(pod, s.config.NodeLister)
kubernetes\plugin\pkg\scheduler\generic_scheduler.go
在调到这一层的时候,我发现本身走过头了,上面s.config.Algorithm.Schedule
并不会直接调用generic_scheduler.go
。对于一门面向对象的语言来讲,最后的执行多是一层接口套一层接口,而接口和实现的分离也形成了当你阅读到某个地方以后就没法深刻下去。或者说,纯粹的自顶向下的阅读方式并不适合面向对象的代码。因此,目前个人阅读方法开始变成了碎片式阅读,先把整个代码目录树给看一遍,而后去最有可能解释我心中疑问的地方去寻找答案,而后一片片把真相拼合起来。
问9.generic_scheduler.go是怎么和scehduler.go产生关系的?
这是代码目录树:
从目录树中,能够看出调度算法的目录在algrorithem
和algrorithemprovider
里面,而把对象组装在一块儿的关键源代码是在:
答8.Binder的操做其实很简单,就是把pod和node的两个字段放到http请求中发送到apiserver去作绑定,这也和系统的总体架构是一致的
factory的最大做用,就是从命令行参数中获取到--algorithm
和--policy-config-file
来获取到必要算法名称和调度策略,来构建Config,Config实际上是调度程序的核心数据结构。schduler这整个程序作的事情能够归纳为:获取配置信息——构建Config——运行Config。这个过程相似于java中的sping组装对象,只不过在这里是经过代码显式进行的。从装配工厂中,咱们看到了关键的一行
algo := scheduler.NewGenericScheduler(predicateFuncs, priorityConfigs, extenders, f.PodLister, r)
这样就把我上面的问9解答了
答9.scheduler.go是形式,generic_scheduler.go是内容,经过factory组装
也解答了问6
答6.factoryProvider仅仅是一个算法注册的键值对表达地,大部分的实现仍是放在generic_scheduler里面的
这就涉及到调度的核心逻辑,就2行
filteredNodes, failedPredicateMap, err := findNodesThatFit().... priorityList, err := PrioritizeNodes()...
这里我就不详细叙述细节了,读者能够按照个人路子去本身寻找答案。