K8s调度插件原理的关键,不是“调度器找一台资源足够的节点”这么简单,而是Pod先进入队列,再经过过滤、打分、预留、许可和绑定等阶段。理解这些阶段后,Pod Pending、亲和性不生效、污点无法容忍、拓扑分布异常等问题都会更容易定位。
调度框架把一次调度拆成多个扩展点
kube-scheduler的调度框架可以理解为一条可插拔的决策流水线。每个扩展点处理不同问题:有的负责排序,有的负责快速计算约束,有的排除不可用节点,有的给可用节点打分,有的控制绑定前后的状态。

图1:kube-scheduler插件扩展点把Pod调度拆成队
在 Kubernetes专题 中,调度器属于控制面核心组件。它不直接运行容器,而是为未绑定节点的Pod选择目标节点,并把选择结果写回API Server。真正创建容器的动作仍由节点侧组件继续完成。
可以先用两条线理解调度过程:
- 调度周期:从队列取出Pod,依次经过QueueSort、PreFilter、Filter和Score,先排除不能运行的节点,再从候选节点中选出更合适的目标。
- 绑定周期:围绕目标节点执行Reserve、Permit、PreBind、Bind和PostBind,处理预留、等待、绑定前准备、最终写入和后置清理。
- 失败回滚:如果绑定前后某个阶段失败,调度框架会通过Unreserve等机制撤销预留状态,避免后续Pod继续受到错误状态影响。
Filter之前:队列和预计算先决定起点
调度并不是所有Pod同时竞争。QueueSort会决定Pod从调度队列中被取出的顺序,PreFilter则为后续Filter准备可复用的上下文。例如,它可以提前计算资源请求、亲和性条件或拓扑相关状态,减少后面每个节点重复计算的成本。
这两个阶段常被忽略,但它们会影响排查思路:如果Pod长时间没有被处理,问题可能在队列、优先级、调度器负载或调度配置;如果进入Filter后大面积失败,再去看节点约束和插件结果更有意义。
Pending Pod
↓ QueueSort
调度队列排序
↓ PreFilter
准备过滤阶段所需状态
↓ Filter
排除不符合条件的节点
Filter:先排除不能运行的节点
Filter阶段负责回答“这个Pod能不能放到这个节点上”。只要某个节点不满足硬约束,就会被排除。常见条件包括资源不足、节点选择器不匹配、亲和性不满足、污点不能容忍、端口冲突、卷绑定受限、拓扑约束不满足等。
| Filter关注点 | 典型信号 | 排查方向 |
|---|---|---|
| 资源请求 | CPU、内存、扩展资源不足 | 看Pod request和节点可分配资源 |
| 节点标签 | `nodeSelector`或节点亲和性不匹配 | 对比Pod约束和节点标签 |
| 污点容忍 | 节点有taint但Pod无toleration | 检查taint和toleration语义 |
| 存储约束 | 卷无法在目标节点挂载 | 看PV、StorageClass和拓扑区域 |
| 拓扑分布 | 分布约束无法满足 | 检查拓扑键、maxSkew和已有副本 |
Filter失败通常不是“调度器坏了”
当事件里出现类似“没有节点满足条件”的提示时,优先把它当作约束不满足,而不是调度器故障。Filter阶段只负责排除不可用节点,它不会为了满足业务目标自动放宽硬约束。

图2:Filter先缩小候选节点范围,Score再在可用节点中
Score:在可运行节点里选更合适的节点
Filter之后,剩下的节点都“能运行”这个Pod,但不一定都一样合适。Score阶段负责给候选节点打分,再经过NormalizeScore等处理形成最终排序。它关注的是偏好和优化目标,而不是硬性可用性。
Score常见影响因素包括资源利用均衡、镜像本地性、Pod亲和/反亲和偏好、拓扑分散、节点优先级策略等。对业务来说,Score决定的是“放哪台更好”,而不是“能不能放”。
排查Score问题时要避免两个误区:
- 误区一:Pod调度到了某台节点,就说明那台节点资源最空。实际可能是多个插件加权后的结果。
- 误区二:偏好没有实现,就是配置不生效。偏好类规则通常可以被其他打分因素抵消。
如果你关注更宽的工作负载编排和调度治理,可以把本文与 容器编排 相关内容结合起来看:调度插件解释单次Pod决策,编排层还要处理副本、升级、队列和资源池。
Reserve、Permit和PreBind:绑定前还有状态关口
选出目标节点后,并不代表调度已经结束。Reserve会为Pod在目标节点上预留调度状态;Permit可以让Pod进入等待或批准流程;PreBind用于在真正绑定前完成额外准备,例如存储或外部系统协同。
这些阶段解释了一个常见现象:Pod通过了过滤和打分,但仍可能在绑定前失败、等待或回滚。调度框架需要在“已经选中节点”和“最终写入绑定结果”之间,保留处理复杂状态的空间。
Score选出节点
↓ Reserve
记录预留状态
↓ Permit
允许、拒绝或等待
↓ PreBind
执行绑定前准备
↓ Bind
写入Pod到Node的绑定关系
Bind失败要区分写入失败和前置失败
Bind阶段负责把Pod绑定到目标节点,通常表现为向API Server写入绑定结果。如果失败发生在Bind之前,应回看Reserve、Permit或PreBind;如果失败发生在Bind本身,则要检查API写入、对象状态变化、并发竞争或目标节点状态变化。
PostBind和失败回滚:一次调度需要闭环
PostBind用于绑定完成后的处理,例如记录事件、清理状态或通知相关插件。与此同时,如果某些阶段失败,调度框架还需要执行Unreserve等回滚动作,避免预留状态残留。
这类闭环机制让调度器可以处理更复杂的资源协同:比如存储预绑定、队列准入、外部容量管理或自定义调度扩展。对运维排查来说,它也提醒我们:不要只看最终Pending状态,还要看事件链路和调度器日志中失败发生的阶段。

