kubernetes 之Pod 优先级与抢占
Pods 可以有优先级(Priority)。 优先级体现的是当前 Pod 与其他 Pod 相比的重要程度。如果 Pod 无法被调度,则 调度器会尝试抢占(逐出)低优先级的 Pod,从而使得悬决的 Pod 可被调度。
警告:
在一个并非所有用户都可信任的集群中,一个有恶意的用户可能创建优先级最高的 Pod,从而导致其他 Pod 被逐出或者无法调度。 管理员可以使用 ResourceQuota 来避免用户创建高优先级的 Pod。
如何使用优先级与抢占
要使用优先级与抢占特性:
- 添加一个或多个
PriorityClass对象 - 创建
Pod或者Deployment对象的时候加上其关联的PriorityClass对象,在模板中添加priorityClassName描述。说明: Kubernetes 发行时已经带有两个 PriorityClasses:system-cluster-critical 和 system-node-critical。 这些优先级类是公共的,用来 确保关键组件总是能够先被调度.
抢占能力是通过kube-scheduler的标志disablePreemption来控制的,该标志默认为false。 如果你在了解上述提示的前提下仍希望禁用抢占,可以将disablePreemption设置为true。
这一选项只能通过组件配置来设置,无法通过命令行选项这种较老的形式设置。 下面是禁用抢占的组件配置示例:1
2
3
4
5
6
7
8apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
provider: DefaultProvider
...
disablePreemption: truePriorityClass
PriorityClass是一种不属于任何名字空间的对象,定义的是从优先级类名向优先级整数值的映射。优先级类名称用PriorityClass对象的元数据的 name 字段指定。 优先级整数值在必须提供的value字段中指定。 优先级值越大,优先级越高。 PriorityClass 对象的名称必须是合法的 DNS 子域名 且不可包含 system- 前缀。
PriorityClass 对象可以设置数值小于等于 10 亿的 32 位整数。 更大的数值保留给那些通常不可被抢占或逐出的系统 Pod。 集群管理员应该为每个优先级值映射创建一个 PriorityClass 对象。
PriorityClass 对象还有两个可选字段:globalDefault 和 description。 前者用来表明此 PriorityClass 的数值应该用于未设置 priorityClassName 的 Pod。 系统中只能存在一个 globalDefault 设为真的 PriorityClass 对象。 如果没有 PriorityClass 对象的 globalDefault 被设置,则未设置 priorityClassName 的 Pod 的优先级为 0。
description 字段可以设置任意字符串值。其目的是告诉用户何时该使用该 PriorityClass。
关于 Pod 优先级与现有集群的说明
如果你要升级一个不支持
Pod优先级的集群,现有Pod的有效优先级都被视为 0。向集群中添加
globalDefault设置为true的PriorityClass不会改变现有Pod的优先级。新添加的PriorityClass值仅适用于PriorityClass被添加之后 新建的Pod。如果你要删除
PriorityClass,则使用所删除的PriorityClass名称的现有Pod都 不会受影响,但是你不可以再创建使用该PriorityClass名称的新Pod。
PriorityClass 示例
1 | apiVersion: scheduling.k8s.io/v1 |
非抢占式的 PriorityClass
配置 preemptionPolicy: Never 的 Pod 在调度队列中会被放在低优先级的 Pod 的前面,但是它们不可以抢占其他 Pod。 非抢占 Pod 会在调度队列中等待调度,直到有足够空闲资源时才被调度。 非抢占 Pod 与其他 Pod 一样,也受调度器回退(Back-off)机制影响。 换言之,如果调度器尝试调度这些 Pod 时发现它们无法调度,它们会被再次尝试,并且 重试的频率会被降低,这样可以使得其他优先级较低的 Pod 有机会在它们之前被调度。
非抢占 Pod 仍有可能被其他高优先级的 Pod 抢占。
preemptionPolicy 默认取值为 PreemptLowerPriority,这会使得该 PriorityClass 的 Pod 能够抢占低优先级的 Pod(这也是当前的默认行为)。 如果 preemptionPolicy 被设置为 Never,则该 PriorityClass 下的 Pod 都是非抢占的。
使用 preemptionPolicy 字段要求启用 NonPreemptingPriority 特性门控。
一种示例应用场景是数据科学负载。 用户可能希望所提交的 Job 比其他负载的优先级都高,但又不希望因为抢占运行中的 Pod 而丢弃现有工作。 只要集群中”自然地”释放出足够的资源,配置了 preemptionPolicy: Never 的高优先级 Job 可以在队列中其他 Pod 之前获得调度机会。
这正是之前调研需要的设计,尤其对那种在线服务来说,当我配置了capacityScheduling的调度插件之后,我要保障跨namespace还回借出的资源,同时还能够实现类似于延时驱逐的概念,此处的逻辑是对pods进行基于PriorityClass以及time的排序,但是在每个namespace内部又不能因为高优先级的pod导致资源饥饿,使得其他业务pod一直获取不到资源
非抢占 PriorityClass 示例
1 | apiVersion: scheduling.k8s.io/v1 |
Pod 优先级
在已经创建了一个或多个 PriorityClass 对象之后,你就可以创建 Pod 并在其规约中 指定这些 PriorityClass 的名字之一。优先级准入控制器使用 priorityClassName 字段来填充优先级整数值。如果所指定优先级类不存在,则 Pod 被拒绝。
下面的 YAML 是一个 Pod 配置,使用了前面例子中创建的 PriorityClass。 优先级准入控制器检查 Pod 的规约并将 Pod 优先级解析为 1000000。
1 | apiVersion: v1 |
优先级对 Pod 调度顺序的影响
当集群启用了 Pod 优先级时,调度器会基于 Pod 的优先级来排序悬决的 Pod。 新 Pod 会被放在调度队列中较低优先级的其他悬决 Pod 前面。 因此,优先级较高的 Pod 在其调度需求被满足的前提下会比优先级低的 Pod 先被调度。 如果优先级较高的 Pod 无法被调度,调度器会继续尝试调度其他较低优先级的 Pod。
抢占
Pod 被创建时会被放入一个队列中等待调度。调度器从队列中选择 Pod,尝试将其调度到某 Node 上。 如果找不到能够满足 Pod 所设置需求的 Node,就会触发悬决 Pod 的抢占逻辑。 假定 P 是悬决的 Pod,抢占逻辑会尝试找到一个这样的节点,在该节点上移除一个或者多个 优先级比 P 低的 Pod 后,P 就可以被调度到该节点。如果调度器能够找到这样的节点, 该节点上的一个或者多个优先级较低的 Pod 就会被逐出。当被逐出的 Pod 从该节点上 消失时,P 就可以调度到此节点。
暴露给用户的信息
当 Pod P 在节点 N上抢占了一个或多个 Pod 时,Pod P 的状态中的nominatedNodeName 字段 会被设置为节点 N 的名字。此字段有助于调度器跟踪为 P 所预留的资源,同时也给用户提供了其集群中发生的抢占的信息。
请注意,Pod P 不一定会被调度到其 “nominated node(提名节点)”。 当选定的 Pod 被抢占时,它们都会有其体面终止时限(Graceful Termination Period)。 如果在调度器等待选定的(被牺牲的)Pod 终止期间有新的节点可用,调度器会使用其他 节点来调度 Pod P。因此,Pod 中的 nominatedNodeName 和 nodeName 并不总是相同。 此外,如果调度器抢占了节点 N 上的 Pod,但接下来出现优先级比 P 还高的 Pod 要被 调度,则调度器会把节点 N 让给新的优先级更高的 Pod。如果发生了这种情况,调度器 会清除 Pod P 的 nominatedNodeName。通过清除操作,调度器使得 Pod P 可以尝试 抢占别的节点上的 Pod。




