调度框架
Kubernetes v1.19 [稳定]
调度框架是 Kubernetes 调度器的可插拔架构。 它由一组直接编译到调度器中的“插件” API 组成。 这些 API 允许将大多数调度功能实现为插件,同时保持调度“核心”的轻量级和可维护性。 有关框架设计的更多技术信息,请参阅调度框架的设计提案。
框架工作流程
调度框架定义了一些扩展点。 调度器插件注册为在一个或多个扩展点被调用。 其中一些插件可以更改调度决策,而另一些插件仅提供信息。
每个尝试调度一个 Pod 的操作都分为两个阶段:调度周期和绑定周期。
调度周期 & 绑定周期
调度周期为 Pod 选择一个节点,而绑定周期将该决策应用于集群。 调度周期和绑定周期一起被称为“调度上下文”。
调度周期是串行运行的,而绑定周期可能并发运行。
如果确定 Pod 不可调度或存在内部错误,则可以中止调度或绑定周期。 Pod 将返回队列并重试。
接口
下图显示了 Pod 的调度上下文以及调度框架公开的接口。
一个插件可以实现多个接口以执行更复杂或有状态的任务。
某些接口与可以通过调度器配置配置的调度器扩展点匹配。
PreEnqueue
这些插件在将 Pod 添加到内部活动队列(Pod 被标记为准备调度)之前调用。
仅当所有 PreEnqueue 插件返回 Success
时,才允许 Pod 进入活动队列。 否则,它将被放置在内部不可调度 Pod 列表中,并且不会获得 Unschedulable
条件。
有关内部调度器队列如何工作的更多详细信息,请阅读 kube-scheduler 中的调度队列。
EnqueueExtension
EnqueueExtension 是插件可以基于集群中的更改来控制是否重试由此插件拒绝的 Pod 的调度的接口。 实现 PreEnqueue、PreFilter、Filter、Reserve 或 Permit 的插件应实现此接口。
QueueingHint
Kubernetes v1.32 [beta]
QueueingHint 是一个回调函数,用于决定是否可以将 Pod 重新排队到活动队列或退避队列。 它在集群中发生某种事件或更改时执行。 当 QueueingHint 发现事件可能会使 Pod 可调度时,Pod 将被放入活动队列或退避队列,以便调度程序将重试 Pod 的调度。
注意
调度期间的 QueueingHint 评估是一项 beta 级功能。 在 Kubernetes 1.32 中,此特性门默认启用,你可以通过SchedulerQueueingHints
特性门禁用它。QueueSort
这些插件用于对调度队列中的 Pod 进行排序。 队列排序插件本质上提供了一个 Less(Pod1, Pod2)
函数。 一次只能启用一个队列排序插件。
PreFilter
这些插件用于预处理有关 Pod 的信息,或检查集群或 Pod 必须满足的某些条件。 如果 PreFilter 插件返回错误,则调度周期中止。
Filter
这些插件用于过滤掉无法运行 Pod 的节点。 对于每个节点,调度程序将按配置的顺序调用过滤器插件。 如果任何过滤器插件将节点标记为不可行,则不会为该节点调用其余插件。 可以并发评估节点。
PostFilter
这些插件在 Filter 阶段之后调用,但仅当未找到 Pod 的可行节点时才调用。 插件按配置的顺序调用。 如果任何 postFilter 插件将节点标记为 Schedulable
,则不会调用其余插件。 一个典型的 PostFilter 实现是抢占,它尝试通过抢占其他 Pod 来使 Pod 可调度。
PreScore
这些插件用于执行“预评分”工作,该工作生成一个可共享的状态供 Score 插件使用。 如果 PreScore 插件返回错误,则调度周期中止。
Score
这些插件用于对已通过筛选阶段的节点进行排名。 调度程序将为每个节点调用每个评分插件。 将有一个明确定义的整数范围,表示最小和最大分数。 在NormalizeScore阶段之后,调度程序将根据配置的插件权重合并所有插件的节点分数。
NormalizeScore
这些插件用于在调度程序计算节点的最终排名之前修改分数。 注册此扩展点的插件将在来自同一插件的Score结果中被调用。 这在每个调度周期中每个插件被调用一次。
例如,假设一个插件 BlinkingLightScorer
根据节点拥有的闪烁灯的数量对节点进行排名。
func ScoreNode(_ *v1.pod, n *v1.Node) (int, error) {
return getBlinkingLightCount(n)
}
但是,与 NodeScoreMax
相比,闪烁灯的最大计数可能很小。 为了解决这个问题,BlinkingLightScorer
也应该注册此扩展点。
func NormalizeScores(scores map[string]int) {
highest := 0
for _, score := range scores {
highest = max(highest, score)
}
for node, score := range scores {
scores[node] = score*NodeScoreMax/highest
}
}
如果任何 NormalizeScore 插件返回错误,则调度周期中止。
注意
希望执行“预保留”工作的插件应使用 NormalizeScore 扩展点。Reserve
实现 Reserve 接口的插件有两个方法,即 Reserve
和 Unreserve
,它们支持分别称为 Reserve 和 Unreserve 的两个信息性调度阶段。 维护运行时状态(也称为“有状态插件”)的插件应使用这些阶段来接收来自调度程序的通知,告知何时为给定的 Pod 保留和取消保留节点上的资源。
Reserve 阶段发生在调度程序实际将 Pod 绑定到其指定节点之前。它的存在是为了防止调度程序等待绑定成功时的竞争条件。 每个 Reserve 插件的 Reserve
方法可能会成功或失败; 如果一个 Reserve
方法调用失败,则不会执行后续插件,并且认为 Reserve 阶段失败。 如果所有插件的 Reserve
方法都成功,则认为 Reserve 阶段成功,并且执行调度周期的其余部分和绑定周期。
如果 Reserve 阶段或稍后阶段失败,则会触发 Unreserve 阶段。 当这种情况发生时,所有 Reserve 插件的 Unreserve
方法将按 Reserve
方法调用的相反顺序执行。 此阶段的存在是为了清理与保留的 Pod 相关的状态。
注意
Reserve 插件中Unreserve
方法的实现必须是幂等的,并且不得失败。Permit
每个 Pod 的调度周期结束时都会调用 Permit 插件,以防止或延迟绑定到候选节点。 许可插件可以执行以下三件事之一
批准
一旦所有 Permit 插件都批准了一个 Pod,它就会被发送以进行绑定。拒绝
如果任何 Permit 插件拒绝了一个 Pod,它将被返回到调度队列。 这将触发Reserve 插件中的 Unreserve 阶段。等待(具有超时)
如果 Permit 插件返回“wait”,则 Pod 将保留在内部“等待”Pods 列表中,并且此 Pod 的绑定周期开始但直接阻塞,直到获得批准。 如果发生超时,wait 将变为 deny,并且 Pod 将返回到调度队列,从而触发 Reserve 插件中的 Unreserve 阶段。
注意
虽然任何插件都可以访问“等待”Pod 的列表并批准它们(请参阅FrameworkHandle
),但我们期望只有许可插件才能批准处于“等待”状态的已保留 Pod 的绑定。 一旦 Pod 获得批准,它就会被发送到 PreBind 阶段。PreBind
这些插件用于在 Pod 绑定之前执行任何必要的工作。 例如,一个 pre-bind 插件可以在允许 Pod 在目标节点上运行之前,配置一个网络卷并将其挂载到目标节点上。
如果任何 PreBind 插件返回错误,则 Pod 将被拒绝并返回到调度队列。
Bind
这些插件用于将 Pod 绑定到节点。 在所有 PreBind 插件完成之前,不会调用 Bind 插件。 每个 bind 插件都按配置的顺序调用。 一个 bind 插件可以选择是否处理给定的 Pod。 如果一个 bind 插件选择处理一个 Pod,则其余的 bind 插件将被跳过。
PostBind
这是一个信息接口。在 Pod 成功绑定后会调用 Post-bind 插件。这是绑定周期的结束,可以用来清理相关的资源。
插件 API
插件 API 分为两个步骤。首先,插件必须注册并完成配置,然后它们才能使用扩展点接口。扩展点接口的形式如下。
type Plugin interface {
Name() string
}
type QueueSortPlugin interface {
Plugin
Less(*v1.pod, *v1.pod) bool
}
type PreFilterPlugin interface {
Plugin
PreFilter(context.Context, *framework.CycleState, *v1.pod) error
}
// ...
插件配置
你可以在调度器的配置中启用或禁用插件。如果你使用的是 Kubernetes v1.18 或更高版本,大多数调度插件默认情况下处于启用状态并正在使用中。
除了默认插件之外,你还可以实现自己的调度插件,并让它们与默认插件一起配置。你可以访问 scheduler-plugins 以了解更多详细信息。
如果你使用的是 Kubernetes v1.18 或更高版本,你可以将一组插件配置为调度器配置文件,然后定义多个配置文件以适应各种类型的工作负载。在多个配置文件中了解更多信息。