Kubernetes 调度器实现原理( 二 )

  • 优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本尽量分布到不同的主机上,使用最低负载的主机等等策略
    • 经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 binding 操作,然后将结果存储到 etcd 中 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作(当然也是 watch APIServer 发现的) 。
    目前调度器已经全部通过插件的方式实现了调度框架,默认开启的内置调度插件如以下代码所示:
    // pkg/scheduler/framework/plugins/registry.go// NewInTreeRegistry 使用所有内部插件构建注册表 。// 外部插件可以通过 WithFrameworkOutOfTreeRegistry 选项注册额外的插件 。func NewInTreeRegistry() runtime.Registry { fts := plfeature.Features{EnableDynamicResourceAllocation:feature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation),EnableReadWriteOncePod:feature.DefaultFeatureGate.Enabled(features.ReadWriteOncePod),EnableVolumeCapacityPriority:feature.DefaultFeatureGate.Enabled(features.VolumeCapacityPriority),EnableMinDomainsInPodTopologySpread:feature.DefaultFeatureGate.Enabled(features.MinDomainsInPodTopologySpread),EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread),EnableMatchLabelKeysInPodTopologySpread:feature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread),EnablePodSchedulingReadiness:feature.DefaultFeatureGate.Enabled(features.PodSchedulingReadiness), } registry := runtime.Registry{dynamicresources.Name:runtime.FactoryAdapter(fts, dynamicresources.New),selectorspread.Name:selectorspread.New,imagelocality.Name:imagelocality.New,tainttoleration.Name:tainttoleration.New,nodename.Name:nodename.New,nodeports.Name:nodeports.New,nodeaffinity.Name:nodeaffinity.New,podtopologyspread.Name:runtime.FactoryAdapter(fts, podtopologyspread.New),nodeunschedulable.Name:nodeunschedulable.New,noderesources.Name:runtime.FactoryAdapter(fts, noderesources.NewFit),noderesources.BalancedAllocationName: runtime.FactoryAdapter(fts, noderesources.NewBalancedAllocation),volumebinding.Name:runtime.FactoryAdapter(fts, volumebinding.New),volumerestrictions.Name:runtime.FactoryAdapter(fts, volumerestrictions.New),volumezone.Name:volumezone.New,nodevolumelimits.CSIName:runtime.FactoryAdapter(fts, nodevolumelimits.NewCSI),nodevolumelimits.EBSName:runtime.FactoryAdapter(fts, nodevolumelimits.NewEBS),nodevolumelimits.GCEPDName:runtime.FactoryAdapter(fts, nodevolumelimits.NewGCEPD),nodevolumelimits.AzurediskName:runtime.FactoryAdapter(fts, nodevolumelimits.NewAzureDisk),nodevolumelimits.CinderName:runtime.FactoryAdapter(fts, nodevolumelimits.NewCinder),interpodaffinity.Name:interpodaffinity.New,queuesort.Name:queuesort.New,defaultbinder.Name:defaultbinder.New,defaultpreemption.Name:runtime.FactoryAdapter(fts, defaultpreemption.New),schedulinggates.Name:runtime.FactoryAdapter(fts, schedulinggates.New), } return registry}从上面我们可以看出调度器的一系列算法由各种插件在调度的不同阶段来完成,下面我们就先来了解下调度框架 。
    调度框架调度框架定义了一组扩展点,用户可以实现扩展点定义的接口来定义自己的调度逻辑(我们称之为扩展),并将扩展注册到扩展点上,调度框架在执行调度工作流时,遇到对应的扩展点时,将调用用户注册的扩展 。调度框架在预留扩展点时,都是有特定的目的,有些扩展点上的扩展可以改变调度程序的决策方法,有些扩展点上的扩展只是发送一个通知 。
    我们知道每当调度一个 Pod 时,都会按照两个过程来执行:调度过程和绑定过程 。
    调度过程为 Pod 选择一个合适的节点,绑定过程则将调度过程的决策应用到集群中(也就是在被选定的节点上运行 Pod),将调度过程和绑定过程合在一起,称之为调度上下文(scheduling context) 。需要注意的是调度过程是??同步??运行的(同一时间点只为一个 Pod 进行调度),绑定过程可异步运行(同一时间点可并发为多个 Pod 执行绑定) 。
    调度过程和绑定过程遇到如下情况时会中途退出:
    • 调度程序认为当前没有该 Pod 的可选节点
    • 内部错误
    这个时候,该 Pod 将被放回到 待调度队列,并等待下次重试 。
    扩展点(Extension Points)下图展示了调度框架中的调度上下文及其中的扩展点,一个扩展可以注册多个扩展点,以便可以执行更复杂的有状态的任务 。
     
    Kubernetes 调度器实现原理


    推荐阅读