3. 调度框架(Scheduling Framework)详解:扩展点与插件注册机制

Kubernetes 调度器从 v1.19 开始引入了 Scheduling Framework,将调度流程拆解为一系列可插拔的扩展点(Extension Points)。这种架构使得调度器的核心逻辑与调度策略解耦,开发者可以通过实现特定扩展点的接口来定制调度行为,而无需修改调度器主代码。

3.1 调度框架整体架构

调度框架将一次 Pod 调度决策划分为多个阶段,每个阶段对应一个或多个扩展点。调度器按照固定顺序依次执行这些扩展点,插件可以在一个或多个扩展点注册回调函数,从而介入调度流程。

核心设计思想: 调度框架采用“流水线 + 插件”模式,每个扩展点负责调度流程中的一个独立环节,插件通过注册机制挂载到对应扩展点,实现调度策略的灵活组合与热插拔。
调度框架扩展点流水线架构 调度框架扩展点流水线 QueueSort PreFilter Filter PostFilter PreScore Score NormalizeScore Reserve Permit PreBind Bind PostBind 说明: 调度器按 QueueSort → PreFilter → Filter → PostFilter → PreScore → Score → NormalizeScore → Reserve → Permit → PreBind → Bind → PostBind 顺序执行。 每个扩展点可注册多个插件,插件按配置顺序执行;Filter 和 Score 是核心筛选与评分阶段,其余扩展点提供预处理、后处理、绑定等能力。

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 优先调度。

性能要点: QueueSort 插件的性能直接影响调度器的吞吐量。如果自定义排序逻辑涉及复杂计算(如依赖外部数据),建议使用缓存或预计算,避免每次排序都重复计算。

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 插件执行完毕后调用,用于修正评分范围。
性能陷阱: 如果多个 Score 插件的评分范围不一致(例如一个返回 0-10,另一个返回 0-100),必须通过 NormalizeScore 进行归一化,否则权重配置会失效。建议所有 Score 插件默认返回 0-100 范围的值。
插件注册与执行流程 插件注册与执行流程 KubeSchedulerConfiguration plugins: filter: enabled: - name: NodeResourcesFit - name: TaintToleration 调度框架核心 • 解析配置文件 • 实例化插件对象 • 挂载到对应扩展点 • 维护插件注册表 调度执行流程 1. QueueSort → 选 Pod 2. PreFilter → 预处理 3. Filter → 过滤节点 4. Score → 评分选优 5. Bind → 绑定节点 关键机制: 插件通过 KubeSchedulerConfiguration 注册,调度框架在启动时解析配置并实例化插件。 每个扩展点可注册多个插件,插件按配置顺序执行;Filter 插件支持短路(一旦失败立即跳过该节点),Score 插件支持权重配置。

3.6 扩展点执行顺序与调度决策

调度框架的扩展点执行顺序决定了调度决策的完整流程。以下是一个 Pod 从入队到绑定完成的完整调度路径:

  1. QueueSort: 从调度队列中选择优先级最高的 Pod。
  2. PreFilter: 对 Pod 进行预处理,检查基本约束(如资源请求是否合理)。
  3. Filter: 遍历所有节点,执行 Filter 插件,生成候选节点列表。
  4. PostFilter(可选): 如果 Filter 后无可用节点,执行抢占逻辑。
  5. PreScore: 准备评分所需的共享数据。
  6. Score: 对候选节点进行评分,选择最优节点。
  7. NormalizeScore: 归一化评分结果。
  8. Reserve: 在选定节点上预留资源。
  9. Permit: 等待外部批准或直接批准。
  10. PreBind: 执行绑定前的准备工作(如挂载卷)。
  11. Bind: 将 Pod 绑定到节点。
  12. PostBind: 绑定完成后的清理或通知。
工程实践建议: 在自定义插件时,应尽量将计算密集型操作放在 PreFilter 或 PreScore 阶段,避免在 Filter 或 Score 阶段重复计算。例如,如果多个 Filter 插件都需要读取同一份外部数据,可以在 PreFilter 中统一加载并缓存到 CycleState 中。

3.7 插件注册机制的最佳实践

实践要点 说明 推荐做法
插件粒度 每个插件只负责一个独立的调度策略 避免在单个插件中实现多个不相关的逻辑
权重配置 Score 插件的权重影响最终节点选择 根据业务重要性合理分配权重,避免权重差距过大导致单一策略主导
错误处理 Filter/Score 插件应返回明确的 Status 使用 framework.NewStatus(framework.Unschedulable, "reason") 返回失败原因
性能优化 避免在 Filter/Score 中执行耗时操作 使用 PreFilter/PreScore 做预计算,结果存入 CycleState
插件顺序 Filter 插件的执行顺序影响调度效率 将计算量小、过滤能力强的插件放在前面,快速剔除不合格节点

3.8 本章小结

调度框架通过 12 个扩展点将调度流程拆解为可插拔的模块,每个扩展点都有明确的职责和调用时机。插件注册机制通过 KubeSchedulerConfiguration 配置文件实现,支持启用、禁用和权重配置。理解扩展点的执行顺序和插件注册机制,是进行调度器性能调优和自定义调度策略的基础。