#yyds干货盘点# Kubernetes 你必须掌握的 Pod 抢占式资源调度(20)

网友投稿 275 2022-10-23

#yyds干货盘点# Kubernetes 你必须掌握的 Pod 抢占式资源调度(20)

随着我们在 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。

欢迎大家扫码关注,获取更多信息

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java十分钟精通反射机制原理
下一篇:谷歌SEO必备工具箱网站分享(谷歌SEO实用工具)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~