3. 调度框架(Scheduling Framework)详解:扩展点与插件注册机制
Kubernetes 调度器从 v1.19 开始引入了 Scheduling Framework,将调度流程拆解为一系列可插拔的扩展点(Extension Points)。这种架构使得调度器的核心逻辑与调度策略解耦,开发者可以通过实现特定扩展点的接口来定制调度行为,而无需修改调度器主代码。
3.1 调度框架整体架构
调度框架将一次 Pod 调度决策划分为多个阶段,每个阶段对应一个或多个扩展点。调度器按照固定顺序依次执行这些扩展点,插件可以在一个或多个扩展点注册回调函数,从而介入调度流程。
3.2 扩展点详解
调度框架定义了 12 个扩展点,每个扩展点有明确的职责和调用时机。下表列出了所有扩展点的核心信息:
| 扩展点 | 阶段 | 核心职责 | 是否可中断调度 | 典型插件示例 |
|---|---|---|---|---|
| QueueSort | 排序 | 对调度队列中的 Pod 进行排序,决定下一个调度的 Pod | 否(排序后返回) | PrioritySort |
| PreFilter | 预处理 | 预处理 Pod 信息,为 Filter 阶段准备数据;可提前拒绝不符合条件的 Pod | 是(返回 Unschedulable) | NodeResourcesFit, PodTopologySpread |
| Filter | 过滤 | 对每个候选节点执行过滤,剔除不满足条件的节点 | 是(返回 Unschedulable) | NodeUnschedulable, NodeName, TaintToleration |
| PostFilter | 后过滤 | 当所有节点都被过滤后执行,尝试替代方案(如抢占) | 是(返回 PostFilterResult) | DefaultPreemption |
| PreScore | 预评分 | 在评分前运行,用于生成评分所需的共享数据 | 否 | NodeResourcesBalancedAllocation |
| Score | 评分 | 对通过过滤的节点进行评分(0-100),分数越高越优先 | 否 | NodeResourcesLeastAllocated, ImageLocality |
| NormalizeScore | 归一化 | 对多个 Score 插件的评分结果进行归一化处理 | 否 | NodeResourcesLeastAllocated |
| Reserve | 预留 | 在选定节点上预留资源,确保后续绑定阶段资源可用 | 是(返回 ReserveError) | NodeResourcesFit |
| Permit | 许可 | 批准或延迟 Pod 的绑定,支持等待/拒绝/批准三种结果 | 是(返回 Wait/Reject) | InterPodAffinity |
| PreBind | 预绑定 | 在正式绑定前执行预处理操作(如挂载卷) | 是(返回 PreBindError) | VolumeBinding |
| Bind | 绑定 | 将 Pod 绑定到选定节点,写入 etcd | 是(返回 BindError) | DefaultBinder |
| PostBind | 后绑定 | 绑定完成后执行清理或通知操作 | 否 | VolumeBinding |
3.3 核心扩展点深度解析
3.3.1 QueueSort — 调度队列排序
QueueSort 是调度流程的入口,决定了下一个被调度的 Pod。该扩展点只有一个插件可以注册,因为排序策略必须是全局一致的。默认使用 PrioritySort 插件,按 Pod 优先级排序,高优先级 Pod 优先调度。
3.3.2 Filter — 节点过滤
Filter 是调度流程中最关键的扩展点之一,每个候选节点都会依次执行所有注册的 Filter 插件。Filter 插件返回 Success 或 Unschedulable,一旦某个插件返回 Unschedulable,该节点立即被剔除,不再执行后续 Filter 插件。
// Filter 插件接口示例
type FilterPlugin interface {
Plugin
Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
}
3.3.3 Score — 节点评分
Score 插件对通过 Filter 的节点进行评分,评分范围 0-100。多个 Score 插件的评分结果会按照插件权重(Weight)加权求和,最终选择总分最高的节点。
// Score 插件接口示例
type ScorePlugin interface {
Plugin
Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status)
ScoreExtensions() ScoreExtensions
}
3.3.4 Permit — 绑定许可
Permit 扩展点提供了“等待-批准”机制,适用于需要外部协调的场景(如跨集群调度、资源配额检查)。Permit 插件可以返回三种结果:
- Approve: 允许继续执行绑定流程。
- Deny: 拒绝绑定,调度失败。
- Wait: 将 Pod 置于等待状态,直到超时或被其他组件批准/拒绝。
3.4 插件注册机制
插件通过 KubeSchedulerConfiguration 配置文件注册到调度框架中。调度器启动时读取配置,实例化插件并挂载到对应的扩展点。
3.4.1 配置文件注册方式
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
plugins:
queueSort:
enabled:
- name: PrioritySort
filter:
enabled:
- name: NodeUnschedulable
- name: NodeName
- name: TaintToleration
- name: NodeResourcesFit
disabled:
- name: "*" # 禁用所有默认 Filter 插件
score:
enabled:
- name: NodeResourcesLeastAllocated
weight: 2
- name: ImageLocality
weight: 1
permit:
enabled:
- name: InterPodAffinity
pluginConfig:
- name: NodeResourcesFit
args:
scoringStrategy:
type: LeastAllocated
3.4.2 代码注册方式
对于自定义插件,需要在调度器启动时通过代码注册:
// 自定义插件注册示例
import (
"k8s.io/kubernetes/pkg/scheduler/framework"
"k8s.io/kubernetes/pkg/scheduler/framework/plugins"
)
func main() {
// 创建调度器配置
cc := &schedulerconfig.KubeSchedulerConfiguration{...}
// 注册自定义插件
registry := frameworkruntime.Registry{
MyCustomPlugin: NewMyCustomPlugin,
}
// 启动调度器
sched, err := scheduler.New(cc, ...)
}
3.5 插件执行顺序与权重
调度框架对插件的执行顺序有明确规则:
- 同一扩展点内的插件: 按照配置文件中
enabled列表的顺序执行。 - Filter 插件: 一旦某个插件返回 Unschedulable,立即停止该节点的后续 Filter 检查。
- Score 插件: 每个 Score 插件独立评分,最终结果 = Σ(插件评分 × 插件权重)。
- NormalizeScore: 在所有 Score 插件执行完毕后调用,用于修正评分范围。
3.6 扩展点执行顺序与调度决策
调度框架的扩展点执行顺序决定了调度决策的完整流程。以下是一个 Pod 从入队到绑定完成的完整调度路径:
- QueueSort: 从调度队列中选择优先级最高的 Pod。
- PreFilter: 对 Pod 进行预处理,检查基本约束(如资源请求是否合理)。
- Filter: 遍历所有节点,执行 Filter 插件,生成候选节点列表。
- PostFilter(可选): 如果 Filter 后无可用节点,执行抢占逻辑。
- PreScore: 准备评分所需的共享数据。
- Score: 对候选节点进行评分,选择最优节点。
- NormalizeScore: 归一化评分结果。
- Reserve: 在选定节点上预留资源。
- Permit: 等待外部批准或直接批准。
- PreBind: 执行绑定前的准备工作(如挂载卷)。
- Bind: 将 Pod 绑定到节点。
- PostBind: 绑定完成后的清理或通知。
3.7 插件注册机制的最佳实践
| 实践要点 | 说明 | 推荐做法 |
|---|---|---|
| 插件粒度 | 每个插件只负责一个独立的调度策略 | 避免在单个插件中实现多个不相关的逻辑 |
| 权重配置 | Score 插件的权重影响最终节点选择 | 根据业务重要性合理分配权重,避免权重差距过大导致单一策略主导 |
| 错误处理 | Filter/Score 插件应返回明确的 Status | 使用 framework.NewStatus(framework.Unschedulable, "reason") 返回失败原因 |
| 性能优化 | 避免在 Filter/Score 中执行耗时操作 | 使用 PreFilter/PreScore 做预计算,结果存入 CycleState |
| 插件顺序 | Filter 插件的执行顺序影响调度效率 | 将计算量小、过滤能力强的插件放在前面,快速剔除不合格节点 |
3.8 本章小结
调度框架通过 12 个扩展点将调度流程拆解为可插拔的模块,每个扩展点都有明确的职责和调用时机。插件注册机制通过 KubeSchedulerConfiguration 配置文件实现,支持启用、禁用和权重配置。理解扩展点的执行顺序和插件注册机制,是进行调度器性能调优和自定义调度策略的基础。