深入理解Kubernetes的容忍

2026-03-28 20:02:00
丁国栋
原创 4
摘要:本文记录和整理关于Kubernetes容忍的知识。

理解 Kubernetes 的容忍

Tolerations(/ˌtɒləˈreɪʃənz/)是 Kubernetes 中一个非常核心且容易混淆的概念。它并不是指“宽容”这种抽象品质,而是一个专有名词,特指 Pod 在调度时能够“忍受”或“允许”被调度到哪些带有“污点”(Taints)的节点上

简单来说,它是 Pod 的“抗污能力”配置。


一、核心定义与作用

在 Kubernetes 的调度机制中,Node(节点)可以被打上“污点”(Taint),就像给节点贴上一个“危险”或“专用”的标签(例如:node-type=production:NoSchedule),这样没有特殊配置的 Pod 就无法调度上去。

Tolerations 就是 Pod 身上携带的“通行证”,它告诉调度器:“我允许(Tolerate)被调度到带有这些污点的节点上”。

  • 没有 Toleration:Pod 无法调度到有 Taint 的节点。
  • 有匹配的 Toleration:Pod 可以(但不一定)调度到该节点(还需考虑其他资源等因素)。

二、关键属性解析

一个标准的 Toleration 在 YAML 中通常包含以下三个核心字段:

tolerations:
- key: "node-type"        # 污点的键(必须匹配节点污点的key)
  operator: "Equal"       # 操作符(Equal 或 Exists)
  value: "production"     # 污点的值(当 operator=Equal 时需指定)
  effect: "NoSchedule"    # 污点的效果(必须匹配)

1. operator(操作符)

这是最容易出错的地方,它决定了匹配规则:

  • Equal:精确匹配。Pod 的 keyvalue 必须与 Node Taint 的 keyvalue 完全相等。
  • Exists:存在匹配。只要 Node 有 key 这个 Taint(无论 value 是什么),Pod 就能容忍。此时不需要指定 value 字段。

2. effect(效果)

必须与 Node Taint 的效果一致,否则容忍无效。常见效果:

  • NoSchedule:仅允许调度,不影响已运行的 Pod。
  • PreferNoSchedule:软限制,调度器尽量不调度,但非强制。
  • NoExecute:最严格,不仅不能调度,如果节点上的已有 Pod 没有此容忍,还会被驱逐(Evict)

三、典型使用场景

场景 1:专用节点(GPU/高性能)

给 GPU 节点打上污点,只有需要 GPU 的 Pod(配置了 Toleration)才能调度上去。

# 节点命令(给节点打污点)
kubectl taint nodes node1 gpu=true:NoSchedule
# Pod 配置(声明容忍)
tolerations:
- key: "gpu"
  operator: "Equal"
  value: "true"
  effect: "NoSchedule"

场景 2:主节点(Master)调度

Kubernetes 默认给 Master 节点打污点,防止用户 Pod 调度上去干扰系统组件。系统组件(如 CoreDNS)自带对应的 Toleration。

# 查看 Master 污点
kubectl describe node master | grep Taint
# Taints: node-role.kubernetes.io/master:NoSchedule
# 系统 Pod 的容忍配置
tolerations:
- key: "node-role.kubernetes.io/master"
  operator: "Exists"
  effect: "NoSchedule"

场景 3:故障节点维护(NoExecute)

当节点需要维护时,可以打上 NoExecute 污点,该节点上所有没有对应容忍的 Pod 会被立即驱逐。

# 驱逐节点上所有 Pod
kubectl taint nodes node1 maintenance=true:NoExecute

四、常见误区与注意事项

  1. Toleration 不是 Affinity(亲和性)

    • Toleration:是“允许去”(Permission)。
    • Affinity:是“想去”(Preference)。
    • 一个 Pod 能调度到某节点,必须同时满足:节点资源足够 + Pod 容忍节点污点 + (节点亲和性规则)
  2. operator: Exists 的危险性: 使用 Exists 且不指定 key 是非常危险的操作,因为它会容忍所有的污点(包括 NoExecute),可能导致 Pod 被调度到不合适的节点或无法被驱逐。

  3. 空键(Empty Key)的特殊情况: 如果 Toleration 的 key 为空且 operatorExists,它会匹配所有的污点键、值和效果。这通常用于 DaemonSet(如 kube-proxy)以确保它们能在所有节点上运行。


五、总结对比

概念 作用对象 功能 类比
Taints(污点) Node(节点) 排斥 Pod:“此节点不欢迎你” 节点上的“禁止入内”标志
Tolerations(容忍) Pod(容器组) 抵抗排斥:“我有权限进入” Pod 的“特别通行证”

一句话定义Tolerations 是 Pod 定义中的一组规则,用于声明该 Pod 能够容忍(即被调度到)哪些带有特定Taints的 Kubernetes 节点。

问:如果一个节点已经标记为禁止调度,但pod允许容忍污点,在其他节点有资源的情况下,这个节点是否依然会被调度?

这是一个非常经典的 Kubernetes 调度逻辑问题。结论非常明确:

不会。 在其他节点有资源的情况下,调度器绝对不会优先选择那个“被禁止(但有容忍)”的节点。


一、核心逻辑:容忍(Toleration)只是“入场券”,不是“邀请函”

