Node 标记/容忍
还有第三类调度,可以通过给 Node 打一些标记,来限制 Pod 调度到某些 Node 上。Kubernetes 把这些标记称之为 Taints,它的字面意思是污染。(污点=key + value + effect)
那我们如何限制 Pod 调度到某些 Node 上呢?比如说现在有个 node 叫 demo-node,这个节点有问题,我想限制一些 Pod 调度上来。这时可以给这个节点打一个 taints,taints 内容包括 key、value、effect:
- key 就是配置的键值
- value 就是内容
- effect 是标记了这个 taints 行为是什么
目前 Kubernetes 里面有三个 taints 行为:
- NoSchedule 禁止新的 Pod 调度上来
- PreferNoSchedul 尽量不调度到这台
- NoExecute 会 evict 没有对应 toleration 的 Pods,并且也不会调度新的上来。这个策略是非常严格的,大家在使用的时候要小心一点
如上图绿色部分,给这个 demo-node 打了 k1=v1,并且 effect 等于 NoSchedule 之后。它的效果是:新建的 Pod 没有专门容忍这个 taint,那就没法调度到这个节点上去了。
假如有些 Pod 是可以调度到这个节点上的,应该怎么来做呢?这时可以在 Pod 上打一个 Pod Tolerations。从上图中蓝色部分可以看到:在 Pod 的 spec 中填写一个 Tolerations,它里面也包含了 key、value、effect,这三个值和 taint 的值是完全对应的,taint 里面的 key,value,effect 是什么内容,Tolerations 里面也要填写相同的内容。
Tolerations 还多了一个选项 Operator,Operator 有两个 value:Exists/Equal。Equal 的概念是必须要填写 value,而 Exists 就跟上文说的 NodeAffinity 一样,不需要填写 value,只要 key 值对上了,就认为它跟 taints 是匹配的。
上图中的例子,给 Pod 打了一个 Tolerations,只有打了这个 Tolerations 的 Pod,才能调度到绿色部分打了 taints 的 Node 上去。这样的好处是 Node 可以有选择性的调度一些 Pod 上来,而不是所有的 Pod 都可以调度上来,这样就做到了限制某些 Pod 调度到某些 Node 的效果。
小结
我们已经介绍完了 Pod/Node 的特殊关系和条件调度,来做一下小结。
首先假如有需求是处理 Pod 与 Pod 的时候,比如 Pod 和另一个 Pod 有亲和的关系或者是互斥的关系,可以给它们配置下面的参数:
- PodAffinity
- PodAntiAffinity
假如存在 Pod 和 Node 有亲和关系,可以配置下面的参数:
- NodeSelector
- NodeAffinity
假如有些 Node 是限制某些 Pod 调度的,比如说一些故障的 Node,或者说是一些特殊业务的 Node,可以配置下面的参数:
- Node -- Taints
- Pod -- Tolerations
Taint 和 Toleration
Taints: 避免Pod调度到特定Node上
Tolerations: 允许Pod调度到持有Taints的Node上
应用场景:
• 专用节点:根据业务线将Node分组管理,希望在默认情况下不调度该节点,只有配置了污点容忍才允许分配
• 配备特殊硬件:部分Node配有SSD硬盘、GPU,希望在默认情况下不调度该节点,只有配置了污点容忍才允许分配
• 基于Taint的驱逐
Nodeselector和afflinte是需要调度到哪个节点上。污点是不调度到相应的节点上,拒绝某个Pod分配过来。
节点亲和性,是pod的一种属性(偏好或硬性要求),它使pod被吸引到一类特定的节点。Taint 则相反,它使节点能够排斥一类特定的 pod。
Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上。
如果你不懂上面枯燥无味的术语没关系,举个例子如下:
当年龄大以后,都会遇到相亲。比如一个女孩相亲一个男孩,会问这个男孩晚上会不会打呼噜,如果打呼噜是容忍不了的。也就是有些男孩特点标签上被贴上了打呼噜的两个字,那么这个女孩就没有可能和这个男孩走在一起。
这就是一个典型的污点和容忍。如果你能容忍这个污点,代表你有可能发生故事,但是不一定会发生故事。这里指的是有发生故事的可能性而且。在k8s里面依然是这个概念。
[root@k8s-master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 22d v1.19.0
k8s-node1 Ready <none> 22d v1.19.0
k8s-node2 Ready <none> 22d v1.19.0
假设我有一个Pod,它不能容忍任何污点,在其中一个node上面,它有一个污点存在,那么这个pod就不会调度到这个节点上面去了。
如果pod能够容忍这个node污点,难道就一定会运行在该节点吗?不一定吧,也可能在其他节点上运行。(有污点的可以容忍,没有污点的凭什么不可以运行呢,只不过污点的节点不会成为选择的障碍而已)
污点(Taint)
1、污点的组成
使用kubectl taint 命令可以给某个node节点设置污点,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝Pod的调度执行,甚至将已经存在得Pod驱逐出去,每个污点的组成如下:
key=value:effect
每个污点有一个 key 和 value 作为污点标签,其中 value 可以为空,effect描述污点的作用,当前 effect 支持如下三个选项:
- NoSchedule:表示 k8s 不会将Pod调度到具有该污点的Node上
- PreferNoSchedule:表示 k8s 将尽量避免将Pod调度到具有该污点的Node上
- NoExecute:表示 k8s 将不会将Pod调度到具有该污点的Node上,同时会将Node上已有的Pod驱逐出去(相当于结婚了还得离婚)
2、污点的设置、查看和去除
k8s的master节点本身就带有effect类型为NoSchedule的污点,这也是为什么k8s在调度Pod时,不会调度到master节点的原因,具体查看如下:(Kubeadm在安装初始化init的时候给master打上了污点,其余节点是没有该污点的。Master节点主要任务是管理集群的,不应该跑具体的业务应用。所以不允许pod落在master节点)
如果不让pod调度到某个节点,那么可以对该节点打污点
[root@k8s-master ~]# kubectl describe node k8s-master
Taints: node-role.kubernetes.io/master:NoSchedule
kubectl taint nodes k8s-master node-role.kubernetes.io/master=:NoSchedule
注意⚠️ : 为master设置的这个taint中, node-role.kubernetes.io/master
为key
, value
为空, effect
为NoSchedule,
如果输入命令时, 你丢掉了=
符号, 写成了node-role.kubernetes.io/master:NoSchedule
, 会报error: at least one taint update is required
错误
过程介绍
# 设置污点
kubectl taint nodes [node name] key1=value:NoSchedule
# 节点说明中,查看Taint字段
kubectl describe node [node name]
# 去除污点
kubectl taint nodes [node name] key1:NoSchedule-
演示
# 查看当前节点所拥有Pod,kubia-859d757f8c-97zn 在ks-master节点
[root@k8s-master ~]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
kubia-859d757f8c-74g6s 1/1 Running 0 33d 10.244.2.4 k8s-node2 <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-97znt 1/1 Running 0 33d 10.244.0.14 k8s-master <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-9mjf9 1/1 Running 0 33d 10.244.1.5 k8s-node1 <none> <none> app=kubia,pod-template-hash=859d757f8c
# 给k8s-master设置NoExecute污点,使已经落在本节点的Pod被驱赶出去
[root@k8s-master ~]# kubectl taint nodes k8s-master app=kubia:NoExecute
node/k8s-master tainted
# 可以看到pod kubia-859d757f8c-97znty因为不能容忍污点,结束了其生命周期
[root@k8s-master ~]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
kubia-859d757f8c-74g6s 1/1 Running 0 33d 10.244.2.4 k8s-node2 <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-97znt 0/1 Terminating 0 33d <none> k8s-master <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-9mjf9 1/1 Running 0 33d 10.244.1.5 k8s-node1 <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-jttrw 1/1 Running 0 46s 10.244.2.5 k8s-node2 <none> <none> app=kubia,pod-template-hash=859d757f8c
# 由于有了deployment控制器,那么就会在其他节点被创建
[root@k8s-master ~]# kubectl get pod -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
kubia-859d757f8c-74g6s 1/1 Running 0 33d 10.244.2.4 k8s-node2 <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-9mjf9 1/1 Running 0 33d 10.244.1.5 k8s-node1 <none> <none> app=kubia,pod-template-hash=859d757f8c
kubia-859d757f8c-jttrw 1/1 Running 0 79s 10.244.2.5 k8s-node2 <none> <none> app=kubia,pod-template-hash=859d757f8c
如果只有一个node节点,并且有污点,那么新建的Pod会一直处于Pending状态,因为没有可用的Node节点,这时候就可以使用容忍(Toleration)了。
容忍(Toleration)
设置了污点的Node将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute和Pod之间产生互斥的关系,Pod将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍(Toleration),意思是设置了容忍的 Pod 将可以在有相对应污点的 Node 中存在。
容忍(Toleration)的策略规则如下:
Pod.spec.tolerations
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"
- 其中 key、value、effect 要与Node中的 taint 保持一致
- operator 的值为 Exists 将会忽略 value 的值,值为 Equal 时则必须匹配相同的value
- tolerationSeconds 用于描述当Pod需要驱逐时可以在Node上继续保留运行的时间(即多少秒后被驱逐)
1、当不指定key时,表示容忍所有污点的key:
tolerations:
- operator: "Exists"
effect: "NoSchedule"
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: nginx
type: web
spec:
containers:
- name: pod-3
image: nginx:1.2.1
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
tolerations:
- operator: "Exists"
effect: "NoSchedule"
yaml策略为:可以容忍所有策略为NoSchedule的污点。
[root@k8s-master ~]# kubectl create -f pod3.yaml
pod/pod-3 created
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
pod-3 1/1 Running 0 9s 10.244.0.107 k8s-master
因为k8s-master为master节点,污点默认为NoSchedule,所以Pod-3被调度到master节点。
2、当不指定 effect 值时,表示容忍所有的污点类型
tolerations:
- key: "key1"
value: "value1"
operator: "Exists"
Pod容忍测试用例:
vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-2
labels:
app: nginx
type: web
spec:
containers:
- name: pod-2
image: nginx:1.2.1
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
tolerations:
- key: "check"
operator: "Equal"
value: "vfan"
以上策略表示:能够容忍所有key:value为check:vfan的污点类型。
[root@k8s-master ~]# kubectl create -f pod2.yaml
pod/pod-2 created
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pod-1 0/1 Pending 0 3m25s
pod-2 1/1 Running 0 4s
设置容忍的Pod,可以正常调度到Node节点,而没有设置容忍的,还一直处于Pending状态
3、具体指定 key、value、effect 值
[root@k8s-master ~]# cat torl.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
app: pod-1
spec:
containers:
- name: pod-1
image: nginx
tolerations:
- key: "app"
operator: "Equal"
value: "kubia"
effect: "NoExecute"
tolerationSeconds: 360
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-1 1/1 Running 0 109s 10.244.0.15 k8s-master <none> <none>
[root@k8s-master ~]# kubectl describe node k8s-master
Taints: app=kubia:NoExecute
可以看到容忍污点了,那么就可以运行了
最后别忘记将Node污点去除:
[root@k8s-master ~]# kubectl taint nodes k8s-master app=kubia:NoExecute-
node/k8s-master untainted
总结
为什么calio pod在每个节点都有,因为calio有污点容忍,所以在master节点也会创建Pod
[root@k8s-master ~]# kubectl get pod -o wide -n kube-system
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
calico-kube-controllers-5c6f6b67db-q5qb6 1/1 Running 3 22d 10.244.36.81 k8s-node1 <none> <none>
calico-node-6hgrq 1/1 Running 3 22d 192.168.179.102 k8s-master <none> <none>
calico-node-jxh4t 1/1 Running 3 22d 192.168.179.103 k8s-node1 <none> <none>
calico-node-xjklb 1/1 Running 6 22d 192.168.179.104 k8s-node2 <none> <none>
nodeSelector:
kubernetes.io/os: linux
tolerations:
# Make sure calico-node gets scheduled on all nodes.
- effect: NoSchedule
operator: Exists
# Mark the pod as a critical add-on for rescheduling.
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute
operator: Exists
flannel
tolerations:
- operator: Exists
effect: NoSchedule
一幅图总结如下: