3. 调度框架(Scheduling Framework)总览:扩展点(Extension Points)与插件机制

Kubernetes 调度器从 v1.19 开始引入了 Scheduling Framework,将调度流程拆解为多个明确定义的阶段,每个阶段称为一个 扩展点(Extension Point)。用户可以通过实现插件(Plugin)接口,在特定扩展点注入自定义逻辑,从而在不修改核心代码的前提下深度定制调度行为。

3.1 调度框架的核心设计理念

传统调度器采用硬编码的“Predicates + Priorities”两阶段模型,扩展性差、维护成本高。Scheduling Framework 将调度流程拆解为多个可插拔的扩展点,每个扩展点对应一个明确的调度阶段,插件可以注册到任意一个或多个扩展点。

核心优势:
  • 解耦:调度逻辑与核心框架分离,插件独立开发、独立部署。
  • 可组合:多个插件可以注册到同一扩展点,按优先级顺序执行。
  • 可观测:每个扩展点都有明确的输入/输出,便于监控和调试。
  • 标准化:插件接口(Plugin API)统一,降低开发门槛。

3.2 扩展点全景图

调度框架定义了 11 个核心扩展点,分布在调度周期的不同阶段。下图展示了扩展点的完整流程及其与调度周期的对应关系:

调度框架扩展点全景图 调度周期阶段 Queue → Filter → Score → Normalize → Reserve → Permit → PreBind → Bind → PostBind 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 扩展点(绿色)是唯一必须实现的扩展点,其他均为可选。 每个扩展点可以注册多个插件,按配置的权重顺序执行。 插件可以同时实现多个扩展点接口,例如一个插件同时实现 Filter 和 Score。

3.3 扩展点详解

每个扩展点都有明确的职责和接口定义。下表总结了所有扩展点的功能、触发时机和典型用途:

扩展点 阶段 功能描述 典型用途
QueueSort 排序 对调度队列中的 Pod 进行排序,决定下一个调度的 Pod 实现自定义优先级排序策略
PreFilter 预过滤 在 Filter 之前执行,用于预处理或校验 Pod 信息 检查 Pod 的拓扑分布约束、资源预留
Filter 过滤 对每个节点执行过滤,排除不满足条件的节点 节点资源检查、端口冲突、污点容忍
PostFilter 后过滤 当 Filter 阶段没有可用节点时执行,尝试替代方案 抢占(Preemption)、节点扩展
PreScore 预评分 在 Score 之前执行,用于生成评分所需的共享数据 计算节点间的拓扑距离、网络延迟矩阵
Score 评分 对通过 Filter 的节点进行评分,分值越高越优先 资源利用率、亲和性、网络拓扑感知
NormalizeScore 归一化 对多个 Score 插件的评分结果进行归一化处理 将不同量纲的评分统一到 [0, 100] 区间
Reserve 预留 在节点上预留资源,确保后续绑定成功 资源锁定、避免竞态条件
Permit 许可 批准或延迟 Pod 的调度决策,支持等待/拒绝 多租户配额、跨集群调度协调
PreBind 预绑定 在正式绑定前执行,用于准备绑定所需的资源 动态创建存储卷、网络配置
Bind 绑定 将 Pod 绑定到选定节点,写入 etcd 默认绑定、自定义绑定逻辑
PostBind 后绑定 绑定完成后执行,用于清理或通知 日志记录、监控指标上报

3.4 插件机制与生命周期

插件是 Scheduling Framework 的核心扩展单元。每个插件需要实现一个或多个扩展点接口,并通过配置文件注册到调度器中。

3.4.1 插件接口定义

每个扩展点对应一个 Go 接口,插件通过实现这些接口来注入逻辑。以 Filter 扩展点为例:

// FilterPlugin 接口定义
type FilterPlugin interface {
    Plugin
    // Filter 在调度周期中为每个节点调用
    // 返回 Success 表示节点可用,Unschedulable 表示不可用
    Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status
}

// ScorePlugin 接口定义
type ScorePlugin interface {
    Plugin
    // Score 为每个节点打分,分值范围 [0, MaxScore]
    Score(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (int64, *Status)
    // ScoreExtensions 返回归一化扩展(可选)
    ScoreExtensions() ScoreExtensions
}

3.4.2 插件注册与配置

插件通过 KubeSchedulerConfiguration 配置文件注册。以下示例展示了如何注册一个自定义的拓扑感知插件:

apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: topology-aware-scheduler
    plugins:
      # 在 Filter 扩展点注册拓扑过滤插件
      filter:
        enabled:
          - name: TopologyFilter
            weight: 1
      # 在 Score 扩展点注册拓扑评分插件
      score:
        enabled:
          - name: TopologyScore
            weight: 3
      # 在 Reserve 扩展点注册资源预留插件
      reserve:
        enabled:
          - name: TopologyReserve
    pluginConfig:
      - name: TopologyFilter
        args:
          topologyKey: "topology.kubernetes.io/zone"
      - name: TopologyScore
        args:
          bandwidthWeight: 0.7
          latencyWeight: 0.3

3.5 插件执行顺序与权重

多个插件可以注册到同一个扩展点,调度框架按照以下规则决定执行顺序:

  1. 内置插件优先:Kubernetes 内置插件(如 NodeResourcesFit、NodeAffinity)默认排在用户自定义插件之前。
  2. 权重排序:对于 Score 扩展点,插件的权重(weight)影响最终评分占比。
  3. 配置顺序:在配置文件中,enabled 列表的顺序决定了插件的执行顺序。
最佳实践:在开发自定义插件时,建议先实现 Filter 扩展点进行节点筛选,再实现 Score 扩展点进行精细化评分。对于网络拓扑感知场景,PreScore 扩展点非常适合预计算节点间的网络距离矩阵,避免在 Score 阶段重复计算。

3.6 扩展点与网络拓扑感知的关联

在容器网络拓扑感知调度场景中,以下扩展点最为关键:

扩展点 网络拓扑感知中的作用
PreFilter 解析 Pod 的网络拓扑约束(如必须部署在同一可用区)
Filter 排除不满足网络连通性要求的节点(如跨区域延迟过高)
PreScore 预计算节点间的网络距离矩阵、带宽可用性
Score 根据网络拓扑距离、带宽、延迟等指标对节点评分
Reserve 预留网络带宽或 IP 地址资源,防止冲突

3.7 调度框架的扩展点执行流程

下图展示了调度框架中扩展点的完整执行流程,以及插件如何在不同阶段介入:

调度框架扩展点执行流程 QueueSort PreFilter Filter PostFilter 失败 PreScore Score NormalizeScore Reserve Permit PreBind Bind PostBind 完成 关键说明 • Filter 阶段失败时,会触发 PostFilter 扩展点(如抢占),若仍无可用节点则调度失败。 • Permit 扩展点可以返回 Wait 状态,调度器会等待直到超时或被批准。 • Bind 扩展点通常只有一个插件生效(默认 Bind 或自定义 Bind),多个 Bind 插件按优先级执行。

3.8 扩展点开发注意事项

开发警告:
  • 性能敏感:Filter 和 Score 扩展点对每个节点都会调用,必须保证执行效率,避免阻塞调度循环。
  • 无状态设计:插件应尽量保持无状态,调度状态通过 CycleState 传递,避免全局变量。
  • 错误处理:Filter 返回 Unschedulable 时,调度器会记录原因并跳过该节点;返回 Error 会导致调度周期终止。
  • 版本兼容:插件接口在不同 Kubernetes 版本间可能发生变化,建议使用 vendor 或 go module 锁定版本。

3.9 本章小结