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

Kubernetes 调度器从 v1.19 开始引入了 Scheduling Framework(调度框架),将调度流程拆解为多个可插拔的扩展点。调度框架的核心设计思想是:将调度决策权从内置算法中解耦,允许用户通过插件在特定阶段注入自定义逻辑,从而在不修改核心代码的前提下实现调度策略的灵活定制。

2.1 调度框架整体架构

调度框架将一次 Pod 调度过程划分为多个连续的阶段,每个阶段对应一个或多个扩展点。调度器按照固定的顺序遍历这些扩展点,并在每个扩展点处调用已注册的插件。下图展示了调度框架的核心扩展点及其执行顺序:

Kubernetes 调度框架扩展点流程图 调度框架扩展点执行流程 QueueSort PreFilter Filter PostFilter Score NormalizeScore 扩展点说明 每个扩展点可注册多个插件 插件按配置顺序依次执行 任一插件失败则调度终止 扩展点职责 QueueSort: 排序待调度 Pod PreFilter: 预处理 Pod 信息 Filter: 过滤不可用节点 PostFilter: 处理过滤失败 Score: 对节点打分排序 NormalizeScore: 归一化分数 Reserve: 预留节点资源 Permit: 准入检查 PreBind: 绑定前处理 Bind: 执行绑定操作 PostBind: 绑定后处理 Unreserve: 释放预留资源
设计要点:调度框架的扩展点设计遵循“先过滤、后打分”的基本原则。Filter 阶段快速剔除不满足条件的节点,Score 阶段对剩余节点进行精细化评分,这种分层设计有效降低了调度器的计算开销。

2.2 插件机制与注册

插件是调度框架的最小执行单元。每个插件可以实现一个或多个扩展点接口。调度器在启动时通过 KubeSchedulerConfiguration 配置文件中定义的插件列表来注册插件。插件注册的核心数据结构如下:

# KubeSchedulerConfiguration 示例
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: default-scheduler
    plugins:
      queueSort:
        enabled:
          - name: PrioritySort
        disabled: null
      preFilter:
        enabled:
          - name: NodeResourcesFit
          - name: NodePorts
      filter:
        enabled:
          - name: NodeUnschedulable
          - name: NodeResourcesFit
          - name: NodeName
          - name: PodTopologySpread
      postFilter:
        enabled:
          - name: DefaultPreemption
      score:
        enabled:
          - name: NodeResourcesBalancedAllocation
            weight: 1
          - name: ImageLocality
            weight: 1
      reserve:
        enabled:
          - name: VolumeBinding
      permit:
        enabled: []
      preBind:
        enabled:
          - name: VolumeBinding
      bind:
        enabled:
          - name: DefaultBinder

2.3 核心扩展点详解

调度框架共定义了 11 个扩展点,每个扩展点对应调度流程中的一个特定阶段。下表列出了所有扩展点及其核心职责:

扩展点 执行时机 核心职责 典型插件
QueueSort 调度队列排序 对等待调度的 Pod 进行排序,决定调度顺序 PrioritySort
PreFilter 过滤前预处理 检查 Pod 的调度条件,预处理 Pod 信息 NodeResourcesFit, NodePorts
Filter 节点过滤 排除不满足 Pod 运行条件的节点 NodeUnschedulable, NodeName
PostFilter 过滤后处理 当所有节点都被过滤时,尝试抢占或返回错误 DefaultPreemption
Score 节点打分 对通过过滤的节点进行评分,分数越高越优先 NodeResourcesBalancedAllocation
NormalizeScore 分数归一化 将各插件的评分结果归一化到统一范围 内置归一化逻辑
Reserve 资源预留 在选定节点上预留 Pod 所需的资源 VolumeBinding
Permit 准入检查 批准或延迟 Pod 的调度决策 自定义准入插件
PreBind 绑定前处理 执行绑定前的准备工作,如挂载卷 VolumeBinding
Bind 绑定执行 将 Pod 绑定到选定节点 DefaultBinder
PostBind 绑定后处理 绑定完成后的清理或通知操作 自定义通知插件

2.4 关键扩展点深度解析

2.4.1 QueueSort — 调度队列排序

QueueSort 是调度框架的第一个扩展点,它决定了 Pod 从调度队列中出队的顺序。调度器会维护一个优先级队列,QueueSort 插件负责定义队列中 Pod 的排序规则。默认的 PrioritySort 插件根据 Pod 的优先级(PriorityClass)进行排序,优先级高的 Pod 优先被调度。

调优要点:如果集群中存在多个优先级类,可以通过自定义 QueueSort 插件实现更复杂的排序策略,例如结合 Pod 的创建时间、资源请求量或业务标签进行排序。

2.4.2 PreFilter — 过滤前预处理

PreFilter 在 Filter 阶段之前执行,用于预处理 Pod 的调度信息。该扩展点可以检查 Pod 的调度约束是否合理,例如验证 Pod 是否请求了不存在的资源类型。PreFilter 插件还可以计算 Pod 的资源需求总量,为后续的 Filter 和 Score 阶段提供缓存数据。

2.4.3 Filter — 节点过滤

Filter 是调度框架中最核心的扩展点之一,负责从所有节点中筛选出满足 Pod 运行条件的节点。Filter 插件按顺序执行,每个插件检查一个特定的调度约束:

  • NodeUnschedulable:排除被标记为不可调度的节点
  • NodeResourcesFit:检查节点剩余资源是否满足 Pod 请求
  • NodeName:根据 Pod 的 nodeName 字段匹配节点
  • PodTopologySpread:检查 Pod 拓扑分布约束
  • NodeAffinity:检查节点亲和性规则
  • TaintToleration:检查污点容忍度
性能注意事项:Filter 阶段会遍历集群中的所有节点,因此 Filter 插件的执行效率直接影响调度器的吞吐量。建议将计算量大的检查逻辑放在 PreFilter 中预处理,Filter 阶段仅做快速判断。

2.4.4 PostFilter — 过滤后处理

当所有节点都被 Filter 阶段排除时,PostFilter 扩展点会被触发。PostFilter 的主要职责是处理调度失败的情况。默认的 DefaultPreemption 插件会尝试抢占低优先级 Pod 来释放资源,使当前 Pod 能够被调度。

2.4.5 Score — 节点打分

Score 扩展点对通过 Filter 阶段的节点进行精细化评分。每个 Score 插件会为节点生成一个分数(通常为 0-100),调度器将所有 Score 插件的分数乘以对应的权重后求和,最终选择分数最高的节点。常见的 Score 插件包括:

Score 插件 评分依据 权重配置
NodeResourcesBalancedAllocation 节点资源分配的均衡程度 weight: 1
NodeResourcesLeastAllocated 节点剩余资源量(越空闲越高分) weight: 1
NodeResourcesMostAllocated 节点资源利用率(越满越高分) weight: 1
ImageLocality 节点上已存在的镜像 weight: 1
InterPodAffinity Pod 间亲和性与反亲和性 weight: 1

2.5 扩展点执行顺序与插件组合

调度框架的扩展点执行顺序是严格固定的,但每个扩展点内的插件执行顺序由配置文件决定。下图展示了多个插件在同一个扩展点内的协作方式:

Filter 扩展点多插件协作流程 Filter 扩展点多插件协作示例 节点列表 NodeUnschedulable NodeResourcesFit PodTopologySpread 可用节点 所有 Filter 插件均通过 → 节点进入 Score 阶段 失败 节点被排除

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

调度框架为插件提供了完整的生命周期管理机制。每个插件在调度器启动时被初始化,在调度过程中被调用,在调度器关闭时被清理。插件可以维护内部状态,但需要注意线程安全性,因为调度器会并发处理多个 Pod 的调度请求。

最佳实践:开发自定义插件时,应遵循以下原则:
1. 插件应尽量无状态,避免在插件内部维护共享状态
2. 如果必须维护状态,使用同步机制(如互斥锁)保护共享数据
3. 插件的执行时间应控制在毫秒级别,避免阻塞调度主流程
4. 优先使用 PreFilter 和 PreScore 进行预处理,减少 Filter 和 Score 阶段的计算量

2.7 扩展点选择策略

在实际调优中,选择合适的扩展点实现自定义逻辑至关重要。下表总结了不同场景下的扩展点选择建议:

场景 推荐扩展点 原因
修改 Pod 调度顺序 QueueSort 唯一控制调度队列排序的扩展点
添加新的过滤条件 Filter 专门用于节点过滤,执行效率高
优化节点选择策略 Score 通过打分机制影响节点选择
处理调度失败 PostFilter 在过滤失败后执行抢占或重试逻辑
资源预留与绑定 Reserve / Bind 控制资源预留和绑定行为
调度决策审批 Permit 实现调度准入控制
扩展点选择原则:尽量选择与目标功能最匹配的扩展点,避免在错误的扩展点中实现不相关的逻辑。例如,不要在 Score 扩展点中实现过滤逻辑,这会导致不必要的计算开销。