Taints和Tolerations(污点和容忍)

Taints和Tolerations(污点和容忍)

上面介绍的NodeAffinity节点亲和性,是在pod上定义的一种属性,是pod能够被调度到某些node上运行(优先选择或强制要求)。Taint则正好相反,使用kubectl taint命令可以给某个Node节点设置污点,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝Pod的调度执行,甚至将Node已经存在的Pod驱逐出去

Taint需要和Toleration配合使用,让pod避开那些不适合的node。在node上设置一个或多个Taint之后,除非pod明确声明能够容忍这些污点,否则无法在这些node上运行。Toleration是pod的属性,让pod能够(注意,只是能够,而非必须)运行在标注了Taint的node上。

每个污点的组成如下:

key=value:effect

每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用

当前 taint effect 支持如下三个选项:

NoSchedule :表示k8s不会将新的不能容忍的Pod调度到具有该污点的Node上,但是之前运行在node节点中的Pod不受影响

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

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

可以用kubectl taint 命令为Node设置Taint信息:

kubectl taint nodes node1 env=pro:NoSchedule

查看所有Node上的污点

kubectl describe no|grep -e Hostname -e Taints|grep -v none|grep -A 1 "Taints"

查看指定node上的污点

kubectl describe no nodename|grep Taints

删除taint:

kubectl taint nodes node1 env:NoSchedule-

这个设置为node1加上一个Taint。该Taint的键为key,值为value,Taint的效果是NoSchedule。这意味着除非pod明确声明可以容忍这个Taint,否则就不会被调度到node1上。

然后,需要在pod上声明 Toleration。下面的两个Toleration都被设置为可以容忍(Toleration)具有该Taint的node,使得pod能够被调度到node1上:

apiVersion: v1

kind: Pod

metadata:

  name: pod-toleration

spec:

  tolerations:

  - key: "key"

    operator: "Equal"

    value: "value"

    effect: "NoSchedule"

  containers:

  - name: pod-toleration

    image: gcr.io/google_containers/pause:2.0

或者

tolerations:

  - key: "key"

    operator: "Exists"

    effect: "NoSchedule"

pod的toleration声明中的key和effect需要与Taint的设置保持一致,并且满足以下条件之一。

  • operator的值是Exists(无需指定value)

  • operator的值是Equal 并且value相等。

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

另外,有如下两个特例:

  • 空的key配合Exists操作符能够匹配所有的键和值。

  • 空的effect匹配所有的effect。 

系统允许在同一个Node上设置多个Taint也可以在Pod上设置多个TolerationKubernetes调度器处理多个Taint和Toleration的逻辑顺序为:首先列出节点中所有的Taint,然后忽略Pod的Toleration能够匹配的部分,剩下的没有忽略的Taint就是对Pod的效果了。下面是几种特殊情况。


  • 如果在剩余的Taint中存在effect=NoSchedule,则调度器不会把该Pod调度到这一节点上。

  • 如果在剩余的Taint中没有NoSchedule效果,但是有PreferNoSchedule效果,则调度器会尝试不把这个Pod指派给这个节点。

  • 如果在剩余的Taint中有NoExecute效果,并且这个Pod已经在该节点上运行,则会被驱逐;如果没有在该节点上运行,则也不会再被调度到该节点上。

例如,我们这样对一个节点进行Taint设置:

kubectl taint nodes node1 key1=value1:NoSchedule

kubectl taint nodes node1 key1=value1:NoExecute

kubectl taint nodes node1 key2=value2:NoSchedule

然后在Pod上设置两个Toleration:

tolerations:

  - key: "key1"

    operator: "Equal"

    value: "value1"

    effect: "NoSchedule"

  - key: "key1"

    operator: "Equal"

    value: "value1"

    effect: "NoExecute"

这样的结果是该Pod无法被调度到node1上,这是因为第3个Taint没有匹配的Toleration。但是如果该Pod已经在node1上运行了,那么在运行时设置第3个Taint,它还能继续在node1上运行,这是因为Pod可以容忍前两个Taint。


一般来说,如果给Node加上effect=NoExecute的Taint,那么在该Node上正在运行的所有无对应Toleration的Pod都会被立刻驱逐,而具有相应Toleration的Pod永远不会被驱逐。不过,系统允许给具有NoExecute效果的Toleration加入一个可选的tolerationSeconds字段,这个设置表明Pod可以在Taint添加到Node之后还能在这个Node上运行多久(单位为s):


 tolerations:

  - key: "key1"

    operator: "Equal"

    value: "value1"

    effect: "NoExecute"

    tolerationSeconds: 3600

