K8s污点容忍度横向主节点

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K8s污点容忍度横向主节点相关的知识,希望对你有一定的参考价值。

参考技术A

污点是K8s高级调度的特性,用于限制哪些Pod可以被调度到某一个节点。在普通节点横向时我们可以使用污点容忍度创建恶意pod来对主节点进行横向控制。

kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群控制面(master)的一部分。对每一个新创建的Pod或者是未被调度的Pod, kube-scheduler 会选择一个最优的Node去运行这个Pod。

然而, Pod 内的每一个容器对资源都有不同的需求,而且Pod本身也有不同的资源需求。因此,Pod在被调度到Node上之前,根据这些特定的资源调度需求,需要对集群中的Node进行一次过滤。

当创建pod时候,会首先把创建的命令请求提交给apiserver,通过一系列认证授权,apiserver把pod数据存储到etcd,创建deployment资源并初始化。然后再是scheduler通过进行list-watch机制进行监测,经过 调度算法 把pod调度到某个node节点上,最后信息更新到etcd,再后面就是kubelet接受信息到创建容器。

当前调度器选择适当的节点时,调度程序会检查每个节点是否有足够的资源满足 Pod 调度,比如查看CPU和内存限制是否满足:

通过资源限制调度程序可确保由于过多 Pod 竞争消耗节点所有可用资源,从而导致节点资源耗尽引起其他系统异常。

在创建pod的时候,节点选择器可以约束pod在特定节点上运行。

nodeSelector 也是节点选择约束的最简单推荐形式, nodeSelector 字段添加到 Pod 的规约中设置希望目标节点所具有的 节点标签 。 K8s 只会将 Pod 调度到拥有你所指定的每个标签的节点上。

例子, 比如多个节点需要调度时候,通过给1,2节点打上标签,创建pod时候使用节点选择器,那么pod会被按照节点选择器希望的目标在相应节点调度。

为节点打上标签:

kubectl label node nodename env_role=env

查看节点的标签:

kubectl get nodes nodename --show-labels

节点亲和性概念上类似于 nodeSelector , 它使可以根据节点上的标签来约束 Pod 可以调度到哪些节点上,这种方法比上面的 nodeSelector 更加灵活,它可以进行一些简单的逻辑组合了,不只是简单的相等匹配。

节点亲和性和节点选择器相比功能更强大,比如还是刚才的图,如果我使用节点选择器 env_role:dev1 的话是找不到相应的节点的,就没有办法调度,会一直是一个等待的状态:

但我如果使用节点亲和性,就算当前没有这个节点,我还是可以根据调度调度策略进行调度,不只是简单的相等匹配。

调度可以分成软策略( 软亲和性 )和硬策略( 硬亲和性 )两种方式:

如图可以看到软亲和性和硬亲和性的字段其实差不多,软亲和性多了一个 weight 字段,表权重:

如上亲和性还有一个字段是 operator 表匹配的逻辑操作符,可以使用 descirbe 命令查看具体的调度情况是否满足我们的要求, K8s 提供的操作符有下面的几种:

如果 nodeSelectorTerms 下面有多个选项的话,满足任何一个条件就可以了;如果 matchExpressions 有多个选项的话,则必须同时满足这些条件才能正常调度 POD。

容忍度( Toleration )是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。污点说白了就是不做普通的调度。

对于节点亲和性无论是软亲和性和硬亲和性,都是调度 POD 到预期节点上,而污点( Taints )恰好与之相反,如果一个节点标记为 Taints , 除非 POD 也被标识为可以容忍污点节点,否则该 Taints 节点不会被调度pod

查看污点情况:

kubectl describe node nodename | grep Taint

可以看到,默认污点也只有master有。

污点里的值有三种:

NoSchedule 就是字面意思,不会被调度, PreferNoSchedule 说白了是尽量不被调度, NoExecute 是不会调度并且还会驱逐 node 已有的 pod 。

创建一个pod:

如果不加污点,可以看到这个pod会随机调度到节点1或者节点2:

这时候把pod删除了,重新创建pod并且给node加上污点:

给节点打污点:

kubectl taint node nodename key=value:NoSchedule

重新创建pod并且deployment多个:

可以发现全部被调度在节点2上,节点1的污点 NoSchedule 起了作用。

删除污点:

容忍度 tolerations 是定义在 Pod 对象上的键值型属性数据,用于配置其可容忍的节点污点,而且调度器仅能将 Pod 对象调度至其能够容忍该节点污点的节点之上。

污点定义在节点的 node Spec 中,而容忍度则定义在 Pod 的 podSpec 中,它们都是键值型数据。

