Kubernetes Toleration 六种写法详解:从精确匹配到全部放行

10 阅读8分钟

在 Kubernetes 中,Taint(污点)与 Toleration(容忍)通常成对出现,用于控制 Pod 能否被调度到特定节点上。Toleration 的语法并不复杂,但真正容易让人困惑的地方在于:

  • EqualExists 的本质区别是什么?
  • 省略 value 会带来什么影响?
  • 省略 effect 又意味着什么?
  • 为什么某些 Toleration 只匹配一个特定 Taint,而另一些却能容忍集群中几乎所有的污点?

事实上,只要厘清 key、value、effect 这三个维度如何参与匹配过程,就能轻松掌握各种 Toleration 写法的含义与适用场景。


一、Toleration 究竟在匹配什么?

先看一个典型的 Taint:

maintenance=draining:NoExecute

它可以拆解为三个组成部分:

maintenance = draining : NoExecute
     ↓           ↓           ↓
    key        value       effect

即:

key    = maintenance
value  = draining
effect = NoExecute

而 Toleration 的核心逻辑,本质上就是决定:

key、value、effect 这三个维度中,哪些需要参与匹配,哪些可以放宽。

例如:

tolerations:
- key: maintenance
  operator: Equal
  value: draining
  effect: NoExecute

该配置限定了全部三个维度,因此只能精确容忍对应的 Taint:

maintenance=draining:NoExecute

反之,随着限制条件的逐步放宽,Toleration 能够匹配的 Taint 范围也会不断扩大。

这正是理解后续六种写法的关键思路。


二、先掌握几条核心匹配规则

在深入具体写法之前,先记住以下几条基本规则。

Equal:同时匹配 key 和 value

key: maintenance
operator: Equal
value: draining

表示要求 Taint 的 keyvalue 都必须与指定值一致。

即匹配:

maintenance=draining

Exists:不比较 value

key: maintenance
operator: Exists

表示只关心 maintenance 这个 key 是否存在,而不要求 value 相同。

因此以下 Taint 均可能满足这部分条件:

maintenance=draining
maintenance=true
maintenance=test
maintenance

effect:显式指定则限制,省略则不做限制

指定:

effect: NoExecute

表示仅匹配 effect 为 NoExecute 的 Taint。

若省略 effect,则表示 effect 不作为匹配条件,NoSchedulePreferNoScheduleNoExecute 均可被匹配。

另外需要特别留意:

当使用 Exists 时,key 同样可以被省略。

一旦 key 也被省略,匹配范围便会大幅扩展。

掌握以上规则后,我们就可以从最严格的 Toleration 出发,逐步减少限制条件,最终演变为“容忍所有污点”的形态。


三、六种写法:从精确匹配到全部放行

1. key + value + effect + Equal:精确匹配

先看限制最严格的写法:

tolerations:
- key: maintenance
  operator: Equal
  value: draining
  effect: NoExecute

该配置明确限定了:

key    = maintenance
value  = draining
effect = NoExecute

三个维度均需完全一致,因此仅容忍:

maintenance=draining:NoExecute

示例对比:

maintenance=draining:NoExecute    ✓

maintenance=draining:NoSchedule   ✗
maintenance=upgrade:NoExecute     ✗
gpu=draining:NoExecute            ✗

第一条匹配;第二条 key 和 value 相同但 effect 不同;第三条 key 和 effect 相同但 value 不同;第四条 value 和 effect 相同但 key 不同。

换言之:

key    ✓
value  ✓
effect ✓

三者缺一不可。此形式可抽象为:

maintenance=draining:NoExecute

这是语义最清晰、约束最严格的写法。若业务明确知道需要容忍哪个具体 Taint,通常应优先采用这种精确匹配方式,而非无目的地扩大容忍范围。


2. key + value + Equal:放开 effect

在上一写法基础上去掉 effect

tolerations:
- key: maintenance
  operator: Equal
  value: draining

此时仍要求:

key   = maintenance
value = draining

但:

effect = 任意

因此以下三种 effect 的 Taint 均可匹配:

maintenance=draining:NoSchedule          ✓
maintenance=draining:PreferNoSchedule    ✓
maintenance=draining:NoExecute           ✓

