2. 调度框架详解:调度框架设计、扩展点、插件机制

Kubernetes 调度器(kube-scheduler)在 v1.19 之后引入了全新的调度框架(Scheduling Framework),将原本耦合在调度算法中的逻辑拆解为一系列可插拔的扩展点。理解调度框架的设计是分析调度器性能瓶颈、制定优化策略的前提。

2.1 调度框架整体设计

调度框架采用“调度周期 + 绑定周期”的双阶段流水线架构。每个 Pod 的调度过程被抽象为一个固定的执行序列,调度器通过注册到各个扩展点的插件来注入自定义逻辑。框架本身负责维护调度队列、缓存(Scheduler Cache)以及调度结果的绑定与回滚。

核心设计原则:
  • 可扩展性:通过插件机制支持自定义调度策略,无需修改调度器核心代码。
  • 可观测性:每个扩展点都有明确的上下文和指标输出,便于性能追踪。
  • 确定性:调度周期内各扩展点的执行顺序固定,保证结果可重现。
调度框架整体架构(双周期流水线) 调度周期(Scheduling Cycle) QueueSort 排序插件 PreFilter 预过滤插件 Filter 过滤插件 PreScore 预评分插件 Score 评分插件 NormalizeScore 规范化评分 绑定周期(Binding Cycle) Reserve 预留资源 Permit 许可/等待 PreBind 预绑定 Bind 绑定 PostBind 绑定后清理 调度结果 说明: 调度周期是同步的,绑定周期可以是异步的。Unreserve 作为回滚点,在绑定失败时释放预留资源。 NormalizeScore 仅在多个 Score 插件同时使用时生效,用于将分数归一化到统一范围。

2.2 扩展点详解

调度框架共定义了 11 个扩展点,分布在调度周期和绑定周期中。每个扩展点都有明确的输入、输出和执行语义。

扩展点 所属周期 执行时机 典型用途 性能敏感度
QueueSort 调度周期 Pod 入队时排序 自定义 Pod 优先级顺序 高(每次入队触发)
PreFilter 调度周期 Filter 之前,每个 Pod 一次 预处理 Pod 信息、预检查集群状态 中
Filter 调度周期 遍历所有候选节点 节点过滤(资源、污点、亲和性) 极高(节点数线性放大)
PreScore 调度周期 Filter 之后,Score 之前 计算评分所需的共享数据 低
Score 调度周期 遍历所有通过 Filter 的节点 节点打分(资源均衡、亲和性权重) 高(节点数线性放大)
NormalizeScore 调度周期 所有 Score 插件执行后 分数归一化处理 低
Reserve 绑定周期 选定节点后,绑定前 在缓存中预留资源 中
Permit 绑定周期 Reserve 之后 等待/批准/拒绝绑定 中(可能阻塞)
PreBind 绑定周期 Permit 通过后 绑定前准备(如挂载卷) 中
Bind 绑定周期 最终绑定 写入 etcd 绑定记录 高(etcd 写入延迟)
PostBind 绑定周期 绑定成功后 清理、日志、指标 低
性能优化提示: Filter 和 Score 扩展点的执行次数与集群节点数成正比。当集群规模超过 5000 节点时,这两个扩展点的耗时占总调度延迟的 70% 以上。优化方向包括:减少不必要的 Filter 插件、使用并行评分、引入节点缓存预计算。

2.3 插件机制与注册

调度框架的插件机制基于 Go 接口实现。每个插件只需实现对应扩展点的接口,并通过调度器配置注册即可生效。调度器启动时,框架会根据配置按顺序加载插件,并在每个扩展点依次调用已注册的插件。

插件注册的核心数据结构如下:

// 插件注册配置示例(KubeSchedulerConfiguration)
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
    plugins:
      # 调度周期插件
      preFilter:
        enabled:
          - name: "NodeResourcesFit"
          - name: "NodePorts"
      filter:
        enabled:
          - name: "NodeUnschedulable"
          - name: "NodeResourcesFit"
          - name: "NodeName"
          - name: "PodTopologySpread"
      score:
        enabled:
          - name: "NodeResourcesBalancedAllocation"
            weight: 1
          - name: "ImageLocality"
            weight: 1
      # 绑定周期插件
      reserve:
        enabled:
          - name: "VolumeBinding"
      bind:
        enabled:
          - name: "DefaultBinder"

2.4 插件执行顺序与权重

在同一个扩展点内,多个插件的执行顺序由配置中的顺序决定。对于 Score 扩展点,每个插件可以配置独立的权重(weight),最终节点的总分是所有 Score 插件加权求和的结果。

插件执行的关键规则:

  • Filter 短路:任何一个 Filter 插件返回不可调度,该节点立即被排除,不再执行后续 Filter 插件。
  • Score 累加:所有 Score 插件的分数乘以权重后累加,NormalizeScore 用于将分数归一化到 [0, 100] 范围。
  • Permit 等待:Permit 插件可以返回 Wait 状态,将 Pod 放入等待队列,直到超时或被批准。
  • Unreserve 回滚:如果绑定周期中任何步骤失败,框架会调用所有已执行 Reserve 插件的 Unreserve 方法释放资源。
Filter 短路与 Score 加权执行流程 开始调度 Pod Filter 插件链(顺序执行,支持短路) NodeUnschedulable NodeResourcesFit NodeName PodTopologySpread 短路退出 Score 插件链(顺序执行,加权累加) NodeResourcesBalanced (w=1) ImageLocality (w=1) InterPodAffinity (w=2) 总分 = Σ(score × w) 选定最优节点 → 进入绑定周期 关键性能点:Filter 短路可减少无效节点遍历;Score 权重配置影响调度均衡性,权重过大会导致调度结果倾斜。

2.5 插件生命周期与状态管理

调度框架为每个插件提供了完整的生命周期管理,包括初始化、运行、清理和指标上报。插件开发者只需关注业务逻辑,框架负责调度上下文(CycleState)的传递和并发安全。

插件状态管理要点:
  • CycleState:每个调度周期独立的状态对象,插件可通过它共享数据,避免重复计算。
  • 并行安全:多个 Pod 的调度周期可以并行执行,插件必须保证 CycleState 的读写安全。
  • 指标注入:框架自动为每个插件记录执行耗时、调用次数、错误次数等指标,通过 Prometheus 暴露。
  • 优雅关闭:调度器关闭时,框架会按逆序调用插件的 Cleanup 方法,确保资源释放。

2.6 调度框架的性能影响分析

调度框架的插件化设计在带来灵活性的同时,也引入了额外的性能开销。以下是需要关注的性能瓶颈点:

性能因素 影响范围 优化建议
插件数量过多 Filter / Score 扩展点 合并功能相似的插件,减少链式调用次数
CycleState 滥用 所有扩展点 避免在 CycleState 中存储大对象,及时清理临时数据
Permit 阻塞 绑定周期 减少 Permit 等待时间,或使用异步批准机制
Bind 串行化 绑定周期 使用批量绑定或异步绑定策略(需自定义 Bind 插件)
插件指标采集 全局 关闭不必要的插件级指标,降低 Prometheus 采集开销
常见误区: 不要为了追求调度精度而注册大量 Score 插件。每个 Score 插件都会遍历所有通过 Filter 的节点,插件数量每增加 1 个,评分阶段的总耗时近似线性增长。在 5000 节点集群中,每增加 1 个 Score 插件,单 Pod 调度延迟增加约 2-5ms。