Pod 优先级与抢占

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

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

如何使用优先级和抢占

要使用优先级和抢占:

  1. 添加一个或多个PriorityClass

  2. 创建 Pod,并在其规约中将priorityClassName设置为先前添加的某个 PriorityClass 的名称。当然,您无需直接创建 Pod;通常,您会将 priorityClassName 添加到 Deployment 等集合对象的 Pod 模板中。

继续阅读以了解有关这些步骤的更多信息。

PriorityClass

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

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

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

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

关于 Pod 优先级和现有集群的注意事项

  • 如果您升级一个没有此特性的现有集群,现有 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 优先级

当您拥有一个或多个 PriorityClass 后,您可以在 Pod 规约中指定这些 PriorityClass 名称之一。优先级准入控制器使用 priorityClassName 字段并填充优先级的整数值。如果找不到 Priority Class,则 Pod 会被拒绝。

下面的 YAML 是一个使用前一个示例中创建的 PriorityClass 的 Pod 配置示例。优先级准入控制器检查规约并将 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 并尝试将其调度到 Node 上。如果没有找到满足 Pod 所有指定要求的 Node,则会为待处理的 Pod 触发抢占逻辑。我们将待处理的 Pod 称为 P。抢占逻辑尝试找到一个 Node,在该 Node 上移除一个或多个优先级低于 P 的 Pod 将使 P 能够调度到该 Node 上。如果找到了这样的 Node,一个或多个优先级较低的 Pod 将从该 Node 被驱逐。在这些 Pod 被移除后,P 就可以调度到该 Node 上。

用户可见信息

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

请注意,Pod P 不一定最终调度到“ nominated Node”。调度器总是优先尝试“ nominated Node”,然后再遍历其他任何 Node。受害者 Pod 被抢占后,它们会获得优雅终止周期。如果在调度器等待受害者 Pod 终止期间有另一个节点可用,调度器可能会使用其他节点来调度 Pod P。因此,Pod 规约中的 nominatedNodeNamenodeName 并不总是相同。此外,如果调度器抢占了 Node N 上的 Pod,但随后出现了一个比 Pod P 优先级更高的 Pod,调度器可能会将 Node N 分配给新的高优先级 Pod。在这种情况下,调度器会清除 Pod P 的 nominatedNodeName 字段。通过这样做,调度器使得 Pod P 可以在另一个 Node 上抢占 Pod。

抢占的限制

抢占受害者的优雅终止

当 Pod 被抢占时,受害者会获得其请求的优雅终止周期,默认为 30 秒。如果受害者 Pod 未在此期间终止,它们将被强制终止。一旦所有受害者都被移除,抢占者 Pod 就可以被调度。

当抢占者 Pod 等待受害者被移除时,可能会创建并适合同一 Node 的更高优先级 Pod。在这种情况下,调度器将调度更高优先级的 Pod,而不是抢占者。

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

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

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

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

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

跨节点抢占

假设正在考虑抢占 Node N,以便将待处理的 Pod P 调度到 N 上。Pod P 只有在另一个节点上的 Pod 被抢占后才有可能在 N 上变得可行。下面是一个例子:

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

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

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

故障排除

Pod 优先级和抢占可能产生不想要的副作用。以下是一些潜在问题及解决方法的示例。

Pod 被不必要地抢占

抢占会在资源压力下移除集群中现有的 Pod,以便为更高优先级的待处理 Pod 腾出空间。如果您错误地将高优先级赋予某些 Pod,这些意外的高优先级 Pod 可能会在您的集群中引起抢占。Pod 优先级通过在 Pod 的规约中设置 priorityClassName 字段来指定。然后解析并填充优先级整数值到 podSpecpriority 字段。

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

当 Pod 被抢占时,会为被抢占的 Pod 记录事件。抢占只应发生在集群没有足够资源容纳某个 Pod 的时候。在这种情况下,只有当待处理 Pod(抢占者)的优先级高于受害者 Pod 时,抢占才会发生。当没有待处理 Pod,或者待处理 Pod 的优先级等于或低于受害者时,不应发生抢占。如果在这种情况下发生抢占,请提交一个 issue。

Pod 被抢占了,但抢占者没有被调度

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

当抢占者 Pod 等待受害者移除时,可能会创建一个更高优先级的 Pod,并且该 Pod 适合于同一个 Node。在这种情况下,调度器将调度更高优先级的 Pod 而不是抢占者。

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

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

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

当有多个节点可用于抢占时,调度器尝试选择一组 Pod 优先级最低的节点。然而,如果这些 Pod 有 PodDisruptionBudget 并且会被违反,那么调度器可能会选择另一个有更高优先级 Pod 的节点。

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

Pod 优先级与服务质量 (QoS) 之间的交互

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

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

  1. 匮乏资源的用量是否超过请求
  2. Pod 优先级
  3. 资源用量相对于请求量

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

当 Pod 的用量未超过其请求时,Kubelet 节点资源压力驱逐不会驱逐这些 Pod。如果一个优先级较低的 Pod 未超过其请求,它就不会被驱逐。而另一个超过其请求的高优先级 Pod 可能会被驱逐。

下一步

最后修改于 2025 年 1 月 20 日下午 8:58 PST: Drop stale reviewers (58b4f374b8)