但下面这些依然不匹配:

maintenance=upgrade:NoExecute    ✗
gpu=draining:NoExecute           ✗

因为 Equal 仍要求 key 和 value 同时匹配。该写法可抽象为:

maintenance=draining:*

注意关键点:

省略 effect 并不表示“没有 effect”,而是表示 effect 不再作为匹配限制条件。

相比上一种写法:

maintenance=draining:NoExecute

现已扩展为:

maintenance=draining:*

key-value 仍受约束,但污点具体产生何种行为已不再重要。


3. key + Exists + effect:放开 value

换个方向,不再关心 maintenance 的具体 value,只要节点存在该类污点,Pod 即可容忍:

tolerations:
- key: maintenance
  operator: Exists
  effect: NoSchedule

关键变化在于:

Equal → Exists

使用 Exists 后不再比较 value,因此匹配条件变为:

key    = maintenance
value  = 任意
effect = NoSchedule

示例:

maintenance=draining:NoSchedule    ✓
maintenance=upgrade:NoSchedule     ✓
maintenance=test:NoSchedule        ✓
maintenance:NoSchedule             ✓

value 的具体内容不再起作用,但以下仍不匹配:

gpu=true:NoSchedule                ✗
maintenance=draining:NoExecute     ✗

第一条 key 不同,第二条 effect 不同。该写法可抽象为:

maintenance=*:NoSchedule

这正是理解 Exists 的关键:

Exists 关心的是指定 key 的 Taint 是否存在,而非该 Taint 的 value 为何值。

因此,如果设计语义是“允许 Pod 容忍 maintenance 这一类 NoSchedule 污点”,而非某个具体的 maintenance=xxx,那么 ExistsEqual 更贴合意图。


4. key + Exists:同时放开 value 和 effect

继续去除 effect:

tolerations:
- key: maintenance
  operator: Exists

此时仅剩一个限制:

key = maintenance

而:

value  = 任意
effect = 任意

因此以下均可匹配:

maintenance=draining:NoSchedule          ✓
maintenance=draining:PreferNoSchedule    ✓
maintenance=draining:NoExecute           ✓
maintenance=upgrade:NoSchedule           ✓
maintenance=test:NoExecute               ✓
maintenance:NoSchedule                   ✓

只要 key 为 maintenance 即可。

但:

gpu=true:NoSchedule
dedicated=database:NoExecute

因 key 不是 maintenance,仍无法匹配。该写法可抽象为:

maintenance=*:*

与上一写法对比一目了然:

key: maintenance
operator: Exists
effect: NoSchedule

表示:

maintenance=*:NoSchedule

而:

key: maintenance
operator: Exists

则扩展为:

maintenance=*:*

也就是说:

Exists 放开了 value,省略 effect 又进一步放开了 effect。

此时 Toleration 已不再针对某个具体污点,而是容忍 整个 maintenance 类别的污点


5. Exists + effect:连 key 都不限制

再放开一个维度:

tolerations:
- operator: Exists
  effect: NoSchedule

此处未指定 key,因此三个维度变为:

key    = 任意
value  = 任意
effect = NoSchedule

示例:

maintenance=draining:NoSchedule    ✓
gpu=true:NoSchedule                ✓
dedicated=database:NoSchedule      ✓
test=foo:NoSchedule                ✓

无论 key 和 value 是什么,只要 effect 为 NoSchedule 即可匹配。

但:

gpu=true:NoExecute
maintenance=draining:NoExecute

仍不匹配,因为 effect 是唯一保留的限制条件。该写法可简化为:

*:*:NoSchedule

此时匹配范围已经相当广泛。

对比之前的:

key: maintenance
operator: Exists
effect: NoSchedule

表达的是:“我允许 maintenance 这一类 NoSchedule 污点。”

而现在:

operator: Exists
effect: NoSchedule

则变为:

无论什么类型的污点,只要其 effect 是 NoSchedule,我都能容忍。

因此,对于普通业务 Pod,这种配置需要格外谨慎。


6. 仅 Exists:全部放行

最后,去掉 effect:

tolerations:
- operator: Exists

此时:

