Kubernetes 中 Taints 和 Tolerations 是调度系统的重要机制之一,通过它们管理服务可以确保 Pod 不会被调度到不合适的节点上。

本文将简单介绍 Kubernetes 的 Taints 和 Tolerations 机制。

Taints

Taints 是 Kubernetes 中定义在 Node 对象上的一些标签,不同于 Labels 和 Annotations 机制使用 key=values 记录信息,Taints 添加了 effect 属性,使用 key=value:effect 的格式描述,其中,Key 和 Value 可以是用户自定义的字符串,而 effect 表示该 Taints 对 Kubernetes 调度 Pod 产生何种影响,目前支持以下三种类型:

  • NoExecute:已经运行在节点的 Pod 将会被驱逐(evicted),并且新的 Pod 不会调度到该节点。
  • NoSchedule:已经运行在节点的 Pod 不受到影响,新的 Pod 不会调度到该节点。
  • PreferNoSchedule:已经运行在节点的 Pod 不受到影响,尽量避免新的 Pod 调度到该节点。

通过 kubectl taint 命令我们可以在快速给 Node 添加污点:

1
2
3
4
# 添加污点,其效果是 NoSchedule ,后续 Pod 不会调度到该节点
kubectl taint nodes <node-name> key1=value1:NoSchedule
# 添加污点,其效果是 NoExecute ,此时省略了 value 值(默认为空字符串)
kubectl taint nodes <node-name> key2:NoExecute-

Tolerations

Tolerations 是定义在 Pod 上的信息,这些信息告诉 Kubernetes 管理服务,指定的 Taints 是否会对这个 Pod 的调度以及故障迁移(Evicted)产生影响。

用户可以通过以下方式定义多个 Tolerations:

1
2
3
4
5
tolerations:
- key: "key1" 
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"

上述定义表示:污点 key1=value1:NoSchedule 不会对这个 pod 的调度产生影响,用户允许 schedule 将这些 pod 调度到拥有这些污点的 Node 。

1
2
3
4
5
6
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

上述定义表示:污点 key1=value1:NoExecute,在前 3600s 内不会对 pod 产生影响,当 Node 上污点存在超过 3600s 时 Pod 将被迁移到其他节点。

在定义 tolerations 是我们可以省略 value 或者同时省略 key 和 value ,此时需要指定 operator 取值为 Exists,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 省略 key
tolerations:
- key: "example-key"
  operator: "Exists"
  effect: "NoSchedule"

# 省略 key 和 value
tolerations:
- effect: NoExecute
  operator: Exists

Taint based Evictions

Kubernetes 的 Node Controller 基于上述 Taint 和 Tolerations 机制实现了节点故障时 Pod 迁移功能。

当节点故障时,Node Controller 将自动在 Node 上添加以下污点:

Key Effect 备注
node.kubernetes.io/not-ready NoExecute NodeCondition Ready == False
node.kubernetes.io/unreachable NoExecute NodeCondition Ready == Unknown
node.kubernetes.io/memory-pressure NoSchedule 节点内存不足
node.kubernetes.io/disk-pressure NoSchedule 节点磁盘空间不足
node.kubernetes.io/pid-pressure NoSchedule 节点Pid不足
node.kubernetes.io/unschedulable NoSchedule 节点不可调度,主要用于主动 codron 节点的场景

Node Controller 基于节点的 NodeCondition 信息添加 Taint,而这些信息由每个节点的 Kubelet 实时更新。Schuduler 在调度 pod 时直接基于节点的 Taint 工作,不再重复考虑 NodeCondition 的具体取值。

上述 Node Controller 管理的 Taint 中只有 node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable 的 effect 是 NoExecute,其他污点为 NoSchedule。

这是由于设置 NoExecute 污点会立即驱逐节点上的所有 pod,为了提高系统的鲁棒性,kubernetes 采取的方案是:当节点存在 memory 压力时,通过污点将节点标记为 NoSchedule ,此时 Kubelet 后台会执行一次资源回收,优先将低保障级别的 Pod 驱赶到其他节点。此时如果内存资源恢复到警戒线之下,kubelet 将更新 NodeCondition 信息,Taint 也会被移除。换句话说,memory-pressure 等类型污点并不是表明节点完全不可用,其 effect 设置为 NoExecute 是不合适的。

默认情况下,kubernetes 在创建的 Pod 自动添加了一些 tolerations,以实现各种场景的 Pod 自动迁移:

Kubernetes 对除 Daemon 以外其他所有控制器创建的 pod ,自动添加了以下 tolerations:

1
2
3
4
5
6
7
8
9
tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300

对于 daemon 管理的 pod,Kubernetes 注入以下 tolerations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
tolerations:
- effect: NoExecute
  key: node.kubernetes.io/not-ready
  operator: Exists
- effect: NoExecute
  key: node.kubernetes.io/unreachable
  operator: Exists
- effect: NoSchedule
  key: node.kubernetes.io/disk-pressure
  operator: Exists
- effect: NoSchedule
  key: node.kubernetes.io/memory-pressure
  operator: Exists
- effect: NoSchedule
  key: node.kubernetes.io/pid-pressure
  operator: Exists
- effect: NoSchedule
  key: node.kubernetes.io/unschedulable
  operator: Exists

官方文档中提及对于 Guaranteed 和 Burstable 类型的 Pod ,Kubernetes 会自动添加 node.kubernetes.io/memory-pressure 类型的 tolerations(甚至当 Burstable 类型的 Pod 没有设置内存配额时)。但在实际测试中,我发现并没有添加。

参考:https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/#taint-nodes-by-condition

参考