k8s污点和容忍度

节点亲和性是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点。 这可能出于一种偏好,也可能是硬性要求。 Taint(污点)则相反,它使节点能够排斥一类特定的 Pod。

容忍度(Tolerations)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。

污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,

表示对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。

污点和容忍度可以认为是过滤的实现,当Pod不能容忍节点上的污点时,节点将被过滤。

污点(Taint)的组成

污点(Taint)是相对于节点来说,使用kubectl taint命令可以给某个Node节点设置污点,Node被设置上污点之后就和Pod之间存在一种互斥的关系,可以让Node拒绝Pod的调度执行,甚至将已经存在的Pod的驱逐出去。

每个污点的组成:

key=value:effect

每个污点都有对应的key和value,其中value可以为空。effect用于描述污点的作用。当前taint的effect支持如下三个选项:
NoSchedule:表示k8s将不会将Pod调度到具有该污点的Node上。

PreferNoSchedule:表示k8s将尽量避免将Pod调度到具有该污点的节点上。

NoExecute:表示k8s将不会将Pod调度到具有该污点的节点上,同时会将Node上已存在且不能容忍该污点的Pod驱逐出去。

一个Node可以有多个污点,一个Pod也可以设置多个容忍度和容忍时间,那么k8s将如何进行调度呢?后面会详细说明。

污点的设置、查看和去除

设置污点

kubectl taint node node1 key1=value1:NoSchedule

上面给node1这个节点设置一个key为key1,value为value1的污点,同时效果为NoSchedule,表示当Pod容忍这个污点时,才可以将Pod调度node1上,否则不能进行调度。
查看污点

kubectl describe node node1

去除污点

kubectl taint nodes node1 key:NoSchedule-

我们先将k8s-node01打上一个污点

kubectl taint node k8s-node01 gpu=false:NoSchedule

该命令表示k8s-node01这个节点没有gpu,不要将包含gpu计算任务的Pod调度到该节点。
我们创建一个名为nginx的Pod,然后查看一下这个Pod被调度到了哪个节点上。

kubectl run nginx --image=wangyanglinux/myapp:v1 --image-pull-policy=IfNotPresent
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/nginx created
kubectl get pod nginx-5445474cc8-8rxqg -o wide
NAME                     READY   STATUS              RESTARTS   AGE   IP       NODE         NOMINATED NODE   READINESS GATES
nginx-5445474cc8-8rxqg   0/1     ContainerCreating   0          57s   <none>   k8s-node02   <none>           <none>

我们发现Pod被调度到了k8s-node02这个节点上,这并不是因为随机调度到了k8s-node02节点,而是因为k8s-node01上存在着 gpu=false:NoSchedule 这个污点,Pod没有容忍这个污点,且调度策略为NoSchedule,即不能调度,因此Pod不会被调度到k8s-node01上。
容忍度(Toleration)
设置了污点的Node将根据taint的effect:NoSchedule、PreNoSchedule、NoExecute和Pod之间产生互斥的关系,Pod在一定程度上不会调度到Node上。但是我们可以在Pod中设置容忍(Toleration),意思是Pod可以容忍该污点的存在,能够被调度到有污点的Node上。
Toleration在pod.spec中进行定义,例如:

cat > pod-toleration.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: wangyanglinux/myapp:v1
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "gpu"
    operator: "Exists"
    effect: "NoSchedule"
EOF

默认的operator是Equal。当然了和污点一样,容忍度可以有多个。
一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果。并且对于value只而言,需要满足如下规则:
如果 operator 是 Exists (此时容忍度不能指定 value)。
如果 operator 是 Equal ,则它们的 value 应该相等。
说明, 存在两种特殊情况:
如果一个容忍度的 key 为空且 operator 为 Exists, 表示这个容忍度与任意的 key 、value 和 effect 都匹配,即这个容忍度能容忍任意taint。
如果 effect 为空,则可以与所有键名 key 的效果相匹配。
我们先创建Pod,然后查看nginx这个Pod的调度情况

kubectl create -f pod-toleration.yaml 
pod/nginx created
kubectl get pod -o wide
NAME    READY   STATUS              RESTARTS   AGE   IP       NODE         NOMINATED NODE   READINESS GATES
nginx   0/1     ContainerCreating   0          1s    <none>   k8s-node01   <none>           <none>

我们可以看到Pod被调度到了k8s-node01节点上(也可能被调度到k8s-node02上),这是由于在Pod的spec中定义了toleration,容忍度和污点相匹配。

前面讲述了,可以节点Node设置多个污点,那么当Pod也设置了多个容忍度,那么k8s的Scheduler该如何处理这种情况呢?
事实上Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点。

简而言之,存在着两个for循环,外循环是节点的污点,内循环是Pod声明的容忍度。从节点的污点开始遍历,依次遍历Pod声明的容忍度,查看容忍度是否和污点匹配。如果内循环结束仍然无法匹配,则表明Pod无法容忍该污点,根据污点的effect值,调度Pod。

特别是以下情况:

如果未被过滤的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 分配到该节点。

如果未被过滤的污点中不存在 effect 值为 NoSchedule 的污点, 但是存在 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 会 尝试 将 Pod 分配到该节点。

如果未被过滤的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

例如,假设给k8s-node01节点添加了如下污点

kubectl taint nodes k8s-node01 key1=value1:NoSchedule
kubectl taint nodes k8s-node01 key1=value1:NoExecute
kubectl taint nodes k8s-node01 key2=value2:NoSchedule

