admin管理员组

文章数量:1035783

Kubernetes 的默认调度器

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

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

  1. Pod 创建 : 用户通过 kubectl 或 API 创建 Pod
  2. API Server 处理 :
  • API Server 对 Pod 的合法性进行校验(如资源请求格式、权限检查)
  • 将 Pod 的元数据写入 etcd
  • Pod 此时处于 Pending 状态
  • 调度器监听 :
  • 调度器通过 Informer 机制监听到新创建的 Pod
  • 发现 spec.nodeName 为空的 Pod,将其标记为待调度
  • 根据优先级将 Pod 加入调度队列(如 ActiveQ
  • 调度决策 :
    1. PreFilter:预处理 Pod 信息
    2. Filter预选 :过滤不满足条件的节点
    3. PostFilter(抢占):无可用节点时的处理,触发抢占(Preemption),驱逐低优先级 Pod 以腾出资源
    4. PreScore:预处理数据,如计算拓扑分布权重
    5. Scoring优选 :对节点进行打分
    6. Reserve资源预留:临时预留节点资源,避免其他 Pod 抢占
    7. Permit审批:可阻塞等待外部条件(如人工审批、依赖资源就绪)
  • 绑定操作 :
    • PreBind:执行绑定前的操作(如挂载存储卷、配置网络)
    • Bind:向 API Server 提交 Binding 对象,设置 Pod 的 spec.nodeName
    • PostBind:清理临时数据或发送通知(如记录调度指标)
  • 节点上的 Kubelet 执行 :
  • Kubelet 监测到被分配的 Pod
  • 拉取镜像并启动容器
  • 状态上报

完整流程:

代码语言:yaml复制
用户创建 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 获取初始全量数据 + 增量事件流。
  1. 更新调度器缓存:
  • Scheduler Cache:维护集群状态的本地缓存(如节点资源余量、已调度 Pod 列表等)。
  • 事件驱动更新:根据监听到的事件(如 PodAddedNodeUpdated),实时更新缓存状态。
  1. 触发调度队列更新:
  • 待调度 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的工作内容:

代码语言:yaml复制
当新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)
  • 允许插件在不同阶段介入调度过程
  • 扩展点示例 :
  • Filter :过滤不符合条件的节点
  • Score :为节点打分
  • Reserve :预留资源
  • Bind :执行绑定操作
  • 自定义调度器 :
  • 可以完全自定义调度器
  • 通过 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

绑定后的清理或通知操作(如记录指标、触发通知)

监控、日志记录

Kubernetes 的默认调度器

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

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

  1. Pod 创建 : 用户通过 kubectl 或 API 创建 Pod
  2. API Server 处理 :
  • API Server 对 Pod 的合法性进行校验(如资源请求格式、权限检查)
  • 将 Pod 的元数据写入 etcd
  • Pod 此时处于 Pending 状态
  • 调度器监听 :
  • 调度器通过 Informer 机制监听到新创建的 Pod
  • 发现 spec.nodeName 为空的 Pod,将其标记为待调度
  • 根据优先级将 Pod 加入调度队列(如 ActiveQ
  • 调度决策 :
    1. PreFilter:预处理 Pod 信息
    2. Filter预选 :过滤不满足条件的节点
    3. PostFilter(抢占):无可用节点时的处理,触发抢占(Preemption),驱逐低优先级 Pod 以腾出资源
    4. PreScore:预处理数据,如计算拓扑分布权重
    5. Scoring优选 :对节点进行打分
    6. Reserve资源预留:临时预留节点资源,避免其他 Pod 抢占
    7. Permit审批:可阻塞等待外部条件(如人工审批、依赖资源就绪)
  • 绑定操作 :
    • PreBind:执行绑定前的操作(如挂载存储卷、配置网络)
    • Bind:向 API Server 提交 Binding 对象,设置 Pod 的 spec.nodeName
    • PostBind:清理临时数据或发送通知(如记录调度指标)
  • 节点上的 Kubelet 执行 :
  • Kubelet 监测到被分配的 Pod
  • 拉取镜像并启动容器
  • 状态上报

完整流程:

代码语言:yaml复制
用户创建 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 获取初始全量数据 + 增量事件流。
  1. 更新调度器缓存:
  • Scheduler Cache:维护集群状态的本地缓存(如节点资源余量、已调度 Pod 列表等)。
  • 事件驱动更新:根据监听到的事件(如 PodAddedNodeUpdated),实时更新缓存状态。
  1. 触发调度队列更新:
  • 待调度 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的工作内容:

代码语言:yaml复制
当新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)
  • 允许插件在不同阶段介入调度过程
  • 扩展点示例 :
  • Filter :过滤不符合条件的节点
  • Score :为节点打分
  • Reserve :预留资源
  • Bind :执行绑定操作
  • 自定义调度器 :
  • 可以完全自定义调度器
  • 通过 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

绑定后的清理或通知操作(如记录指标、触发通知)

监控、日志记录

本文标签: Kubernetes 的默认调度器