需要严格区分两个概念:

  1. 容忍(Toleration):解决的是 “能不能” 的问题。

    • 没有容忍,Pod 绝对进不去(被 Taint 挡在门外)。
    • 有容忍,只是意味着这个节点被加入了“可选节点池”,解除了硬性禁止。
  2. 调度评分(Scoring):解决的是 “好不好” 的问题。

    • 调度器默认的策略是优先选择资源最充足、最空闲的节点。
    • 污点(Taint)本身并不加分。一个被打上污点的节点,在调度器眼中通常是“有问题的”或“专用的”,默认评分策略下,它绝不会比一个干净且资源充足的节点更有吸引力。

二、场景推演

假设集群状态如下:

  • Node A(问题节点):已打污点 gpu=true:NoSchedule,资源充足。
  • Node B(正常节点):无污点,资源充足。
  • Pod:配置了容忍 key: gpu

调度过程

  1. 过滤(Filter)阶段:调度器检查所有节点。

    • Node B:无污点,通过
    • Node A:有污点,但 Pod 有 Toleration,通过
    • ✅ 此时,Node A 和 Node B 都在候选列表中。
  2. 评分(Score)阶段:调度器对候选节点排序。

    • Node B:标准、干净、无负担的节点 → 高分
    • Node A:带有“特殊标记”的节点 → 默认低分(除非有特殊亲和性)。
  3. 结果:调度器会选择分数最高的 Node B。Node A 只有在 Node B 资源不足(或通过亲和性强制指定)时才会被选为“备胎”。


三、如何强制调度到该节点?

如果希望 Pod 必须调度到那个有污点的节点(例如,那是唯一有 GPU 的机器),需要使用 节点亲和性(Node Affinity)节点选择器(nodeSelector)

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  tolerations:
  - key: "gpu"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: In
            values:
            - "true"
  containers:
  - name: nginx
    image: nginx

逻辑转变

  • 只有 Toleration:Pod 可以去任何地方(包括干净节点),只是不排斥那个脏节点。
  • Toleration + NodeAffinity:Pod 必须去带有 gpu=true 标签的节点,并且它能容忍该节点的污点。

四、总结

配置 效果
仅有 Taint Pod 无法调度到该节点(被拒之门外)
Taint + Toleration Pod 可以调度到该节点,但优先选择无污点的正常节点
Taint + Toleration + NodeAffinity Pod 必须调度到该节点(强制绑定)

一句话回答:容忍只是解除了节点的“禁止访问”限制,并不会让该节点获得任何调度优先级。在资源充足的情况下,Kubernetes 调度器默认会优先使用最干净、最标准的节点。

又问:

如果Pod的容忍是:

  tolerations:
  - operator: Exists

是不是也会接受调度到标记为不可调度节点?

是的,完全正确。

operator: Exists 是 Kubernetes 容忍机制中最“宽泛”也最“危险”的配置。它意味着 Pod 对污点(Taint)完全不设防,会无条件接受调度到任何标记为不可调度的节点(包括 NoScheduleNoExecute)。


一、为什么 Exists 如此“霸道”?

Kubernetes 的容忍匹配规则非常直接:

  • operator: Exists只要节点上存在任意一个污点(Taint),无论这个污点的 key 是什么、value 是什么、effect 是什么(NoSchedule/NoExecute),这个 Pod 都会直接匹配
  • operator: Equal:需要精确匹配 keyvalue,相对安全。

配置解析:

tolerations:
- operator: Exists

这行代码的逻辑是:“我不管节点上有什么脏东西(污点),我全都能忍(Tolerate)”。


二、对“不可调度”节点的具体影响

1. 针对 NoSchedule(禁止调度)

  • 普通 Pod:被节点污点阻挡,无法调度。
  • 你的 Pod可以正常调度。因为 Exists 容忍了 NoSchedule 效果,调度器视其为“允许进入”。

2. 针对 NoExecute(驱逐)

  • 普通 Pod:如果节点被打上 NoExecute 污点,会被立即驱逐。
  • 你的 Pod不会被驱逐Exists 同样容忍了 NoExecute 效果,Pod 会像“钉子户”一样继续运行,即使节点正在维护。

三、严重风险警告

tolerations: - operator: Exists 在生产环境中是极其危险的配置,通常被视为“反模式”。

  • 失去节点隔离:Master 节点、GPU 专用节点、故障节点上的污点全部失效,Pod 可能被调度到任何地方,破坏集群隔离策略。
  • 无法驱逐:运维人员无法通过 kubectl taint ...:NoExecute 的手段驱逐该 Pod,导致节点维护困难。
  • DaemonSet 专用:这种配置通常只应出现在系统级 DaemonSet(如 kube-proxycalico-node)中,因为它们需要确保在所有节点上运行。

四、正确做法:精准匹配

除非有绝对特殊的全集群穿透需求,否则永远不要使用裸奔式的 Exists。请使用精确匹配来限制 Pod 的容忍范围:

tolerations:
- key: "node-type"        # 明确指定 key
  operator: "Equal"        # 使用 Equal 而非 Exists
  value: "gpu"            # 明确指定 value
  effect: "NoSchedule"    # 明确指定 effect

结论:这个配置不仅会接受调度到标记为不可调度的节点,甚至会接受调度到任何有污点的节点,包括正在被驱逐的节点。请立即检查并修正为精确匹配。

发表评论
博客分类