scheduleOne

kube-scheduler源码分析(三)之 scheduleOne

以下代码分析基于 kubernetes v1.12.0 版本。

本文主要分析/pkg/scheduler/中调度的基本流程。具体的预选调度逻辑优选调度逻辑节点抢占逻辑待后续再独立分析。

scheduler的pkg代码目录结构如下:

scheduler
├── algorithm         # 主要包含调度的算法
   ├── predicates    # 预选的策略
   ├── priorities    # 优选的策略
   ├── scheduler_interface.go    # ScheduleAlgorithm、SchedulerExtender接口定义
   ├── types.go      # 使用到的type的定义
├── algorithmprovider
   ├── defaults
      ├── defaults.go    # 默认算法的初始化操作,包括预选和优选策略
├── cache      # scheduler调度使用到的cache
   ├── cache.go    # schedulerCache
   ├── interface.go
   ├── node_info.go
   ├── node_tree.go
├── core       # 调度逻辑的核心代码
   ├── equivalence
      ├── eqivalence.go       # 存储相同pod的调度结果缓存,主要给预选策略使用
   ├── extender.go
   ├── generic_scheduler.go    # genericScheduler,主要包含默认调度器的调度逻辑
   ├── scheduling_queue.go     # 调度使用到的队列,主要用来存储需要被调度的pod
├── factory
   ├── factory.go   # 主要包括NewConfigFactory、NewPodInformer,监听pod事件来更新调度队列
├── metrics
   └── metrics.go   # 主要给prometheus使用
├── scheduler.go # pkg部分的Run入口(核心代码),主要包含Run、scheduleOne、schedule、preempt等函数
└── volumebinder
    └── volume_binder.go   # volume bind

此部分代码位于pkg/scheduler/scheduler.go

此处为具体调度逻辑的入口。

此部分代码位于pkg/scheduler/scheduler.go

scheduleOne主要为单个pod选择一个适合的节点,为调度逻辑的核心函数。

对单个pod进行调度的基本流程如下:

  1. 通过podQueue的待调度队列中弹出需要调度的pod。

  2. 通过具体的调度算法为该pod选出合适的节点,其中调度算法就包括预选和优选两步策略。

  3. 如果上述调度失败,则会尝试抢占机制,将优先级低的pod剔除,让优先级高的pod调度成功。

  4. 将该pod和选定的节点进行假性绑定,存入scheduler cache中,方便具体绑定操作可以异步进行。

  5. 实际执行绑定操作,将node的名字添加到pod的节点相关属性中。

完整代码如下:

以下对重要代码分别进行分析。

3. config.NextPod

通过podQueue的方式存储待调度的pod队列,NextPod拿出下一个需要被调度的pod。

NextPod的具体函数在factory.go的CreateFromKey函数中定义,如下:

3.1. getNextPod

通过一个podQueue来存储需要调度的pod的队列,通过队列Pop的方式弹出需要被调度的pod。

4. Scheduler.schedule

此部分代码位于pkg/scheduler/scheduler.go

此部分为调度逻辑的核心,通过不同的算法为具体的pod选择一个最合适的节点。

schedule通过调度算法返回一个最优的节点。

4.1. ScheduleAlgorithm

ScheduleAlgorithm是一个调度算法的接口,主要的实现体是genericScheduler,后续分析genericScheduler.Schedule

ScheduleAlgorithm接口定义如下:

此部分代码位于/pkg/scheduler/core/generic_scheduler.go

genericScheduler.Schedule实现了基本的调度逻辑,基于给定需要调度的pod和node列表,如果执行成功返回调度的节点的名字,如果执行失败,则返回错误和原因。主要通过预选和优选两步操作完成调度的逻辑。

基本流程如下:

  1. 对pod做基本性检查,目前主要是对pvc的检查。

  2. 通过findNodesThatFit预选策略选出满足调度条件的node列表。

  3. 通过PrioritizeNodes优选策略给预选的node列表中的node进行打分。

  4. 在打分的node列表中选择一个分数最高的node作为调度的节点。

完整代码如下:

5.1. podPassesBasicChecks

podPassesBasicChecks主要做一下基本性检查,目前主要是对pvc的检查。

podPassesBasicChecks具体实现如下:

5.2. findNodesThatFit

预选,通过预选函数来判断每个节点是否适合被该Pod调度。

具体的findNodesThatFit代码实现细节待后续文章独立分析。

genericScheduler.Schedule中对findNodesThatFit的调用过程如下:

5.3. PrioritizeNodes

优选,从满足的节点中选择出最优的节点。

具体操作如下:

  • PrioritizeNodes通过并行运行各个优先级函数来对节点进行优先级排序。

  • 每个优先级函数会给节点打分,打分范围为0-10分。

  • 0 表示优先级最低的节点,10表示优先级最高的节点。

  • 每个优先级函数也有各自的权重。

  • 优先级函数返回的节点分数乘以权重以获得加权分数。

  • 最后组合(添加)所有分数以获得所有节点的总加权分数。

具体PrioritizeNodes的实现逻辑待后续文章独立分析。

genericScheduler.Schedule中对PrioritizeNodes的调用过程如下:

5.4. selectHost

scheduler在最后会从priorityList中选择分数最高的一个节点。

selectHost获取优先级的节点列表,然后从分数最高的节点以循环方式选择一个节点。

具体代码如下:

5.4.1. findMaxScores

findMaxScores返回priorityList中具有最高Score的节点的索引。

6. Scheduler.preempt

如果pod在预选和优选调度中失败,则执行抢占操作。抢占主要是将低优先级的pod的资源空间腾出给待调度的高优先级的pod。

具体Scheduler.preempt的实现逻辑待后续文章独立分析。

7. Scheduler.assume

将该pod和选定的节点进行假性绑定,存入scheduler cache中,方便可以继续执行调度逻辑,而不需要等待绑定操作的发生,具体绑定操作可以异步进行。

如果假性绑定成功则发送请求给apiserver,如果失败则scheduler会立即释放已分配给假性绑定的pod的资源。

assume方法的具体实现:

8. Scheduler.bind

异步的方式给pod绑定到具体的调度节点上。

bind具体实现如下:

9. 总结

本文主要分析了单个pod的调度过程。具体流程如下:

  1. 通过podQueue的待调度队列中弹出需要调度的pod。

  2. 通过具体的调度算法为该pod选出合适的节点,其中调度算法就包括预选和优选两步策略。

  3. 如果上述调度失败,则会尝试抢占机制,将优先级低的pod剔除,让优先级高的pod调度成功。

  4. 将该pod和选定的节点进行假性绑定,存入scheduler cache中,方便具体绑定操作可以异步进行。

  5. 实际执行绑定操作,将node的名字添加到pod的节点相关属性中。

其中核心的部分为通过具体的调度算法选出调度节点的过程,即genericScheduler.Schedule的实现部分。该部分包括预选和优选两个部分。

genericScheduler.Schedule调度的基本流程如下:

  1. 对pod做基本性检查,目前主要是对pvc的检查。

  2. 通过findNodesThatFit预选策略选出满足调度条件的node列表。

  3. 通过PrioritizeNodes优选策略给预选的node列表中的node进行打分。

  4. 在打分的node列表中选择一个分数最高的node作为调度的节点。

参考:

  • https://github.com/kubernetes/kubernetes/blob/v1.12.0/pkg/scheduler/scheduler.go

  • https://github.com/kubernetes/kubernetes/blob/v1.12.0/pkg/scheduler/core/generic_scheduler.go

最后更新于

这有帮助吗?