key    = 任意
value  = 任意
effect = 任意

示例:

maintenance=draining:NoSchedule          ✓
maintenance=draining:NoExecute           ✓
gpu=true:NoSchedule                      ✓
dedicated=database:NoExecute             ✓
test=foo:PreferNoSchedule                ✓

全部容忍。该写法可抽象为:

*:*:*

这是 Toleration 匹配范围最大的状态:

容忍所有 Taint。

由于不再对 key、value、effect 设置任何约束,这种配置并不是“更通用的便捷写法”,而是明确表示:

该工作负载不应因节点上的 Taint 而被排斥。

某些系统级工作负载(如 DaemonSet)可能需要这种广泛容忍,但对于普通业务 Pod,通常应极为审慎,否则节点通过 Taint 建立的隔离边界可能对该 Pod 失效。


四、六种写法总览

回顾上述六种配置,它们并非需要死记硬背的孤立条目,而是随着逐步放宽 key / value / effect 的约束,自然演变而来。

key + value + effect + Equal
maintenance=draining:NoExecute
                │
                │ 去掉 effect
                ▼
key + value + Equal
maintenance=draining:*
                │
                │ 改用 Exists,不再比较 value
                ▼
key + Exists + effect
maintenance=*:NoSchedule
                │
                │ 去掉 effect
                ▼
key + Exists
maintenance=*:*
                │
                │ 去掉 key,仅保留 effect
                ▼
Exists + effect
*:*:NoSchedule
                │
                │ 再去掉 effect
                ▼
Exists
*:*:*

精确匹配 ───────────────────────→ 全部放行
             匹配范围逐步扩大

汇总如下表:

写法keyvalueeffect匹配范围
key + value + effect + Equal指定指定指定精确匹配
key + value + Equal指定指定任意指定 key-value
key + Exists + effect指定任意指定指定 key 和 effect
key + Exists指定任意任意指定 key 的所有污点
Exists + effect任意任意指定指定 effect 的所有污点
Exists任意任意任意所有污点

因此,遇到任意 Toleration 时,只需依次确认三个问题:

key 是否限定?
value 是否限定?
effect 是否限定?

再结合:

Equal  → 比较 key 和 value
Exists → 不比较 value

即可迅速判断其实际匹配范围。


五、实际使用中如何选择?

一个实用的指导原则是:

若明确知道需要容忍哪个具体 Taint,就尽量将条件描述清楚;只有在确实需要容忍“某一类污点”时,才主动放宽匹配范围。

例如,明确需要容忍:

maintenance=draining:NoExecute

则直接写作:

tolerations:
- key: maintenance
  operator: Equal
  value: draining
  effect: NoExecute

语义最为清晰。

若业务设计本身是:

无论 maintenance 的 value 为何值,只要属于该类 NoSchedule 污点,均允许容忍。

则:

tolerations:
- key: maintenance
  operator: Exists
  effect: NoSchedule

反而更契合设计意图。

真正需要高度警惕的是:

tolerations:
- operator: Exists

因为它已经不是“容忍某个污点”,而是在消除几乎所有 Taint 对该 Pod 的排斥作用,极易破坏节点隔离策略。


六、一个常见误区:Toleration ≠ 节点选择

最后,务必区分一个重要概念。

假设某 GPU 节点存在:

gpu=true:NoSchedule

Pod 配置了:

tolerations:
- key: gpu
  operator: Equal
  value: "true"
  effect: NoSchedule

这并不意味着

该 Pod 一定会被调度到 GPU 节点

其真实含义是:

在评估该 GPU 节点时,
gpu=true:NoSchedule
这个 Taint 不会将 Pod 排斥在外。

可以简要归纳为:

Taint
→ 节点表态:哪些 Pod 不该过来

Toleration
→ Pod 表态:这个 Taint 我可以接受

nodeSelector / Node Affinity
→ Pod 表态:我希望 / 必须去哪些节点

因此,Toleration 解决的是“能否进入”的部分限制,而非“应该去哪里”的选择问题。

理解了这一点,再回头看前面的六种写法,其本质始终围绕同一个核心:

你究竟准备放宽多少 Taint 对这个 Pod 的限制。