Pod 优先级和抢占
Kubernetes v1.14 [稳定]
Pod 可以有 *优先级*。优先级表示 Pod 相对于其他 Pod 的重要性。如果 Pod 无法调度,调度器会尝试抢占(驱逐)低优先级 Pod,以使待调度 Pod 的调度成为可能。
警告
在并非所有用户都值得信赖的集群中,恶意用户可以创建最高优先级的 Pod,导致其他 Pod 被驱逐/无法调度。管理员可以使用资源配额来阻止用户创建高优先级 Pod。
有关详细信息,请参阅 默认情况下限制优先级类消耗。
如何使用优先级和抢占
要使用优先级和抢占
添加一个或多个 优先级类。
创建具有
priorityClassName
设置为已添加的优先级类之一的 Pod。当然,您不需要直接创建 Pod;通常,您会在像 Deployment 这样的集合对象的 Pod 模板中添加priorityClassName
。
继续阅读以了解更多有关这些步骤的信息。
PriorityClass
优先级类是一个无命名空间对象,它定义了从优先级类名称到优先级整数值的映射。该名称在优先级类对象的元数据的 name
字段中指定。该值在所需的 value
字段中指定。值越大,优先级越高。优先级类对象的名称必须是有效的 DNS 子域名,并且不能以 system-
为前缀。
优先级类对象可以具有任何小于或等于 10 亿的 32 位整数。这意味着优先级类对象的取值范围为 -2147483648 到 1000000000(包含)。较大的数字保留给表示关键系统 Pod 的内置优先级类。集群管理员应该为他们想要创建的每个 such 映射创建一个优先级类对象。
优先级类还有两个可选字段:globalDefault
和 description
。globalDefault
字段表示此优先级类的值应用于没有 priorityClassName
的 Pod。系统中只能存在一个 globalDefault
设置为 true 的优先级类。如果不存在 globalDefault
设置的优先级类,则没有 priorityClassName
的 Pod 的优先级为零。
description
字段是一个任意字符串。它旨在告诉集群用户何时应使用此优先级类。
关于 Pod 优先级和现有集群的说明
如果您在没有此功能的情况下升级现有集群,则您现有 Pod 的优先级实际上为零。
添加
globalDefault
设置为true
的优先级类不会改变现有 Pod 的优先级。此类优先级类的值仅用于在添加优先级类后创建的 Pod。如果您删除一个优先级类,则使用已删除优先级类名称的现有 Pod 保持不变,但您无法再创建使用已删除优先级类名称的 Pod。
示例优先级类
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."
非抢占优先级类
Kubernetes v1.24 [稳定]
具有 preemptionPolicy: Never
的 Pod 将被放置在调度队列中,优先于较低优先级的 Pod,但它们不能抢占其他 Pod。一个非抢占 Pod 在等待被调度时将一直停留在调度队列中,直到有足够的资源可用,它才能被调度。非抢占 Pod 与其他 Pod 一样,会受到调度器回退的影响。这意味着如果调度器尝试这些 Pod 并且它们无法调度,它们将以较低的频率重试,从而允许其他具有较低优先级的 Pod 在它们之前被调度。
非抢占 Pod 仍然可能被其他更高优先级的 Pod 抢占。
preemptionPolicy
默认值为 PreemptLowerPriority
,这将允许该优先级类的 Pod 抢占较低优先级的 Pod(这是现有的默认行为)。如果 preemptionPolicy
设置为 Never
,则该优先级类的 Pod 将是非抢占的。
一个示例用例是数据科学工作负载。用户可能会提交他们想要优先于其他工作负载的任务,但他们不希望通过抢占正在运行的 Pod 来丢弃现有工作。具有 preemptionPolicy: Never
的高优先级任务将在足够的集群资源“自然”可用时,优先于其他排队的 Pod 被调度。
示例非抢占优先级类
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 优先级
拥有一个或多个优先级类后,您可以创建 Pod,并在它们的规范中指定其中一个优先级类名称。优先级准入控制器使用 priorityClassName
字段并填充优先级的整数。如果找不到优先级类,则 Pod 将被拒绝。
以下 YAML 是使用在上一个示例中创建的优先级类的 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 并尝试将其调度到某个节点上。如果找不到满足 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 规范的 nominatedNodeName
和 nodeName
不总是相同。此外,如果调度器在节点 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 的受害者,但如果找不到这样的受害者,抢占仍然会发生,并且较低优先级的 Pod 即使违反了它们的 PDB 也会被删除。
较低优先级 Pod 上的 Pod 间亲和性
只有当以下问题的答案为“是”时,才会将节点视为抢占对象:“如果从节点中删除所有优先级低于待调度 Pod 的 Pod,是否可以将待调度 Pod 调度到该节点?”
注意
抢占并不一定移除所有低优先级 Pod。如果挂起 Pod 可以通过移除少于所有低优先级 Pod 来调度,那么只会移除部分低优先级 Pod。即便如此,对前面问题的答案必须是肯定的。如果答案是否定的,则该节点不会被考虑抢占。如果一个挂起 Pod 与节点上的一个或多个低优先级 Pod 具有 Pod 间亲和性,那么在没有这些低优先级 Pod 的情况下,Pod 间亲和性规则将无法满足。在这种情况下,调度器不会抢占节点上的任何 Pod。而是,它会寻找另一个节点。调度器可能会找到合适的节点,也可能找不到。无法保证挂起 Pod 可以被调度。
我们建议针对此问题的解决方案是,仅对优先级相同或更高的 Pod 创建 Pod 间亲和性。
跨节点抢占
假设一个节点 N 正在被考虑抢占,以便一个挂起 Pod P 可以被调度到 N 上。P 只有在另一个节点上的 Pod 被抢占的情况下才有可能在 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 有 PodDisruptionBudget,并且如果它们被抢占,该预算将被违反,那么调度器可能会选择另一个具有更高优先级 Pod 的节点。
当存在多个可供抢占的节点,并且上述情况都不适用时,调度器选择优先级最低的节点。
Pod 优先级和服务质量之间的交互
Pod 优先级和QoS 类是两个正交的功能,它们之间的交互很少,并且没有关于根据 Pod 的 QoS 类设置 Pod 优先级的默认限制。调度器的抢占逻辑在选择抢占目标时不考虑 QoS。抢占会考虑 Pod 优先级,并尝试选择一组优先级最低的目标。只有在移除最低优先级的 Pod 不足以让调度器调度抢占者 Pod,或者最低优先级的 Pod 受 PodDisruptionBudget
保护的情况下,才会考虑抢占更高优先级的 Pod。
kubelet 使用优先级来确定节点压力驱逐的 Pod 顺序。您可以使用 QoS 类来估计 Pod 最有可能被驱逐的顺序。kubelet 根据以下因素对 Pod 进行驱逐排序
- 资源使用是否超过请求
- Pod 优先级
- 相对于请求的资源使用量
有关更多详细信息,请参阅kubelet 驱逐的 Pod 选择。
当 Pod 的使用不超过其请求时,kubelet 节点压力驱逐不会驱逐 Pod。如果优先级较低的 Pod 未超过其请求,则不会被驱逐。另一个优先级较高的 Pod 可能会超过其请求而被驱逐。
下一步
- 了解在 PriorityClasses 中使用 ResourceQuotas:默认情况下限制 Priority Class 的使用
- 了解Pod 中断
- 了解API 驱逐
- 了解节点压力驱逐