K8S的scheduler的主要作用是将用户申请的pods调度到合适的node节点上。具体的来说,就是它通过监听API server提供的watch等接口,获取到未调度的pods和node的相关信息,通过对node的筛选,选择出最合适的也就是优先级最高的node节点,将其与pods进行绑定,并将绑定的结果固化到etcd中去。下面就通过走读源码来详细解析一下scheduler的流程:
scheduler的脑图:
源码解析:
main函数的入口:
func main() {
//新建schedulerserver,它主要是读取master地址和kubeconf文件
s := options.NewSchedulerServer()
.......
//启动参数结构体新建以后,开始执行主函数RUN
if err := app.Run(s); err != nil {
glog.Fatalf("scheduler app failed to run: %v", err)
}
Run函数的入口(Run函数一单启动起来之后就不会在退出,一直以一个进程的身份运行在后台,当检测到有pods未进行调度的时候直接启动):
func Run(s *options.SchedulerServer) error {
........
//新建scheduler它主要是分为两步:1.创建Scheduler需求的各种结构体,如监听器,node,pods的信息等 2.根据获取的信息新建一个scheduler对象,利用这个对象来开启调度。
sched, err := CreateScheduler(
s,
kubecli,
informerFactory.Core().V1().Nodes(),
podInformer,
informerFactory.Core().V1().PersistentVolumes(),
informerFactory.Core().V1().PersistentVolumeClaims(),
informerFactory.Core().V1().ReplicationControllers(),
informerFactory.Extensions().V1beta1().ReplicaSets(),
informerFactory.Apps().V1beta1().StatefulSets(),
informerFactory.Core().V1().Services(),
recorder,
)
........
//根据上面创建的scheduler的对象来调用Run函数来启动调度过程。
run := func(stopCh <-chan struct{}) {
sched.Run()
<-stopCh
}
........
}
创建scheduler对象:
func CreateScheduler(...../*传递进来的参数*/.....) (*scheduler.Scheduler, error) {
//调用NewConfigFactory()函数来构建各种结构体
configurator := factory.NewConfigFactory(
s.SchedulerName,
kubecli,
nodeInformer,
podInformer,
pvInformer,
pvcInformer,
replicationControllerInformer,
replicaSetInformer,
statefulSetInformer,
serviceInformer,
s.HardPodAffinitySymmetricWeight,
utilfeature.DefaultFeatureGate.Enabled(features.EnableEquivalenceClassCache),
)
.......
//根据新建的scheduler结构体创建scheduler对象
return scheduler.NewFromConfigurator(configurator, func(cfg *scheduler.Config) {
cfg.Recorder = recorder
})
}
scheduler各种结构体的构建:
func NewConfigFactory(...../*传递进来的参数*/.....) scheduler.Configurator {
......
//所有的pods和node的信息都会先记录在schedulerCache中,这样做的目的是,可以一次性的先获取所有的pods和node的信息不用再反复的从etcd中读取,这样会节约很多的时间提高一定的效率,这个New()方法是用来将已经绑定好的pods和node信息固化到etcd中的,它是一个多线程的操作,30s启动一次,默认最多可以起16个线程同时去处理这件事情。
schedulerCache := schedulercache.New(30*time.Second, stopEverything)
c := &ConfigFactory{
client: client,
podLister: schedulerCache,
podQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc),
//注意这些lister,他们用来获取各种资源列表,它们会和 apiserver 保持实时同步
nodeLister nodeInformer.Lister(),
pVLister: pvInformer.Lister(),
pVCLister: pvcInformer.Lister(),
serviceLister: serviceInformer.Lister(),
controllerLister: replicationControllerInformer.Lister(),
replicaSetLister: replicaSetInformer.Lister(),
statefulSetLister: statefulSetInformer.Lister(),
schedulerCache: schedulerCache,