Kubernetes对Pod的几种调度及影响;

文章目录

  • 一、nodeSelector节点调度选择器
  • 二、k8s亲和性和反亲和性调度
    • 1.nodeAffinity(节点亲和性)
      • (1)硬策略
      • (2)软策略
    • 2.podAffinity、podAntiAffinity(pod亲和性和反亲和性)
      • (1)亲和性
      • (2)反亲和性
  • 三、nodeName直接调度
  • 四、k8s Node污点和Pod容忍
    • 1.污点(Taints) ——节点
      • (1)设置污点
      • (2)删除污点
    • 2.容忍(toleratints) ——Pod
      • (1)Deployment中设置容忍
      • (2)设置容忍时间
    • 3.容忍示例
      • (1)当Operator为Exists时
      • (2)当Operator为Equal时
  • 五、污点驱逐


一、nodeSelector节点调度选择器

用于将Pod调度到匹配LabelNode上,如果没有匹配的标签就会调度失败(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亲和性和反亲和性调度

k8s亲和性分为节点亲和性pod亲和性,其中pod还具有pod反亲和性

  • nodeAffinity(节点亲和性):根据节点的标签去调度Pod;
  • podAffinity(pod亲和性):根据已有的Pod标签去调度Pod;

亲和性调度可以分为软策略和硬策略:

  • 软策略(preferred):软策略就是优先满足调度要求的节点的话,如果没有满足的话,POD 就会忽略这条规则,继续完成调度过程;说白了就是优先满足符合条件的节点,但是不保证,如果没有符合条件的节点的话就会调度到其他节点上;
  • 硬策略(required):如果没有满足条件的节点的话,就不断重试直到满足条件为止,简单说就是你必须满足我的要求,不然就不干了;

1.nodeAffinity(节点亲和性)

nodeAffinity:节点亲和性,与nodeSelector作用一样,但相比nodeSelector更灵活,能满足更多条件,例如匹配有更多的逻辑组合,不只是字符串的完全相等,支持的操作符有:In、Notln、Exits、DoesNotExist、Gt、Lt;

例如:将pod分配到核心业务池(带有ResourcePool=kernel标签的node)上;

(1)硬策略

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标签的节点上;

(2)软策略

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状态,这就是软策略

2.podAffinity、podAntiAffinity(pod亲和性和反亲和性)

  • Pod 亲和性(podAffinity)主要解决 Pod 可以和哪些 Pod 部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等);
    用途:比如一个 Pod 在一个节点上了,那么我这个也得在这个节点(亲和性),
  • Pod 反亲和性(podAntiAffinity)主要是解决 Pod 不能和哪些 Pod 部署在同一个拓扑域中的问题;
    用途:假如你这个 Pod 在节点上了,那么我就不想和你待在同一个节点上(反亲和性)。

它们都是基于已经在节点上运行的pod标签来约束Pod可以调度的节点,而不是基于节点上的标签;

Pod 间亲和性与反亲和性的类型
与节点亲和性类似,Pod 的亲和性与反亲和性也有两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution
    用途:可以使用 requiredDuringSchedulingIgnoredDuringExecution 亲和性来告诉调度器, 将两个服务的 Pod 放到同一个云提供商可用区内,因为它们彼此之间通信非常频繁。
  • preferredDuringSchedulingIgnoredDuringExecution
    用途:可以使用 preferredDuringSchedulingIgnoredDuringExecution 反亲和性来将同一服务的多个 Pod 分布到多个云提供商可用区中(生产中常用的是 利用反亲和性将一个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   >           >

(1)亲和性

#应用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>

(2)反亲和性

#应用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直接调度

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是否有容忍来判断调度;
  • 不经过调度器:不会被节点污点影响,指哪打哪;


四、k8s Node污点和Pod容忍

在kubernetes中,节点亲和性(nodeAffinity)和Pod亲和性(podAffinity)都是Pod上定义的一种属性,能够使Pod按照我们的要求调度到某个节点上,而污点(Taints)则相反,它是Node上的一个属性,可以让Pod不能调度到带污点的节点上,甚至会对带污点节点上的Pod进行驱逐。当然对应的kubernetes可以给Pod设置容忍(Tolerations)属性Pod能够容忍节点上设置的污点,这样在调度时就会忽略节点上设置的污点,将Pod调度到该节点上。一般TaintsTolerations配合使用;
Kubernetes对Pod的几种调度及影响;_第1张图片

1.污点(Taints) ——节点

拒绝常规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;

(1)设置污点

一般情况下,我们想要某个节点只允许特定的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

(2)删除污点

上面给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
...

2.容忍(toleratints) ——Pod

为了使某些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会选择没有污点的节点,然后再次选择容忍污点的节点;

(1)Deployment中设置容忍

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"

(2)设置容忍时间

在正常的情况下,如果一个污点带有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就不会被剔掉;

3.容忍示例

Operator的值默认是Equal,可以设置为EqualExists两种;

(1)当Operator为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"

(2)当Operator为Equal时

容忍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选项行后添加;


你可能感兴趣的:(Kubernetes学习分享;,kubernetes,docker,运维)