2、调度框架基础:Scheduling Framework 的 Plugin 机制、Queue、Cache、Scheduler Cycle
Kubernetes 调度器(kube-scheduler)在 v1.19 之后引入了 Scheduling Framework,将调度流程拆解为可插拔的扩展点。理解这一框架是排查调度一致性问题的前提。本节聚焦四个核心概念:Plugin 机制、调度队列(Queue)、调度缓存(Cache)以及调度周期(Scheduler Cycle)中的关键阶段。
2.1 Plugin 机制:可插拔的调度扩展点
Scheduling Framework 将调度流程抽象为多个扩展点(Extension Points),每个扩展点允许用户通过 Plugin 注入自定义逻辑。Plugin 通过 kube-scheduler --config 配置文件注册,支持多级评分与过滤。
| 扩展点 | 阶段 | 典型 Plugin 示例 | 作用 |
|---|---|---|---|
| QueueSort | 队列排序 | PrioritySort | 定义 Pod 在调度队列中的排序规则 |
| PreFilter | 预过滤 | NodeResourcesFit | 检查 Pod 是否满足集群基本资源条件 |
| Filter | 过滤 | NodeName、TaintToleration | 筛选出可调度节点(硬约束) |
| PostFilter | 后过滤 | DefaultPreemption | 无可用节点时触发抢占 |
| PreScore | 预评分 | NodeResourcesFit | 为评分阶段准备节点信息 |
| Score | 评分 | NodeResourcesBalancedAllocation | 对过滤后的节点打分(软约束) |
| NormalizeScore | 评分归一化 | ImageLocality | 将评分结果归一化到 [0,100] |
| Reserve | 预留 | VolumeBinding | 在缓存中预留资源,防止并发冲突 |
| Permit | 许可 | InterPodAffinity | 等待外部条件或批准,支持超时与拒绝 |
| PreBind | 预绑定 | NodeAffinity | 绑定前执行准备工作 |
| Bind | 绑定 | DefaultBinder | 将 Pod 与 Node 绑定(写入 etcd) |
| PostBind | 绑定后 | NodeResourcesFit | 绑定后清理或记录 |
关键理解:Plugin 的注册顺序影响调度结果。例如,NodeResourcesFit 同时出现在 Filter 和 Score 中,但 Filter 阶段先执行,Score 阶段后执行。若 Filter 阶段未通过,Score 阶段不会触发。
2.2 调度队列(Queue):Pod 的等待与排序
调度队列是 Pod 进入调度周期的第一站。kube-scheduler 内部维护三种队列:
- activeQ:存放等待调度的 Pod,按 QueueSort Plugin 排序(默认按优先级)。
- backoffQ:存放调度失败的 Pod,等待重试(指数退避)。
- unschedulableQ:存放因节点资源不足或污点无法调度的 Pod,等待集群事件触发重新入队。
一致性排查要点:当集群资源变更(如节点新增、Pod 删除)后,检查 unschedulableQ 中的 Pod 是否被正确移动到 activeQ。若未移动,可能导致 Pod 长时间处于 Pending 状态。
2.3 调度缓存(Cache):节点状态的快照
调度缓存(SchedulerCache)是 kube-scheduler 内部维护的节点资源视图。它通过监听 API Server 的 Node、Pod 事件实时更新,但存在短暂的不一致窗口。
| 缓存组件 | 存储内容 | 更新方式 |
|---|---|---|
| NodeInfo | 节点资源总量、已分配资源、Pod 列表 | Node/Pod 事件触发 |
| PodInfo | Pod 的请求资源、亲和性、污点容忍 | Pod 事件触发 |
| AssumedPod | 已通过 Reserve 但未 Bind 的 Pod | Reserve 阶段写入 |
风险提示:Cache 与 API Server 之间的数据不一致是调度一致性问题的主要来源。例如,Cache 中节点资源已更新,但 API Server 尚未同步,导致调度器做出错误决策。排查时需对比 kube-scheduler --v=5 日志中的 Cache 快照与 kubectl describe node 的输出。
2.4 Scheduler Cycle:Filter → Score → Reserve → Permit 核心流程
一个完整的调度周期(Scheduling Cycle)包含以下关键阶段。以下 SVG 展示了从 Pod 入队到绑定完成的完整流程。
2.5 关键阶段详解:Filter、Score、Reserve、Permit
2.5.1 Filter(过滤)
Filter 阶段执行硬约束检查。所有 Filter Plugin 必须全部通过,Pod 才能进入下一阶段。常见的 Filter 包括:
- NodeName:检查 Pod 是否指定了
spec.nodeName。 - NodeUnschedulable:检查节点是否被标记为
unschedulable。 - TaintToleration:检查 Pod 是否能容忍节点的污点。
- NodeResourcesFit:检查节点剩余资源是否满足 Pod 请求。
2.5.2 Score(评分)
Score 阶段对通过 Filter 的节点进行打分,分数范围 [0, 100]。调度器选择总分最高的节点。常见评分策略:
- NodeResourcesBalancedAllocation:优先选择资源分配更均衡的节点。
- ImageLocality:优先选择已缓存所需镜像的节点。
- InterPodAffinity:根据 Pod 亲和性/反亲和性打分。
2.5.3 Reserve(预留)
Reserve 阶段在调度缓存中为 Pod 预留资源。这是防止并发调度冲突的关键步骤。若 Reserve 失败(如资源已被其他 Pod 预留),调度器会回退并重试。
2.5.4 Permit(许可)
Permit 阶段允许外部条件控制调度是否继续。例如,等待资源配额批准或准入 Webhook 响应。Permit 支持三种结果:
- 批准(Approved):继续执行 Bind。
- 拒绝(Denied):Pod 进入失败回退队列。
- 等待(Wait):Pod 进入等待状态,超时后自动拒绝。
一致性复核要点:在资源变更场景下,重点关注 Reserve 阶段是否成功更新 Cache。若 Reserve 成功但后续 Bind 失败,Cache 中的 AssumedPod 可能导致资源泄漏,需通过 kube-scheduler --v=5 日志中的 Cache after scheduling 信息进行校验。
2.6 调度周期与绑定周期的分离
Scheduling Framework 将调度周期(Scheduling Cycle)与绑定周期(Binding Cycle)分离。调度周期是串行的(同一时间只调度一个 Pod),而绑定周期是并发的。这种设计提高了吞吐量,但也引入了一致性风险:
- 调度周期:Filter → Score → Reserve → Permit(串行,加锁)。
- 绑定周期:PreBind → Bind → PostBind(并发,无锁)。
当绑定周期中的 Pod 因 API Server 延迟而长时间未完成 Bind 时,调度周期可能基于过时的 Cache 做出决策。这是资源变更后调度不一致的常见根因。
排查技巧:使用 kubectl get events --field-selector involvedObject.kind=Pod 查看 Pod 的调度事件。若出现 FailedScheduling 事件,结合 kube-scheduler --v=5 日志中的 Cache 和 Queue 状态,可快速定位是 Filter 失败还是 Reserve 冲突。