引言
Kubernetes 已经成为云原生时代的事实标准,然而多数工程师对它的理解仍停留在 "会写 YAML" 的层面。事实上,Kubernetes 调度器是整个系统中最具技术深度的模块之一——它融合了分布式系统、图论、约束优化、并发编程等多个领域的精华。本文将深入剖析 Kubernetes 调度器的内部机制,并以构建自定义调度器为实战目标,揭示 Pod 从 Pending 到 Running 的完整生命周期。
一、调度器架构概貌
Kubernetes 调度器(kube-scheduler)本质上是一个无限循环:从 Incoming Queue 中取出 Pending 的 Pod,为每个 Pod 选出最优节点,然后进行绑定(Bind)。这个过程看似简单,但内部包含了两个核心阶段:Scheduling Cycle 和 Binding Cycle。
Scheduling Cycle(调度周期)是串行的,包含 Filtering(过滤不满足条件的节点)和 Scoring(对可选节点评分排序)两个步骤。Filtering 阶段使用 Predicates(如 PodFitsResources、PodFitsHost)剔除不可行节点;Scoring 阶段则运行 Priority 函数(如 LeastRequestedPriority、ImageLocalityPriority)对剩余节点打分。
Binding Cycle(绑定周期)是并行的,通过 Kubernetes API 执行实际的节点绑定操作。这一设计是出于性能考虑——如果绑定也串行化,大规模的调度吞吐将受到严重制约。
二、调度队列:PriorityQueue 的三层结构
调度器的调度队列(scheduling.PriorityQueue)由三个子队列组成:
activeQ:存放等待调度的活跃 Pod,是一个堆结构(heap),按照 Pod 的优先级(PriorityClass)排序。高优先级 Pod 总是被优先调度。
podBackoffQ:存放调度失败但仍在退避期内的 Pod。退避时间从 1 秒开始按指数级增长,最大到 10 秒。Pod 被放置到此队列的前提是被某个插件reject,且集群没有任何变化。
unschedulableQ:存放因集群资源不足等原因暂时无法调度的 Pod。当集群事件(如 Node 上线、Pod 删除)触发时,这些 Pod 可被重新激活。
这种三层队列设计的精妙之处在于平衡了新 Pod 的快速响应与被拒绝 Pod 的耐心等待。
三、Scheduling Framework:插件化调度
Kubernetes 1.18 引入了 Scheduling Framework,将调度流程划分为以下扩展点(Extension Points):
QueueSort:定义 Pod 在活跃队列中的排序方式
PreFilter:在预过滤阶段进行前置计算,如校验 Pod 的某些前提条件
Filter:节点级过滤,剔除不满足硬性要求的节点
PreScore:评分前的预处理
Score:节点级打分,范围 0-100
Reserve / Unreserve:资源预留与释放
Permit:许可检查,决定 Pod 是否可被绑定、等待或拒绝
PreBind / Bind / PostBind:绑定各阶段钩子
插件化架构让开发者可以通过 Scheduler Extender 或 Framework Plugins 自定义调度逻辑,而无需修改 kube-scheduler 的源码。
四、Affinity 与 Topology Spread:多维度调度约束
Pod Affinity/Anti-Affinity 允许基于节点标签声明 Pod 与其他 Pod 的关系。比如让 Web 服务器和 Redis 在同一机架(亲和),或将数据库主备分布在不同数据中心(反亲和)。
Topology Spread Constraints 则从另一个维度保证工作负载在指定拓扑域(如 zone、rack、host)间的均匀分布。当集群中一个可用区故障时,其他可用区的 Pod 仍能提供服务。
实战中,亲和性与拓扑分散往往需要精细调度的平衡。过度追求亲和性可能导致资源碎片,过度追求分散性则可能增加延迟。
五、自定义调度器实战
除了使用默认的 kube-scheduler,Kubernetes 也允许部署自定义调度器。最简单的做法是 spec.schedulerName 字段指定调度器名称。
构建自定义调度器的核心步骤:
1. 创建调度器二进制:基于 client-go 实现调度循环,利用 Informer 机制监听 Pod 和 Node 的变更
2. 实现核心调度逻辑:可以选择实现 framework.FilterPlugin 和 framework.ScorePlugin 以复用调度框架,也可以从零开始实现自己的过滤和打分逻辑
3. 自定义调度器部署:Deployment 方式部署,将其注册为 Kubernetes Event 和 Pod 事件监听者
4. Pod 使用自定义调度器:在 Pod manifest 中设置 schedulerName: my-custom-scheduler
典型场景包括:GPU 拓扑感知调度、能耗感知调度(绿色计算)、SLA 约束调度(延迟敏感型应用)等。
六、未来演进方向
动态资源分配 (DRA):Kubernetes 1.26 引入的 Dynamic Resource Allocation,让调度器可以直接调度非 Kubernetes 管理的外部资源(如 FPGA、DPU)。
拓扑感知提示 (Topology Hints):EndpointSlice 的 hints 实现了流量路由与拓扑区域的协同,减少跨可用区流量。
调度模拟器:community 项目 scheduler-planner 提供了离线模拟调度场景的能力,支持缓存和回溯,是集群容量规划的利器。
总结
Kubernetes 调度器是一个设计精良的分布式决策系统。理解其队列结构、插件化扩展点和多维度调度约束,是从 "会用 YAML" 到 "精通调度" 的关键跃迁。当默认调度器无法满足业务需求时,自定义调度器为我们打开了一扇通往全方位资源编排自由的大门。随着 DRA 和 AI 驱动调度的发展,Kubernetes 调度器正在向更智能、更灵活的方向持续进化。

发表评论 取消回复