上述定义的意思是,如果Pod正在运行,所在节点都被加入一个匹配的Taint,则这个Pod会持续在这个节点上存活3600s后被逐出。如果在这个宽限期内Taint被移除,则不会触发驱逐事件。

Taint和Toleration是一种处理节点并且让Pod进行规避或者驱逐Pod的弹性处理方式,下面列举一些常见的用例。


1.专用节点(dedicated)


如果想要拿出一部分节点专门给一些特定应用使用,则可以为节点添加这样的Taint:

kubectl taint nodes nodename dedicated=groupName:NoSchedule

然后给这些应用的Pod加入对应的Toleration。这样,带有合适Toleration的Pod就会被允许同使用其他节点一样使用有Taint的节点。

通过自定义Admission Controller也可以实现这一目标。如果希望让这些应用独占一批节点,并且确保它们只能使用这些节点,则还可以给这些Taint节点加入类似的标签dedicated=groupName,然后Admission Controller需要加入节点亲和性设置,要求Pod只会被调度到具有这一标签的节点上。

2.具有特殊(special)硬件设备的节点

在集群里可能有一小部分节点安装了特殊的硬件设备(如GPU芯片),用户自然会希望把不需要占用这类硬件的Pod排除在外,以确保对这类硬件有需求的Pod能够被顺利调度到这些节点。

可以用下面的命令为节点设置Taint:

kubectl taint nodes nodename special=true:NoSchedule

kubectl taint nodes nodename special=true:PreferNoSchedule

然后在Pod中利用对应的Toleration来保障特定的Pod能够使用特定的硬件。

和上面的独占节点的示例类似,使用Admission Controller来完成这一任务会更方便。例如,Admission Controller使用Pod的一些特征来判断这些Pod,如果可以使用这些硬件,就添加Toleration来完成这一工作。要保障需要使用特殊硬件的Pod只被调度到安装这些硬件的节点上,则还需要一些额外的工作,比如将这些特殊资源使用opaque-int-resource的方式对自定义资源进行量化,然后在PodSpec中进行请求;也可以使用标签的方式来标注这些安装有特别硬件的节点,然后在Pod中定义节点亲和性来实现这个目标。

3.基于污点的驱逐,以应对节点故障

前面提到的NoExecute这个Taint效果对节点上正在运行的Pod有以下影响。

◎ 没有设置Toleration的Pod会被立刻驱逐。

◎ 配置了对应Toleration的Pod,如果没有为tolerationSeconds赋值,则会一直留在这一节点中。

◎ 配置了对应Toleration的Pod且指定了tolerationSeconds值,则会在指定时间后驱逐。

◎ 把节点故障标记为Taint

当某种条件为真时,节点控制器会自动给节点添加一个污点。当前内置的污点包括:


  • 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: 节点不可调度。

  • node.cloudprovider.kubernetes.io/uninitialized:如果 kubelet 启动时指定了一个 "外部" 云平台驱动, 它将给当前节点添加一个污点将其标志为不可用。在 cloud-controller-manager 的一个控制器初始化这个节点后,kubelet 将删除这个污点。

在节点被驱逐时,节点控制器或者 kubelet 会添加带有 NoExecute 效应的相关污点。 如果异常状态恢复正常,kubelet 或节点控制器能够移除相关的污点。

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

例如,一个包含很多本地状态的应用可能需要在网络发生故障时,还能持续在节点上运行,期望网络能够快速恢复,从而避免被从这个节点上驱逐。

Pod的Toleration可以这样定义:

tolerations :

  - key : "node.kubernetes.io/unreachable"

    operator:"Exists"

    effect : "NoExecute"

    tolerationSeconds : 6000

说明:

如果没有为Pod指定node.kubernetes.io/not-ready的Toleration,那么Kubernetes会自动为Pod加入tolerationSeconds=300 的node.kubernetes.io/not-ready类型的Toleration。

同样,如果Pod没有定义node.kubernetes.io/unreachable的Toleration,那么系统会自动为其加入tolerationSeconds=300 的node.kubernetes.io/unreachable类型的Toleration。

这些系统自动设置的toleration在Node发现问题时,能够为Pod确保驱逐前再运行5min。这两个默认的Toleration由AdmissionController“DefaultTolerationSeconds”自动加入。

DaemonSet 中的 Pod 被创建时, 针对以下污点自动添加的 NoExecute 的容忍度将不会指定 tolerationSeconds:

  • node.kubernetes.io/unreachable

  • node.kubernetes.io/not-ready

这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐。


猜你喜欢

转载自blog.51cto.com/7072753/2621488