diff --git a/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/index.md b/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/index.md new file mode 100644 index 0000000000..2729fae71c --- /dev/null +++ b/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/index.md @@ -0,0 +1,267 @@ +--- +layout: blog +title: "Kubernetes v1.32:QueueingHint 为优化 Pod 调度带来了新的可能" +date: 2024-12-12 +slug: scheduler-queueinghint +Author: > + [Kensei Nakada](https://github.com/sanposhiho) (Tetrate.io) +translator: > + [Xin Li](https://github.com/my-git9) (DaoCloud) +--- + + + + +Kubernetes [调度器](/zh-cn/docs/concepts/scheduling-eviction/kube-scheduler/)是为新 +Pod 选择运行节点的核心组件,调度器会**逐一**处理这些新 Pod。 +因此,集群规模越大,调度器的吞吐量就越重要。 + +多年来,Kubernetes SIG Scheduling 通过多次增强改进了调度器的吞吐量。 +本博客文章描述了 Kubernetes v1.32 中对调度器的一项重大改进: +一个名为 **QueueingHint** 的[调度上下文元素](/zh-cn/docs/concepts/scheduling-eviction/scheduling-framework/#extension-points)。 +本页面提供了关于调度器的背景知识,并解释了 QueueingHint 如何提升调度吞吐量。 + + +## 调度队列 + +调度器将所有未调度的 Pod 存储在一个名为**调度队列**的内部组件中。 + +调度队列由以下数据结构组成: + +- **ActiveQ**:保存新创建的 Pod 或准备重试调度的 Pod。 +- **BackoffQ**:保存准备重试但正在等待退避期结束的 Pod。退避期取决于调度器对该 Pod 执行的不成功调度尝试次数。 +- **无法调度的 Pod 池**:保存调度器不会尝试调度的 Pod,原因可能包括以下几点: + - 调度器之前尝试调度这些 Pod 但未能成功。自那次尝试以来,集群没有发生任何使得这些 Pod 可以被调度的变化。 + - 这些 Pod 被 [PreEnqueue 插件](/zh-cn/docs/concepts/scheduling-eviction/pod-scheduling-readiness/#configuring-pod-schedulinggates)阻止进入调度周期, + 例如,它们具有一个[调度门控](/zh-cn/docs/concepts/scheduling-eviction/pod-scheduling-readiness/#configuring-pod-schedulinggates),并被调度门控插件阻止。 + + +## 调度框架和插件 + +Kubernetes 调度器的实现遵循 Kubernetes 的[调度框架](/zh-cn/docs/concepts/scheduling-eviction/scheduling-framework/)。 + +并且,所有的调度特性都是以插件的形式实现的 +(例如,[Pod 亲和性](/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity)是在 +`InterPodAffinity` 插件中实现的。) + + +调度器按照称为**周期**的阶段来处理待调度的 Pod,具体如下: + +1. **调度周期(Scheduling cycle)**:调度器从调度队列的 activeQ 组件中**逐一**取出待调度的 Pod。 + 对于每个 Pod,调度器会运行来自每个调度插件的过滤/评分逻辑。然后,调度器决定最适合该 Pod 的节点, + 或者决定当前无法调度该 Pod。 + + 如果调度器决定一个 Pod 无法被调度,该 Pod 将进入调度队列的无法调度的 Pod + 池(Unschedulable Pod Pool)组件。然而,如果调度器决定将 Pod 放置到某个节点上, + 该 Pod 将进入绑定周期(Binding cycle)。 + +2. **绑定周期(Binding cycle)**:调度器将节点分配决策传达给 Kubernetes API 服务器。 + 这一操作将 Pod 绑定到选定的节点。 + + +除了少数例外情况,大多数未调度的 Pod 在每次调度周期后都会进入无法调度的 Pod 池。 +无法调度的 Pod 池组件至关重要,因为调度周期是逐个处理 Pod 的。 +如果调度器需要不断重试放置那些无法调度的 Pod,而不是将这些 Pod 分载到无法调度的 Pod 池中, +将会在这些 Pod 上浪费很多调度周期。 + + +## 使用 QueueingHint 改进 Pod 调度重试 + +无法调度的 Pod 仅在集群发生可能允许调度器将这些 Pod 放置到节点上的变化时, +才会重新移入调度队列的 ActiveQ 或 BackoffQ 组件。 + +在 v1.32 之前,每个插件通过 `EnqueueExtensions`(`EventsToRegister`)注册哪些集群变化 +(称为**集群事件**,即集群中的对象创建、更新或删除)可以解决其失败情况。当某个插件在之前的调度周期中拒绝了某个 Pod 后, +调度队列会在出现该插件注册的事件时重试该 Pod 的调度。 + +此外,我们还拥有一个名为 `preCheck` 的内部特性,它基于 Kubernetes 核心调度约束进一步过滤事件以提高效率; +例如,`preCheck` 可以在节点状态为 `NotReady` 时过滤掉与节点相关的事件。 + + +然而,这些方法存在两个问题: + +- 基于事件的重新排队过于宽泛,可能会导致毫无来由的调度重试。 + - 新调度的 Pod **可能**解决 `InterPodAffinity` 失败的问题,但并非所有新 Pod 都能做到。 + 例如,如果创建了一个新的 Pod,但该 Pod 没有与无法调度的 Pod 的 `InterPodAffinity` 匹配的标签, + 则该 Pod 仍然无法被调度。 +- `preCheck` 依赖于 in-tree 插件的逻辑,并且不适用于自定义插件,如在问题 + [#110175](https://github.com/kubernetes/kubernetes/issues/110175) 中所述。 + + +在这里,QueueingHints 发挥了作用;QueueingHint 订阅特定类型的集群事件,并决定每个传入的事件是否可以使 Pod 变得可调度。 + +例如,考虑一个名为 `pod-a` 的 Pod,它具有必需的 Pod 亲和性。`pod-a` 在调度周期中被 +`InterPodAffinity` 插件拒绝,因为没有节点上有现有的 Pod 符合 `pod-a` 的 Pod 亲和性规约。 + + +{{< figure src="queueinghint1.svg" alt="显示调度队列和被 InterPodAffinity 插件拒绝的 pod-a 的图示" caption="显示调度队列和被 InterPodAffinity 插件拒绝的 pod-a 的图示" >}} + +`pod-a` 移入无法调度的 Pod 池 (Unschedulable Pod Pool)。调度队列记录了导致 Pod +调度失败的插件。对于 `pod-a`,调度队列记录了 `InterPodAffinity` 插件拒绝了该 Pod。 + + + +`pod-a` 在 `InterPodAffinity` 失败被解决之前将永远不会被调度。 +有一些情景可以解决这一失败,例如,一个现有的运行中的 Pod 获取了标签更新并符合 Pod 亲和性要求。 +在这种情况下,`InterPodAffinity` 插件的 `QueuingHint` 回调函数会检查集群中发生的每一个 Pod 标签更新。 +然后,如果一个 Pod 的标签更新符合 `pod-a` 的 Pod 亲和性要求,`InterPodAffinity` 插件的 +`QueuingHint` 会提示调度队列将 `pod-a` 重新移入 ActiveQ 或 BackoffQ 组件。 + +{{< figure src="queueinghint2.svg" alt="显示调度队列和由 InterPodAffinity QueuingHint 移动的 pod-a 的图示" caption="显示调度队列和由 InterPodAffinity QueuingHint 移动的 pod-a 的图示" >}} + + +## QueueingHint 的历史及 v1.32 中的新变化 + +在 SIG Scheduling,我们自 Kubernetes v1.28 开始就致力于 QueueingHint 的开发。 + +尽管 QueueingHint 并不是面向用户的特性,我们在最初添加此特性时还是实现了 `SchedulerQueueingHints` +特性门控作为安全措施。在 v1.28 中,我们实验性地为几个 in-tree 插件实现了 QueueingHints,并将该特性门控默认启用。 + + +然而,用户报告了一个内存泄漏问题,因此我们在 v1.28 的一个补丁版本中禁用了该特性门控。从 v1.28 到 v1.31, +我们一直在其余的 in-tree 插件中继续开发 QueueingHint,并修复相关 bug。 + +在 v1.32 中,我们再次默认启用了这一特性。我们完成了所有插件中 QueueingHints 的实现,并且找到了内存泄漏的原因! + +我们感谢所有参与此特性开发的贡献者,以及那些报告和调查早期问题的用户。 + + +## 参与其中 + +这些特性由 Kubernetes [SIG Scheduling](https://github.com/kubernetes/community/tree/master/sig-scheduling) 管理。 + +请加入我们并分享你的反馈。 + + +## 如何了解更多? + +- [KEP-4247:为调度队列中的高效重新排队实现每插件回调函数](https://github.com/kubernetes/enhancements/blob/master/keps/sig-scheduling/4247-queueinghint/README.md) diff --git a/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/queueinghint1.svg b/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/queueinghint1.svg new file mode 100644 index 0000000000..d2dd2a6cb9 --- /dev/null +++ b/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/queueinghint1.svg @@ -0,0 +1,4 @@ + + + +
unsched
unsched
FailedBy:
PodAffinity
FailedBy:...
Queueing Hint    
Queueing Hint    
EventHandler
EventHandl...
ActiveQ
ActiveQ
BackoffQ
BackoffQ
Cluster events
Cluster events
PreEnqueue   
PreEnqueue   
Scheduling Queue
Scheduling Queue
Cluster events
Cluster events
Cluster events
Cluster events
PodAffinity
"I'm in charge of requeueing pod-a"
PodAffinity...
Go to the scheduling cycle
Go to the...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/queueinghint2.svg b/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/queueinghint2.svg new file mode 100644 index 0000000000..9f7c65682f --- /dev/null +++ b/content/zh-cn/blog/_posts/2024-12-12-scheduler-queueinghint/queueinghint2.svg @@ -0,0 +1,4 @@ + + + +
unsched
unsched
FailedBy:
PodAffinity
FailedBy:...
Queueing Hint    
Queueing Hint    
EventHandler
EventHandl...
ActiveQ
ActiveQ
BackoffQ
BackoffQ
PodUpdated
PodUpdated
PreEnqueue   
PreEnqueue   
Scheduling Queue
Scheduling Queue
PodAffinity
"Oh, this event shows that an existing Pod gets a new label which matches with PodA's PodAffinity!"
PodAffinity...
Go to the scheduling cycle
Go to the...
Text is not SVG - cannot display
\ No newline at end of file