首页 > 其他分享 >21-优先级调度:你必须掌握的 Pod 抢占式资源调度

21-优先级调度:你必须掌握的 Pod 抢占式资源调度

时间:2024-01-11 13:44:07浏览次数:34  
标签:优先级 21 调度 priority nginx Pod PriorityClass

随着我们在 Kubernetes 集群中部署越来越多的业务,势必要考虑集群的资源利用率问题。尤其是当集群资源比较紧张的时候,如果此时还要部署一些比较重要的关键业务,那么该如何去提前“抢占”集群资源,从而使得关键业务在集群中跑起来呢?

这里一个最常见的做法就是采用优先级方案。通过给 Pod 设置高优先级,让其比其他 Pod 显得更为重要,通过这种“插队”的方式优先获得调度器的调度。现在我们就来看看 Kubernetes 是如何定义这种优先级的。

PriorityClass

Kubernetes 通过对象 PriorityClass 来定义上述优先级,具体怎么定义呢?我们可以通过之前说过的kubectl create命令来创建这个对象:

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."

这里我们创建了就是一个名为 high-priority 的 PriorityClass 定义,使用的 API 版本是 scheduling.k8s.io/v1。这个对象是个集群级别的定义,并不属于任何 namespace,可以被全局使用,即各个 namespace 中创建的 Pod 都能使用 PriorityClass。我们在定义这样一个 PriorityClass 对象的时候,名字不可以包含 system- 这个前缀。

关于这个数值的设置,有几点要说明。系统自定义了两个优先级值:

// HighestUserDefinablePriority is the highest priority for user defined priority classes. Priority values larger than 1 billion are reserved for Kubernetes system use.
HighestUserDefinablePriority = int32(1000000000)
// SystemCriticalPriority is the beginning of the range of priority values for critical system components.
SystemCriticalPriority = 2 * HighestUserDefinablePriority

那么当你自己创建 PriorityClass 的时候,定义的优先级值是不能高于这里的 HighestUserDefinablePriority 的, 即 1000000000 (10 亿)。

Kubernetes 在初始化的时候就自带了两个 PriorityClasses,即 system-cluster-critical 和 system-node-critical,优先级值为 2000000000。 你可以通过下面的命令查看到这两个最高优先级的 PriorityClasse:

$ kubectl get priorityclass
NAME                      VALUE        GLOBAL-DEFAULT   AGE
system-cluster-critical   2000000000   false            59d
system-node-critical      2000001000   false            59d

这是两个公共的优先级类,主要用来确保 Kubernetes 系统的关键组件或者关键插件总是能够优先被调度,比如 coredns 等。因为一旦这些关键组件、关键插件被驱逐,或者由于镜像需要升级,新建的 Pod 无法被调度,始终处于 Pending 的状态,这些都会导致整个集群异常,甚至停止工作。所以对于这些关键的组件,我们务必保证其优先级是最高的,这样才会被最先调度。我们可以来参考下 coredns 的做法,以下是截取的一部分配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
  name: coredns
  namespace: kube-system
  ...
spec:
  ...
  template:
    ...
    spec:
      ...
      priorityClassName: system-cluster-critical
      ...
status:
  ...

下面我们来看看如何在 Pod 中使用 PriorityClass:通过 spec.priorityClassName 即可指定要使用的 PriorityClass:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  priorityClassName: high-priority

通过kubectl create创建好该 Pod 后,我们查看详情可以看到 PriorityClass 的名字和优先级数值都被写入了 Pod 中:

$ kubectl describe pod nginx
Name:                 nginx
Namespace:            default
Priority:             1000000
Priority Class Name:  high-priority
...

注意,在使用的时候不允许自己去设置 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 文件如下:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority
value: 1000
globalDefault: false

创建好了以后,通过kubectl get可以查看到创建的这两个 PriorityClass:

$ kubectl get priorityclass | grep -v system
NAME                      VALUE        GLOBAL-DEFAULT   AGE
high-priority             1000000      false            30m
low-priority              1000         false            8m35s

现在我们分别使用这两个 PriorityClass 创建两个 Pod。先来创建一个使用 low-priority 作为优先级的 Pod,下面是其对应的 YAML 文件:

apiVersion: v1
kind: Pod
metadata:
 name: nginx-low-pc
spec:
 containers:
 - name: nginx
   image: nginx
   imagePullPolicy: IfNotPresent
   resources:
    requests:
     memory: "64Mi"
     cpu: "1200m"       #CPU需求设置较大
    limits:
     memory: "128Mi"
     cpu: "1300m"
 priorityClassName: low-priority     #使用低优先级

这里我们指定了一个较大比重的 CPU 资源。我们假定集群中的节点为 2 核,把 Pod 需要的最低 CPU 需求设置为1200m,超过了 2核 CPU 总算力的一半。

同样我们使用 high-priority 作为其优先级创建一个 Pod,其对应的 YAML 文件如下:

apiVersion: v1
kind: Pod
metadata:
 name: nginx-high-pc
spec:
 containers:
 - name: nginx
   image: nginx
   imagePullPolicy: IfNotPresent
   resources:
    requests:
     memory: "64Mi"
     cpu: "1200m"
    limits:
     memory: "128Mi"
     cpu: "1300m"
 priorityClassName: high-priority        #使用高优先级

如此一来,这两个 Pod 无法被调度到同一个只有 2 核 CPU 的节点上的,因为 2 个 Pod 加起来的 CPU 请求总量超过了 2 核。

我们可以先创建低优先级 Pod 命名为 nginx-low-pc ,可以看到该 Pod 已经成功运行,并消耗了超过一半的 CPU:

$ kubectl get pods
NAME           READY   STATUS    RESTARTS   AGE
nginx-low-pc   1/1     Running   0          22s
$ kubectl describe pod nginx-low-pc
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
--------           --------     ------
  cpu                1220m (61%)  1300m (65%)       #Node的CPU使用率已经过半
  memory             64Mi (1%)    128Mi (3%)
  ephemeral-storage  0 (0%)       0 (0%)

现在我们再创建高优先级 Pod 命名为 nginx-high-pc,可以看到低优先级 Pod nginx-low-pc 由于高优先级的资源抢占被驱逐,运行状态也从 Runing 状态变为了 Terminating:

$ kubectl get pods
NAME            READY   STATUS        RESTARTS   AGE
nginx-high-pc   0/1     Pending       0          7s
nginx-low-pc    0/1     Terminating   0          87s
$ kubectl get pods
NAME            READY   STATUS    RESTARTS   AGE
nginx-high-pc   1/1     Running   0          12s

最终,高优先级 Pod 成功完成了抢占,整个节点上只有它在运行。当新 Pod 被创建后,它们就进入了调度器的队列,等待被调度。调度器会根据 PriorityClass 的优先级来挑选出优先级最高的 Pod,并且尝试把它调度到一个节点上。

如果这个时候没有任何一个节点能够满足这个 Pod的所有要求,包括资源、调度偏好等要求,就会触发抢占逻辑。调度器会尝试寻找一个节点,通过移除一个或者多个比该 Pod 的优先级低的 Pod, 尝试使目标 Pod 可以被调度。

如果找到了这样一个节点,那么运行在该节点上的一个或者多个低优先级的 Pod 就会被驱逐,这样这个高优先级的 Pod 就可以调度到目标节点上了。哈哈,是不是有点“鸠占鹊巢”的感觉!

当然对于某些场景,如果你并不希望 Pod 被驱逐掉,只是希望可以优先调度,那么你可以使用非抢占式的 PriorityClass。比如这是一个非抢占式的 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."

这个配置跟我们上面的定义基本一致,只不过多了一个字段 preemptionPolicy。这样就可以避免属于该 PriorityClass 的 Pod 抢占其他 Pod。当然你也可以通过给 kube-scheduler 设置参数禁用抢占能力。但是关键组件的 Pod 还是依赖调度器这种抢占机制,以便在集群资源压力较大时可以优先得到调度。因此,不建议你禁用抢占能力。

kube-scheduler 的抢占能力是通过 disablePreemption 这个参数来控制的,该标志默认为 false。如果你还是坚持禁用抢占,可以将 disablePreemption 设置为true,目前这一参数的配置,只能通过 KubeSchedulerConfiguration 来设置,即:

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider
...
disablePreemption: true

写在最后

提高集群的资源利用率最常见的做法就是采用优先级的方案。在实际使用的时候,要避免恶意用户创建高优先级的 Pod,这样会导致其他 Pod 被驱逐或者无法调度,严重影响集群的稳定性和安全性。集群管理员可以为特定用户创建特定优先级级别,来防止他们恶意使用高优先级的 PriorityClass。

