Kubernetes之集群调度
1、调度说明
1.1 调度器简介
- Scheduler是Kubernetes的调度器,主要的任务是把定义的pod分配到集群的节点上,需要考虑以下问题:
- 公平:如何保证每个节点都能被分配资源。
- 资源高效利用:集群所有资源最大化被使用。
- 效率:调度的性能要好,能够尽快地对大批量的pod完成调度工作。
- 灵活:允许用户根据自己的需求控制调度的逻辑。
- Scheduler是作为单独的程序运行的,启动之后会一直连接apiserver获取PodSpec.NodeName为空的pod,对每个pod都会创建一个binding,表明该pod应该放到哪个节点上。
1.2 调度过程
调度分为几个部分:
- 首先是过滤掉不满足条件的节点,这个过程称为predicate(预选)。
- 然后对通过的节点按照优先级排序,这个是priority(优选)。
- 最后从中选择优先级最高的节点。
Predicate有一系列的算法可以使用:
- PodFitsResources:节点上剩余的资源是否大于pod请求的资源。
- PodFitsHost:如果pod指定了NodeName,检查节点名称是否和NodeName匹配。
- PodFitsHostPorts:节点上已经使用的port是否和pod申请的port冲突。
- PodSelectorMatches:过滤掉和pod指定的label不匹配的节点。
- NoDiskConflict:已经mount的volume和pod指定的volume不冲突,除非它们都是只读。
如果在predicate过程中没有合适的节点,pod会一直在pending状态(等待),不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续priorities过程:按照优先级大小对节点排序。优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重,这些优先级选项包括:
- LeastRequestedPriority:通过计算CPU和Memory的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点。
- BalancedResourceAllocation:节点上CPU和Memory使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用。
- ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高。
通过算法对所有的优先级项目和权重进行计算,得出最终的结果。
1.3 自定义调度器
- 除了K8S自带的调度器,可以自定义调度器。通过spec:schedulername参数指定调度器的名字,可以为pod选择某个调度器进行调度。比如下面的
pod
选择my-scheduler
进行调度,而不是默认的default-scheduler
:
2、调度亲和性
2.1 pod与node的亲和性
spec.affinity.nodeAffinity
:preferredDuringSchedulingIgnoredDuringExecution
(优先执行计划):软策略1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33[root@master ~]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 14d v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
node1 Ready <none> 14d v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2 Ready <none> 14d v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
[root@master ~]# vim node-preferred-pod.yaml
[root@master ~]# cat node-preferred-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node3
[root@master ~]# kubectl create -f node-preferred-pod.yaml
pod/affinity created
# 因为配置的是软策略,而node3节点并没有,那就只能在其它节点找
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity 1/1 Running 0 12s 10.244.1.75 node1 <none> <none>requiredDuringSchedulingIgnoredDuringExecution
(要求执行计划):硬策略1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38[root@master ~]# kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 14d v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
node1 Ready <none> 14d v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2 Ready <none> 14d v1.15.1 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
[root@master ~]# vim node-required-pod.yaml
[root@master ~]# cat node-required-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity2
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: nginx:v1
affinity: #亲和性
nodeAffinity: #node亲和性
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node3
[root@master ~]# kubectl create -f node-required-pod.yaml
pod/affinity2 created
# 因为指定了该pod必须要在node3节点运行的硬策略,而node3并不存在,所以只能不运行
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
affinity2 0/1 Pending 0 8s <none> <none> <none> <none>
[root@master ~]# kubectl describe pod affinity2
…………
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 85s (x2 over 85s) default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.键值运算关系:
键 说明 In label 的值在某个列表中 NotIn label 的值不在某个列表中 Gt label 的值大于某个值 Lt label 的值小于某个值 Exists 某个 label 存在 DoesNotExist 某个 label 不存在
2.2 pod与pod的亲和性
pod.affinity.podAffinity/podAntiAffinity
:preferredDuringSchedulingIgnoredDuringExecution
(优先执行计划):软策略requiredDuringSchedulingIgnoredDuringExecution
(要求执行计划):硬策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124[root@master ~]# vim pod1.yaml
[root@master ~]# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: node01
labels:
app: node01
spec:
containers:
- name: with-node-affinity
image: nginx:v1
[root@master ~]# kubectl create -f pod1.yaml
pod/node01 created
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node01 1/1 Running 0 36s 10.244.1.76 node1 <none> <none>
[root@master ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
node01 1/1 Running 0 3m13s app=node01
[root@master ~]# vim pod-required.yaml
[root@master ~]# cat pod-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: nginx:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- node01
topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl create -f pod-required.yaml
pod/pod-3 created
# 上方指定了pod-3与label为app=node01的pod亲和硬策略,由于满足条件的node01在node1节点上运行,所以pod-3也会一起运行在node1节点
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node01 1/1 Running 0 4m33s 10.244.1.76 node1 <none> <none>
pod-3 1/1 Running 0 20s 10.244.1.77 node1 <none> <none>
[root@master ~]# kubectl delete pod pod-3
pod "pod-3" deleted
# 将podAffinity改成podAntiAffinity
[root@master ~]# vim pod-required.yaml
[root@master ~]# cat pod-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: nginx:v1
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- node01
topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl create -f pod-required.yaml
pod/pod-3 created
# 由于podAnitAffinity指定了不在同一拓扑域,即pod3必须和label为app=node01的pod不在同一个节点,所以pod-3运行在了node2而不是node1
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node01 1/1 Running 0 63m 10.244.1.76 node1 <none> <none>
pod-3 1/1 Running 0 16s 10.244.2.68 node2 <none> <none>
[root@master ~]# kubectl delete pod pod-3
pod "pod-3" deleted
# 将podAnitAffinity重新改成podAffinity,并且label为app=node01改成app=node02
[root@master ~]# vim pod-required.yaml
[root@master ~]# cat pod-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: nginx:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- node02
topologyKey: kubernetes.io/hostname
[root@master ~]# kubectl create -f pod-required.yaml
pod/pod-3 created
# 由于设置了pod3必须要运行在label为app=node02的pod所在的节点上,而目前没有,所以不运行
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node01 1/1 Running 0 69m 10.244.1.76 node1 <none> <none>
pod-3 0/1 Pending 0 8s <none> <none> <none> <none>
[root@master ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
node01 1/1 Running 0 71m app=node01
pod-3 0/1 Pending 0 2m25s app=pod-3
# 将node01的label改成满足配置条件后,pod-3即可正常运行
[root@master ~]# kubectl label pod node01 app=node02 --overwrite=true
pod/node01 labeled
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node01 1/1 Running 0 77m 10.244.1.76 node1 <none> <none>
pod-3 1/1 Running 0 8m38s 10.244.1.78 node1 <none> <none>亲和性/反亲和性调度策略比较如下:
调度策略 匹配标签 操作符 拓扑域支持 调度目标 nodeAffinity 主机 In, NotIn, Exists,DoesNotExist, Gt, Lt 否 指定主机 podAffinity POD In, NotIn, Exists,DoesNotExist 是 POD与指定POD同一拓扑域 podAnitAffinity POD In, NotIn, Exists,DoesNotExist 是 POD与指定POD不在同一拓扑域
3、污点
3.1 Taint和Toleration
- 节点亲和性,是pod的一种属性(偏好或硬性要求),它使
pod
被吸引到一类特定的节点。Taint则相反,它使节点能够排斥一类特定的pod。 - Taint和toleration相互配合,可以用来避免pod被分配到不合适的节点上。每个节点上都可以应用一个或多个taint,这表示对于那些不能容忍这些taint的pod,是不会被该节点接受的。如果将toleration应用于pod上,则表示这些pod可以(但不要求)被调度到具有匹配taint的节点上。
3.1.1 Taint
污点(Taint)的组成:
使用kubectl taint命令可以给某个Node节点设置污点,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝Pod的调度执行,甚至将Node已经存在的Pod驱逐出去,每个污点的组成如下:
key=value:effect
。每个污点有一个key和value作为污点的标签,其中value可以为空,effect描述污点的作用。当前taint effect支持如下三个选项:
NoSchedule:K8S将不会将Pod调度到具有该污点的Node上。
1
2
3
4
5
6
7# 例如master节点是由于设置了NoSchedule,所以才不会有pod运行在master节点上
[root@master ~]# kubectl describe node master
…………
CreationTimestamp: Wed, 06 Oct 2021 13:31:13 +0800
Taints: node-role.kubernetes.io/master:NoSchedule
Unschedulable: false
…………PreferNoSchedule:K8S将尽量避免将Pod调度到具有该污点的Node上。
NoExecute:K8S将不会将Pod调度到具有该污点的Node上,同时会将Node上已经存在的Pod驱逐出去。
污点的设置、查看和去除:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#查看节点污点,查找 Taints 字段:kubectl describe node node-name
[root@master ~]# kubectl describe node master
…………
CreationTimestamp: Wed, 06 Oct 2021 13:31:13 +0800
Taints: node-role.kubernetes.io/master:NoSchedule
Unschedulable: false
…………
# 设置污点:kubectl taint nodes node1 key1=value1:effect
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
node01 1/1 Running 1 24h
pod-3 1/1 Running 1 22h
[root@master ~]# kubectl taint nodes node1 check=zhu:NoExecute
node/node1 tainted
[root@master ~]# kubectl get pod
No resources found.
# 去除污点:kubectl taint nodes node1 key1=value1:effect-
[root@master ~]# kubectl taint nodes node1 check=zhu:NoExecute-
node/node1 untainted
3.1.2 Tolerations
设置了污点的Node将根据taint的effect和Pod之间产生互斥的关系,Pod将在一定程度上不会被调度到Node上。但我们可以在Pod上设置容忍 (Toleration) ,设置了容忍的Pod将可以容忍污点的存在,可以被调度到存在污点的Node上。
toleration的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41spec:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
[root@master ~]# kubectl taint nodes node1 check=zhu:NoExecute
node/node1 tainted
[root@master ~]# vim test-toleration.yaml
[root@master ~]# cat test-toleration.yaml
apiVersion: v1
kind: Pod
metadata:
name: test
labels:
app: test
spec:
containers:
- name: test
image: nginx:v1
tolerations:
- key: "check"
operator: "Equal"
value: "zhu"
effect: "NoExecute"
tolerationSeconds: 3600
[root@master ~]# kubectl create -f test-toleration.yaml
pod/test created
# 由于设置了该pod可以容忍check=zhu:NoExecute的污点,因此可以在node1节点上正常运行
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 1/1 Running 0 2m30s 10.244.1.81 node1 <none> <none>- 其中key、vaule、effect要与Node上设置的taint保持一致。
- operator的值为Exists将会忽略value值。
- tolerationSeconds:当Pod需要被驱逐时可以在Pod上继续保留运行的时间。
①当不指定key值时,表示容忍所有的污点key。
1
2tolerations:
- operator: "Exists"②当不指定effect值时,表示容忍所有的污点作用。
1
2
3tolerations:
- key: "key"
operator: "Exists"③有多个Master存在时,防止资源浪费,可以如下设置:
1
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
4、固定节点
Pod.spec.nodeName将Pod直接调度到指定的Node节点上,会跳过Scheduler的调度策略,该匹配规则是强制匹配:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30[root@master ~]# vim test-fixnode.yaml
[root@master ~]# cat test-fixnode.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 7
template:
metadata:
labels:
app: myweb
spec:
nodeName: node1
containers:
- name: myweb
image: nginx:v1
ports:
- containerPort: 80
[root@master ~]# kubectl apply -f test-fixnode.yaml
deployment.extensions/myweb created
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-6565474876-2lgjf 1/1 Running 0 12s 10.244.1.86 node1 <none> <none>
myweb-6565474876-7nsx8 1/1 Running 0 12s 10.244.1.85 node1 <none> <none>
myweb-6565474876-kbzxh 1/1 Running 0 12s 10.244.1.83 node1 <none> <none>
myweb-6565474876-m7nmg 1/1 Running 0 12s 10.244.1.82 node1 <none> <none>
myweb-6565474876-nkzw9 1/1 Running 0 12s 10.244.1.87 node1 <none> <none>
myweb-6565474876-rwkxh 1/1 Running 0 12s 10.244.1.84 node1 <none> <none>
myweb-6565474876-wrw7k 1/1 Running 0 12s 10.244.1.88 node1 <none> <none>Pod.spec.nodeSelector:通过kubernetes的label-selector机制选择节点,由调度器调度策略匹配label,而后调度Pod到目标节点,该匹配规则属于强制约束。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32[root@master ~]# vim test-fixnode2.yaml
[root@master ~]# cat test-fixnode2.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 2
template:
metadata:
labels:
app: myweb
spec:
nodeSelector:
disk: ssd
containers:
- name: myweb
image: nginx:v1
ports:
- containerPort: 80
[root@master ~]# kubectl apply -f test-fixnode2.yaml
deployment.extensions/myweb created
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-74586cff8f-2srj6 0/1 Pending 0 21s <none> <none> <none> <none>
myweb-74586cff8f-kqn5v 0/1 Pending 0 21s <none> <none> <none> <none>
[root@master ~]# kubectl label node node1 disk=ssd
node/node1 labeled
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-74586cff8f-2srj6 1/1 Running 0 69s 10.244.1.90 node1 <none> <none>
myweb-74586cff8f-kqn5v 1/1 Running 0 69s 10.244.1.89 node1 <none> <none>