用于将Pod
调度到匹配Label
的Node
上,如果没有匹配的标签就会调度失败(Pending);也就是将Pod分配给指定的节点;
作用:
约束Pod到特定的节点运行
完全匹配节点标签
应用场景:
专用节点
:根据业务线将Node进行分组管理,例如划分资源池:核心资源池(独占资源)、一级资源池和二级资源池;配备特殊硬件的节点
:部分Node配有SSD硬盘,GPU;如果两个节点打同样的命名标签,那么会随机分配到同命名标签的一台上;
例如:我们现在想将pod分配到核心业务池上;
1.我们可以通过如下命令查看node现有label
[root@k8s-master ~]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready control-plane,master 367d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=
k8s-node1 Ready <none> 367d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2 Ready <none> 367d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
k8s-node3 Ready <none> 317d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node3,kubernetes.io/os=linux
k8s-node4 Ready <none> 317d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node4,kubernetes.io/os=linux
k8s-node7 Ready <none> 317d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node7,kubernetes.io/os=linux
2.然后现在给k8s-node1这个节点添加一个核心资源池的标签
#kubectl label nodes/节点名称 标签key=标签value
[root@k8s-master ~]# kubectl label nodes/k8s-node1 ResourcePool=kernel
node/k8s-node1 labeled
#查看添加的标签是否生效
[root@k8s-master ~]# kubectl get nodes/k8s-node1 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready <none> 367d v1.20.11 ResourcePool=kernel,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
3.将pod调度到带有核心资源池标签的node上
当node被添加了标签之后,我们就可以将pod调度到带有指定标签的node上了,只需要在pod的spce字段内添加nodeSelector字段然后指定标签即可;
apiVersion: v1
kind: Pod
metadata:
name: "pod-nodeselector"
namespace: default
labels:
app: "nginx"
spec:
containers:
- name: my-web
image: "nginx:latest"
#节点选择器配置
nodeSelector:
#键入标签键:值 (需有node拥有此标签,否则会报错pending)
ResourcePool: kernel
4.然后运行此yaml查看这个pod的调度情况
[root@k8s-master goodgood-study]# kubectl apply -f pod-nodeSelector.yaml
pod/pod-nodeselector created
[root@k8s-master goodgood-study]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 1/1 Running 0 20s 10.244.36.77 k8s-node1 <none> <none>
[root@k8s-master goodgood-study]# kubectl describe pod/pod-nodeselector
Name: pod-nodeselector
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 5m56s default-scheduler Successfully assigned default/pod-nodeselector to k8s-node1
Normal Pulling 5m56s kubelet Pulling image "nginx:latest"
Normal Pulled 5m55s kubelet Successfully pulled image "nginx:latest" in 432.324889ms
Normal Created 5m55s kubelet Created container my-web
Normal Started 5m55s kubelet Started container my-web
根据上面的Events事件我们可以看到pod通过默认的调度器default-scheduler
被调度到了k8s-node1
上,也就是我们带有核心资源池ResourcePool=kernel标签
的节点上;
k8s亲和性分为节点亲和性
和pod亲和性
,其中pod还具有pod反亲和性
;
nodeAffinity(节点亲和性)
:根据节点的标签去调度Pod;podAffinity(pod亲和性)
:根据已有的Pod标签去调度Pod;亲和性调度可以分为软策略和硬策略:
软策略(preferred)
:软策略就是优先满足调度要求的节点的话,如果没有满足的话,POD 就会忽略这条规则,继续完成调度过程;说白了就是优先满足符合条件的节点,但是不保证,如果没有符合条件的节点的话就会调度到其他节点上;硬策略(required)
:如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然就不干了;nodeAffinity:节点亲和性,与nodeSelector作用一样,但相比nodeSelector更灵活,能满足更多条件,例如匹配有更多的逻辑组合,不只是字符串的完全相等,支持的操作符有:In、Notln、Exits、DoesNotExist、Gt、Lt;
例如:将pod分配到核心业务池(带有ResourcePool=kernel标签的node)上;
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-nodeaffinity
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: my-web
image: nginx:latest
#亲和性配置
affinity:
#节点亲和性
nodeAffinity:
#硬策略
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
#标签键
- key: ResourcePool
#操作符,NotIn为分配不满足的节点
operator: In
#标签值
values:
- kernel
然后运行此yaml,那么这三个pod只会运行在 满足 node label ResourcePool=kernel
的节点(k8s-node1)上,如果没有节点满足这个条件,则一直处于 pending 状态;
[root@k8s-master goodgood-study]# kubectl apply -f pod-nodeAffinity.yaml
deployment.apps/nginx-nodeaffinity created
[root@k8s-master goodgood-study]# kubectl get nodes/k8s-node1 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready <none> 368d v1.20.11 ResourcePool=kernel,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
[root@k8s-master goodgood-study]# kubectl get pod
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-nodeaffinity-6b9665566d-dqlfd 1/1 Running 0 10m 10.244.36.127 k8s-node1 <none> <none>
nginx-nodeaffinity-6b9665566d-jd2dq 1/1 Running 0 10m 10.244.36.68 k8s-node1 <none> <none>
nginx-nodeaffinity-6b9665566d-qdcds 1/1 Running 0 10m 10.244.36.78 k8s-node1 <none> <none>
[root@k8s-master goodgood-study]# kubectl describe pod/nginx-nodeaffinity-6b9665566d-bb95s
Name: nginx-nodeaffinity-6b9665566d-bb95s
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m51s default-scheduler Successfully assigned default/nginx-nodeaffinity-6b9665566d-dqlfd to k8s-node1
Normal Pulling 3m51s kubelet Pulling image "nginx:latest"
Normal Pulled 3m35s kubelet Successfully pulled image "nginx:latest" in 15.776948953s
Normal Created 3m35s kubelet Created container my-web
Normal Started 3m35s kubelet Started container my-web
根据上面的Events事件我们可以看到使用nodeAffinity策略
分配的pod也是通过默认的调度器default-scheduler
被调度到了k8s-node1
上,也就是我们带有核心资源池ResourcePool=kernel标签
的节点上;
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-nodeaffinity
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: my-web
image: nginx:latest
#亲和性配置
affinity:
#节点亲和性
nodeAffinity:
#软策略
preferredDuringSchedulingIgnoredDuringExecution:
#权重值1-100,值越大匹配此节点的概率就越高
- weight: 1
preference:
matchExpressions:
#标签键
- key: ResourcePool
#操作符,NotIn为分配不满足的节点
operator: In
#标签值
values:
- kernel
如果存在两个候选节点,都满足 preferredDuringSchedulingIgnoredDuringExecution 规则,
其中一个节点具有标签 label-1:key-1,另一个节点具有标签 label-2:key-2, 调度器会考察各个节点的 weight
取值,并将该权重值添加到节点的其他得分值之上;具体可参考官网释意:
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity
然后运行此yaml可以发现:软策略就是第一选择是 node label ResourcePool=kernel 的节点,如果没有,就采用默认 scheduler 的调度策略,没有强制性
。
[root@k8s-master ~]# kubectl get nodes/k8s-node1 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready <none> 368d v1.20.11 ResourcePool=kernel,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-nodeaffinity-86d5c88744-gbgl2 1/1 Running 0 88m 10.244.36.82 k8s-node1 <none> <none>
nginx-nodeaffinity-86d5c88744-hkt62 1/1 Running 0 88m 10.244.36.92 k8s-node1 <none> <none>
nginx-nodeaffinity-86d5c88744-vnbrp 1/1 Running 0 88m 10.244.36.117 k8s-node1 <none> <none>
此时把k8s-node1节点上的"ResourcePool=kernel"标签删除,然后重建pod看是否会被调度到其他节点
kubectl label node/节点名称
标签键-
[root@k8s-master ~]# kubectl label node/k8s-node1 ResourcePool-
node/k8s-node1 labeled
[root@k8s-master ~]# kubectl get nodes/k8s-node1 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready <none> 368d v1.20.11 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
##重建pod后查看
[root@k8s-master goodgood-study]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-nodeaffinity-86d5c88744-98nwl 1/1 Running 0 80s 10.244.36.110 k8s-node1 <none> <none>
nginx-nodeaffinity-86d5c88744-qd97r 1/1 Running 0 80s 10.244.189.40 k8s-node7 <none> <none>
nginx-nodeaffinity-86d5c88744-r2jbx 1/1 Running 0 80s 10.244.107.237 k8s-node3 <none> <none>
[root@k8s-master goodgood-study]# kubectl describe pod/nginx-nodeaffinity-86d5c88744-qd97r
Name: nginx-nodeaffinity-86d5c88744-qd97r
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 115s default-scheduler Successfully assigned default/nginx-nodeaffinity-86d5c88744-qd97r to k8s-node7
Normal Pulling 115s kubelet Pulling image "nginx"
Normal Pulled 99s kubelet Successfully pulled image "nginx" in 15.777615402s
Normal Created 99s kubelet Created container my-web
Normal Started 99s kubelet Started container my-web
可以发现若是没有标签符合的节点,将会调度到其他节点,不会处于pending状态,这就是软策略
Pod 亲和性(podAffinity)
主要解决 Pod 可以和哪些 Pod 部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等);Pod 反亲和性(podAntiAffinity)
主要是解决 Pod 不能和哪些 Pod 部署在同一个拓扑域中的问题;它们都是基于已经在节点上运行的pod
的标签
来约束Pod可以调度的节点
,而不是基于节点上的标签;
Pod 间亲和性与反亲和性的类型
与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
(生产中常用的是 利用反亲和性将一个pod的多个副本分配在不同的节点上形成高可用)
。由于我们这里只有一个集群,并没有区域或者机房的概念,所以我们这里直接使用主机名来作为拓扑域:
测试1- Pod的亲和性
(把 my-web应用 Pod
创建在和测试pod是同一个主机
上面)
测试2- Pod的反亲和性
(把 my-web应用 Pod
创建在和测试pod不是同一个主机
上面)。
要使用 Pod 间亲和性,可以使用 Pod 规约中的 .affinity.podAffinity 字段。 对于 Pod 间反亲和性,可以使用
Pod 规约中的 .affinity.podAntiAffinity 字段。
[root@k8s-master ~]# kubectl label nodes/k8s-node1 ResourcePool=kernel
node/k8s-node1 labeled
[root@k8s-master ~]# kubectl get nodes/k8s-node1 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-node1 Ready <none> 368d v1.20.11 ResourcePool=kernel,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
测试pod配置如下
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox-podaffinity
name: test-busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: test-busybox
nodeSelector:
ResourcePool: kernel
#运行结果如下,被分配在了k8s-node1节点上
[root@k8s-master goodgood-study]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-busybox 1/1 Running 0 105s 10.244.36.84 k8s-node1 > >
#应用pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
#亲和性配置
affinity:
#pod亲和性
podAffinity:
#硬策略
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
#标签键
- key: app
#操作符
operator: In
#标签值
values:
- busybox-podaffinity
#根据节点上面的标签来划分拓扑域(这里使用的是hostname)
topologyKey: kubernetes.io/hostname
containers:
- name: my-web
image: nginx:latest
运行此yaml可以看到应用容器my-web三个副本均和测试容器test-busybox在同一个节点上(k8s-node1)
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-7766bc6558-9s6ds 1/1 Running 0 2d15h 10.244.36.100 k8s-node1 <none> <none>
my-web-7766bc6558-s85bx 1/1 Running 0 2d15h 10.244.36.93 k8s-node1 <none> <none>
my-web-7766bc6558-zh5tn 1/1 Running 0 2d15h 10.244.36.81 k8s-node1 <none> <none>
test-busybox 1/1 Running 65 2d17h 10.244.36.84 k8s-node1 <none> <none>
#应用pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
affinity:
#pod反亲和性配置
podAntiAffinity:
#软策略
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
#标签选择器
labelSelector:
matchExpressions:
#标签键
- key: app
#操作符
operator: In
#标签值
values:
- busybox-podaffinity
#根据节点上面的标签来划分拓扑域(这里使用的是hostname)
topologyKey: kubernetes.io/hostname
#硬策略配置如下
#requiredDuringSchedulingIgnoredDuringExecution:
#- labelSelector:
# matchExpressions:
# - key: app
# operator: In
# values:
# - busybox-podaffinity
# topologyKey: kubernetes.io/hostname
containers:
- name: my-web
image: nginx:latest
然后运行此yaml会发现我们3个的应用容器my-web会和测试容器test-busybox不在同一个节点上,当然现在是软限制,如果没有合适的节点的话可能还会随机调度到同一个节点上,所以反亲和性在生产上强烈建议使用硬限制
;因为要做到高可用,多个副本肯定是不能在同一个节点上的,要不然所在节点挂了的话就无pod可用了;
[root@k8s-master goodgood-study]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-web-6464ccd4bd-5sz6n 1/1 Running 0 5m57s 10.244.169.131 k8s-node2 <none> <none>
my-web-6464ccd4bd-6ft54 1/1 Running 0 5m57s 10.244.189.55 k8s-node7 <none> <none>
my-web-6464ccd4bd-hfz6k 1/1 Running 0 5m57s 10.244.189.44 k8s-node7 <none> <none>
test-busybox 1/1 Running 0 10m 10.244.36.127 k8s-node1 <none> <none>
在亲和性与反亲和性配置中:
[nodeSelectorTerms配置]
和[labelSelector配置]
下面有多个选项的话,满足任何一个条件就可以了;如果[matchExpressions]
有多个选项的话,则必须同时满足这些条件才能正常调度POD。
operator操作符配置:提供如下几种操作
- In:label的值在列表中;
- NotIn:label的值不在列表中;
- Gt:label 的值大于某个值;
- Lt:label 的值小于某个值;
- Exists:某个 label 存在;
- DoesNotExist:某个 label 不存在;
nodeName
:指定节点名称,用于将Pod调度到指定的Node上,不经过调度器,不经过调度器!
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-nodename
spec:
selector:
matchLabels:
app: pod-nodename
replicas: 3
template:
metadata:
labels:
app: pod-nodename
spec:
##选择要调度到节点的节点名称
nodeName: k8s-node1
containers:
- name: pod-nodename
image: nginx:latest
运行depolyment查看验证
[root@k8s-master1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename-5f4f6d7dbf-99xbn 1/1 Running 0 3m14s 10.244.36.87 k8s-node1 <none> <none>
pod-nodename-5f4f6d7dbf-ks6m5 1/1 Running 0 3m14s 10.244.36.96 k8s-node1 <none> <none>
pod-nodename-5f4f6d7dbf-pq6b9 1/1 Running 0 3m14s 10.244.36.72 k8s-node1 <none> <none>
经过调度器与不经过调度器的区别
:
- 经过调度器:会被节点上的污点影响,如果node上有污点的话则会根据pod是否有容忍来判断调度;
- 不经过调度器:不会被节点污点影响,指哪打哪;
在kubernetes中,节点亲和性(nodeAffinity)和Pod亲和性(podAffinity)都是Pod上定义的一种属性,能够使Pod按照我们的要求调度到某个节点上,而污点(Taints)
则相反,它是Node上的一个属性,可以让Pod不能调度到带污点的节点上
,甚至会对带污点节点上的Pod进行驱逐。当然对应的kubernetes可以给Pod设置容忍(Tolerations)属性
让Pod
能够容忍节点上设置的污点,这样在调度时就会忽略节点上设置的污点,将Pod调度到该节点上
。一般Taints
和Tolerations
配合使用;
拒绝常规Pod分配过来!
查看污点:
我们使用kubeadm搭建的集群默认就给master节点添加了一个污点标记,所以我们可以看到平时都没有pod调度到master上
查看node的污点
[root@k8s-master goodgood-study]# kubectl describe nodes/k8s-master
...
Taints: node-role.kubernetes.io/master:NoSchedule
...
#也可以通过下面操作查看:
[root@k8s-master goodgood-study]# kubectl get nodes/k8s-master -o go-template={{.spec.taints}}
[map[effect:NoSchedule key:node-role.kubernetes.io/master]]
我们可以使用上面的命令查看 master 节点的信息,其中有一条关于 Taints
的信息:node-role.kubernetes.io/master:NoSchedule,就表示master 节点打了一个污点的标记;
污点的内容组成一般为key、value及一个effect三个元素,表现为:
这里的value可以为空,例如master上默认的污点,表现形式为:
node-role.kubernetes.io/master:NoSchedule
- key:node-role.kubernetes.io/master
- value:空
- effect:NoSchedule
注:master为什么默认有污点?
- 安全性,因为master节点一般具备整个集群的管理权限,如果不设置污点的话 会有业务pod被分配进来,那样如果被有心的互联网用户盯上的话,通过种种手段进入pod内提权到宿主机,那将会有严重的危害;
- 管理角色;master节点上运行着各个组件的管理pod,随着后期组件越来越多,master的负载也就越来越大,所以尽量只做管理角色,不跑业务pod;
一般情况下,我们想要某个节点只允许特定的Pod
进行调度,这时就得对节点进行设置污点,可以按kubectl taint node [节点名称] key=[value]:[effect]
格式进行设置,其中effect
的可取值如下:
PreferNoSchedule
:尽量不要调度;NoSchedule
:一定不能被调度;NoExecute
:不仅不会调度,还会驱逐Node上已有的Pod(正在运行的pod没有容忍配置);设置污点方法如下:
##设置污点并不允许Pod调度到该节点
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key1=value1:NoSchedule
##设置污点尽量阻止Pod调度到该节点
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key2=value2:PreferNoSchedule
##设置污点,不允许普通Pod调度到该节点,并且将该节点已经存在的Pod进行驱逐
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key3=value3:NoExecute
上面给Node添加了污点阻止Pod进行调度,如果想要删除节点上的污点的话可以使用如下命令:
kubectl taint node [节点名称] [key名称]-
删除污点的命令和创建污点类似,不过需要注意的是删除污点需要知道key和最后面设置一个"-
"即可将污点删除,示例如下:
#为了方便演示,先给节点设置污点:
##设置污点1
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key1=value1:PreferNoSchedule
##设置污点2
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key2=value2:NoSchedule
##设置污点3,并且不设置value
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key2=:PreferNoSchedule
查看污点,可以看到上面设置的三个值:
[root@k8s-master goodgood-study]# kubectl describe nodes/k8s-master
...
Taints: key2=value2:NoSchedule
node-role.kubernetes.io/master:NoSchedule
key1=value1:PreferNoSchedule
key2:PreferNoSchedule
...
然后删除污点
##删除污点,可以不指定value,指定 [effect] 值就可删除该 key[effect] 的污点
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key1:PreferNoSchedule-
##也可以根据 key 直接将该 key2 的所有 [effect] 都删除:
[root@k8s-master goodgood-study]# kubectl taint node k8s-master key2-
再次查看污点,可以看到以上污点都被删除了
[root@k8s-master goodgood-study]# kubectl describe nodes/k8s-master
...
Taints: node-role.kubernetes.io/master:NoSchedule
...
为了使某些Pod禁止调度到某些特定的节点上,就可以对节点设置污点taints;当然如果希望有些Pod
能够忽略节点
上的污点
继续能够调度到该节点,就可以对Pod
设置容忍
,让Pod能够容忍节点
上的污点
;
例如:
对一个节点设置污点:
[root@k8s-master goodgood-study]# kubectl taint node k8s-node1 key=value:NoSchedule
然后下面对Pod设置容忍:
##容忍的key、value 和对应的 effect 也必须和污点 taints保持一致;
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
##容忍tolertions的key和污点的tains的key一致是,且设置的effect也相同,不需要设置value;
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"
Node和Pod对于污点与容忍的基本概念
一个node可以有多个污点;
一个pod可以有多个容忍;
kubernetes执行多个污点和容忍方法类似于过滤器;
如果一个node上有多个污点,且pod上也有多个容忍,只要`pod中容忍能包含node上设置的全部污点,则可以将pod调度到该node上;
如果pod上设置的容忍不能够包含node上设置的全部污点,且node上剩下不能被包含的污点 effect 为PreferNoSchedule,那么也可能被调度到该节点;
反之如果pod上设置的容忍不能够包含node上设置的全部污点,且node上剩下不能被包含的污点 effect 为NoSchedule或NoExecute,则不会被调度!!;
注:当Pod存在容忍时,首先Pod会选择没有污点的节点,然后再次选择容忍污点的节点;
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-tolerations
spec:
selector:
matchLabels:
app: pod-tolerations
replicas: 3
template:
metadata:
labels:
app: pod-tolerations
spec:
containers:
- name: pod-tolerations
image: nginx:latest
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
在正常的情况下,如果一个污点带有effect=NoExecute
被添加到了这个Node
。那么不能容忍这个污点的所有Pod都会被剔除;而带有容忍标签的Pod就不剔掉。然而,一个带有effect=NoExecute
的容忍可以指定一个tolerationSeconds
来指定当这个污点被添加的时候在多长时间内 Pod不会被剔掉;
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoExecute"
tolerationSeconds: 120
如果这个Pod已经在这个带污点且 effect 为 NoExecute的node上,这个Pod就可以一直运行120s后
再被剔除;如果这个时候Node的污点被移除了,这个Pod就不会被剔掉;
Operator的值默认是Equal,可以设置为Equal
和Exists
两种;
容忍任何污点
例如一个空的key,将匹配node上所有的key、value、effect,即容忍所有的污点;
#污点:
key1=value1:NoSchedule
key2=value2:NoExecute
#pod设置如下
tolerations:
- operator: "Exists"
容忍某个key值的所有污点
例如一个空的effect,但是key不为空,那么将匹配所有与key相同的effect;
#污点:
key=value1:NoSchedule
key=value2:NoExecute
#pod设置如下
tolerations:
- key: "key"
operator: "Exists"
容忍某个effect值的所有污点
例如一个空的key和空的value,将只匹配effect值相同的污点
#污点:
key=value:NoExecute
key2=value2:NoExecute
#pod设置如下
tolerations:
- effect: "NoExecute"
operator: "Exists"
容忍node上有一个污点
Node和Pod的key值、value值与effect值相同则能调度:
#污点:
key1=value1:NoSchedule
#pod设置如下
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
容忍node上有多个污点
Node上污点的key值、value值、effect值和pod容忍都相同则能被调度
#污点:
key1=value1:NoSchedule
key2=value2:NoExecute
#pod设置如下
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key2"
operator: "Equal"
value: "value2"
effect: "NoExecute"
Node的污点和Pod的大部分都相同,不同的是Node污点effect的值为PreferNoSchedule的,可能会调度
#污点:
key1=value1:NoSchedule
key2=value2:NoExecute
key3=value3:PreferNoSchedule
#pod设置如下
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key2"
operator: "Equal"
value: "value2"
effect: "NoExecute"
Node的污点和Pod的大部分相同,不同的是Node污点的effect的值是NoExecute和NoSchedule的话,则不会被调度
#污点:
key1=value1:NoSchedule
key2=value2:NoExecute
key3=value3:PreferNoSchedule
#pod设置如下
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key3"
operator: "Equal"
value: "value3"
effect: "PreferNoSchedule"
对比理解Exits和Equal之间的区别:
Exists是包含,Equal是等于,Exists使用范围更广,而Equal则是精准匹配;
当污点中存在NoExecute时,而容忍中不存在NoExecute时,则不会被调度到该节点;
Exists可以不写value,而Equal则一定要指定对应的value
在使用kubernetes时,会遇到某个node节点明明已经宕机了,查看node状态从Ready状态变为了NotReady状态,但是节点所在的Pod却已经处于running状态,过了很长一段时间才会转为Terminating状态,原因如下:
污点是对于node节点来讲的,如果node节点设置为NoExecute,它会影响节点上已经运行的Pod,如下所示
立即将没有匹配容忍的pod驱逐;
设置容忍但是没有指定tolerationSeconds参数的,那么该容忍永久生效,Pod永远不会被驱逐;
设置容忍但是有指定的tolerationSeconds参数的,那么在指定的时间内容忍有效,超过指定的时间后将会被驱逐;(pod默认设置,tolerationSeconds = 300s)
此外,当某些条件为true时,节点控制器会自动污染节点。k8s内置以下污点:
https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/taint-and-toleration/#taint-based-evictions
关于“节点压力驱逐”的官方解释
key | 注释 |
---|---|
node.kubernetes.io/not-ready | 节点未准备好。这相当于节点状况 Ready 的值为 “False”。 |
node.kubernetes.io/unreachable | 节点控制器访问不到节点. 这相当于节点状况 Ready 的值为 “Unknown”。 |
node.kubernetes.io/memory-pressure | 节点存在内存压力。 |
node.kubernetes.io/disk-pressure | 节点存在磁盘压力。 |
node.kubernetes.io/pid-pressure | 节点存在 PID 压力。 |
node.kubernetes.io/network-unavailable | 节点网络不可用。 |
node.kubernetes.io/unschedulable | 节点不可调度。 |
node.cloudprovider.kubernetes.io/uninitialized | 如果 kubelet 启动时指定了一个“外部”云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。 |
通过上面节点状态的铺垫,当一个节点宕机时,kubernetes集群会给节点打上标签,如下:
一个Ready状态的节点:
[root@k8s-master ~]# kubectl get nodes k8s-node1 -o go-template={{.spec.taints}}
<no value>
一个NotReady状态的节点:
[root@k8s-master ~]# kubectl get nodes k8s-node1 -o go-template={{.spec.taints}}
[map[effect:NoSchedule key:node.kubernetes.io/unreachable timeAdded:2023-05-29T08:27:09Z] map[effect:NoExecute key:node.kubernetes.io/unreachable timeAdded:2023-05-29T08:27:14Z]]
处于NotReady状态的节点被打上了下面两个污点:
Taints: node.kubernetes.io/unreachable:NoExecute
node.kubernetes.io/unreachable:NoSchedule
接下来测试kubernetes集群会给Pod分配上面样的容忍。
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300 ##300/60=5min
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300 ##300/60=5min
通过上面我们就可以看出Pod的失效机制
了,当node节点处于NotReady状态或者unreachable状态时,Pod会容忍它5分钟,然后被驱逐。而这5分钟内就算Pod处于running状态,也是无法正常提供服务的。因此,可以在yaml清单中手动指明0容忍,清单文件内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-tolerations5
spec:
selector:
matchLabels:
app: pod-tolerations5
replicas: 3
template:
metadata:
labels:
app: pod-tolerations5
spec:
containers:
- name: pod-tolerations5
image: nginx:latest
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 0
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 0
生成pod如下
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-tolerations5-6c84bc5f7f-6klwv 1/1 Running 0 13m 10.244.36.102 k8s-node1 <none> <none>
pod-tolerations5-6c84bc5f7f-gmlcx 1/1 Running 0 13m 10.244.36.100 k8s-node2 <none> <none>
pod-tolerations5-6c84bc5f7f-vlrj2 1/1 Running 0 13m 10.244.36.92 k8s-node1 <none> <none>
接下来强制关闭k8s-node1节点,查看Pod是否转移
[root@k8s-master ~]# kubectl get pod -o wide
pod-tolerations5-6c84bc5f7f-vlrj2 1/1 Terminating 0 18m 10.244.36.92 k8s-node1 <none> <none>
pod-tolerations5-6c84bc5f7f-gmlcx 1/1 Terminating 0 18m 10.244.36.100 k8s-node2 <none> <none>
pod-tolerations5-6c84bc5f7f-6klwv 1/1 Terminating 0 18m 10.244.36.102 k8s-node1 <none> <none>
pod-tolerations5-6c84bc5f7f-9zdk4 1/1 Running 0 19s 10.244.235.201 k8s-node2 <none> <none>
pod-tolerations5-6c84bc5f7f-xpplf 1/1 Running 0 35s 10.244.235.202 k8s-node2 <none> <none>
pod-tolerations5-6c84bc5f7f-plw7z 1/1 Running 0 35s 10.244.235.203 k8s-node2 <none> <none>
在node节点转换为NotReady状态后,Pod立刻进行了转移。这是通过在yaml清单文件中明确指定了容忍时间
;还可以直接修改apiserver配置来进行修改默认容忍时间。
https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/admission-controllers/#defaulttolerationseconds
kubeadm方式:
[root@k8s-master ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=10.8.0.2
- --default-not-ready-toleration-seconds=0 ##新增行
- --default-unreachable-toleration-seconds=0 ##新增行
...
修改保存后, kube-apiserver-k8s-master
pod会自动重载最新配置。
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 0
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 0
二进制方式:
[root@k8s-master1 ~]# vim ~/kubernetes/cfg/kube-apiserver.conf
KUBE_APISERVER_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/hqtbj/hqtwww/kubernetes/logs \
--etcd-servers=https://172.32.0.11:2379,https://172.32.0.12:2379,https://172.32.0.13:2379 \
--bind-address=172.32.0.11 \
--secure-port=6443 \
--advertise-address=172.32.0.11 \
--default-not-ready-toleration-seconds=0 \ ##新增行
--default-unreachable-toleration-seconds=0 \ ##新增行
--allow-privileged=true \
--service-cluster-ip-range=10.0.0.0/24 \
...
[root@k8s-master1 ~]# systemctl restart kube-apiserver.service
注:二进制方式设置容忍时间为0的话需要在 --advertise-address选项行后添加;