为了深刻学习 kube-scheduler,本系从源码和实战角度深度学 习kube-scheduler,该系列一共分6篇文章,如下:
kube-scheduler 整体架构初始化一个 scheduler一个 Pod 是如何调度的如何开发一个属于本人的scheduler插件开发一个 prefilter 扩大点的插件开发一个 socre 扩大点的插件上一篇文章咱们讲了一个 kube-scheduler 是怎么初始化进去的,有了 调度器之后就得开始让他干活了 这一篇文章咱们来讲讲一个 Pod 是怎么被调度到某个 Node 的。
我把调度一个 Pod 分为3个阶段
获取须要被调度的 Pod运行每个扩大点的所有插件,给 Pod 抉择一个最合适的 Node将 Pod 绑定到选出来的 Node感知 Pod要可能获取到 Pod 的前提是:kube-scheduler 能感知到有 Pod 须要被调度,得悉有 Pod 须要被调度后还须要有中央寄存被调度的 Pod 的信息。为了感知有 Pod 须要被调度,kube-scheduler 启动时通过 Informer watch Pod 的变动,它把待调度的 Pod 分了两种状况,代码如下
// pkg/scheduler/eventhandlers.gofunc addAllEventHandlers(...) { //曾经调度过的 Pod 则加到本地缓存,并判断是退出到调度队列还是退出到backoff队列 informerFactory.Core().V1().Pods().Informer().AddEventHandler( cache.FilteringResourceEventHandler{ FilterFunc: func(obj interface{}) bool { switch t := obj.(type) { case *v1.Pod: return assignedPod(t) case cache.DeletedFinalStateUnknown: if _, ok := t.Obj.(*v1.Pod); ok { // The carried object may be stale, so we don't use it to check if // it's assigned or not. Attempting to cleanup anyways. return true } utilruntime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, sched)) return false default: utilruntime.HandleError(fmt.Errorf("unable to handle object in %T: %T", sched, obj)) return false } }, Handler: cache.ResourceEventHandlerFuncs{ AddFunc: sched.addPodToCache, UpdateFunc: sched.updatePodInCache, DeleteFunc: sched.deletePodFromCache, }, }, ) // 没有调度过的Pod,放到调度队列 informerFactory.Core().V1().Pods().Informer().AddEventHandler( cache.FilteringResourceEventHandler{ FilterFunc: func(obj interface{}) bool { switch t := obj.(type) { case *v1.Pod: return !assignedPod(t) && responsibleForPod(t, sched.Profiles) case cache.DeletedFinalStateUnknown: if pod, ok := t.Obj.(*v1.Pod); ok { // The carried object may be stale, so we don't use it to check if // it's assigned or not. return responsibleForPod(pod, sched.Profiles) } utilruntime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod in %T", obj, sched)) return false default: utilruntime.HandleError(fmt.Errorf("unable to handle object in %T: %T", sched, obj)) return false } }, Handler: cache.ResourceEventHandlerFuncs{ AddFunc: sched.addPodToSchedulingQueue, UpdateFunc: sched.updatePodInSchedulingQueue, DeleteFunc: sched.deletePodFromSchedulingQueue, }, }, )......}曾经调度过的 Pod辨别是不是调度过的 Pod 是通过:len(pod.Spec.NodeName) != 0 来判断的,因为调度过的 Pod 这个字段总是会被赋予被选中的 Node 名字。然而,既然是调度过的 Pod 上面的代码中为什么还要辨别:sched.addPodToCache 和 sched.updatePodInCache 呢?起因在于咱们能够在创立 Pod 的时候人为给它调配一个 Node(即给 pod.Spec.NodeName 赋值),这样 kube-scheduler 在监听到该 Pod 后,判断这个 Pod 的该字段不为空就会认为这个 Pod 曾经调度过了,然而这个字段不为空并不是 kube-scheduler 调度的后果,而是人为赋值的,那么 kube-scheduler 的 cache(能够参考上一篇 cache 相干的内容)中没有这个 Pod 的信息,所以就须要将 Pod 信息退出到 cache 中。至于在监听到 Pod 后 sched.addPodToCache 和 sched.updatePodInCache 哪个会被调用,这是 Informer 决定的,它会依据监听到变动的 Pod 和 Informer 的本地缓存做比照,要是缓存中没有这个 Pod,那么就调用 add 函数,否则就调用 update 函数。退出或更新缓存后,还须要做一件事:去 unschedulablePods(调度失败的Pod) 中获取 Pod,这些 Pod 的亲和性和刚刚退出的这个 Pod 匹配,而后依据上面的规定判断是把 Pod 放入 backoffQ 还是放入 activeQ
...