k8s优先级调度

优先级和抢占机制,解决的是 Pod 调度失败时该怎么办的问题。

正常情况下,当一个 Pod 调度失败后,它就会被暂时“搁置”起来,直到 Pod 被更新,或者集群状态发生变化,调度器才会对这个 Pod 进行重新调度。

当一个高优先级的 Pod 调度失败后,该 Pod 并不会被“搁置”,而是会“挤走”某个 Node 上的一些低优先级的 Pod 。这样就可以保证这个高优先级 Pod 的调度成功

优先级

提高集群的资源利用率最常见的做法就是采用优先级的方案。

通过给 Pod 设置高优先级,让其比其他 Pod 显得更为重要,通过这种“插队”的方式优先获得调度器的调度

PriorityClass

Kubernetes 在初始化的时候就自带了两个 PriorityClasses

1
2
3
4
[root@k8s01 ~]# kubectl get priorityclass
NAME VALUE GLOBAL-DEFAULT AGE
system-cluster-critical 2000000000 false 24d
system-node-critical 2000001000 false 24d

这是两个公共的优先级类,主要用来确保 Kubernetes 系统的关键组件或者关键插件总是能够优先被调度,比如 coredns 等。

我们在定义这样一个 PriorityClass 对象的时候,名字不可以包含 system- 这个前缀。且不能高于这里的 HighestUserDefinablePriority 的, 即 1000000000 (10 亿)。

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

high-priority.yaml

1
2
3
4
5
6
7
apiVersion: scheduling.k8s.io/v1  # 使用的 API 版本是 scheduling.k8s.io/v1。这个对象是个集群级别的定义,并不属于任何 namespace,可以被全局使用
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false # 是否将该 PriorityClass 的数值作为默认值,并将其应用在所有未设置 priorityClassName 的 Pod 上(新增 Pod ,之前的存量pod优先级默认为 0). 整个 Kubernetes 集群中只能存在一个 globalDefault 设为 true 的 PriorityClass 对象。
description: "This priority class should be used for XYZ service pods only."

low-priority.yaml

1
2
3
4
5
6
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 1000
globalDefault: false

应用这两个优先级

1
2
3
4
5
6
7
8
9
[root@k8s01 ~]# kubectl apply -f low-priority.yaml
priorityclass.scheduling.k8s.io/low-priority created
[root@k8s01 ~]# kubectl apply -f high-priority.yaml
priorityclass.scheduling.k8s.io/high-priority created

[root@k8s01 ~]# kubectl get priorityclass | grep -v system
NAME VALUE GLOBAL-DEFAULT AGE
high-priority 1000000 false 69s
low-priority 1000 false 90s

nginx-low-priority.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: nginx-low-pc
spec:
containers:
- name: nginx
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "64Mi"
cpu: "2400m" # 因为是4核,总数 4000, 2400 超过 4000 一半
limits:
memory: "128Mi"
cpu: "2500m"
priorityClassName: low-priority #使用低优先级

nginx-high-priority.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: nginx-high-pc
spec:
containers:
- name: nginx
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "64Mi"
cpu: "2400m"
limits:
memory: "128Mi"
cpu: "2500m"
priorityClassName: high-priority #使用高优先级

应用上面两个 pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@k8s01 ~]# cat /proc/cpuinfo | grep 'model name'  | wc -l
4 # 4 核
[root@k8s01 ~]# kubectl apply -f nginx-low-priority.yaml
pod/nginx-low-pc created

[root@k8s01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-low-pc 1/1 Running 0 4m33s

[root@k8s01 ~]# kubectl apply -f nginx-high-priority.yaml
pod/nginx-high-pc created

# 调度器会尝试寻找一个节点,通过移除一个或者多个比该 Pod 的优先级低的 Pod, 尝试使目标 Pod 可以被调度。 (当前 k8s01,k8s03节点已设置不能被调度)
# kubectl taint nodes k8s01 node-role.kubernetes.io/master=:NoSchedule
# kubectl taint nodes k8s03 node-role.kubernetes.io/master=:NoSchedule
[root@k8s01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-high-pc 0/1 Pending 0 7s
nginx-low-pc 0/1 Terminating 0 87s # 低优先级的被挤占
[root@k8s01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-high-pc 1/1 Running 0 12s

非抢占式

并不希望 Pod 被驱逐掉,只是希望可以优先调度

preemptionPolicy.yaml

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

kube-scheduler 的抢占能力是通过 disablePreemption 这个参数来控制的,该标志默认为 false ( 不建议使用)

1
2
3
4
5
6
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
provider: DefaultProvider
...
disablePreemption: true

集群管理员可以为特定用户创建特定优先级级别,来防止他们恶意使用高优先级的 PriorityClass。

Pod 里的每一个 Container 都同时设置了 requestslimits,并且 requestslimits 值相等的时候(或者只有 limits ),这个 Pod 就属于 Guaranteed 类别

Pod 不满足 Guaranteed 的条件,但至少有一个 Container 设置了 requests。那么这个 Pod 就会被划分到 Burstable 类别

Pod 既没有设置 requests,也没有设置 limits,那么它的 QoS 类别就是 BestEffort

QoS 划分的主要应用场景,是当宿主机资源紧张的时候,kubeletPod 进行 Eviction(即资源回收)时需要用到的。

默认值

1
2
3
4
memory.available<100Mi
nodefs.available<10%
nodefs.inodesFree<5%
imagefs.available<15%

可自行配置

1
kubelet --eviction-hard=imagefs.available<10%,memory.available<500Mi,nodefs.available<5%,nodefs.inodesFree<5% --eviction-soft=imagefs.available<30%,nodefs.available<10% --eviction-soft-grace-period=imagefs.available=2m,nodefs.available=2m --eviction-max-pod-grace-period=600

建议将 DaemonSetPod 都设置为 GuaranteedQoS 类型。否则,一旦 DaemonSetPod 被回收,它又会立即在原宿主机上被重建出来,这就使得前面资源回收的动作,完全没有意义了