到这里这节课就结束了,如果你对本节课有什么想法或者疑问,欢迎你在留言区留言,我们一起讨论。


标签:优先级,21,调度,priority,nginx,Pod,PriorityClass
From: https://www.cnblogs.com/huangjiale/p/17958417

相关文章

  • 05-K8 Pod:最小调度单元的使用进阶及实践
    通过上一节课的学习,相信你已经知道了Pod是Kubernetes中原子化的部署单元,它可以包含一个或多个容器,而且容器之间可以共享网络、存储资源。在日常使用过程中,也应该尽量避免在一个Pod内运行多个不相关的容器,具体原因在上一节课中也已经详细阐述。在实际生产使用的过程中,通过k......
  • Oracle 21c-创建数据库
    1、创建数据库报错‘ORA-01501:CREATEDATABASE失败,ORA-01100:数据库已装载’  执行‘CREATEDATABASETest12;’时报错如下:  2、原因及正确创建数据库的方法  oracle自12C版本开始后,有了多租户的概念(PDB和CDB);可使用创建用户的方式创建数据库。,语句如下:    cre......
  • JVM 21 调优指南:如何进行JVM调优,JVM调优参数
    聊聊关于JVM21的优化指南。这篇文章将会深入探讨如何进行JVM调优,介绍一些关键的JVM调优参数,并提供12个实用的代码示例。由于篇幅较长,我会分几个部分来详细讲解本文已收录于,我的技术网站ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享JVM调优概览JVM(Java虚拟机)调优是......
  • CH9121网口配置协议及说明
    (1)结构体定义(2)通信流程详解1.通讯方式2.通讯结构体3.通讯过程①搜索②获取配置③配置④恢复出厂设置(3)相关文档下载CH9121搜素配置协议(1)结构体定义1//定义了与网络CH9121通信的基本数据结构,和配置结构,参数等23#ifndef__MODULECONFIG_H__4#define__MO......
  • 软件测试/测试开发/全日制|Pytest测试用例调度与运行
    前言Pytest是一个功能强大的Python测试框架,它具有灵活的测试用例调度和运行机制。在本文中,我们将深入了解Pytest是如何收集、选取和运行测试用例的。测试用例的收集在Pytest中,测试用例是通过函数来表示的。为了进行测试,Pytest需要收集这些测试函数。默认情况下,Pytest会在......
  • python系列教程218——生成器表达式
    声明:在人工智能技术教学期间,不少学生向我提一些python相关的问题,所以为了让同学们掌握更多扩展知识更好地理解AI技术,我让助理负责分享这套python系列教程,希望能帮到大家!由于这套python教程不是由我所写,所以不如我的AI技术教学风趣幽默,学起来比较枯燥;但它的知识点还是讲到位的了,也值......
  • Linux Debian11使用国内源安装 Podman环境
    一、Podman简介Podman是一个开源的容器运行时项目,可在大多数Linux平台上使用。Podman提供与Docker非常相似的功能。正如前面提到的那样,它不需要在你的系统上运行任何守护进程,并且它也可以在没有root权限的情况下运行。Podman可以管理和运行任何符合OCI(OpenContainerI......
  • 1212
    #include<bits/stdc++.h>usingnamespacestd;constintmaxn=1e3+9;longlongsum[maxn][maxn];intmain(){intn,m,c;cin>>n>>m>>c;for(inti=1;i<=n;i++){for(intj=1;j<=m;j++)......
  • 软件设计21
    [实验任务一]:股票提醒当股票的价格上涨或下降5%时,会通知持有该股票的股民,当股民听到价格上涨的消息时会买股票,当价格下降时会大哭一场。实验要求:1. 提交源代码;packagetest21; importjava.util.ArrayList;publicclassGufenextendsSubject{    publicvoidup()......
  • k8s 调度
    k8s调度Scheduler是kubernetes的调度器,主要的任务是把定义的pod分配到集群的节点上。听起来非常简单,但有很多要考虑的问题:公平:如何保证每个节点都能被分配资源资源高效利用:集群所有资源最大化被使用效率:调度的性能要好,能够尽快地对大批量的pod完成调度工作灵活:允许用户根据......