2. 调度框架详解:插件机制与扩展点
Kubernetes 调度器从 v1.19 开始引入了 Scheduling Framework(调度框架),将调度流程拆解为多个可插拔的扩展点。调度框架的核心设计思想是:将调度决策权从内置算法中解耦,允许用户通过插件在特定阶段注入自定义逻辑,从而在不修改核心代码的前提下实现调度策略的灵活定制。
2.1 调度框架整体架构
调度框架将一次 Pod 调度过程划分为多个连续的阶段,每个阶段对应一个或多个扩展点。调度器按照固定的顺序遍历这些扩展点,并在每个扩展点处调用已注册的插件。下图展示了调度框架的核心扩展点及其执行顺序:
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 优先被调度。
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:检查污点容忍度
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 扩展点执行顺序与插件组合
调度框架的扩展点执行顺序是严格固定的,但每个扩展点内的插件执行顺序由配置文件决定。下图展示了多个插件在同一个扩展点内的协作方式:
2.6 插件生命周期与状态管理
调度框架为插件提供了完整的生命周期管理机制。每个插件在调度器启动时被初始化,在调度过程中被调用,在调度器关闭时被清理。插件可以维护内部状态,但需要注意线程安全性,因为调度器会并发处理多个 Pod 的调度请求。
1. 插件应尽量无状态,避免在插件内部维护共享状态
2. 如果必须维护状态,使用同步机制(如互斥锁)保护共享数据
3. 插件的执行时间应控制在毫秒级别,避免阻塞调度主流程
4. 优先使用 PreFilter 和 PreScore 进行预处理,减少 Filter 和 Score 阶段的计算量
2.7 扩展点选择策略
在实际调优中,选择合适的扩展点实现自定义逻辑至关重要。下表总结了不同场景下的扩展点选择建议:
| 场景 | 推荐扩展点 | 原因 |
|---|---|---|
| 修改 Pod 调度顺序 | QueueSort | 唯一控制调度队列排序的扩展点 |
| 添加新的过滤条件 | Filter | 专门用于节点过滤,执行效率高 |
| 优化节点选择策略 | Score | 通过打分机制影响节点选择 |
| 处理调度失败 | PostFilter | 在过滤失败后执行抢占或重试逻辑 |
| 资源预留与绑定 | Reserve / Bind | 控制资源预留和绑定行为 |
| 调度决策审批 | Permit | 实现调度准入控制 |