调度框架(Scheduling Framework)设计理念与扩展点详解
Kubernetes 调度器从 v1.19 开始引入了 Scheduling Framework(调度框架),这是一套插件化的调度架构。它将调度流程拆解为多个明确定义的阶段(Phase),每个阶段都开放了扩展点(Extension Point),允许开发者通过编写插件来注入自定义调度逻辑,而无需修改调度器核心代码。
设计理念:调度框架的核心思想是“流程固定、逻辑可插拔”。调度器负责编排扩展点的执行顺序,插件负责实现具体的调度策略。这种设计极大提升了调度器的可扩展性和可维护性。
2.1 调度框架的核心设计理念
调度框架的设计围绕以下三个核心理念展开:
- 阶段化(Phasing):将调度周期(Scheduling Cycle)和绑定周期(Binding Cycle)拆分为多个顺序执行的阶段,每个阶段职责单一。
- 插件化(Plugin):每个扩展点可以注册多个插件,插件之间通过优先级排序,按序执行。
- 可组合(Composable):用户可以根据集群需求,通过
KubeSchedulerConfiguration配置文件灵活组合插件,实现定制化调度行为。
调度框架将一次 Pod 调度过程分为两个主要周期:
| 周期 | 说明 | 包含扩展点 |
|---|---|---|
| Scheduling Cycle | 为 Pod 选择一个合适的 Node,该过程在调度器内部串行执行 | QueueSort、PreFilter、Filter、PostFilter、PreScore、Score、NormalizeScore、Reserve、Permit |
| Binding Cycle | 将 Pod 绑定到选定的 Node,该过程可异步执行 | PreBind、Bind、PostBind |
关键理解:Scheduling Cycle 是同步的,确保同一时间只有一个 Pod 进入调度决策;Binding Cycle 是异步的,允许绑定操作并发执行,提升吞吐量。
2.2 调度框架整体流程(SVG)
下图展示了调度框架中 Scheduling Cycle 与 Binding Cycle 的完整流程,以及各扩展点的执行顺序:
2.3 扩展点详解
调度框架共定义了 11 个扩展点,分布在调度周期和绑定周期中。每个扩展点都有明确的输入、输出和职责。
2.3.1 Scheduling Cycle 扩展点
| 扩展点 | 执行时机 | 核心职责 | 典型插件示例 |
|---|---|---|---|
| QueueSort | 调度队列排序时 | 对等待调度的 Pod 进行排序,决定出队顺序 | PrioritySort |
| PreFilter | Filter 之前 | 预处理 Pod 信息,检查 Pod 是否满足调度前提条件 | NodeResourcesFit、PodTopologySpread |
| Filter | 节点过滤阶段 | 过滤掉不满足 Pod 调度要求的节点 | NodeResourcesFit、NodeName、NodeUnschedulable |
| PostFilter | Filter 之后,无可用节点时 | 处理调度失败场景,如抢占、备用节点推荐 | Preemption、DefaultPreemption |
| PreScore | Score 之前 | 为打分阶段准备数据,如计算拓扑分布信息 | PodTopologySpread |
| Score | 节点打分阶段 | 对通过 Filter 的节点进行打分,分值越高越优先 | NodeResourcesFit、ImageLocality |
| NormalizeScore | Score 之后 | 对多个 Score 插件的打分结果进行归一化处理 | NodeResourcesFit |
| Reserve | 选定节点后,绑定前 | 在调度器缓存中预留 Pod 资源,防止并发冲突 | NodeResourcesFit |
| Permit | Reserve 之后,绑定前 | 批准或拒绝 Pod 调度,支持等待/超时机制 | 默认允许、自定义准入控制 |
2.3.2 Binding Cycle 扩展点
| 扩展点 | 执行时机 | 核心职责 | 典型插件示例 |
|---|---|---|---|
| PreBind | 绑定之前 | 执行绑定前的准备工作,如创建存储卷、网络资源 | VolumeBinding |
| Bind | 绑定阶段 | 将 Pod 与 Node 的绑定关系写入 API Server | DefaultBinder |
| PostBind | 绑定之后 | 绑定成功后的清理或通知操作 | 无默认实现 |
注意:Bind 扩展点比较特殊,调度框架允许多个 Bind 插件共存,但最终只有一个插件会执行实际的绑定操作(通常按优先级选择)。如果所有 Bind 插件都跳过,调度器会使用默认的 DefaultBinder。
2.4 扩展点执行顺序与插件优先级
调度框架通过 KubeSchedulerConfiguration 中的 plugins 字段来配置每个扩展点启用的插件及其执行顺序。每个插件可以设置 weight(权重)和 order(顺序)。
以下是一个典型的调度器配置片段:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
plugins:
filter:
enabled:
- name: NodeResourcesFit
- name: NodeName
- name: NodeUnschedulable
disabled:
- name: "*" # 禁用所有默认 Filter 插件,仅保留上面启用的
score:
enabled:
- name: NodeResourcesFit
weight: 2
- name: ImageLocality
weight: 1
permit:
enabled:
- name: CustomPermitPlugin
pluginConfig:
- name: NodeResourcesFit
args:
scoringStrategy:
type: LeastAllocated # 使用最少资源分配策略
实践建议:在配置插件时,建议先了解每个扩展点的默认插件列表。使用
disabled: ["*"] 会清空该扩展点的所有默认插件,需谨慎操作。推荐采用“增量式”配置,即只添加或禁用特定插件,保留其他默认插件。
2.5 扩展点与节点资源碎片治理的关系
在节点资源碎片治理场景中,调度框架的以下扩展点尤为关键:
- Filter 扩展点:通过
NodeResourcesFit插件过滤掉资源碎片严重的节点,避免将 Pod 调度到剩余资源零散、无法容纳新 Pod 的节点。 - Score 扩展点:通过自定义打分策略(如
MostAllocated或LeastAllocated)引导 Pod 优先调度到资源利用率高或碎片程度低的节点。 - PreFilter 扩展点:在调度前检查 Pod 的资源需求,提前排除明显不满足条件的节点,减少后续 Filter 的计算开销。
- Reserve 扩展点:确保在并发调度场景下,资源预留不会导致碎片加剧。
通过合理配置这些扩展点的插件,调度器可以在调度决策中主动考虑节点资源碎片因素,从而提升集群整体资源利用率。
2.6 扩展点插件开发接口
调度框架为每个扩展点定义了 Go 接口,开发者只需实现对应接口即可注册为插件。以下是最常用的两个扩展点接口示例:
// FilterPlugin 接口
type FilterPlugin interface {
Plugin
Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
}
// ScorePlugin 接口
type ScorePlugin interface {
Plugin
Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status)
ScoreExtensions() ScoreExtensions
}
开发提示:编写自定义插件时,建议使用
framework.Handle 接口访问调度器缓存、客户端等基础设施。插件注册后,需要在 KubeSchedulerConfiguration 中显式启用才能生效。
2.7 调度框架的优势总结
| 特性 | 传统调度器 | 调度框架 |
|---|---|---|
| 扩展方式 | 修改核心代码、重新编译 | 编写插件、动态配置 |
| 调度流程 | 硬编码、难以定制 | 阶段化、可插拔 |
| 并发模型 | 串行调度 | 调度周期串行 + 绑定周期异步 |
| 配置灵活性 | 通过命令行参数调整 | 通过 API 配置精细控制 |
| 社区生态 | 官方插件有限 | 丰富的内置插件 + 自定义插件 |