很长时间没有写文章,一直在啃kubernetes文档,本来立志一定要读完所有的文档。还有它的最佳实践openshift的文档。但目前为止,我并没有读完kubernetes的文档。当前,我们有需求需要客制化kubernetes的调度函数,所以开始研究kube-scheduler的代码。
kube-scheduler的代码的风格和逻辑对于Gophers并不陌生。权威的作者这样评价kubernetes的源码:代码简洁,设计巧妙,程序逻辑分解得恰到好处,每个组件各司其职,从而化繁为简,主体流程清晰直观,犹如行云流水,一气呵成。(虽然v.1.8.1已经和书中v1.3的代码发生了不小的改变。)总之,kubernetes的源码值得反复阅读学习,主要是每个版本代码都不一样,变化一直在进行中。
kubernetes tag v.1.8.1
kube-scheduler的代码路径为:plugin/
入口程序:plugin/cmd/kube-scheduler/scheduler.go main()
- 创建NewSchedulerServer和启动调度服务;
- componentconfig.KubeSchedulerConfiguration{}定义了kube-scheduler的参数信息;
启动程序:plugin/cmd/kube-scheduler/app/server.go Run()
- 创建apiserver客户端–通过REST方式访问APIserver提供的API服务,用来watch
pod和node,并调用apiserver bind接口完成node和pod的Bind操作; - 创建eventBroadcaster对象–发送event到logging函数,发送event到eventSink,同时EventRecorder记录event source;
- 创建sharedInformerFactory对象,并创建PodInformer对象,PodInformer用于watch/list non-terminal pods并缓存;
- 创建schedulerConfigurator对象
- 创建genericScheduler对象(接口Scheduler) ,genericScheduler对象的创建过程:
a. 创建schedulerConfigurator对象,它包含ConfigFactory对象(接口Configurator) plugin/cmd/kube-scheduler/app/configurator.gob. 调用schedulerConfigurator对象的create()方法创建Scheduler对象;genericScheduler对象是由ConfigFactory对象(Configurator是接口)的createFromProvider()方法创建的plugin/cmd/kube-scheduler/pkg/scheduler/factory/factory.goc. 创建Scheduler需要如下Informer参数:nodeInformer、pvInformer、pvcInformer、rcInformer、rsInformer、statefulsetInformer、serviceInformer ; - 运行http Server–提供必要的性能分析(profiling)和性能指标度量(Metrics),Handler包括/debug/pprof/和/metrics;
- 启动informerFactory.start() ,开始运行Informer,进行缓存;
- 运行调度程序;
- 创建Leaderelection对象,并启动leaderElector.Run() — 创建resourcelock对一些资源上锁。同时,值得一提,controller-manager和kube-scheduler两个组件可以配置跟本机的APIServer通信,也可以不是。在高可用部署情况下,controller-manager和kube-scheduler两个组件,存在选举机制,为了保证选举成功,需要奇数节点部署组件,而当前工作组件只有一个,用于更新集群状态,并与其他节点组件同步信息。
SharedInformers模式设计同时用在k8s的”Controller”中,下面是一段关于SharedInformers模式设计的英文介绍:(摘自https://github.com/kubernetes/ … rs.md)
Use SharedInformers. SharedInformers provide hooks to receive notifications of adds, updates, and deletes for a particular resource. They also provide convenience functions for accessing shared caches and determining when a cache is primed.
SharedInformers提供勾子机制,获得特定资源的添加、更新、删除通知。并提供函数更新缓存,启动执行。简而言之kube-scheduler的”informer”负责:watch/list non-terminal pods, 缓存,并从podQueue中获得NextPod,执行调度;
调度程序:plugin/pkg/scheduler/scheduler.go Run()
1. 等待缓存更新完成;
2. 运行调度流程;
调度流程:plugin/pkg/scheduler/scheduler.go scheduleOne()
1.从podQueue缓存中获得一个Pod;
2. 获得一个suggestedHost,如果获得失败将调用抢占逻辑sched.preempt(),记录算法延迟的度量metrics.SchedulingAlgorithmLatency;–获得suggestedHost是同步操作;
3. Pod将标注为assumedPod;此时Pod并没有被成功调度;
4. 绑定(bind) Pod到suggestedHost,记录调度延迟的度量metrics.E2eSchedulingLatency;–绑定操作是异步操作;
调度逻辑:plugin/pkg/scheduler/scheduler.go Schedule()
1. 根据调度策略算法确定一个suggestedHost;
调度算法函数:
1. 接口类plugin/pkg/scheduler/algorithm/scheduler_interface.go, 默认使用plugin/pkg/scheduler/core/generic_scheduler.go
2. 调度算法函数支持plugin模式plugin/pkg/scheduler/algorithmprovider/plugins.go,scheduler的commandLine参数AlgorithmProvider可以指定调度算法函数;默认使用defaultProvider, defaultPredicates(), defaultPriorities() plugin/pkg/scheduler/algorithmprovider/defaults/default.go;同时,scheduler的commandLine参数PolicyConfigFile,可以加载自定义的调度策略文件。如:openshift中,/etc/origin/master/scheduler.json定义了调度策略文件。(参考信息:https://docs.openshift.com/con … .html)
3. FitPredicates:k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates
4. PrioritiesFunc:k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities
5. 调度流程plugin/pkg/scheduler/core/generic_scheduler.go schedule(),调度流程图如下:(摘自kubernetes调度详解:http://dockone.io/article/2885)
绑定逻辑:plugin/pkg/scheduler/scheduler.go bind()
1. 绑定接口Binder plugin/pkg/scheduler/scheduler.go
2. 更新cache中assumedPod为expired SchedulerCache.FinishBinding(),接口/实现: plugin/pkg/scheduler/schedulercache/interface.go;plugin/pkg/scheduler/schedulercache/cache.go;
3. 记录绑定延迟的度量metrics.BindingLatency;
4. 记录绑定事件;
阅读代码的建议:
1. 读文档,k8s的文档写得非常好,如果文档没有提及的,读代码;
2. 要带着问题探索性的阅读代码,比如:error handling是如何处理的?如果pod没有被正确的调度会发生什么?
调度的性能:
k8s SIG Scale Group对k8s调度服务进行了性能测试,得到的数据:1000 pods跑在1000个节点上,调度延迟为23s,调度程序的吞吐量是51 pods/秒;
https://docs.google.com/presen … _2866