Scheduler
Scheduler 是整个 kube-scheduler 的一个 structure,提供了 kube-scheduler 运行所需的组件。
| |
作为实际执行的两个核心,SchedulingQueue ,与 scheduleOne 将会分析到这两个
SchedulingQueue
在知道 kube-scheduler 初始化过程后,需要对 kube-scheduler 的整个 structure 和 workflow 进行分析
在 Run 中,运行的是 一个 SchedulingQueue 与 一个 scheduleOne ,从结构上看是属于 Scheduler
| |
SchedulingQueue 是一个队列的抽象,用于存储等待调度的Pod。该接口遵循类似于 cache.FIFO 和 cache.Heap 的模式。
| |
而 PriorityQueue 是 SchedulingQueue 的实现,该部分的核心构成是两个子队列与一个数据结构,即 activeQ、backoffQ 和 unschedulablePods
activeQ:是一个 heap 类型的优先级队列,是 sheduler 从中获得优先级最高的Pod进行调度backoffQ:也是一个 heap 类型的优先级队列,存放的是不可调度的PodunschedulablePods:保存确定不可被调度的Pod
| |
在New scheduler 时可以看到会初始化这个queue
| |
而 NewSchedulingQueue 则是初始化这个 PriorityQueue
| |
了解了Queue的结构,就需要知道 入队列与出队列是在哪里操作的。在初始化时,需要注册一个 addEventHandlerFuncs 这个时候,会注入三个动作函数,也就是controller中的概念;而在AddFunc中可以看到会入队列。
注入是对 Pod 的informer注入的,注入的函数 addPodToSchedulingQueue 就是入栈
| |
而这个 SchedulingQueue 的实现就是 PriorityQueue ,而Add中则对 activeQ进行的操作
| |
在上面看 scheduler 结构时,可以看到有一个 nextPod的,nextPod就是从队列中弹出一个pod,这个在scheduler 时会传入 MakeNextPodFunc 就是这个 nextpod
| |
而这个 queue.Pop() 对应的就是 PriorityQueue 的 Pop() ,在这里会将作为 activeQ 的消费端
| |
在上面入口部分也看到了,scheduleOne 和 scheduler,scheduleOne 就是去消费一个Pod,他会调用 NextPod,NextPod就是在初始化传入的 MakeNextPodFunc ,至此回到对应的 Pop来做消费。
schedulerOne是为一个Pod做调度的流程。
| |
调度上下文
当了解了scheduler结构后,下面分析下调度上下文的过程。看看扩展点是怎么工作的。这个时候又需要提到官网的调度上下文的图。
而 scheduler 对于调度上下文来就是这个 scheduleOne ,下面就是看这个调度上下文
Sort
Sort 插件提供了排序功能,用于对在调度队列中待处理 Pod 进行排序。一次只能启用一个队列排序。
在进入 scheduleOne 后,NextPod 从 activeQ 中队列中得到一个Pod,然后的 frameworkForPod 会做打分的动作就是调度上下文的第一个扩展点 sort
| |
回顾,因为在New scheduler时会初始化这个 sort 函数
| |
preFilter
preFilter作为第一个扩展点,是用于在过滤之前预处理或检查 Pod 或集群的相关信息。这里会终止调度
| |
schedulePod 尝试将给定的 pod 调度到节点列表中的节点之一。如果成功,它将返回节点的名称。
| |
findNodesThatFitPod 会执行对应的过滤插件来找到最适合的Node,包括备注,以及方法名都可以看到,这里运行的插件😁😁,后面会分析算法内容,只对workflow学习。
| |
filter
filter插件相当于调度上下文中的 Predicates,用于排除不能运行 Pod 的节点。Filter 会按配置的顺序进行调用。如果有一个filter将节点标记位不可用,则将 Pod 标记为不可调度(即不会向下执行)。
对于代码中来讲,filter还是处于 findNodesThatFitPod 函数中,findNodesThatPassFilters 就是获取到 FN,即可行节点,而这个过程就是 filter 扩展点
| |
Postfilter
当没有为 pod 找到FN时,该插件会按照配置的顺序进行调用。如果任何postFilter插件将 Pod 标记为schedulable,则不会调用其余插件。即 filter 成功后不会进行这步骤,那我们来验证下这里把😊
还是在 scheduleOne 中,当我们运行的 SchedulePod 完成后(成功或失败),这时会返回一个err,而 postfilter 会根据这个 err进行选择执行或不执行,符合官方给出的说法。
| |
PreScore,Score
可用于进行预Score工作,作为通知性的扩展点,会在在filter完之后直接会关联 preScore 插件进行继续工作,而不是返回,如果配置的这些插件有任何一个返回失败,则Pod将被拒绝。
| |
priorityNodes 会通过配置的插件给Node打分,并返回每个Node的分数,将每个插件打分结果计算总和获得Node的分数,最后获得节点的加权总分数。
| |
Reserve
Reserve 因为绑定事件时异步发生的,该插件是为了避免Pod在绑定到节点前时,调度到新的Pod,使节点使用资源超过可用资源情况。如果后续阶段发生错误或失败,将触发 UnReserve 回滚(通知性扩展点)。这也是作为调度周期中最后一个状态,要么成功到 postBind ,要么失败触发 UnReserve。
| |
permit
Permit 插件可以阻止或延迟 Pod 的绑定
| |
Binding Cycle
在选择好 FN 后则做一个假设绑定,并更新到cache中,接下来回去执行真正的bind操作,也就是 binding cycle
| |
调度上下文中的失败流程
上面说到的都是正常的请求,下面会对失败的请求是如何重试的进行分析,而 scheduler 中关于失败处理方面相关的属性会涉及到上面 scheduler 结构中的 backoffQ 与 unschedulablePods
backoffQ:也是一个 heap 类型的优先级队列,存放的是不可调度的PodunschedulablePods:保存确定不可被调度的Pod,一个map类型
backoffQ 与 unschedulablePods 会在初始化 scheduler 时初始化,
| |
对于初始化 backoffQ 会产生的两个函数,getBackoffTime 与 calculateBackoffDuration
| |
对于整个故障错误会按照如下流程进行,在初始化 scheduler 会注册一个 Error 函数,这个函数用作对不可调度Pod进行处理,实际上被注册的函数是 MakeDefaultErrorFunc。这个函数将作为 Error 函数被调用。
| |
而在 调度周期中,也就是 scheduleOne 可以看到,每个扩展点操作失败后都会调用 handleSchedulingFailure 而该函数,使用了注册的 Error 函数来处理Pod
| |
来到了注册的 Error 函数 MakeDefaultErrorFunc
| |
下面来到 AddUnschedulableIfNotPresent ,这个也是操作 backoffQ 和 unschedulablePods 的真正的动作
AddUnschedulableIfNotPresent 函数会吧无法调度的 pod 插入队列,除非它已经在队列中。通常情况下,PriorityQueue 将不可调度的 Pod 放在 unschedulablePods 中。但如果最近有 move request,则将 pod 放入 podBackoffQ 中。
| |
在启动 scheduler 时,会将这两个队列异步启用两个loop来操作队列。表现在 Run()
| |
可以看到 flushBackoffQCompleted 作为 BackoffQ 实现;而 flushUnschedulablePodsLeftover 作为 UnschedulablePods 实现。
flushBackoffQCompleted 是用于将所有已完成回退的 pod 从 backoffQ 移到 activeQ 中
| |
flushUnschedulablePodsLeftover 函数用于将在 unschedulablePods 中的存放时间超过 podMaxInUnschedulablePodsDuration 值的 pod 移动到 backoffQ 或 activeQ 中。
podMaxInUnschedulablePodsDuration 会根据配置传入,当没有传入,也就是使用了 Deprecated 那么会为5分钟。
| |
对于 flushUnschedulablePodsLeftover 就是做一个时间对比,然后添加到对应的队列中
| |
总结调度上下文流程
- 在构建一个 scheduler 时经历如下步骤:
- 准备cache,informer,queue,错误处理函数等
- 添加事件函数,会监听资源(如Pod),当有变动则触发对应事件函数,这是入站
activeQ
- 构建完成后会 run,run时会run一个
SchedulingQueue,这个是作为不可调度队列BackoffQUnschedulablePods- 不可调度队列会根据注册时定期消费队列中Pod将其添加到
activeQ中
- 启动一个
scheduleOne的loop,这个是调度上下文中所有的扩展点的执行,也是activeQ的消费端scheduleOne获取 pod- 执行各个扩展点,如果出错则 Error 函数
MakeDefaultErrorFunc将其添加到不可调度队列中 - 回到不可调度队列中消费部分
