Pod 优先级与抢占
Kubernetes v1.14 [稳定]
Pod 可以具有 优先级。优先级表明一个 Pod 相对于其他 Pod 的重要性。如果 Pod 无法调度,调度器会尝试抢占(驱逐)优先级较低的 Pod,以便可以调度待处理的 Pod。
警告
在一个并非所有用户都受信任的集群中,恶意用户可能会创建具有最高优先级的 Pod,导致其他 Pod 被驱逐/无法调度。管理员可以使用 ResourceQuota 来防止用户创建具有高优先级的 Pod。
有关详细信息,请参阅默认限制 Priority Class 消费。
如何使用优先级和抢占
要使用优先级和抢占:
添加一个或多个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。如果没有 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 规约中的 nominatedNodeName
和 nodeName
并不总是相同。此外,如果调度器抢占了 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 有 PodDisruptionBudget 并且会被违反,那么调度器可能会选择另一个有更高优先级 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
字段来指定。然后解析并填充优先级整数值到 podSpec
的 priority
字段。
要解决此问题,您可以更改这些 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 进行驱逐排序:
- 匮乏资源的用量是否超过请求
- Pod 优先级
- 资源用量相对于请求量
有关详细信息,请参阅Kubelet 驱逐的 Pod 选择。
当 Pod 的用量未超过其请求时,Kubelet 节点资源压力驱逐不会驱逐这些 Pod。如果一个优先级较低的 Pod 未超过其请求,它就不会被驱逐。而另一个超过其请求的高优先级 Pod 可能会被驱逐。
下一步
- 阅读关于结合 PriorityClass 使用 ResourceQuota 的信息:默认限制 Priority Class 消费
- 了解Pod 中断
- 了解API 发起的驱逐
- 了解节点资源压力驱逐