Pods 可以有优先级(Priority)。 优先级体现的是当前 Pod 与其他 Pod 相比的重要程度。如果 Pod 无法被调度,则 调度器会尝试抢占(逐出)低优先级的 Pod,从而使得悬决的 Pod 可被调度。

警告:
在一个并非所有用户都可信任的集群中,一个有恶意的用户可能创建优先级最高的 Pod,从而导致其他 Pod 被逐出或者无法调度。 管理员可以使用 ResourceQuota 来避免用户创建高优先级的 Pod。

如何使用优先级与抢占

要使用优先级与抢占特性:

  1. 添加一个或多个PriorityClass对象
  2. 创建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
    8
    apiVersion: kubescheduler.config.k8s.io/v1alpha1
    kind: KubeSchedulerConfiguration
    algorithmSource:
    provider: DefaultProvider

    ...

    disablePreemption: true

    PriorityClass

    PriorityClass 是一种不属于任何名字空间的对象,定义的是从优先级类名向优先级整数值的映射。优先级类名称用 PriorityClass 对象的元数据的 name 字段指定。 优先级整数值在必须提供的 value 字段中指定。 优先级值越大,优先级越高。 PriorityClass 对象的名称必须是合法的 DNS 子域名 且不可包含 system- 前缀。

PriorityClass 对象可以设置数值小于等于 10 亿的 32 位整数。 更大的数值保留给那些通常不可被抢占或逐出的系统 Pod。 集群管理员应该为每个优先级值映射创建一个 PriorityClass 对象。

PriorityClass 对象还有两个可选字段:globalDefaultdescription。 前者用来表明此 PriorityClass 的数值应该用于未设置 priorityClassNamePod。 系统中只能存在一个 globalDefault 设为真的 PriorityClass 对象。 如果没有 PriorityClass 对象的 globalDefault 被设置,则未设置 priorityClassNamePod 的优先级为 0。

description 字段可以设置任意字符串值。其目的是告诉用户何时该使用该 PriorityClass

关于 Pod 优先级与现有集群的说明

  • 如果你要升级一个不支持 Pod 优先级的集群,现有 Pod 的有效优先级都被视为 0。

  • 向集群中添加 globalDefault 设置为 truePriorityClass 不会改变现有 Pod 的优先级。新添加的 PriorityClass 值仅适用于 PriorityClass 被添加之后 新建的 Pod

  • 如果你要删除 PriorityClass,则使用所删除的 PriorityClass 名称的现有 Pod 都 不会受影响,但是你不可以再创建使用该 PriorityClass 名称的新 Pod

PriorityClass 示例

1
2
3
4
5
6
7
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

配置 preemptionPolicy: NeverPod 在调度队列中会被放在低优先级的 Pod 的前面,但是它们不可以抢占其他 Pod。 非抢占 Pod 会在调度队列中等待调度,直到有足够空闲资源时才被调度。 非抢占 Pod 与其他 Pod 一样,也受调度器回退(Back-off)机制影响。 换言之,如果调度器尝试调度这些 Pod 时发现它们无法调度,它们会被再次尝试,并且 重试的频率会被降低,这样可以使得其他优先级较低的 Pod 有机会在它们之前被调度。

非抢占 Pod 仍有可能被其他高优先级的 Pod 抢占。

preemptionPolicy 默认取值为 PreemptLowerPriority,这会使得该 PriorityClassPod 能够抢占低优先级的 Pod(这也是当前的默认行为)。 如果 preemptionPolicy 被设置为 Never,则该 PriorityClass 下的 Pod 都是非抢占的。

使用 preemptionPolicy 字段要求启用 NonPreemptingPriority 特性门控。

一种示例应用场景是数据科学负载。 用户可能希望所提交的 Job 比其他负载的优先级都高,但又不希望因为抢占运行中的 Pod 而丢弃现有工作。 只要集群中”自然地”释放出足够的资源,配置了 preemptionPolicy: Never 的高优先级 Job 可以在队列中其他 Pod 之前获得调度机会。
这正是之前调研需要的设计,尤其对那种在线服务来说,当我配置了 capacityScheduling 的调度插件之后,我要保障跨namespace 还回借出的资源,同时还能够实现类似于延时驱逐的概念,此处的逻辑是对pods进行基于PriorityClass以及time的排序,但是在每个namespace内部又不能因为高优先级的pod导致资源饥饿,使得其他业务pod一直获取不到资源

非抢占 PriorityClass 示例

1
2
3
4
5
6
7
8
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 字段来填充优先级整数值。如果所指定优先级类不存在,则 Pod 被拒绝。

下面的 YAML 是一个 Pod 配置,使用了前面例子中创建的 PriorityClass。 优先级准入控制器检查 Pod 的规约并将 Pod 优先级解析为 1000000。

1
2
3
4
5
6
7
8
9
10
11
12
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,尝试将其调度到某 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 中的 nominatedNodeNamenodeName 并不总是相同。 此外,如果调度器抢占了节点 N 上的 Pod,但接下来出现优先级比 P 还高的 Pod 要被 调度,则调度器会把节点 N 让给新的优先级更高的 Pod。如果发生了这种情况,调度器 会清除 Pod PnominatedNodeName。通过清除操作,调度器使得 Pod P 可以尝试 抢占别的节点上的 Pod