在 Pod 对象上定义容忍度时,它支持两种操作符:一种是等值比较 Equal ,表示容忍度与污点必须在 key 、 value 和 effect 三者之上完全匹配;另一种是存在性判断 Exists ,表示二者的 key 和 effect 必须完全匹配,而容忍度中的 value 字段要使用空值。

这里的key和value对应的值都是你自己设置的key和value:

说白了就是:

而污点容忍的作用举个例子,如果像上面污点一样设置了 NoSchedule 污点的节点,那么创建pod的时候是必不被调度到的,但是如果我使用污点容忍,那这个节点可以在设置 NoSchedule 污点的情况下可能又被调度,类似于亲和性那种作用。

污点和污点容忍度的作用也就是获取 主节点的shell ,因为像常见或者节点shell的流程是创建pod--》分配到正常node---》通过常规挂载目录拿到节点的shell,而默认主节点是不被调度的,所以只有使用污点容忍度,创建一个能够被调度到master节点的pod,然后通过挂载之类的手法来拿到主节点的shell。

通过创建一个具有 node-role.kubernetes.io/master:NoSchedule 的容忍度让Pod被Kubernetes Master所调度。

如上的Pod中将宿主机的根目录挂载到容器中(volumes与volumeMounts)即可逃逸至Kubernetes Master中接管集群。

查看节点,当前是在普通节点:

多次创建可以发现在master节点上了:

可以通过挂载操作master节点母机shell:

k8s 污点和容忍

文章目录

污点和容忍

在 Kubernetes 中,节点亲和性 NodeAffinity 是 Pod 上定义的一种属性,能够使 Pod 按我们的要求调度到某个节点上,而 Taints(污点) 则恰恰相反,它是 Node 上的一个属性,可以让 Pod 不能调度到带污点的节点上,甚至会对带污点节点上已有的 Pod 进行驱逐。当然,对应的 Kubernetes 可以给 Pod 设置 Tolerations(容忍) 属性来让 Pod 能够容忍节点上设置的污点,这样在调度时就会忽略节点上设置的污点,将 Pod 调度到该节点。一般时候 Taints 通常与 Tolerations 配合使用。

  • 一个 node 可以有多个污点;
  • 一个 pod 可以有多个容忍;
  • kubernetes 执行多个污点和容忍方法类似于过滤器

如果一个 node 有多个污点,且 pod 上也有多个容忍,只要 pod 中容忍能包含 node 上设置的全部污点,就可以将 pod 调度到该 node 上。如果 pod 上设置的容忍不能够包含 node 上设置的全部污点,且 node 上剩下不能被包含的污点 effect 为 PreferNoSchedule,那么也可能会被调度到该节点。

注意:

当 pod 存在容忍,首先 pod 会选择没有污点的节点,然后再次选择容忍污点的节点

  • 如果 node 上带有污点 effect 为 NoSchedule,而 pod 上不带响应的容忍,kubernetes 就不会调度 pod 到这台 node 上。
  • 如果 Node 上带有污点 effect 为 PreferNoShedule,这时候 Kubernetes 会努力不要调度这个 Pod 到这个 Node 上。
  • 如果 Node 上带有污点 effect 为 NoExecute,这个已经在 Node 上运行的 Pod 会从 Node 上驱逐掉。没有运行在 Node 的 Pod 不能被调度到这个 Node 上。一般使用与当某个节点处于 NotReady 状态下,pod 迅速在其他正常节点启动。

污点(Taints)

查看污点:

$ kubectl get nodes k8s-master -o go-template=.spec.taints
[map[effect:NoSchedule key:node-role.kubernetes.io/master]]


污点内容一般组成为 key、value 及一个 effect 三个元素,表现为:

```go
<key>=<value>:<effect>

这里的 value 可以为空,表现形式为:

node-role.kubernetes.io/master:NoSchedule
- key: node-role.kubernetes.io/master
- value:- effect: NoSchedule

设置污点

一般我们需要想要设置某个节点只允许特定的 Pod 进行调度,这时候就得对节点设置污点,可以按 kubectl taint node nodename key=value:effect 格式进行设置,其中 effect 可取值如下:

  • PreferNoSchedule: 尽量不要调度。
  • NoSchedule: 一定不能被调度。
  • NoExecute: 不仅不会调度, 还会驱逐 Node 上已有的 Pod。

一般时候我们设置污点,就像下面例子一样对其进行设置:

# 设置污点并不允许 Pod 调度到该节点
$ kubectl taint node k8s-master key1=value1:NoSchedule
# 设置污点尽量阻止污点调度到该节点
$ kubectl taint node k8s-master key2=value2:PreferNoSchedule
# 设置污点,不允许普通 Pod 调度到该节点,且将该节点上已经存在的 Pod 进行驱逐
$ kubectl taint node k8s-master key3=value3:NoExecute

删除污点

上面说明了如何对 Node 添加污点阻止 Pod 进行调度,下面再说一下如何删除节点上的污点,可以使用下面命令:

kubectl taint node [node] [key]-

上面语法和创建污点类似,不过需要注意的是删除污点需要知道 key 和最后面设置一个 “-” 两项将污点删除,示例如下:

为了方便演示,先给节点设置污点:

# 设置污点1
$ kubectl taint node k8s-master key1=value1:PreferNoSchedule
node/k8s-master tainted
# 设置污点2
$ kubectl taint node k8s-master key2=value2:NoSchedule
node/k8s-master tainted
# 设置污点3,并且不设置 value
$ kubectl taint node k8s-master key2=:PreferNoSchedule
node/k8s-master tainted

查看污点,可以看到上面设置的三个值:

$ kubectl describe nodes k8s-master
...
Taints:             key2=value2:NoSchedule
                    node-role.kubernetes.io/master:NoSchedule
                    key1=value1:PreferNoSchedule
                    key2:PreferNoSchedule
...

然后删除污点

删除污点,可以不指定 value,指定 [effect] 值就可删除该 key[effect] 的污点
$ kubectl taint node k8s-master key1:PreferNoSchedule-

也可以根据 key 直接将该 key2 的所有 [effect] 都删除:
$ kubectl taint node k8s-master key2-

再次查看污点,可以看到以上污点都被删除:

$ kubectl describe nodes k8s-master
...
Taints:             node-role.kubernetes.io/master:NoSchedule
...

容忍 (toleratints)

Pod 设置容忍

为了使某些 Pod 禁止调度到某些特定节点上,就可以对节点设置污点 taints。当然,如果希望有些 Pod 能够忽略节点的污点,继续能够调度到该节点,就可以对 Pod 设置容忍,让 Pod 能够容忍节点上设置的污点,例如:

对一个节点设置污点:

kubectl taint node k8s-node01 key=value:NoSchedule

对于 Pod 设置容忍, 以下两种方式都可以:

# 容忍的 key、value 和对应 effect 也必须和污点 taints 保持一致
......
tolerations:
- key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
# 容忍 tolerations 的 key 和要污点 taints 的 key 一致,且设置的 effect 也相同,不需要设置 value
......
tolerations:
- key: "key"
  operator: "Exists"
  effect: "NoSchedule"

如果不指定 operator,则 operator 默认为 equal。

此外,有如下两个特例:

  • 空的 key 配合 Exist 操作符可以匹配所有的键值对
  • 空的 effect 匹配所有的 effect

那么,问题来了:
如果我的集群中有三个 node,我有两组 pod 要部署,每组 pod 要部署到同一个节点上,且两组 pod 不能部署到同一节点上,怎么操作最简单?

(好吧,nodeSelector 最简单…)


apiVersion: apps/vl
kind: Deployment
metadata:
  name: example
spec:
  replicas: 5
  template:
    spec:
      ......
      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: 3600

如果这个 Pod 已经在这个带污点且 effect 为 NoExecute 的 node 上。这个 pod 可以一直运行到 3600s 后再被踢掉。如果这时候 Node 的污点被移除了,这个 Pod 就不会被踢掉。


容忍示例

Operator 默认是 Equal,可设置为 EqualExists 两种,按这两种进行示例:

Operator 是 Exists

容忍任何污点

例如一个空的 key,将匹配所有的 key、value、effect。即容忍任何污点。

tolerations:
- operator: "Exists"

容忍某 key 值的污点

例如一个空的 effect,并且 key 不为空,那么将匹配所有与 key 相同的 effect:

tolerations:
- key: "key"
  operator: "Exists"

Operator 是 Equal

node 上有一个污点

Node 和 Pod 的 key 为 key1、value1 与 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 为 NoSchedule 和 NoExecute 的,不会被调度:

## 污点
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"

对比理解 Exists 和 Equal 之间的区别:

  • Exists 是包含,Equal 是等于,Exists 使用范围更广,而 Equal 则是精准匹配。
  • 当污点中存在 NoExecute 时,而容忍中不存在 NoExecute 时,不会被调度到该节点。
  • Exists 可以不写 value , 而 Equal 则一定要指定对应的 value

节点自污染,pod 应对节点故障

此外,当某些条件为 true 时,节点控制器会自动污染节点。内置以下污点:

key注释
node.kubernetes.io/not-ready节点尚未准备好。这对应于 NodeCondition Ready 为 false。
node.kubernetes.io/unreachable无法从节点控制器访问节点。这对应于 NodeCondition Ready 为 Unknown。
node.kubernetes.io/out-of-disk节点磁盘不足。
node.kubernetes.io/memory-pressure节点有内存压力。
node.kubernetes.io/disk-pressure节点有磁盘压力。
node.kubernetes.io/network-unavailable节点的网络不可用。
node.kubernetes.io/unschedulable节点不可调度。
node.cloudprovider.kubernetes.io/uninitialized当 kubelet 从 “外部” 云提供程序开始时,此污点在节点上设置为将其标记为不可用。来自 cloud-controller-manager 的控制器初始化此节点后,kubelet 删除此污点。

通过上面知识的铺垫,当一个节点宕机时,kubernetes 集群会给它打上什么样的污点呢?

一个 Ready 状态的节点

$ kubectl get node k8s-node02 -o go-template=.spec.taints
<no value>

一个 NotReady 状态的节点

$ kubectl get node k8s-node02 -o go-template=.spec.taints
[map[effect:NoSchedule key:node.kubernetes.io/unreachable timeAdded:2022-07-0423T13:49:58Z] 
map[effect:NoExecute key:node.kubernetes.io/unreachable timeAdded:2021-07-04T13:50:03Z]]

处于 NotReady 状态的节点被打上了下面两个污点:

Taints:             node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule

接下来测试 kubernetes 集群会给 Pod 分配什么样的容忍。
注:这两个是 kubernetes 自动给 pod 添加的容忍。

$ kubectl get po nginx-745b4df97d-mgdmp -o yaml
...
  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:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      tolerations:
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 0
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 0
      containers:
      - image: nginx:alpine
        name: nginx

生成 Pod

$ kubectl get po -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
nginx-84f6f75c6-c76fm   1/1     Running   0          6s    10.244.3.16   k8s-node02   <none>           <none>
nginx-84f6f75c6-hsxq5   1/1     Running   0          6s    10.244.3.15   k8s-node02   <none>           <none>
nginx-84f6f75c6-wkt52   1/1     Running   0          6s    10.244.1.63   k8s-node01   <none>           <none>
nginx-84f6f75c6-xmkjs   1/1     Running   0          6s    10.244.3.17   k8s-node02   <none>           <none>

接下来强制关闭 k8s-node02 节点,查看 Pod 是否转移。

$ kubectl get po -o wide
NAME                    READY   STATUS        RESTARTS   AGE    IP            NODE         NOMINATED NODE   READINESS GATES
nginx-84f6f75c6-c76fm   1/1     Terminating   0          116s   10.244.3.16   k8s-node02   <none>           <none>
nginx-84f6f75c6-csqf4   1/1     Running       0          13s    10.244.1.66   k8s-node01   <none>           <none>
nginx-84f6f75c6-hsxq5   1/1     Terminating   0          116s   10.244.3.15   k8s-node02   <none>           <none>
nginx-84f6f75c6-r2v4p   1/1     Running       0          13s    10.244.1.64   k8s-node01   <none>           <none>
nginx-84f6f75c6-v4knq   1/1     Running       0          13s    10.244.1.65   k8s-node01   <none>           <none>
nginx-84f6f75c6-wkt52   1/1     Running       0          116s   10.244.1.63   k8s-node01   <none>           <none>
nginx-84f6f75c6-xmkjs   1/1     Terminating   0          116s   10.244.3.17   k8s-node02   <none>           <none>

在 node 节点转为 NotReady 状态后,Pod 立刻进行了转移。这是通过 在 yaml 清单文件中明确指定 容忍时间。还可以直接修改 apiserver 配置来修改默认容忍时间。

vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.1.11
    - --default-not-ready-toleration-seconds=1    ## 新增行
    - --default-unreachable-toleration-seconds=1  ## 新增行
...

修改保存后, kube-apiserver-k8s-masterpod 会自动重载最新配置。

$ kubectl get po nginx-84f6f75c6-wkt52 -o yaml
...
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 0
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 0
...

对于小型集群,可以直接设置全局变量。

注意:当 kubernetes 集群只有一个 node 节点时,无法做到 Pod 转移,因为 Pod 已经无路可退了。

以上是关于K8s污点容忍度横向主节点的主要内容,如果未能解决你的问题,请参考以下文章

k8s 污点和容忍

k8s 污点和容忍

k8s 污点和容忍

k8s学习-污点和容忍(概念模版创建删除)

k8s学习-污点和容忍(概念模版创建删除)

k8s 亲和反亲和污点容忍