Pod 优先级和抢占

特性状态: Kubernetes v1.14 [稳定]

Pod 可以具有优先级。优先级表示 Pod 相对于其他 Pod 的重要性。如果 Pod 无法被调度,调度器会尝试抢占(驱逐)优先级较低的 Pod,以便调度待定的 Pod。

如何使用优先级和抢占

要使用优先级和抢占

  1. 添加一个或多个PriorityClasses

  2. 创建 Pod 时,将priorityClassName 设置为添加的 PriorityClasses 之一。当然,你不需要直接创建 Pod;通常,你会将 priorityClassName 添加到 Deployment 等集合对象的 Pod 模板中。

请继续阅读以获取有关这些步骤的更多信息。

PriorityClass

PriorityClass 是一个非命名空间对象,定义了从优先级类名称到优先级整数值的映射。该名称在 PriorityClass 对象的元数据的 name 字段中指定。该值在必需的 value 字段中指定。值越高,优先级越高。PriorityClass 对象的名称必须是有效的 DNS 子域名,并且不能以 system- 为前缀。

PriorityClass 对象可以具有小于或等于 10 亿的任何 32 位整数值。这意味着 PriorityClass 对象的值范围为 -2147483648 到 1000000000(包括)。较大的数字保留给表示关键系统 Pod 的内置 PriorityClasses。集群管理员应为他们想要的每个此类映射创建一个 PriorityClass 对象。

PriorityClass 还有两个可选字段:globalDefaultdescriptionglobalDefault 字段表示此 PriorityClass 的值应被用于没有 priorityClassName 的 Pod。系统中只能存在一个 globalDefault 设置为 true 的 PriorityClass。如果没有 globalDefault 设置的 PriorityClass,则没有 priorityClassName 的 Pod 的优先级为零。

description 字段是一个任意字符串。它旨在告知集群用户何时应使用此 PriorityClass。

关于 PodPriority 和现有集群的注意事项

  • 如果升级没有此特性的现有集群,则现有 Pod 的优先级实际上为零。

  • 添加 globalDefault 设置为 true 的 PriorityClass 不会更改现有 Pod 的优先级。此类 PriorityClass 的值仅用于在添加 PriorityClass 后创建的 Pod。

  • 如果删除 PriorityClass,则使用已删除 PriorityClass 名称的现有 Pod 保持不变,但你不能创建更多使用已删除 PriorityClass 名称的 Pod。

PriorityClass 示例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."

非抢占式 PriorityClass

特性状态: Kubernetes v1.24 [稳定]

具有 preemptionPolicy: Never 的 Pod 将被放置在调度队列中,位于优先级较低的 Pod 之前,但它们不能抢占其他 Pod。等待被调度的非抢占式 Pod 将保留在调度队列中,直到有足够的资源可用,并且可以被调度。非抢占式 Pod,与其他 Pod 一样,会受到调度器退避的影响。这意味着如果调度器尝试这些 Pod,但它们无法被调度,则会以较低的频率重试它们,从而允许先调度优先级较低的其他 Pod。

非抢占式 Pod 仍然可能被其他高优先级 Pod 抢占。

preemptionPolicy 默认为 PreemptLowerPriority,这将允许该 PriorityClass 的 Pod 抢占优先级较低的 Pod(与现有的默认行为相同)。如果 preemptionPolicy 设置为 Never,则该 PriorityClass 中的 Pod 将是非抢占式的。

一个示例用例是用于数据科学工作负载。用户可以提交一个作业,他们希望该作业的优先级高于其他工作负载,但不希望通过抢占正在运行的 Pod 来放弃现有工作。具有 preemptionPolicy: Never 的高优先级作业将在其他排队的 Pod 之前被调度,只要有足够的集群资源“自然地”可用。

非抢占式 PriorityClass 示例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "This priority class will not cause other pods to be preempted."

Pod 优先级

在你拥有一个或多个 PriorityClasses 后,你可以创建 Pod,并在它们的规范中指定这些 PriorityClass 名称之一。优先级准入控制器使用 priorityClassName 字段,并填充优先级的整数值。如果找不到优先级类,则 Pod 将被拒绝。

以下 YAML 是 Pod 配置的示例,该配置使用前面示例中创建的 PriorityClass。优先级准入控制器检查规范并将 Pod 的优先级解析为 1000000。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

Pod 优先级对调度顺序的影响

