3-11-6-pod优先级和抢占
Pod 可以有优先级。 优先级表示一个 Pod 相对于其他 Pod 的重要性。 如果一个 Pod 无法被调度 , 调度程序会尝试抢占(驱逐)较低优先级的 Pod , 以使悬决 Pod 可以被调度。
警告: 在一个并非所有用户都是可信的集群中 , 恶意用户可能以最高优先级创建
Pod, 导致其他Pod被驱逐或者无法被调度。 管理员可以使用ResourceQuota来阻止用户创建高优先级的Pod。
1. 如何使用优先级和抢占
要使用优先级和抢占:
新增一个或多个
PriorityClass。创建
Pod, 并将其priorityClassName设置为新增的PriorityClass。 当然你不需要直接创建Pod。通常 , 你将会添加priorityClassName到集合对象 ( 如Deployment) 的Pod模板中。
说明:
Kubernetes 已经提供了 2 个 PriorityClass : system-cluster-critical 和 system-node-critical。 这些是常见的类 , 用于确保始终优先调度关键组件
2. PriorityClass
PriorityClass 是一个无命名空间对象 , 它定义了从优先级类名称到优先级整数值的映射。 名称在 PriorityClass 对象元数据的 name 字段中指定。 值在必填的 value 字段中指定。值越大 , 优先级越高。 PriorityClass 对象的名称必须是有效的 DNS 子域名 , 并且它不能以 system- 为前缀。
PriorityClass 对象可以设置任何小于或等于 10 亿的 32 位整数值。 这意味着 PriorityClass 对象的值范围是从 -2,147,483,648 到 1,000,000,000(含)。 保留更大的数字 , 用于表示关键系统 Pod 的内置 PriorityClass。 集群管理员应该为这类映射分别创建独立的 PriorityClass 对象。
PriorityClass 还有两个可选字段 : globalDefault 和 description。
globalDefault字段表示这个PriorityClass的值应该用于没有priorityClassName的Pod。 系统中只能存在一个globalDefault设置为true的PriorityClass。 如果不存在设置了globalDefault的PriorityClass, 则没有priorityClassName的Pod的优先级为零。description字段是一个任意字符串。 它用来告诉集群用户何时应该使用此PriorityClass。
2.1 关于 PodPriority 和现有集群的注意事项
如果你升级一个已经存在的但尚未使用此特性的集群 , 该集群中已经存在的 Pod 的优先级等效于零。
添加一个将
globalDefault设置为true的PriorityClass不会改变现有Pod的优先级。 此类PriorityClass的值仅用于添加PriorityClass后创建的Pod。如果你删除了某个
PriorityClass对象 , 则使用被删除的PriorityClass名称的现有Pod保持不变 , 但是你不能再创建使用已删除的PriorityClass名称的Pod。
2.2 PriorityClass 示例
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "此优先级类应仅用于 XYZ 服务 Pod。"3. 非抢占式 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."4. Pod 优先级
在你拥有一个或多个 PriorityClass 对象之后 , 你可以创建在其规约中指定这些 PriorityClass 名称之一的 Pod。 优先级准入控制器使用 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-priorityPod 优先级对调度顺序的影响
当启用 Pod 优先级时 , 调度程序会按优先级对悬决 Pod 进行排序 , 并且每个悬决的 Pod 会被放置在调度队列中其他优先级较低的悬决 Pod 之前。 因此 , 如果满足调度要求 , 较高优先级的 Pod 可能会比具有较低优先级的 Pod 更早调度。 如果无法调度此类 Pod , 调度程序将继续并尝试调度其他较低优先级的 Pod。
5. 抢占
Pod 被创建后会进入队列等待调度。 调度器从队列中挑选一个 Pod 并尝试将它调度到某个节点上。 如果没有找到满足 Pod 的所指定的所有要求的节点 , 则触发对悬决 Pod 的抢占逻辑。 让我们将悬决 Pod 称为 P。抢占逻辑试图找到一个节点 , 在该节点中删除一个或多个优先级低于 P 的 Pod , 则可以将 P 调度到该节点上。 如果找到这样的节点 , 一个或多个优先级较低的 Pod 会被从节点中驱逐。 被驱逐的 Pod 消失后 , P 可以被调度到该节点上。
5.1 用户暴露的信息
当 Pod P 抢占节点 N 上的一个或多个 Pod 时 , Pod P 状态的 nominatedNodeName 字段被设置为节点 N 的名称。 该字段帮助调度程序跟踪为 Pod P 保留的资源 , 并为用户提供有关其集群中抢占的信息。
请注意 , Pod P 不一定会调度到" 被提名的节点(Nominated Node)" 。 调度程序总是在迭代任何其他节点之前尝试" 指定节点" 。
在 Pod 因抢占而牺牲时 , 它们将获得体面终止期。 如果调度程序正在等待牺牲者 Pod 终止时另一个节点变得可用 , 则调度程序可以使用另一个节点来调度 Pod P。 因此 , Pod 规约中的 nominatedNodeName 和 nodeName 并不总是相同。
此外 , 如果调度程序抢占节点 N 上的 Pod , 但随后比 Pod P 更高优先级的 Pod 到达 , 则调度程序可能会将节点 N 分配给新的更高优先级的 Pod。 在这种情况下 , 调度程序会清除 Pod P 的 nominatedNodeName。 通过这样做 , 调度程序使 Pod P 有资格抢占另一个节点上的 Pod。
5.2 抢占的限制
被抢占牺牲者的体面终止
当
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间亲和性规则。 在这种情况下 , 调度程序不会抢占节点上的任何Pod。 相反 , 它寻找另一个节点。调度程序可能会找到合适的节点 , 也可能不会。无法保证悬决Pod可以被调度。我们针对此问题推荐的解决方案是仅针对同等或更高优先级的
Pod设置Pod间亲和性。
5.3 跨节点抢占
假设正在考虑在一个节点 N 上执行抢占 , 以便可以在 N 上调度待处理的 Pod P。 只有当另一个节点上的 Pod 被抢占时 , P 才可能在 N 上变得可行。 下面是一个例子 :
调度器正在考虑将
Pod P调度到节点N上。Pod Q正在与节点N位于同一区域的另一个节点上运行。Pod P与Pod Q具有Zone维度的反亲和(topologyKey:topology.kubernetes.io/zone)设置。Pod P与Zone中的其他Pod之间没有其他反亲和性设置。为了在节点
N上调度Pod P, 可以抢占Pod Q, 但调度器不会进行跨节点抢占。 因此, Pod P将被视为在节点N上不可调度。
如果将 Pod Q 从所在节点中移除 , 则不会违反 Pod 间反亲和性约束 , 并且 Pod P 可能会被调度到节点 N 上。
如果有足够的需求 , 并且如果我们找到性能合理的算法 , 我们可能会考虑在未来版本中添加跨节点抢占。
6. 故障排除
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, 当它们被抢占时 , 则会违反PodDisruptionBudget, 那么调度程序可能会选择另一个具有更高优先级Pod的节点。当存在多个节点抢占且上述场景均不适用时 , 调度器会选择优先级最低的节点。
Pod优先级和服务质量之间的相互作用Pod优先级和QoS类是两个正交特征 , 交互很少 , 并且对基于QoS类设置Pod的优先级没有默认限制。 调度器的抢占逻辑在选择抢占目标时不考虑QoS。 抢占会考虑Pod优先级并尝试选择一组优先级最低的目标。 仅当移除优先级最低的Pod不足以让调度程序调度抢占式Pod, 或者最低优先级的Pod受PodDisruptionBudget保护时 , 才会考虑优先级较高的Pod。kubelet使用优先级来确定 节点压力驱逐Pod的顺序。 你可以使用QoS类来估计Pod最有可能被驱逐的顺序。kubelet根据以下因素对Pod进行驱逐排名 :对紧俏资源的使用是否超过请求值
Pod优先级相对于请求的资源使用量
当某
Pod的资源用量未超过其请求时 , kubelet 节点压力驱逐不会驱逐该Pod。 如果优先级较低的Pod的资源使用量没有超过其请求 , 则不会被驱逐。 另一个优先级较高且资源使用量超过其请求的Pod可能会被驱逐。
参考链接
调度、抢占和驱逐 - Pod 优先级和抢占 - 《Kubernetes v1.28 中文文档》 - 书栈网 · BookStack