污点和容忍机制

节点亲和性(affinity),是 pods 的一种属性,可以将 pods 调度到一类节点上去(作为优先选择或者一个硬性要求)。污点(Taints)则相反,它们允许节点排斥一类 pods。

Taints 和 tolerations 一起工作以确保 pods 不调度到不适合的节点上去。一个或者多个 taints 规则应用于节点,这标记节点不会接受任何没有容忍这些 taints 规则的 pods。Tolerations 规则应用于 pods,并且允许(非强制)这些 pods 调度到匹配 taints 规则的节点上。

概念

可以通过 kubectl taint 给节点添加一个 taint 规则。如:

kubectl taint nodes node1 key=value:NoSchedule

给节点 node1 标记了一个 taint。这个 taint 包含键 key,值 value,以及 taint effect NoSchedule。这意味着没有 pod 能够调度到 node1 上,除非它有匹配的 toleration。

如果要移除刚刚添加的 taint,可以运行:

kubectl taint nodes node1 key:NoSchedule-

你可以在 PodSpec 字段指定一个 toleration。以下两个 tolerations 都匹配上面通过 kubectl taint 创建的 taint,因此有任何一个 toleration 都可以调度到 node1

tolerations:
- key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
tolerations:
- key: "key"
  operator: "Exists"
  effect: "NoSchedule"

这儿有一个 pod 使用 tolerations 的例子:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

如果在 keyeffect 相同的情况下,toleration 则匹配 taint,其中:

  • operatorExists(在这种情况下不应指定任何 value),或

  • operatorEqualvalue 相等

如果不指定 operator,默认为 Equal

注意: 这里有两个特殊的案例:

  • key 为空,operatorExists 则匹配所有的键值和效果,意味着可以 tolerate 一切。

tolerations:
- operator: "Exists"
  • 一个空的 effect 匹配所有键为 key 的效果。

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

上面的例子使用了 effectNoSchedule。或者,你可以使用 effectPreferNoSchedule。这是 NoSchedule 的 “优先选项” 或者 “软” 版本 -- 系统会尝试避免调度一个没有容忍 taint 的 pod 到该节点上,但是这不是强制的。第三种类型的 effectNoExecute,后面再描述。

你可以在同一个节点上设置多个 taints,也可以在同一个 pod 上设置多个 tolerations。Kubernetes 处理多个 taints 和 tolerations 的方式就像一个过滤器:遍历一个节点上的所有 taints,然后忽略 pod 上有匹配 toleration 的 taints。其它未忽略的 taints 会对 pod 产生作用。特别是:

  • 如果至少有一个 effectNoSchedule 的未忽略的 taint,那么 Kubernetes 将不会在该节点上调度这个 pod

  • 如果没有 effectNoSchedule 的未忽略 taint,但至少有一个 effectPreferNoSchedule 的未忽略的 taint,那么 Kubernetes 会尝试不让该 pod 调度到此节点上

  • 如果有至少一个 effectNoExecute 的未忽略 taint,那么 pod 会从该节点上驱离(如果 pod 已经运行在该节点),并且不会被调度到该节点(如果 pod 没有在节点运行)。

举例来说,有个节点有如下 taint:

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

pod 有两个 tolerations:

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

这个案例,pod 不会被调度到这个节点上,因为 pod 没有匹配第三个 taint。但是如果在节点添加 taint 的时 pod 已经运行了,那么 pod 会继续在该节点运行(简单说 NoSchedule 只在调度时生效)。

正常情况下,如果一个 effectNoExecute 的 taint 添加到节点,那些没有容忍此 taint 的将会被直接驱逐,然后那些容忍这个 taint 的 pods 将永远不会被驱逐。另外,一个 effectNoExecute 的 toleration 可以指定一个可选的 tolerationSeconds 字段,以指示当节点 taint 被添加之后 pod 运行在节点的时长,如:

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

意思是 pod 在节点运行时,当一个匹配的 taint 被添加到该节点后,那么这个 pod 将继续在当前节点运行 3600s,之后会被驱逐。如果 taint 在时间到达之前被移除,那么 pod 不会被驱逐。

用例

