21优先级调度:你必须掌握的Pod抢占式资源调度
文章目录
你好,我是正范。
随着我们在 Kubernetes 集群中部署越来越多的业务,势必要考虑集群的资源利用率问题。尤其是当集群资源比较紧张的时候,如果此时还要部署一些比较重要的关键业务,那么该如何去提前“抢占”集群资源,从而使得关键业务在集群中跑起来呢?
这里一个最常见的做法就是采用优先级方案。通过给 Pod 设置高优先级,让其比其他 Pod 显得更为重要,通过这种“插队”的方式优先获得调度器的调度。现在我们就来看看 Kubernetes 是如何定义这种优先级的。
PriorityClass
Kubernetes 通过对象 PriorityClass 来定义上述优先级,具体怎么定义呢?我们可以通过之前说过的 kubectl create 命令来创建这个对象:
|
|
这里我们创建了就是一个名为 high-priority 的 PriorityClass 定义,使用的 API 版本是 scheduling.k8s.io/v1。这个对象是个集群级别的定义,并不属于任何 namespace,可以被全局使用,即各个 namespace 中创建的 Pod 都能使用 PriorityClass。我们在定义这样一个 PriorityClass 对象的时候,名字不可以包含 system- 这个前缀。
关于这个数值的设置,有几点要说明。系统自定义了两个优先级值:
|
|
那么当你自己创建 PriorityClass 的时候,定义的优先级值是不能高于这里的 HighestUserDefinablePriority 的, 即 1000000000 (10 亿)。
Kubernetes 在初始化的时候就自带了两个 PriorityClasses,即 system-cluster-critical 和 system-node-critical,优先级值为 2000000000。 你可以通过下面的命令查看到这两个最高优先级的 PriorityClasse:
|
|
这是两个公共的优先级类,主要用来确保 Kubernetes 系统的关键组件或者关键插件总是能够优先被调度,比如 coredns 等。因为一旦这些关键组件、关键插件被驱逐,或者由于镜像需要升级,新建的 Pod 无法被调度,始终处于 Pending 的状态,这些都会导致整个集群异常,甚至停止工作。所以对于这些关键的组件,我们务必保证其优先级是最高的,这样才会被最先调度。我们可以来参考下 coredns 的做法,以下是截取的一部分配置:
|
|
下面我们来看看如何在 Pod 中使用 PriorityClass:通过 spec.priorityClassName 即可指定要使用的 PriorityClass:
|
|
通过 kubectl create 创建好该 Pod 后,我们查看详情可以看到 PriorityClass 的名字和优先级数值都被写入了 Pod 中:
|
|
注意,在使用的时候不允许自己去设置 spec.priority 的数值,我们只能通过 spec.priorityClassName 来设置优先级。现在,我们回过头再来观察下上面定义的这个 high-priority 的 PriorityClass 对象,里面有两个可选字段:globalDefault 和 description。
globalDefault 用来表明是否将该 PriorityClass 的数值作为默认值,并将其应用在所有未设置 priorityClassName 的 Pod 上。 既然是默认值,那么就意味着整个 Kubernetes 集群中只能存在一个 globalDefault 设为 true 的 PriorityClass 对象。
如果没有任何一个 PriorityClass 对象的 globalDefault 被设置为 true 的话,就意味着所有未设置 spec.priorityClassName 的 Pod 的优先级为 0。即便你新建了某个 PriorityClass 对象,并将其 globalDefault 设置为 true,那么也不不影响集群中的存量 Pod,只会对新建的 Pod 生效。
你可以在 description 字段中对该 PriorityClass 进行一些使用上的解释说明。
现在我们就来做个优先级抢占的测试,使用一个单节点的集群做演示,该节点的 CPU 为 2 核。先创建一个优先级值更小的名为 low-priority 的 PriorityClass,其 YAML 文件如下:
|
|
创建好了以后,通过 kubectl get 可以查看到创建的这两个 PriorityClass:
|
|
现在我们分别使用这两个 PriorityClass 创建两个 Pod。先来创建一个使用 low-priority 作为优先级的 Pod,下面是其对应的 YAML 文件:
|
|
这里我们指定了一个较大比重的 CPU 资源。我们假定集群中的节点为 2 核,把 Pod 需要的最低 CPU 需求设置为1200m,超过了 2核 CPU 总算力的一半。
同样我们使用 high-priority 作为其优先级创建一个 Pod,其对应的 YAML 文件如下:
|
|
如此一来,这两个 Pod 无法被调度到同一个只有 2 核 CPU 的节点上的,因为 2 个 Pod 加起来的 CPU 请求总量超过了 2 核。
我们可以先创建低优先级 Pod 命名为 nginx-low-pc ,可以看到该 Pod 已经成功运行,并消耗了超过一半的 CPU:
|
|
现在我们再创建高优先级 Pod 命名为 nginx-high-pc,可以看到低优先级 Pod nginx-low-pc 由于高优先级的资源抢占被驱逐,运行状态也从 Runing 状态变为了 Terminating:
|
|
最终,高优先级 Pod 成功完成了抢占,整个节点上只有它在运行。当新 Pod 被创建后,它们就进入了调度器的队列,等待被调度。调度器会根据 PriorityClass 的优先级来挑选出优先级最高的 Pod,并且尝试把它调度到一个节点上。
如果这个时候没有任何一个节点能够满足这个 Pod的所有要求,包括资源、调度偏好等要求,就会触发抢占逻辑。调度器会尝试寻找一个节点,通过移除一个或者多个比该 Pod 的优先级低的 Pod, 尝试使目标 Pod 可以被调度。
如果找到了这样一个节点,那么运行在该节点上的一个或者多个低优先级的 Pod 就会被驱逐,这样这个高优先级的 Pod 就可以调度到目标节点上了。哈哈,是不是有点“鸠占鹊巢”的感觉!
当然对于某些场景,如果你并不希望 Pod 被驱逐掉,只是希望可以优先调度,那么你可以使用非抢占式的 PriorityClass。比如这是一个非抢占式的 PriorityClass 的配置:
|
|
这个配置跟我们上面的定义基本一致,只不过多了一个字段 preemptionPolicy。这样就可以避免属于该 PriorityClass 的 Pod 抢占其他 Pod。当然你也可以通过给 kube-scheduler 设置参数禁用抢占能力。但是关键组件的 Pod 还是依赖调度器这种抢占机制,以便在集群资源压力较大时可以优先得到调度。因此,不建议你禁用抢占能力。
kube-scheduler 的抢占能力是通过 disablePreemption 这个参数来控制的,该标志默认为 false。如果你还是坚持禁用抢占,可以将 disablePreemption 设置为true,目前这一参数的配置,只能通过 KubeSchedulerConfiguration 来设置,即:
|
|
写在最后
提高集群的资源利用率最常见的做法就是采用优先级的方案。在实际使用的时候,要避免恶意用户创建高优先级的 Pod,这样会导致其他 Pod 被驱逐或者无法调度,严重影响集群的稳定性和安全性。集群管理员可以为特定用户创建特定优先级级别,来防止他们恶意使用高优先级的 PriorityClass。
到这里这节课就结束了,如果你对本节课有什么想法或者疑问,欢迎你在留言区留言,我们一起讨论。
-– ### 精选评论 ##### **8761: > 请教老师: 能不能给出一个例子来展示如何做到给某一个特定用户指定优先级
文章作者 anonymous
上次更新 2024-06-11