kube-scheduler 源码解析

阅读数:57 2019 年 11 月 27 日 16:24

kube-scheduler 源码解析

本篇文章带大家了解部署在我们 HULK 容器服务 master 节点上的重要组件之一,kube-scheduler 的运行机制解读和核心代码分析,给想要阅读学习 Kubernetes 源码的同学一个参考。

本文所涉及的源码为 Kubernetes 1.9,git commit id 为 925c127ec。本文前半部分讲解 scheduler 的原理,后半部分对 scheduler 源码进行分析。

1 kubernetes scheduler 基本原理

kubernetes scheduler 作为一个单独的进程部署在 master 节点上,它会 watch kube-apiserver 进程去发现 PodSpec.NodeName 为空的 Pod,然后根据指定的算法将 Pod 调度到合适的 Node 上, 这一过程也叫绑定 (Bind)。scheduler 的输入是需要被调度的 Pod 和 Node 的信息,输出是经过调度算法筛选出条件最优的 Node,并将该 Pod 绑定到这个 Node 上。如下图所示:

kube-scheduler 源码解析

scheduler 调度算法分为两个阶段:

  • 预选 (Predicates)

根据 Predicates 策略去滤掉不符合 Policies 的 Node.

  • 优选 (Priorities)

经过 Predicates 剩下的 Node,需要经过 Priorities 策略选出一个最优的 Node,并将 Pod 绑定到该 Node 上。根据下面这张调度图详细描述下:

  1. 首先 scheduler 根据 predicates 集合过滤掉不符合的 Node。例如,如果 PodSpec 指定的请求资源 (resource requests),那么 scheduler 会过滤掉没有足够资源的 Node。

  2. 其次 scheduler 会根据 priority functions 集合从 predicates 中过滤出来的 Node 中,选出一个最优的 Node。

算法实现:

对每一个 Node, priority functions 会计算出一个 0-10 之间的数字,表示 Pod 放到该 Node 的合适程度,其中 10 表示非常合适,0 表示不合适,priority functions 集合中的每一个函数都有一个权重 (weight),最终的值为 weight 和 priority functions 的乘积,而一个节点的 weight 就是所有 priority functions 结果的加和。例如,有两个 priority functions: priorityFunc1 和 priorityFunc2,对应的 weight 分别为 weight1 和 weight2,那么 NodeA 的最终得分是:

kube-scheduler 源码解析

  1. 最终,得分最高的 Node 胜出(如果有多个得分相同的 Node,会随机的选取一个 Node 作为最终胜出的 Node)。

kube-scheduler 源码解析

2 kubernetes scheduler 源码分析

scheduler 的代码结构

kube-scheduler 源码解析

  • k8s.io/kubernetes/plugin/cmd/scheduler.go 为程序入口文件 (main.go)
  • k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/server.go 包含 scheduler 的基础配置项

除了入口函数,scheduler 的具体逻辑实现均在 k8s.io/kubernetes/plugin/pkg/scheduler 目录下,这里就不一一介绍了。

scheduler 的具体实现

kube-scheduler 源码解析

上面的时序图就是整个 scheduler 的具体实现逻辑。下面根据这个时序图来对 scheduler 的源码进行解析。

  • NewSchedulerCommand 创建一个 scheduler 命令行实例,用来对 scheduler 的命令行参数进行解析校验并且包含 scheduler 程序的入口函数 Run 的函数定义。
  • command.Execute() 会执行命令行实例中的 options.Run 方法。

在 options.Run 中主要进行了如下的操作:

  • loadConfigFromFile 加载 scheduler 配置文件信息。
  • NewSchedulerServer 创建 scheduler server 实例,使用 scheduler 配置参数对 scheduler server 进行初始化,如:

1.createClients 创建一系列 client,如连接 k8s 的 client,进行 scheduler 选主的 client 及 event client.
2.makeLeaderElectionConfig 生成 Leader Election 配置信息 (scheduler 做了 HA,可以同时运行多个实例进程,但只有一个能正常工作,如果主的 scheduler 挂了,会重新进行选举)。
3.makeHealthzServer 初始化 healthz server,用于健康检查。
4.makeMetricsServer 初始化 metrics server,用于 prometheus 性能监控。

  • SchedulerServer.Run 启动 SchedulerServer, 用于监控还是否有 Pod 待调度,并且进行相应的调度工作。具体的代码实现:

kube-scheduler 源码解析

  • SchedulerConfig() 创建 Scheduler Config,其中关键性函数是 NewConfigFactory 和 CreateFromProvider。