Taints 和 tolerations 是一种灵活的方式,将 pod 从节点上移除或驱逐不应运行的 pods。

  • 专用节点: 如果你想将一组节点给一类特定的用户使用,你可以在这些节点上添加 taint(也就是说,kubectl taint nodes nodename dedicated=groupName:NoSchedule )并且在他们的 pods 上添加 toleration(通过自定义准入控制器会更容易做到)。这些拥有 tolerations 的 pods 将被允许使用 tainted 的节点以及集群中的其它节点。如果你想要专门使用这些节点并确保只会使用这些节点,那么还需要像给这组节点添加 taint 一样添加一个标签(如,kubectl label nodes nodename edicated=groupName),并且准入控制器还要添加一个节点 affinity,以要求 pods 只能调度到有 dedicated=groupName 标签的节点上。

  • 拥有特定硬件的节点: 在一小部分节点具有专用硬件(如 GPU)的集群中,最好将不需要专用硬件的 pods 排除在这些节点之外,从而为专用硬件的 pods 留出空间。这个可以通过给特定硬件的节点添加 taint(如 kubectl taint nodes nodename special=true:NoSchedule 或者 kubectl taint nodes nodename special=true:PreferNoSchedule)并在需要使用这些特定硬件的 pods 上添加相应的 toleration。像在专用节点用例中一样,使用自定义准入控制器来应用 tolerations 可能是最简单的。举例来说,推荐使用 Extended Resources 来表示特殊硬件,用扩展资源的名称 taint 你的特定硬件并且运行 ExtendedResourceToleration 准入控制器。现在,因为节点已经被 tainted,没有相应 toleration 的 pods 不会被调度到这些节点,ExtendedResourceToleration 准入控制器会自动的向 pods 添加正确的 toleration,然后 pod 将会被调度在这些特定硬件的节点上。这样可以确保这些特定硬件的节点被请求此类硬件的 pods 使用,而你不需要手动向 pods 中添加 tolerations。

  • 通过 taint 驱逐: 节点出现问题时,可以按每个节点配置驱逐行为,这个后面详解。

通过 taint 驱逐

前面我们提到了 NoExecute taint effect,它会影响已经运行在节点的 pods:

  • 没有相应 tolerations 的 pods 将会被直接驱逐

  • 有相应 tolerations 并且未指定 tolerationSeconds 的 pods 永远保持运行

  • 有相应 tolerations 且指定 tolerationSeconds 的 pods 会在时间到达之后被驱逐

此外,Kubernetes 1.6 时以 alpha 状态引入该功能来支持表示节点问题。换句话说,当满足特定条件时,节点控制器会自动对节点进行 taints。以下为内建的 taints:

  • node.kubernetes.io/not-ready:节点没有就绪。对应 NodeCondition Ready 为 "False"

  • node.kubernetes.io/unreachable:从节点控制器无法访问到节点。对应 NodeCondition ReadyUnknown

  • 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:当使用外部 cloud provider 启动 kubelet 时,将节点设置 taint 以标记为不可用。来自 cloud-controller-manager 的控制器初始化此节点后,kubelet 删除此 taint。

如果要驱逐节点,则节点控制器或者 kubelet 会添加具有 NoExecuteeffect 相关 taints。如果故障情况恢复正常,则 kubelet 或节点控制器会移除相关的 taints。

注意: 为了维持由于节点问题导致驱逐的速率限制行为,系统实际以速率限制的方式添加 taints。 这样可以防止主节点和节点连接中断发生大规模的 pod 驱逐。

该功能和 tolerationSeconds 结合使用,允许一个 pod 指定当一个或者多个问题的时候在节点运行的时间。

例如,一个具有很多本地状态的应用在网络中断事件中像停留一段事件,以期望网络能够恢复,这样可以避免 pod 被驱逐。这个 pod 的 toleration 设置可以如下:

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

注意 Kubernetes 会自动添加一个 node.kubernetes.io/not-readytolerationSeconds=300 的 toleration,除非 pod 配置中已经设置 node.kubernetes.io/not-ready 的 toleration。同样的它会添加一个 node.kubernetes.io/unreachabletolerationSeconds=300 的 toleration,除非 pod 配置中已经设置 node.kubernetes.io/unreachable 的 toleration。

这些自动添加的 tolerations 确保在检测到这些问题之后,pod 默认会在当前节点保留运行 5 分钟。这两个默认 tolerations 被 DefaultTolerationSeconds admission controller 添加。

DaemonSet 创建含有 NoExecute tolerations 的 pods 针对以下 taints 没有 tolerationSeconds 选项:

  • node.kubernetes.io/unreachable

  • node.kubernetes.io/not-ready

这样可以确保 DeamonSet pods 不会因为这些问题而被驱逐。

根据节点状态 taint

节点生命周期控制器自动会为对应节点状态的节点创建 effectNoSchedule 的 taints。同样调度器不会检查节点状态,而是检查 taints。这样可以保证节点状态不会影响到已调度的 pods。用户可以通过添加适当的 Pod tolerations 以选择忽略某些节点问题。

从 Kubernetes 1.8 开始,DaemonSet 控制器自动给所有的 daemons 添加 NoSchedule tolerations,以阻止 DaemonSets 中断。

  • node.kubernetes.io/memory-pressure

  • node.kubernetes.io/disk-pressure

  • node.kubernetes.io/out-of-disk (only for critical pods)

  • node.kubernetes.io/unschedulable (1.10 or later)

  • node.kubernetes.io/network-unavailable (host network only)

添加这些 tolerastions 确保向后兼容。你可以向 DaemonSets 中添加任意的 tolerations。

扩展阅读

最后更新于