启用 Pod 优先级后,调度器会按优先级对挂起的 Pod 进行排序,并且挂起的 Pod 在调度队列中位于优先级较低的其他挂起 Pod 之前。因此,如果满足其调度要求,则更高优先级的 Pod 可能比优先级较低的 Pod 更早被调度。如果无法调度此类 Pod,则调度器将继续并尝试调度其他较低优先级的 Pod。

抢占

创建 Pod 后,它们会进入队列并等待被调度。调度器从队列中选取一个 Pod,并尝试将其调度到节点上。如果找不到满足 Pod 所有指定要求的节点,则会为待定的 Pod 触发抢占逻辑。让我们将待定的 Pod 称为 P。抢占逻辑尝试查找一个节点,从该节点移除一个或多个优先级低于 P 的 Pod 将使 P 能够在该节点上调度。如果找到这样的节点,则从该节点驱逐一个或多个较低优先级的 Pod。Pod 被删除后,P 可以被调度到该节点上。

用户公开的信息

当 Pod P 抢占节点 N 上的一个或多个 Pod 时,Pod P 的状态的 nominatedNodeName 字段将设置为节点 N 的名称。此字段有助于调度器跟踪为 Pod P 保留的资源,并向用户提供有关其集群中抢占的信息。

请注意,Pod P 不一定被调度到“提名节点”。调度器始终在迭代任何其他节点之前尝试“提名节点”。在被抢占的 Pod 被抢占后,它们会获得优雅终止期。如果在调度器等待被抢占的 Pod 终止时有另一个节点可用,则调度器可以使用另一个节点来调度 Pod P。因此,Pod 规范的 nominatedNodeNamenodeName 并不总是相同的。此外,如果调度器在节点 N 上抢占 Pod,但随后到达优先级高于 Pod P 的 Pod,则调度器可能会将节点 N 提供给新的更高优先级 Pod。在这种情况下,调度器会清除 Pod P 的 nominatedNodeName。通过这样做,调度器使 Pod P 有资格在另一个节点上抢占 Pod。

抢占的限制

优雅终止抢占受害者

当 Pod 被抢占时,受害者会获得其优雅终止期。他们有那么多时间来完成他们的工作并退出。如果他们没有,他们将被杀死。此优雅终止期在调度器抢占 Pod 的点与待定 Pod (P) 可以被调度到节点 (N) 的时间之间创建了一个时间间隔。同时,调度器继续调度其他待定的 Pod。随着受害者退出或被终止,调度器会尝试在待定队列中调度 Pod。因此,在调度器抢占受害者的时间点与调度 Pod P 的时间点之间通常存在一个时间间隔。为了尽量减小此间隔,可以将较低优先级 Pod 的优雅终止期设置为零或较小的数字。

支持 PodDisruptionBudget,但不保证

PodDisruptionBudget (PDB) 允许应用程序所有者限制复制应用程序中同时因自愿中断而停机的 Pod 数量。Kubernetes 在抢占 Pod 时支持 PDB,但尊重 PDB 是尽力而为。调度器会尝试查找其 PDB 不会被抢占违反的受害者,但如果找不到此类受害者,仍然会发生抢占,并且即使违反其 PDB,也会删除较低优先级的 Pod。

较低优先级 Pod 上的 Pod 间亲和性

仅当以下问题的答案为是时,才考虑对节点进行抢占:“如果从节点中删除所有优先级低于待定 Pod 的 Pod,那么是否可以将待定 Pod 调度到该节点上?”

如果待调度的 Pod 与节点上的一个或多个低优先级 Pod 存在 Pod 间亲和性,则在没有这些低优先级 Pod 的情况下,Pod 间亲和性规则将无法满足。在这种情况下,调度器不会抢占节点上的任何 Pod。相反,它会寻找另一个节点。调度器可能会找到一个合适的节点,也可能找不到。不能保证待调度的 Pod 能够被调度。

我们针对此问题的推荐解决方案是仅创建与优先级相等或更高的 Pod 之间的 Pod 间亲和性。

跨节点抢占

假设正在考虑对节点 N 进行抢占,以便将待调度的 Pod P 调度到 N 上。只有在另一个节点上的 Pod 被抢占的情况下,P 才可能在 N 上可行。以下是一个示例:

  • 正在考虑将 Pod P 调度到节点 N 上。
  • Pod Q 正在与节点 N 位于同一区域的另一个节点上运行。
  • Pod P 与 Pod Q 具有区域范围的反亲和性(topologyKey: topology.kubernetes.io/zone)。
  • Pod P 和该区域中的其他 Pod 之间没有其他反亲和性案例。
  • 为了将 Pod P 调度到节点 N 上,可以抢占 Pod Q,但调度器不会执行跨节点抢占。因此,Pod P 将被认为在节点 N 上不可调度。