1.NewConfigFactory 定义了 podQueue 用来存储需要被调度的 Pod,每当新的 Pod 建立后,就会将 Pod 添加到该 queue 中。
2.CreateFromProvider 根据 algorithm provider 名称创建一个 scheduler 配置信息。其中 GetAlgorithmProvider 则根据 provider 名称去获取指定的 provider。

kube-scheduler 源码解析

scheduler 默认使用的 provider 是 DefaultProvider。它主要实现了如下数据结构:

kube-scheduler 源码解析

AlgorithmProviderConfig 这个数据结构包含预选和优选相关算法 key 的集合 (一个算法对应一个 key,key 是算法的名字,value 是算法的具体实现 funtion),而这些算法注册是在 scheduler/algorithmprovider/defaults/defaults.go 文件的 init() 方法中进行注册 (实现使用的是工厂模式)。 如果想了解预选和优选算法的详细信息,请参看官方文档:

https://github.com/kubernetes/community/blob/master/contributors/devel/scheduler_algorithm.md

接着往下说: 通过 GetAlgorithmProvider 得到了 provider 关联的预选和优选算法集合的 Key。然后通过调用 CreateFromKeys (预选和优选的 Key 作为参数) 来获取预选和优选算法的具体实现 (funtion), 并对 NewGenericScheduler 实例进行初始化,返回最终的 scheduler 配置信息。

  • NewFromConfig 由 Scheduler Config 创建一个 schduler。
  • Start up the healthz server 启动健康检查服务
  • Start up the metrics server 启动 metrics 服务,供 Prometheus 进行性能监控数据的抓取。
  • LeaderElection 如果指定选举的方式来启动 scheduler,则使用这种方式来执行 scheduler。(使用 CallBack 的方式执行 Run 方法。如果主的 scheduler 出现问题,还会指定优雅处理函数对其进行处理)。

下面就到了 scheduler 真正干活的逻辑了, 每次调度一个 Pod 都会执行下面的 Scheduler.Run(),具体的代码如下:

kube-scheduler 源码解析

  • WaitForCacheSync() 将最新的数据同步到 SchedulerCache 缓存中。
  • scheduleOne() 调度 Pod 的整体逻辑。具体的实现可以看下面代码:

kube-scheduler 源码解析

  • NextPod() 从 PodQueue 中获取一个未绑定的 Pod。
  • schedule(pod) 执行对应 Algorithm 的 Schedule,进行预选和优选。接口定义如下:

kube-scheduler 源码解析

  • Schedule 主要包含如下几个重要的方法:

1.nodeLister.List() 获取可用的 Node 列表。
2.findNodesThatFit() 进行预选。
3.PrioritizeNodes() 进行优选。
4.selectHost() 如果优选出的多个得分相同的 Node,则随机选取一个 Node。

  • assume() 更新 SchedulerCache 中 Pod 的状态,标志该 Pod 为 scheduled,并更新到 NodeInfo 中。
  • bind() 调用 kube-apiserver API,将 Pod 绑定到选出的 Node,之后 Kube-apiserver 会将元数据写入 etcd 中。接口定义如下:

kube-scheduler 源码解析

这样一个 Pod 绑定到 Node 的流程就完成了。

3 总结

kube-scheduler 作为 Kubernetes master 上一个单独的进程提供调度服务,通过 master 指定 kube-api-server 的地址,用来 watch Pod 和 watch Node 并调用 api server bind 接口完成 Node 和 Pod 的 Bind 操作。

kube-scheduler 中维护了一个 FIFO 类型的 PodQueue cache (其实还有一种 PriorityQueue 用于指定 Pod 的优先级,需要指定参数开启,默认是 FIFO 队列),新创建的 Pod 都会被 ConfigFactory watch 到,被添加到该 PodQueue 中,每次调度都从该 PodQueue 中 NextPod() 一个即将调度的 Pod。

获取到待调度的 Pod 后,就执行 AlgorithmProvider 配置 Algorithm 的 Schedule 方法进行调度,整个调度过程分两个关键步骤:Predicates 和 Priorities,最终选出一个最适合该 Pod 的 Node。

更新 SchedulerCache 中 Pod 的状态 (AssumePod),标志该 Pod 为 scheduled,并更新到 NodeInfo 中。

调用 api server 的 Bind 接口,完成 Node 和 Pod 的 Bind 操作,如果 Bind 失败,从 SchedulerCache 中删除上一步中已经 Assumed 的 Pod。

本文转载自公众号 360 云计算(ID:hulktalk)。

原文链接:

https://mp.weixin.qq.com/s/egG7g6Krtya24NxjPqAmJA

评论

发布