图3:Pod Pending排查可以按调度插件阶段映射到资源、
把Pod Pending映射到调度插件阶段
理解K8s调度插件原理后,排查Pod Pending可以更有层次。不要一上来就扩容节点或删除Pod,先判断失败阶段。
| 现象 | 更可能关联阶段 | 优先检查 |
|---|---|---|
| Pod长时间排队 | QueueSort或调度器负载 | 优先级、队列状态、调度器健康 |
| 全部节点不匹配 | Filter | 资源、标签、污点、存储、拓扑约束 |
| 调度到非预期节点 | Score | 打分权重、偏好规则、资源均衡策略 |
| 选中节点后等待 | Permit或PreBind | 准入等待、外部容量、存储准备 |
| 绑定失败或反复重试 | Bind或对象竞争 | API写入、节点状态、Pod对象变更 |
事件、日志和配置要一起看
单看Pod事件只能看到结果,未必能看到插件阶段细节;单看调度器配置又不一定能复现现场。建议把三类信息合并判断:Pod事件说明用户侧现象,调度器日志说明阶段和插件原因,调度配置说明插件是否启用、权重如何设置。
kubectl describe pod <pod-name> -n <namespace>
kubectl get nodes --show-labels
kubectl describe node <node-name>
kubectl logs -n kube-system deploy/kube-scheduler --tail=200
自定义插件适合解决什么问题
自定义调度插件适合处理标准调度能力无法准确表达的场景,例如特殊硬件、业务优先级、队列准入、外部资源容量、许可证数量、机房级约束或复杂亲和策略。但它不适合替代基础资源配置和标签治理。
引入自定义插件前建议先确认:
- 标准的node affinity、taint/toleration、topology spread是否已经足够。
- 需求是硬约束还是偏好,应该放在Filter还是Score阶段。
- 插件失败时是否会影响所有Pod调度,是否有降级策略。
- 调度结果是否可观测,平台团队能否解释每次决策。
如果需求只是“让某类Pod优先去某类节点”,通常先用标签、亲和性和污点容忍更稳妥。只有当策略涉及外部状态、复杂评分或组织级准入时,才值得考虑插件化扩展。
小结
K8s调度插件原理可以概括为:Filter决定哪些节点不能用,Score在可用节点里排序,Reserve、Permit、PreBind和Bind保证选中节点到最终绑定之间的状态可控。理解这条链路后,调度问题就不再只是“资源够不够”的单一判断。
对平台团队来说,最重要的是把插件阶段与可观测信号对应起来。能说清失败发生在Filter、Score还是Bind附近,才能决定是改Pod约束、调节点标签、调度器配置,还是处理外部资源协同。
常见问题
1. K8s调度插件原理和普通调度算法有什么区别?
普通调度算法常被理解为一次性选择最优节点,而Kubernetes调度框架把过程拆成多个扩展点。这样做的好处是不同插件可以在不同阶段参与决策,例如Filter处理硬约束,Score处理偏好,Bind处理最终绑定动作。
2. Filter和Score哪个更影响Pod Pending?
Pod Pending更常见地与Filter阶段相关,因为没有节点通过硬约束时,Pod无法进入有效打分结果。但Score也可能导致调度结果“不符合预期”。判断时先看事件是否提示没有可用节点,再进一步看打分偏好和调度器配置。
3. 为什么节点资源看起来足够,Pod仍然调度不上?
资源只是Filter阶段的一部分。节点标签、亲和性、污点容忍、卷拓扑、端口、拓扑分布约束等都可能让节点被排除。建议把Pod约束、节点标签、PV拓扑和事件原因放在一起检查,而不是只看CPU和内存。
4. 自定义调度插件应该放在Filter还是Score?
如果规则表示“不满足就不能运行”,更适合Filter;如果规则表示“满足更好,但不是必须”,更适合Score。设计插件前应先把业务需求拆成硬约束和偏好,否则容易把可调度问题变成全局阻断问题。
5. Bind阶段失败和 kubelet 启动失败是一回事吗?
不是。Bind阶段失败意味着调度器还没有成功把Pod绑定到目标节点;kubelet启动失败通常发生在Pod已经绑定到节点之后,例如镜像、挂载、运行时或探针问题。排查时先看Pod是否已有`nodeName`,再判断问题属于调度阶段还是节点执行阶段。