本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 1.26:Pod 调度就绪性

Kubernetes 1.26 引入了一个新的 Pod 特性:调度门控(scheduling gates)。在 Kubernetes 中,调度门控是一些键(key),用来告诉调度器一个 Pod 何时准备好被调度。

它解决了什么问题?

当 Pod 被创建时,调度器将不断尝试为它寻找一个合适的节点。这个无限循环会一直持续,直到调度器为 Pod 找到一个节点,或者 Pod 被删除。

长时间处于不可调度状态的 Pod(例如,那些被某些外部事件阻塞的 Pod)会浪费调度周期。一个调度周期可能需要 ≅20 毫秒或更长时间,具体取决于 Pod 调度约束的复杂性。因此,在大规模场景下,这些被浪费的周期会显著影响调度器的性能。请参见下面“调度器”框中的箭头。

graph LR; pod((新 Pod))-->queue subgraph Scheduler queue(调度器队列) sched_cycle[/调度周期/] schedulable{可调度?} queue==>|弹出|sched_cycle sched_cycle==>schedulable schedulable==>|否|queue subgraph note [因不断重新调度“未就绪”的 Pod 而浪费周期] end end classDef plain fill:#ddd,stroke:#fff,stroke-width:1px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; classDef Scheduler fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; classDef note fill:#edf2ae,stroke:#fff,stroke-width:1px; class queue,sched_cycle,schedulable k8s; class pod plain; class note note; class Scheduler Scheduler;

调度门控有助于解决这个问题。它允许声明新创建的 Pod 尚未准备好进行调度。当 Pod 上存在调度门控时,调度器会忽略该 Pod,从而节省不必要的调度尝试。如果你在集群中安装了 Cluster Autoscaler,这些 Pod 也会被其忽略。

清除门控是外部控制器的责任,这些控制器知道 Pod 何时应被考虑进行调度(例如,配额管理器)。

graph LR; pod((新 Pod))-->queue subgraph Scheduler queue(调度器队列) sched_cycle[/调度周期/] schedulable{可调度?} popout{弹出?} queue==>|入队前检查|popout popout-->|是|sched_cycle popout==>|否|queue sched_cycle-->schedulable schedulable-->|否|queue subgraph note [一个控制 Pod 调度的开关] end end classDef plain fill:#ddd,stroke:#fff,stroke-width:1px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; classDef Scheduler fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; classDef note fill:#edf2ae,stroke:#fff,stroke-width:1px; classDef popout fill:#f96,stroke:#fff,stroke-width:1px; class queue,sched_cycle,schedulable k8s; class pod plain; class note note; class popout popout; class Scheduler Scheduler;

它是如何工作的?

调度门控的工作方式通常与 Finalizer 非常相似。spec.schedulingGates 字段非空的 Pod 将显示为 SchedulingGated 状态,并且其调度将被阻止。请注意,可以添加多个门控,但它们都应在 Pod 创建时添加(例如,你可以将它们作为规约的一部分或通过一个变更性质的 Webhook 来添加)。

NAME       READY   STATUS            RESTARTS   AGE
test-pod   0/1     SchedulingGated   0          10s

要清除门控,你需要更新 Pod,从其 schedulingGates 字段中移除所有项。门控不必一次性全部移除,但只有当所有门控都被移除后,调度器才会开始考虑调度该 Pod。

在底层,调度门控是作为 PreEnqueue 调度器插件实现的,这是一个新的调度器框架扩展点,在每个调度周期的开始时被调用。

使用场景

此功能实现的一个重要使用场景是动态配额管理。Kubernetes 支持ResourceQuota,但 API 服务器在你尝试创建 Pod 时强制执行配额。例如,如果一个新的 Pod 超过了 CPU 配额,它将被拒绝。API 服务器不会将 Pod 排队;因此,创建 Pod 的一方需要不断地尝试重新创建它。这要么意味着资源可用和 Pod 实际运行之间存在延迟,要么意味着由于不断的尝试而给 API 服务器和调度器带来负载。

调度门控允许外部配额管理器解决 ResourceQuota 的上述限制。具体来说,该管理器可以(使用一个变更性质的 Webhook)向集群中创建的所有 Pod 添加一个 example.com/quota-check 调度门控。当有配额启动 Pod 时,该管理器再移除该门控。

下一步是什么?

要使用此功能,必须在 API 服务器和调度器中启用 PodSchedulingReadiness 特性门控。我们非常欢迎你进行测试并告诉我们(SIG Scheduling)你的想法!

其他资源