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 @@ + + + +unschedunschedFailedBy:PodAffinityFailedBy:...Queueing Hint Queueing Hint EventHandlerEventHandl...ActiveQActiveQBackoffQBackoffQCluster eventsCluster eventsPreEnqueue PreEnqueue Scheduling QueueScheduling QueueCluster eventsCluster eventsCluster eventsCluster eventsPodAffinity"I'm in charge of requeueing pod-a"PodAffinity...Go to the scheduling cycleGo 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 @@ + + + +unschedunschedFailedBy:PodAffinityFailedBy:...Queueing Hint Queueing Hint EventHandlerEventHandl...ActiveQActiveQBackoffQBackoffQPodUpdatedPodUpdatedPreEnqueue PreEnqueue Scheduling QueueScheduling QueuePodAffinity"Oh, this event shows that an existing Pod gets a new label which matches with PodA's PodAffinity!"PodAffinity...Go to the scheduling cycleGo to the...Text is not SVG - cannot display \ No newline at end of file