3. 调度框架详解:核心扩展点
Kubernetes 调度器从 v1.19 开始引入了可扩展的调度框架(Scheduling Framework),将调度流程拆解为多个独立的扩展点(Extension Points)。每个扩展点对应调度周期中的一个特定阶段,允许通过插件机制注入自定义逻辑。理解这些扩展点的执行顺序、输入输出以及典型用途,是进行调度性能调优和故障排查的基础。
3.1 扩展点全景图
调度框架共定义了 11 个扩展点,按执行顺序排列如下。其中 PreFilter、Filter、PostFilter、PreScore、Score、NormalizeScore 属于调度周期;Reserve、Permit、PreBind、Bind、PostBind 属于绑定周期。
3.2 扩展点详解
每个扩展点都有明确的职责和输入输出。下表总结了各扩展点的核心语义、典型用途和性能影响。
| 扩展点 | 所属周期 | 核心职责 | 输入 | 输出 | 典型用途 |
|---|---|---|---|---|---|
| PreFilter | 调度 | 预处理 Pod 和集群状态,生成过滤所需的数据 | Pod, NodeInfo | PreFilterResult / Status | 检查 Pod 的 QoS、拓扑约束预处理 |
| Filter | 调度 | 过滤掉不满足条件的节点 | Pod, Node | Status(通过/不通过) | 节点资源检查、污点容忍、节点选择器 |
| PostFilter | 调度 | 所有节点 Filter 失败后执行,尝试抢占 | Pod, FailedNodes | PostFilterResult / Status | 抢占调度、日志记录、指标上报 |
| PreScore | 调度 | 为 Score 阶段准备评分数据 | Pod, FilteredNodes | PreScoreResult / Status | 预计算亲和性权重、加载缓存 |
| Score | 调度 | 对过滤后的节点进行评分 | Pod, Node | Score(0-100) | 资源均衡、亲和性评分、自定义打分 |
| NormalizeScore | 调度 | 对多个 Score 插件的评分结果进行归一化 | Pod, NodeScores | NormalizedScores | 权重调整、分数范围统一 |
| Reserve | 绑定 | 在选定节点上预留 Pod 资源 | Pod, Node | Status | 资源预留、IP 分配、存储卷预挂载 |
| Permit | 绑定 | 批准或拒绝 Pod 的绑定操作 | Pod, Node | Status(批准/等待/拒绝) | 配额检查、多租户准入、全局限流 |
| PreBind | 绑定 | 执行绑定前的准备工作 | Pod, Node | Status | 存储卷绑定、网络配置、Sidecar 注入 |
| Bind | 绑定 | 将 Pod 绑定到节点(写入 etcd) | Pod, Node | Status | 默认绑定、自定义绑定策略 |
| PostBind | 绑定 | 绑定完成后的清理和通知 | Pod, Node | (无返回值) | 日志记录、指标上报、事件通知 |
3.3 关键扩展点深入分析
3.3.1 PreFilter 与 Filter:调度准入的第一道关卡
PreFilter 在 Filter 之前执行,用于预处理 Pod 和集群的全局信息。例如,NodeResourcesFit 插件在 PreFilter 阶段计算 Pod 的请求资源总量,避免在 Filter 阶段重复计算。PreFilter 返回的 PreFilterResult 可以传递给后续的 Filter 插件使用。
Filter 是调度器中最核心的扩展点,负责逐节点检查 Pod 是否满足调度条件。Filter 插件可以并行执行,每个节点独立判断。常见的 Filter 插件包括:
- NodeResourcesFit:检查节点剩余 CPU、内存是否满足 Pod 请求
- NodeName:检查 Pod 的
spec.nodeName是否匹配 - NodeUnschedulable:检查节点是否被标记为不可调度
- TaintToleration:检查 Pod 是否能容忍节点的污点
- NodeAffinity:检查节点亲和性规则
- PodTopologySpread:检查拓扑分布约束
nodeResourceFitScoringStrategy 和 nodeAffinityScoringStrategy 的缓存机制,避免每次调度都重新计算节点资源。同时,合理配置 percentageOfNodesToScore 参数(默认 50%),控制参与 Filter 的节点比例。
3.3.2 PostFilter:调度失败的兜底机制
当所有节点都未通过 Filter 检查时,调度器会触发 PostFilter 扩展点。PostFilter 的核心职责是尝试通过抢占(Preemption)为 Pod 腾出调度空间。抢占逻辑会:
- 找出优先级低于当前 Pod 的候选 Pod
- 模拟删除这些 Pod 后节点是否满足条件
- 选择最优的抢占目标并驱逐
PostFilter 插件也可以用于记录调度失败日志、上报监控指标,或者执行自定义的降级策略。
disablePreemption 参数。
3.3.3 Score 与 NormalizeScore:节点择优
Score 扩展点对通过 Filter 的节点进行评分,分数范围 0-100。调度器会运行多个 Score 插件,每个插件独立打分。常见的 Score 插件包括:
- NodeResourcesBalancedAllocation:优先选择资源分配更均衡的节点
- NodeResourcesLeastAllocated:优先选择剩余资源最多的节点
- ImageLocality:优先选择已缓存 Pod 所需镜像的节点
- InterPodAffinity:根据 Pod 间亲和性/反亲和性打分
- NodeAffinity:根据节点亲和性权重打分
NormalizeScore 在 Score 之后执行,用于将多个 Score 插件的分数进行归一化处理。默认情况下,调度器会将所有 Score 插件的分数加权求和。NormalizeScore 允许插件调整自己的分数范围,或者修改其他插件的分数权重。
3.3.4 Reserve 与 Permit:绑定周期的关键控制点
Reserve 是绑定周期的第一个扩展点,用于在选定节点上预留 Pod 所需的资源。预留操作包括:
- 更新节点的资源使用量(CPU、内存、存储)
- 分配 IP 地址(如果使用静态 IP 分配)
- 预挂载存储卷
Reserve 是幂等的,如果绑定失败,调度器会调用 Unreserve 回滚预留的资源。
Permit 是绑定周期中唯一支持等待语义的扩展点。Permit 插件可以返回三种状态:
| 状态 | 含义 | 后续行为 |
|---|---|---|
| 批准(Approved) | 允许继续执行绑定 | 进入 PreBind 阶段 |
| 等待(Wait) | 暂停绑定,等待外部条件满足 | 调度器定期检查,超时后拒绝 |
| 拒绝(Denied) | 禁止绑定,调度失败 | 触发 Unreserve 回滚,重新调度 |
permitWaitingTimeoutSeconds 配置(默认 10 秒)。
3.3.5 PreBind、Bind 与 PostBind:绑定执行与收尾
PreBind 在绑定之前执行,用于执行绑定前的准备工作。典型的 PreBind 插件包括:
- VolumeBinding:等待存储卷挂载完成
- NodePorts:分配 NodePort 端口
- InterPodAffinity:检查 Pod 间亲和性约束
Bind 是调度器的最终决策点,负责将 Pod 绑定到节点。默认的 Bind 插件会调用 Kubernetes API 创建 Pod 的 NodeName 字段。自定义 Bind 插件可以实现多集群调度、边缘节点绑定等高级功能。
PostBind 是调度周期的最后一个扩展点,用于执行绑定后的清理和通知工作。PostBind 插件没有返回值,即使执行失败也不会影响调度结果。典型用途包括:
- 记录调度延迟指标
- 发送调度事件到监控系统
- 清理临时缓存数据
3.4 扩展点执行顺序与错误处理
调度框架定义了严格的执行顺序和错误处理规则:
- 顺序执行:扩展点按 PreFilter → Filter → PostFilter → PreScore → Score → NormalizeScore → Reserve → Permit → PreBind → Bind → PostBind 的顺序执行
- 短路机制:如果某个扩展点返回错误,调度器会立即停止当前周期的执行,并触发回滚
- 回滚路径:如果绑定周期失败(Permit 拒绝、PreBind 失败、Bind 失败),调度器会调用 Unreserve 回滚 Reserve 阶段预留的资源
- 重试策略:调度失败后,Pod 会被重新放入调度队列,等待下一次调度尝试
1. 查看 Pod 的 Events:
kubectl describe pod <pod-name>2. 检查调度器日志:
kubectl logs -n kube-system <scheduler-pod>3. 分析调度框架的扩展点执行记录(启用
--v=5 日志级别)4. 使用
SchedulingFailure 事件追踪失败原因
3.5 扩展点配置示例
以下是一个自定义调度器配置的 YAML 示例,展示了如何启用和配置多个扩展点插件:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: custom-scheduler
plugins:
# 调度周期插件
preFilter:
enabled:
- name: NodeResourcesFit
- name: NodePorts
filter:
enabled:
- name: NodeResourcesFit
- name: NodeName
- name: TaintToleration
postFilter:
enabled:
- name: DefaultPreemption
preScore:
enabled:
- name: InterPodAffinity
score:
enabled:
- name: NodeResourcesBalancedAllocation
weight: 2
- name: ImageLocality
weight: 1
normalizeScore:
enabled:
- name: DefaultNormalizeScore
# 绑定周期插件
reserve:
enabled:
- name: VolumeBinding
permit:
enabled:
- name: CustomQuotaPlugin
preBind:
enabled:
- name: VolumeBinding
bind:
enabled:
- name: DefaultBinder
postBind:
enabled:
- name: MetricsReporter
pluginConfig:
- name: CustomQuotaPlugin
args:
maxConcurrentBindings: 50
permitTimeoutSeconds: 15