假定有一个 Pod,它有两个容忍度:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

在这种情况下,上述 Pod 不会被分配到上述节点,因为其没有容忍度和第三个污点相匹配。 但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的。
通常情况下,如果给一个节点添加了一个 effect 值为 NoExecute 的污点, 则任何不能忍受这个污点的 Pod 都会马上被驱逐, 任何可以忍受这个污点的 Pod 都不会被驱逐。 但是,如果 Pod 存在一个 effect 值为 NoExecute 的容忍度指定了可选属性 tolerationSeconds 的值,则表示在给节点添加了上述污点之后, Pod 还能继续在节点上运行的时间。例如,

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

这表示如果这个 Pod 正在运行,同时一个匹配的污点被添加到其所在的节点, 那么 Pod 还将继续在节点上运行 3600 秒,然后被驱逐。 如果在此之前上述污点被删除了,则 Pod 不会被驱逐。

通过污点和容忍度,可以灵活地让 Pod 避开 某些节点或者将 Pod 从某些节点驱逐。下面是几个使用例子:

专用节点:如果您想将某些节点专门分配给特定的一组用户使用,您可以给这些节点添加一个污点(即, kubectl taint nodes nodename dedicated=groupName:NoSchedule), 然后给这组用户的 Pod 添加一个相对应的 toleration(通过编写一个自定义的 准入控制器,很容易就能做到)。 拥有上述容忍度的 Pod 就能够被分配到上述专用节点,同时也能够被分配到集群中的其它节点。 如果您希望这些 Pod 只能被分配到上述专用节点,那么您还需要给这些专用节点另外添加一个和上述 污点类似的 label (例如:dedicated=groupName),同时 还要在上述准入控制器中给 Pod 增加节点亲和性要求上述 Pod 只能被分配到添加了 dedicated=groupName 标签的节点上。

配备了特殊硬件的节点:在部分节点配备了特殊硬件(比如 GPU)的集群中, 我们希望不需要这类硬件的 Pod 不要被分配到这些特殊节点,以便为后继需要这类硬件的 Pod 保留资源。 要达到这个目的,可以先给配备了特殊硬件的节点添加 taint (例如 kubectl taint nodes nodename special=true:NoSchedule 或 kubectl taint nodes nodename special=true:PreferNoSchedule), 然后给使用了这类特殊硬件的 Pod 添加一个相匹配的 toleration。 和专用节点的例子类似,添加这个容忍度的最简单的方法是使用自定义 准入控制器。 比如,我们推荐使用扩展资源 来表示特殊硬件,给配置了特殊硬件的节点添加污点时包含扩展资源名称, 然后运行一个 ExtendedResourceToleration 准入控制器。此时,因为节点已经被设置污点了,没有对应容忍度的 Pod 会被调度到这些节点。但当你创建一个使用了扩展资源的 Pod 时, ExtendedResourceToleration 准入控制器会自动给 Pod 加上正确的容忍度, 这样 Pod 就会被自动调度到这些配置了特殊硬件件的节点上。 这样就能够确保这些配置了特殊硬件的节点专门用于运行需要使用这些硬件的 Pod, 并且您无需手动给这些 Pod 添加容忍度。

基于污点的驱逐: 这是在每个 Pod 中配置的在节点出现问题时的驱逐行为。

基于污点的驱逐

1)Pod被驱逐的情况

前文提到过污点的 effect 值 NoExecute会影响已经在节点上运行的 Pod

如果 Pod 不能忍受 effect 值为 NoExecute 的污点,那么 Pod 将马上被驱逐。

如果 Pod 能够忍受 effect 值为 NoExecute 的污点,但是在容忍度定义中没有指定 tolerationSeconds,则 Pod 还会一直在这个节点上运行。

如果 Pod 能够忍受 effect 值为 NoExecute 的污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。

2)Node节点发生异常状态时的情况

当节点控制器检测到某个节点Node发生异常情况时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:

node.kubernetes.io/not-ready:节点未准备好。这相当于节点状态 Ready 的值为 “False”。
node.kubernetes.io/unreachable:节点控制器访问不到节点. 这相当于节点状态 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: 节点不可调度。

为了保证由于节点问题引起的 Pod 驱逐 速率限制行为正常, 系统实际上会以限定速率的方式添加污点。在像主控节点与工作节点间通信中断等场景下, 这样做可以避免 Pod 被大量驱逐。

使用这个功能特性,结合 tolerationSeconds,Pod 就可以指定当节点出现一个 或全部上述问题时还将在这个节点上运行多长的时间。

比如,一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段较长的时间, 愿意等待网络恢复以避免被驱逐。在这种情况下,Pod 的容忍度可能是下面这样的:

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

Kubernetes 会自动给 Pod 添加一个 key 为 node.kubernetes.io/not-ready 的容忍度 并配置 tolerationSeconds=300,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/not-ready 的容忍度。

同样,Kubernetes 会给 Pod 添加一个 key 为 node.kubernetes.io/unreachable 的容忍度 并配置 tolerationSeconds=300,除非用户提供的 Pod 配置中已经已存在了 key 为 node.kubernetes.io/unreachable 的容忍度。

这种自动添加的容忍度意味着在其中一种问题被检测到时 Pod 默认能够继续停留在当前节点运行 5 分钟。

describe一个Pod,查看默认设置的容忍度

......
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
......

原文地址:https://blog.csdn.net/weixin_43849415/article/details/108865140

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://sulao.cn/post/837.html

我要评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。