# kubernetes组件开发-自定义调度器（一）


初步认识 kubernetes 的调度器框架.

<!--more-->

##  简介

> 前面[Clientset实战]({{<ref "/posts/Client-go/Client-go简单教程（四）-Clientset 实战/index.zh-cn.md">}} "传送门")一文，我们为了使集群的 worker 节点 Pods 的调度均衡，使用 Client-go 开发了一个简单的应用给 worker 节点打污点，虽然也可以实现我们想要的效果，但是不够优雅。
>
> 这里，我们使用 kubernetes 的调度器框架来实现一个**自定义调度器**，针对调度框架某个**扩展点**开发一个**插件**，来优雅实现我们的 worker 节点 Pods 的均衡调度。

## 调度框架

> 在开始前，我们先一起认识一下调度框架。
> 
> 调度框架是面向 Kubernetes 调度器的一种插件架构， 它为现有的调度器添加了一组新的“插件” API。插件会被编译到调度器之中。 这些 API 允许大多数调度功能以插件的形式实现，同时使调度“核心”保持简单且可维护。

调度框架定义了一些扩展点。调度器插件注册后在一个或多个扩展点处被调用。 这些插件中的一些可以改变调度决策，而另一些仅用于提供信息。

> 每次调度一个 Pod 的尝试都分为两个阶段，即 调度周期 和 绑定周期。

- 调度周期为 Pod 选择一个节点，绑定周期将该决策应用于集群。 调度周期和绑定周期一起被称为“调度上下文”。

- 调度周期是串行运行的，而绑定周期可能是同时运行的。

- 如果确定 Pod 不可调度或者存在内部错误，则可以终止调度周期或绑定周期。 Pod 将返回队列并重试。

## 扩展点

> 调度框架定义了一些扩展点，有些扩展点上的扩展可以更改调度程序的决策，有些扩展点上的扩展只是发送一个通知。

下图显示了一个 Pod 的调度上下文以及调度框架公开的扩展点。 在此图片中，“过滤器”等同于“断言”，“评分”相当于“优先级函数”。

一个插件可以在多个扩展点处注册，以执行更复杂或有状态的任务。

{{< image src="scheduling-framework-extensions.png" caption="调度框架扩展点" >}}

### 调度周期扩展点

#### 队列排序

这些插件用于对调度队列中的 Pod 进行排序。 队列排序插件本质上提供 less(Pod1, Pod2) 函数。 一次只能启动一个队列插件。

#### PreFilter

这些插件用于预处理 Pod 的相关信息，或者检查集群或 Pod 必须满足的某些条件。 如果 PreFilter 插件返回错误，则调度周期将终止。

#### Filter

这些插件用于过滤出不能运行该 Pod 的节点。对于每个节点， 调度器将按照其配置顺序调用这些过滤插件。如果任何过滤插件将节点标记为不可行， 则不会为该节点调用剩下的过滤插件。节点可以被同时进行评估。

#### PostFilter

这些插件在 Filter 阶段后调用，但仅在该 Pod 没有可行的节点时调用。 插件按其配置的顺序调用。如果任何 PostFilter 插件标记节点为“Schedulable”， 则其余的插件不会调用。典型的 PostFilter 实现是抢占，试图通过抢占其他 Pod 的资源使该 Pod 可以调度。

#### PreScore

这些插件用于执行 “前置评分（pre-scoring）” 工作，即生成一个可共享状态供 Score 插件使用。 如果 PreScore 插件返回错误，则调度周期将终止。

#### Score

这些插件用于对通过过滤阶段的节点进行排序。调度器将为每个节点调用每个评分插件。 将有一个定义明确的整数范围，代表最小和最大分数。 在标准化评分阶段之后，调度器将根据配置的插件权重 合并所有插件的节点分数。

#### NormalizeScore

这些插件用于在调度器计算 Node 排名之前修改分数。 在此扩展点注册的插件被调用时会使用同一插件的 Score 结果。 每个插件在每个调度周期调用一次。

### 绑定周期扩展点

#### Reserve

Reserve 是一个信息性的扩展点。 管理运行时状态的插件（也成为“有状态插件”）应该使用此扩展点，以便 调度器在节点给指定 Pod 预留了资源时能够通知该插件。 这是在调度器真正将 Pod 绑定到节点之前发生的，并且它存在是为了防止 在调度器等待绑定成功时发生竞争情况。

这个是调度周期的最后一步。 一旦 Pod 处于保留状态，它将在绑定周期结束时触发 Unreserve 插件 （失败时）或 PostBind 插件（成功时）。

#### Permit

Permit 插件在每个 Pod 调度周期的最后调用，用于防止或延迟 Pod 的绑定。 一个允许插件可以做以下三件事之一：

- 批准：一旦所有 Permit 插件批准 Pod 后，该 Pod 将被发送以进行绑定。

- 拒绝：如果任何 Permit 插件拒绝 Pod，则该 Pod 将被返回到调度队列。 这将触发Unreserve 插件。

- 等待（带有超时）：如果一个 Permit 插件返回 “等待” 结果，则 Pod 将保持在一个内部的 “等待中” 的 Pod 列表，同时该 Pod 的绑定周期启动时即直接阻塞直到得到 批准。如果超时发生，等待 变成 拒绝，并且 Pod 将返回调度队列，从而触发 Unreserve 插件。

说明： 尽管任何插件可以访问 “等待中” 状态的 Pod 列表并批准它们 (查看 FrameworkHandle)。 我们期望只有允许插件可以批准处于 “等待中” 状态的预留 Pod 的绑定。 一旦 Pod 被批准了，它将发送到 PreBind 阶段。

#### PreBind

这些插件用于执行 Pod 绑定前所需的所有工作。 例如，一个 PreBind 插件可能需要制备网络卷并且在允许 Pod 运行在该节点之前 将其挂载到目标节点上。

如果任何 PreBind 插件返回错误，则 Pod 将被 拒绝 并且 退回到调度队列中。

#### Bind

Bind 插件用于将 Pod 绑定到节点上。直到所有的 PreBind 插件都完成，Bind 插件才会被调用。 各 Bind 插件按照配置顺序被调用。Bind 插件可以选择是否处理指定的 Pod。 如果某 Bind 插件选择处理某 Pod，剩余的 Bind 插件将被跳过。

#### PostBind

这是个信息性的扩展点。 PostBind 插件在 Pod 成功绑定后被调用。这是绑定周期的结尾，可用于清理相关的资源。

#### Unreserve

这是个信息性的扩展点。 如果 Pod 被保留，然后在后面的阶段中被拒绝，则 Unreserve 插件将被通知。 Unreserve 插件应该清楚保留 Pod 的相关状态。

使用此扩展点的插件通常也使用 Reserve。

## 结束

我们这里介绍完调度框架和调度框架的扩展点后，下一节再来实现一个调度器插件。