如果从其节点移除 Pod Q,则 Pod 反亲和性冲突将消失,并且 Pod P 可能会被调度到节点 N 上。

如果需求足够并且我们找到性能合理的算法,我们可能会在未来的版本中考虑添加跨节点抢占。

问题排查

Pod 优先级和抢占可能会产生不希望的副作用。以下是一些潜在问题示例以及处理方法。

Pod 被不必要地抢占

抢占会从资源紧张的集群中移除现有 Pod,为更高优先级的待调度 Pod 腾出空间。如果错误地为某些 Pod 设置了高优先级,这些意外的高优先级 Pod 可能会导致集群中发生抢占。Pod 优先级通过在 Pod 的规范中设置 priorityClassName 字段来指定。然后解析优先级整数值并将其填充到 podSpecpriority 字段中。

为了解决此问题,您可以更改这些 Pod 的 priorityClassName 以使用较低优先级的类,或者将该字段留空。默认情况下,空的 priorityClassName 将解析为零。

当 Pod 被抢占时,将为被抢占的 Pod 记录事件。只有当集群没有足够的资源来容纳 Pod 时才会发生抢占。在这种情况下,只有当待调度 Pod(抢占者)的优先级高于受害者 Pod 时才会发生抢占。当没有待调度 Pod,或者当待调度 Pod 的优先级等于或低于受害者时,不得发生抢占。如果在此类情况下发生抢占,请提交问题报告。

Pod 被抢占,但抢占者未被调度

当 Pod 被抢占时,它们会收到请求的优雅终止期,默认为 30 秒。如果受害者 Pod 在此期间未终止,则会被强制终止。一旦所有受害者消失,就可以调度抢占者 Pod。

在抢占者 Pod 等待受害者消失时,可能会创建一个适合同一节点的更高优先级 Pod。在这种情况下,调度器将调度更高优先级的 Pod 而不是抢占者。

这是预期的行为:具有更高优先级的 Pod 应取代具有较低优先级的 Pod。

高优先级 Pod 在低优先级 Pod 之前被抢占

调度器尝试查找可以运行待调度 Pod 的节点。如果找不到节点,调度器会尝试从任意节点中删除低优先级的 Pod,以便为待调度的 Pod 腾出空间。如果具有低优先级 Pod 的节点不可行来运行待调度的 Pod,则调度器可能会选择另一个具有较高优先级 Pod(与另一个节点上的 Pod 相比)的节点进行抢占。受害者必须仍然具有低于抢占者 Pod 的优先级。

当有多个节点可用于抢占时,调度器会尝试选择一组优先级最低的 Pod 的节点。但是,如果此类 Pod 具有 PodDisruptionBudget,并且如果抢占它们将违反该预算,则调度器可能会选择另一个具有更高优先级 Pod 的节点。

当存在多个节点可用于抢占并且以上情况均不适用时,调度器会选择优先级最低的节点。

Pod 优先级和服务质量之间的交互

Pod 优先级和 QoS 类 是两个正交的功能,它们之间的交互很少,并且基于其 QoS 类设置 Pod 的优先级没有任何默认限制。调度器的抢占逻辑在选择抢占目标时不考虑 QoS。抢占会考虑 Pod 优先级,并尝试选择一组优先级最低的目标。只有在移除优先级最低的 Pod 不足以让调度器调度抢占者 Pod 时,或者如果优先级最低的 Pod 受 PodDisruptionBudget 保护时,才会考虑抢占更高优先级的 Pod。

kubelet 使用优先级来确定 节点压力驱逐 的 Pod 顺序。您可以使用 QoS 类来估计 Pod 最有可能被驱逐的顺序。kubelet 根据以下因素对 Pod 进行驱逐排名:

  1. 资源饥饿使用量是否超出请求
  2. Pod 优先级
  3. 相对于请求的资源使用量

有关更多详细信息,请参阅kubelet 驱逐的 Pod 选择

当 Pod 的使用量未超过其请求时,kubelet 节点压力驱逐不会驱逐 Pod。如果优先级较低的 Pod 未超出其请求,则不会被驱逐。另一个超出其请求的优先级较高的 Pod 可能会被驱逐。

下一步

上次修改时间为 2024 年 2 月 19 日下午 1:54 PST:修复调度程序部分中的尾随空格(2f298d2077)