kube-scheduler 的调度流程是怎样的
Kubernetes 的 kube-scheduler 是集群的默认调度器,它的核心职责是:为一个新创建的 Pod(尚未分配节点)在集群中寻找一个最合适的 Node。
整个调度流程可以概括为三个核心步骤:过滤 (Filtering) -> 打分 (Scoring) -> 绑定 (Binding)。
以下是详细的调度流程解析:
1. 整体架构概览
现代的 kube-scheduler 基于 Scheduling Framework(调度框架) 构建。调度过程分为两个大的周期:
- 调度周期 (Scheduling Cycle):在内存中进行,为 Pod 选择节点。这是同步过程,一次只能处理一个 Pod。
- 绑定周期 (Binding Cycle):将 Pod 与节点绑定。这是异步过程。
2. 详细调度步骤
第一步:监听与入队 (Watch & Queue)
- 监听 (Informer):Scheduler 通过 API Server 的 Watch 机制,持续监听
spec.nodeName为空的 Pod。 - 优先级队列 (Scheduling Queue):
- 新发现的 Pod 会被放入内部的调度队列。
- 队列会根据 Pod 的
PriorityClass(优先级)进行排序,高优先级的 Pod 先出队。 - 队列分为:活动队列 (ActiveQ)、回退队列 (BackoffQ) 和不可调度队列 (UnschedulableQ)。
第二步:过滤阶段 (Filtering / Predicates) —— "哪些节点能用?"
Scheduler 从队列中取出一个 Pod,遍历集群中所有的 Node,排除掉不满足硬性条件的节点。如果某个节点不满足任何一个过滤条件,它就会被淘汰。
常见的过滤策略包括:
- PodFitsResources:节点剩余的 CPU/内存是否满足 Pod 的 Request?
- PodFitsHostPorts:Pod 请求的 HostPort 是否已被占用?
- MatchNodeSelector:节点的标签是否匹配 Pod 的
nodeSelector? - NoDiskConflict:Pod 请求的 Volume 是否与节点上已有的冲突?
- TaintToleration:Pod 是否容忍(Tolerate)了节点上的污点(Taint)?
- PodAffinity/AntiAffinity:是否满足 Pod 的亲和性/反亲和性规则?
结果:如果过滤后没有节点剩余,Pod 会进入 Pending 状态,并触发抢占 (Preemption) 逻辑(如果配置了高优先级)。如果有剩余节点,进入下一阶段。
第三步:打分阶段 (Scoring / Priorities) —— "哪个节点最好?"
Scheduler 对通过过滤的节点进行打分(通常是 0-100 分)。分数越高,表示该节点越适合放置该 Pod。
常见的打分策略包括:
- ImageLocality:节点上是否已经存在 Pod 需要的镜像?(有镜像的节点分更高,因为启动快)。
- LeastRequested:选择资源空闲率最高的节点(负载均衡)。
- BalancedResourceAllocation:选择 CPU 和内存使用率最均衡的节点。
- NodeAffinity:根据 NodeAffinity 的偏好(Preferred)规则打分。
- TaintToleration:根据容忍度的匹配程度打分。
结果:计算出每个候选节点的总分。
第四步:选择 (Select)
- Scheduler 选择得分最高的节点。
- 如果最高分有多个节点,则从中随机选择一个(通常是 Round-Robin)。
第五步:绑定阶段 (Binding) —— "落实分配"
一旦选定节点,Scheduler 不会直接写数据库(etcd),而是分两步走:
- Assume (乐观绑定):Scheduler 先在自己的本地缓存中将 Pod 标记为“已绑定到该节点”。这样做是为了立刻开始调度队列中的下一个 Pod,提高吞吐量,而不需要等待 API Server 的慢速响应。
- Bind (实际绑定):Scheduler 异步向 API Server 发送绑定请求,更新 Pod 的
spec.nodeName字段。- API Server 收到请求后,将结果写入 etcd。
- 目标节点上的 Kubelet 监听到 Pod 被分配给自己,开始拉取镜像、启动容器。
3. 调度框架的扩展点 (Extension Points)
为了让用户能自定义调度逻辑,Kubernetes 将上述流程细化为多个扩展点(插件机制)。开发者可以编写插件挂载到这些点上:
- QueueSort:决定队列中 Pod 的排序方式。
- PreFilter:在过滤前预处理(如检查集群整体资源)。
- Filter:对应上述的过滤阶段。
- PostFilter:核心用于抢占 (Preemption)。如果 Filter 阶段没找到节点,这里决定是否要驱逐低优先级的 Pod 来腾位置。
- PreScore:打分前的预处理。
- Score:对应上述的打分阶段。
- NormalizeScore:调整分数范围。
- Reserve:在内存中预留资源(对应 Assume)。
- Permit:允许、拒绝或等待(用于像 Gang Scheduling 这种需要一组 Pod 同时就绪的场景)。
- PreBind:绑定前的操作(如挂载网络卷)。
- Bind:执行绑定操作。
- PostBind:绑定后的通知。
总结
简单来说,kube-scheduler 的工作流就是:
排队 (Queue) -> 硬性筛选 (Filter) -> 软性打分 (Score) -> 乐观绑定 (Assume) -> 写入 API (Bind)。
右滑查看面试常问