Pod 优先级和抢占
Pod 可以有优先级。优先级表示 Pod 相对于其他 Pod 的重要性。如果 Pod 无法被调度,调度器会尝试抢占(驱逐)低优先级的 Pod,以便使得待处理 Pod 可以被调度。
警告
在并非所有用户都可信的集群中,恶意用户可能会创建具有最高优先级的 Pod,导致其他 Pod 被驱逐或无法被调度。管理员可以使用 ResourceQuota 来阻止用户创建高优先级的 Pod。
有关详细信息,请参阅默认限制 PriorityClass 消耗。
如何使用优先级和抢占
要使用优先级和抢占,请执行以下操作:
添加一个或多个 PriorityClass。
创建 Pod,并将 `priorityClassName` 设置为其中一个添加的 PriorityClass。当然,你不需要直接创建 Pod;通常,你会将 `priorityClassName` 添加到 Deployment 等集合对象的 Pod 模板中。
继续阅读以获取有关这些步骤的更多信息。
注意
Kubernetes 已经提供了两个 PriorityClass:`system-cluster-critical` 和 `system-node-critical`。这些是常见的类,用于确保关键组件始终首先被调度。PriorityClass
PriorityClass 是一个非命名空间对象,它定义了从优先级类名称到优先级整数值的映射。该名称在 PriorityClass 对象的元数据中的 `name` 字段中指定。该值在必需的 `value` 字段中指定。值越高,优先级越高。PriorityClass 对象的名称必须是有效的DNS 子域名,并且不能以 `system-` 为前缀。
一个 PriorityClass 对象可以具有小于或等于 10 亿的任何 32 位整数值。这意味着 PriorityClass 对象的值范围为 -2147483648 到 1000000000(包括)。较大的数字保留给表示关键系统 Pod 的内置 PriorityClass。集群管理员应为每个他们想要的此类映射创建一个 PriorityClass 对象。
PriorityClass 还有两个可选字段:`globalDefault` 和 `description`。`globalDefault` 字段表示该 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 [stable]
具有 `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 后,你可以创建在规范中指定其中一个 PriorityClass 名称的 Pod。优先级准入控制器使用 `priorityClassName` 字段并填充优先级的整数值。如果找不到优先级类,则 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 不一定会调度到“提名节点”。调度器总是尝试“提名节点”,然后才迭代任何其他节点。被抢占的受害者 Pod 在被抢占后会获得它们的优雅终止期。如果调度器在等待受害者 Pod 终止期间有另一个节点变得可用,调度器可能会使用另一个节点来调度 Pod P。因此,Pod 规范的 `nominatedNodeName` 和 `nodeName` 不总是相同的。此外,如果调度器抢占了节点 N 上的 Pod,但随后一个优先级高于 Pod P 的 Pod 到来,调度器可能会将节点 N 分配给新的高优先级 Pod。在这种情况下,调度器会清除 Pod P 的 `nominatedNodeName`。通过这样做,调度器使得 Pod P 可以在另一个节点上抢占 Pod。
抢占的局限性
抢占受害者的优雅终止
当 Pod 被抢占时,受害者会获得它们的优雅终止期。它们有足够的时间来完成工作并退出。如果它们不退出,它们将被杀死。这个优雅终止期在调度器抢占 Pod 和待处理 Pod (P) 可以调度到 Node (N) 之间造成了一个时间间隔。在此期间,调度器会继续调度其他待处理的 Pod。随着受害者退出或被终止,调度器会尝试调度等待队列中的 Pod。因此,调度器抢占受害者和 Pod P 被调度之间通常会有一个时间间隔。为了最大限度地减少这个间隔,可以将低优先级 Pod 的优雅终止期设置为零或一个较小的数字。
支持 PodDisruptionBudget,但不保证
PodDisruptionBudget (PDB) 允许应用程序所有者限制复制应用程序在自愿中断时同时宕机的 Pod 数量。Kubernetes 在抢占 Pod 时支持 PDB,但尊重 PDB 是尽力而为的。调度器会尝试找到在抢占时不会违反其 PDB 的受害者,但如果找不到此类受害者,抢占仍会发生,低优先级 Pod 将被移除,即使它们的 PDB 被违反。
低优先级 Pod 上的 Pod 间亲和性
仅当这个问题的答案为是时,才考虑对一个节点进行抢占:“如果所有优先级低于待处理 Pod 的 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` 字段来指定。然后,优先级的整数值会被解析并填充到 `podSpec` 的 `priority` 字段中。
要解决此问题,你可以更改这些 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 集合所在的节点。但是,如果这些 Pod 具有在抢占后会违反的 PodDisruptionBudget,那么调度器可能会选择另一个具有更高优先级 Pod 的节点。
当存在多个可用于抢占的节点且上述任何情况都不适用时,调度器会选择优先级最低的节点。
Pod 优先级与服务质量之间的交互
Pod 优先级和 QoS 类别 是两个正交的功能,它们之间很少有交互,并且没有默认的限制根据 Pod 的 QoS 类别设置其优先级。调度器的抢占逻辑在选择抢占目标时不会考虑 QoS。抢占会考虑 Pod 优先级,并尝试选择一组优先级最低的目标。只有在移除最低优先级 Pod 不足以让调度器调度抢占者 Pod,或者最低优先级 Pod 受到 `PodDisruptionBudget` 保护时,才会考虑抢占更高优先级的 Pod。
kubelet 使用优先级来确定节点压力驱逐的 Pod 顺序。你可以使用 QoS 类别来估计 Pod 最有可能被驱逐的顺序。kubelet 根据以下因素对 Pod 进行驱逐排序:
- 资源匮乏使用量是否超过请求量
- Pod 优先级
- 相对于请求量的资源使用量
有关详细信息,请参阅kubelet 驱逐的 Pod 选择。
当 Pod 的使用量未超过其请求量时,kubelet 节点压力驱逐不会驱逐 Pod。如果优先级较低的 Pod 未超过其请求量,它将不会被驱逐。而优先级较高但超过其请求量的另一个 Pod 可能会被驱逐。
下一步
- 阅读有关结合使用 ResourceQuota 和 PriorityClass 的信息:默认限制 PriorityClass 消耗
- 了解Pod 中断
- 了解API 启动的驱逐
- 了解节点压力驱逐