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
。