Kubernetes 的默认调度器

news/2025/3/20 1:00:47/文章来源:https://www.cnblogs.com/rxg456/p/18782224

一. 新建的Pod如何调度到Node节点?

Kubernetes 调度过程是将 Pod 分配到集群中合适节点的过程。具体流程如下:

  1. Pod 创建 :
    用户通过 kubectl 或 API 创建 Pod

  2. API Server 处理 :

    • API Server 对 Pod 的合法性进行校验(如资源请求格式、权限检查)
    • 将 Pod 的元数据写入 etcd
    • Pod 此时处于 Pending 状态
  3. 调度器监听 :

    • 调度器通过 Informer 机制监听到新创建的 Pod
    • 发现 spec.nodeName 为空的 Pod,将其标记为待调度
    • 根据优先级将 Pod 加入调度队列(如 ActiveQ
  4. 调度决策 :

    1. PreFilter:预处理 Pod 信息
    2. Filter预选 :过滤不满足条件的节点
    3. PostFilter(抢占):无可用节点时的处理,触发抢占(Preemption),驱逐低优先级 Pod 以腾出资源
    4. PreScore:预处理数据,如计算拓扑分布权重
    5. Scoring优选 :对节点进行打分
    6. Reserve资源预留:临时预留节点资源,避免其他 Pod 抢占
    7. Permit审批:可阻塞等待外部条件(如人工审批、依赖资源就绪)
  5. 绑定操作 :

    • PreBind:执行绑定前的操作(如挂载存储卷、配置网络)
    • Bind:向 API Server 提交 Binding 对象,设置 Pod 的 spec.nodeName
    • PostBind:清理临时数据或发送通知(如记录调度指标)
  6. 节点上的 Kubelet 执行 :

    • Kubelet 监测到被分配的 Pod
    • 拉取镜像并启动容器
    • 状态上报

完整流程:

用户创建 Pod → API Server 写入 etcd → 调度器监听到 Pod → 加入调度队列 → 调度框架处理(Filter→Score→Reserve→Bind) → Kubelet 启动容器 → Pod 状态更新为 Running

二. 调度机制的工作原理

Kubernetes 调度器的核心是 两个协同工作的控制循环,分别负责状态同步和调度决策。

第一个控制循环:Informer Path(状态同步)

实时同步集群状态到调度器缓存,为调度决策提供最新数据。

  1. 监听资源变化:

    • 通过 Informer 监听 API Server 的资源变更事件(Pod、Node、PV/PVC 等)。
    • 使用 List-Watch 机制 从 API Server 获取初始全量数据 + 增量事件流。
  2. 更新调度器缓存:

    • Scheduler Cache:维护集群状态的本地缓存(如节点资源余量、已调度 Pod 列表等)。
    • 事件驱动更新:根据监听到的事件(如 PodAddedNodeUpdated),实时更新缓存状态。
  3. 触发调度队列更新:

    • 待调度 Pod 入队:当监听到 Podspec.nodeName 为空且 status.phasePending 时,将其加入调度队列(如 ActiveQ)。
    • 重调度处理:若已调度 Pod 的依赖资源发生变化(如节点宕机),触发重新入队(如加入 UnschedulableQ)。

第二个控制循环:Scheduling Path(调度决策)

从队列中取出待调度 Pod,通过多阶段流水线完成调度决策。

核心机制:自 Kubernetes 1.19 引入的 调度框架(Scheduling Framework),将调度过程拆解为多个扩展点(Extension Points)。

调度框架(Scheduling Framework)的扩展点与流程:

阶段 功能 关键插件示例
1. QueueSort 决定调度队列中 Pod 的排序顺序(如按优先级排序) PrioritySort
2. PreFilter 预处理 Pod 信息或检查前置条件(如校验资源请求合法性) PodTopologySpread(校验拓扑约束)
3. Filter 过滤不符合条件的节点(如资源不足、亲和性冲突) NodeResourcesFitNodeAffinity
4. PostFilter 当没有可用节点时,执行备选逻辑(如抢占低优先级 Pod) DefaultPreemption
5. PreScore 为 Score 阶段预处理数据(如计算拓扑分布权重) InterPodAffinity
6. Score 为通过 Filter 的节点打分(0-100 分),确定最优节点 NodeResourcesBalancedAllocation
7. Reserve 预留节点资源(防止其他 Pod 占用),此阶段后资源被视为“已分配” VolumeBinding(预留存储卷)
8. Permit 批准或拒绝调度决策(可阻塞等待外部条件满足) Approval(人工审批插件)
9. PreBind 绑定前的准备工作(如创建网络配置、挂载存储卷) VolumeBinding(执行存储卷绑定)
10. Bind 将 Pod 绑定到节点(通过 API Server 提交 Binding 对象) DefaultBinder
11. PostBind 绑定后的清理或通知操作(如更新监控指标) Metrics(记录调度耗时)

调度周期的关键细节

  1. 调度阶段(Scheduling Cycle)
    • 包含 QueueSortPreFilterFilterPostFilterPreScoreScoreReservePermit
    • 无状态计算:此阶段仅基于缓存状态进行计算,不修改集群实际状态。
  2. 绑定阶段(Binding Cycle)
    • 包含 PreBindBindPostBind
    • 异步提交:通过 API Server 异步执行绑定操作,绑定失败时触发重试或回滚。
  3. 插件机制
    • 每个扩展点可注册多个插件(例如同时使用 NodeAffinityPodTopologySpread)。
    • 插件可配置执行顺序(通过 Score 插件的权重值决定最终节点得分)。

调度Pod为例

  1. Informer Path 监听到新 Pod,将其加入 ActiveQ
  2. Scheduling Path 从队列中取出 Pod,执行 PreFilter 校验资源请求。
  3. Filter 排除资源不足的节点,Score 为剩余节点打分,选择最高分节点。
  4. Reserve 预留节点资源,Permit 批准调度。
  5. PreBind 挂载存储卷,Bind 向 API Server 提交绑定请求。
  6. 若绑定成功,PostBind 更新监控指标;若失败,Pod 重新入队。

三. Kubernetes 调度器的"Cache化"

Scheduler Cache在创建Pod的工作内容:

当新Pod创建后,整个调度过程是一个流畅的工作流。首先,通过Informer机制,调度器会收到Pod的Add事件,这是整个流程的起点。接着,Scheduler Cache会立即将这个新Pod的信息添加到内存缓存中,为后续的调度决策做好准备。随后,当调度器从队列中取出这个Pod准备进行调度时,它会创建一个缓存快照。这个快照非常重要,因为它提供了一个一致的集群状态视图,调度器就基于这个快照进行调度决策,包括节点过滤和打分。经过一系列的计算和比较后,调度器为Pod选择出最佳节点,然后执行Assume操作。这个操作会在内存缓存中预先更新Pod和节点的关联关系,是一种乐观的更新机制。Assume操作完成后,调度器并不会立即等待API Server的响应,而是会异步执行实际的绑定操作。这种设计减少了关键路径上的延迟。

Scheduler Cache 它维护了集群资源状态的内存缓存,以提高调度决策的效率。以下是其工作流程:

1. 初始化阶段

Scheduler Cache 在调度器启动时初始化,主要包括:

  • 创建 Node、Pod 等资源的内存数据结构
  • 注册 Informer 的事件处理函数
  • 初始化缓存同步机制

2. 数据填充阶段

通过 Informer 机制从 API Server 获取初始数据:

  • List-Watch 机制获取集群中所有 Node、Pod 等资源
  • 将资源信息填充到内存缓存中
  • 建立资源之间的关联关系(如 Pod 与 Node 的映射)

3. 实时更新阶段

通过 Informer 的事件处理函数实时更新缓存:

  • Add 事件:将新资源添加到缓存
  • Update 事件:更新缓存中的资源状态
  • Delete 事件:从缓存中移除资源

4. Assume 机制

调度决策做出后,使用 Assume 机制预先更新缓存:

  • 当调度器为 Pod 选择了目标 Node 后
  • 在向 API Server 发送绑定请求前,先在缓存中"假设"该 Pod 已绑定
  • 这种乐观更新机制避免了在关键路径上的 API Server 访问

5. 异步确认

Assume 后异步执行实际的绑定操作:

  • 创建 Goroutine 向 API Server 发送绑定请求
  • 如果绑定成功,Informer 会收到更新事件,确认缓存状态
  • 如果绑定失败,下一次缓存同步会修正不一致状态

6. 快照机制(1.19 后引入)

调度周期开始时创建缓存快照:

  • 快照提供了一个一致的、不可变的集群状态视图
  • 调度决策基于快照进行,避免了在决策过程中状态变化导致的问题
  • 多个调度周期可以并行执行,每个周期使用自己的快照

7. 资源跟踪

Scheduler Cache 精确跟踪各种资源:

  • 节点资源:CPU、内存、存储等
  • Pod 资源请求和限制
  • 拓扑约束和亲和性规则
  • 存储卷状态和容量

这种内存缓存机制大大提高了调度器的性能,特别是在大规模集群中,避免了频繁访问 API Server 的开销。

四. Kubernetes 调度器的“乐观”绑定的设计

在Kubernetes调度系统中,Assume操作是一种非常巧妙的性能优化设计。当调度器为Pod选择好目标节点后,不会立即向API Server发送绑定请求,而是先在内存中的Scheduler Cache里更新Pod和Node的关联信息。这种方式被称为"乐观"更新,因为它假设后续的实际绑定操作大概率会成功。

这种设计的核心优势在于避免了在关键调度路径上对API Server的远程访问。在大规模集群中,API Server可能成为性能瓶颈,尤其是在高并发调度场景下。通过Assume操作,调度器可以快速完成一个Pod的调度决策并立即处理下一个Pod,而不必等待API Server的响应。

从代码实现角度看,Assume操作通常会使用锁机制来保护Scheduler Cache的并发访问。就像编写代码中的 Lock() 一样,在更新共享数据结构时需要加锁保护。在Scheduler Cache中,当执行Assume操作时,也会使用类似的锁机制来确保数据一致性。

完成Assume操作后,调度器会创建一个异步的goroutine来执行实际的Bind操作,向API Server发送请求。如果绑定成功,一切正常;如果失败,下一次缓存同步时会修正这种不一致状态。

这种"乐观"更新策略是Kubernetes调度器性能优化的重要手段之一,它与缓存机制一起,大大提高了调度器的吞吐量和响应速度。

"乐观设计"优势 :

  • 减少锁竞争
  • 提高并发性能

五. Kubernetes 调度器的"无锁化"

Kubernetes 调度器的“无锁化”设计是其高效处理大规模集群资源调度的核心机制之一。这种设计通过避免传统锁机制(如互斥锁或读写锁)的竞争,显著提升了调度器的并发性能和可扩展性。

调度过程的状态隔离

调度器的核心职责是将 Pod 与 Node 匹配,但调度过程本身不直接修改集群状态。所有状态变更(如 Pod 绑定到节点)通过 API Server 的原子操作完成,调度器仅负责计算调度结果。这种职责分离避免了调度过程中对共享状态的直接竞争。

  • 调度周期(Scheduling Cycle):每个 Pod 的调度过程是独立的,调度器通过 Informer 监听集群状态的变化,但调度决策基于当前状态的快照(通过 SharedIndexInformer 的本地缓存),避免了频繁与 API Server 交互。
  • 绑定周期(Binding Cycle):调度结果通过 API Server 异步提交,使用乐观并发控制(Optimistic Concurrency Control, OCC)保证一致性(基于资源的 ResourceVersion)。

队列化与异步处理

调度器通过多级队列管理待调度的 Pod,不同队列之间通过优先级和调度条件隔离,避免了全局锁的需求:

  • 调度队列(Scheduling Queue)
    • ActiveQ:按优先级排序的待调度 Pod 队列(基于 PriorityClass)。
    • BackoffQ:调度失败的 Pod 进入退避队列,延迟重试。
    • UnschedulableQ:暂时无法调度的 Pod(如资源不足)暂存于此。
  • 无锁队列操作:队列内部通过原子操作(如 Go 的 heap 结构)管理顺序,无需显式加锁。

单调度器实例的串行化处理

尽管 Kubernetes 支持多调度器实例,但默认情况下:

  • 每个调度器实例独立工作,通过 Leader Election 机制确保同一时间只有一个实例处理特定绑定操作。
  • 调度器的核心逻辑(如 Filter 和 Score 阶段)是无状态的,多个实例可并行处理不同 Pod 的调度,仅绑定阶段需要协调。

事件驱动架构

调度器通过 Watch 机制 监听集群状态变化(如新 Pod 创建、Node 资源变更等),而非主动轮询:

  • 事件驱动的设计减少了不必要的状态查询,降低了对共享资源的竞争。
  • 当事件触发时,调度器仅处理受影响的 Pod 或 Node,而非全局状态。

六. Kubernetes 默认调度器的可扩展性设计

Kubernetes 默认调度器(kube-scheduler)的可扩展性设计是其核心优势之一,它允许用户在不修改核心代码的情况下,通过多种机制扩展调度能力,满足不同场景的定制化需求。

调度器提供了多种扩展机制:

  1. 调度框架 :

    • 定义了多个扩展点(Extension Points)
    • 允许插件在不同阶段介入调度过程
  2. 扩展点示例 :

    • Filter :过滤不符合条件的节点
    • Score :为节点打分
    • Reserve :预留资源
    • Bind :执行绑定操作
  3. 自定义调度器 :

    • 可以完全自定义调度器
    • 通过 Pod 的 schedulerName 字段指定

关键扩展点与功能

扩展点 功能 典型应用场景
QueueSort 定义调度队列中 Pod 的排序规则(如按优先级排序) 高优先级 Pod 优先调度
PreFilter 预处理 Pod 信息或校验前置条件(如资源请求合法性) 校验 Pod 的拓扑分布约束
Filter 过滤不符合条件的节点(资源不足、亲和性冲突等) 排除资源不足的节点
PostFilter 当无可用节点时触发备选逻辑(如抢占低优先级 Pod) 实现抢占(Preemption)机制
PreScore 预处理数据(如计算拓扑分布权重) 为 Score 阶段准备数据
Score 为节点打分(0-100 分),确定最优节点 节点资源均衡、亲和性权重
Reserve 预留节点资源(临时锁定资源,防止其他 Pod 抢占) 避免资源竞争冲突
Permit 批准或延迟调度决策(如等待外部审批或资源就绪) 人工审批、依赖资源等待
PreBind 绑定前的准备工作(如挂载存储卷、配置网络) 存储卷绑定(VolumeBinding)
Bind 将 Pod 绑定到节点(可自定义绑定逻辑) 多调度器协作、自定义绑定策略
PostBind 绑定后的清理或通知操作(如记录指标、触发通知) 监控、日志记录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/901813.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《自然语言处理实战 : 预训练模型应用及其产品化》 | PDF免费下载

《自然语言处理实战:预训练模型应用及其产品化》系统讲解了 NLP 领域的最新技术,涵盖 BERT、GPT 等预训练模型的应用,并结合实际案例展示如何将 NLP 技术产品化。适合开发者、数据科学家及 AI 从业者,助力高效落地 NLP 解决方案。点击下载 书籍信息 作者: Ankur A. Patel /…

[以太网/汽车网络] 车载服务通信(SOME/IP)设计实践 [转]

序 1 引入在SOA架构中,服务是构成系统的基本单元,它代表了系统中的某个功能或操作。服务通过明确的接口与外界进行交互,实现了功能的封装和重用。 SOA架构的核心就是服务: 它通过将应用程序划分为一系列的服务来降低系统的复杂度,提高系统的灵活性和可维护性。 在SOA中,服…

小白尖叫!DeepSeek安装竟偷占C盘?这样做路径配置 直接根治存储焦虑!

🚀 个人主页 极客小俊 ✍🏻 作者简介:web开发者、设计师、技术分享 🐋 希望大家多多支持, 我们一起学习和进步! 🏅 欢迎评论 ❤️点赞💬评论 📂收藏 📂加关注前言 之前给大家讲解了关于Ollama+DeepSeek的使用和本地部署, 有些朋友表示遇到一些问题,无法解决! …

智能工厂搭建:系统数量与选型的深度剖析

当今制造业加速迈向智能化的时代,智能工厂成为众多企业追求的目标。它宛如一座现代化的智慧堡垒,融合了先进技术与高效管理流程,能大幅提升生产效率、优化产品质量。然而,构建智能工厂并非一蹴而就,其中一个关键且容易让人困惑的问题便是:究竟要引入多少系统?搞懂这一点…

20款好用的SSH客户端工具,你在用哪个?

20款好用的SSH客户端工具,你在用哪个? 有些小伙伴购买了Linux服务器之后,不知道该用什么工具来实现本地连接,而不是每次打开服务器厂家所提供的 web 命令页面来操作。操作步骤:登录账号 - 找到服务器 - 打开web命令页面SSH工具:打开软件 - 配置连接通过SSH工具大大减少了…

Spring AOP 的实现原理

一、AOP的基本概念 将横切关注点(日志、事务、权限)从业务逻辑中分离出来,提高代码的可维护性。 下面将解释,AOP专属名词,切面、连接点、切点、通知、目标对象、代理对象:切面:切面是封装横切关注点的模块,比如日志记录。 @Aspect 修饰类,如 LoggingAspect 连接点:连…

drm study

学习过程 0319:对于任何驱动来说,buffer是最重要的,知道了buffer的创建使用这个驱动就会一半了;现在感觉是一个无头苍蝇,感觉非常复杂:数据结构非常多,之间的关系也非常复杂;不过没关系,先研究buffer通路;可以看见应用层对mmap写入的hello world,驱动中vkms_obj->…

pcie 简介及引脚定义

随着现代处理器技术的发展,在互连领域中,使用高速差分总线替代并行总线是大势所趋。与单端并行信号相比,高速差分信号可以使用更高的时钟频率,从而使用更少的信号线,完成之前需要许多单端并行数据信号才能达到的总线带宽。 PCI总线使用并行总线结构,在同一条总线上的所有…

C++ 基础(1)

0x01 第一个C++程序 #include <iostream>int main() {std::cout << "Hello World!\n"; } // std::cout 向控制台输出内容的指令 // << 输出的运算符 // "" 字符串内容的边界符 // \n 输出换行 // Hello World 输出字符…

在ubuntu系统下与开发板连接问题记录

对我所遇到的问题以及解决方法进行简单的记录在开发板与ubuntu(非虚拟机)连接之后使用lsmod查看是否连接lsusb 我的显示如下:如果可以看到自己的USB设备 那么就说明你已经安装了驱动 如果没有 请安装你的串口对应的驱动 我的驱动是CH340 没有安装的朋友可以去下面网站进行…

maven为什么发生依赖冲突?怎么解决依赖冲突?

maven为什么发生依赖冲突?怎么解决依赖冲突? 我们在开发的时候,偶尔会遇到依赖冲突的时候,一般都是NoClassDefFoundError、ClassNotFoundException、NoSuchMethodError。打开搜索框又发现有这个类,明明就是引入进来了,就是找不到,让人头疼 1. 依赖冲突场景 在maven中依赖…

unstructured

unstructured 是一个开源的 Python 库,专门用于处理非结构化数据,如从 PDF、Word 文档、HTML 文件等中提取文本内容,并将其转换为结构化格式(1)安装依赖库pip install unstructured使用textfrom unstructured.partition.auto import partitionfilename = "a.txt"…