Kubernetes的默认调度器在为Pod选择节点时,会综合评估资源余量、亲和性规则、拓扑分布等因素,但有一类需求是这些机制覆盖不了的:某些节点只想留给特定的 workload 使用,比如仅允许日志收集组件运行在存储节点上,或者禁止普通业务Pod占用GPU机器。要实现这种单向排斥,靠的正是污点与容忍度机制。理解它的工作方式,是掌握Kubernetes高级调度技巧的必经之路。

污点与容忍度的基本原理
污点的本质是给节点附加一个“排斥属性”,它由三部分组成:key、value 和 effect。前两者就是普通的键值对,用来描述污点的含义,而 effect 决定了这个污点对不兼容Pod的实际影响力度。一个节点可以同时携带多个污点,调度器在处理某个Pod时,会逐条检查节点的每个污点能否被该Pod容忍,只要存在哪怕一个无法容忍的污点,且其 effect 是硬性的,调度器就不会把Pod放到这个节点上。
与污点对应的是容忍度,它写在Pod的spec中,声明这个Pod能够接受哪些污点。需要注意的是,容忍度是一种许可而非强制,Pod拥有对某个节点污点的容忍度,不代表它一定会被调度到该节点,调度器依然会执行打分排序;但缺少必要容忍度的Pod,则会被硬性排除在外。这种“节点排斥为主、Pod申请豁免”的设计,和节点亲和性的“Pod主动挑选节点”方向恰好相反,两者组合起来就能覆盖绝大多数调度需求。
一个典型的例子是Master节点。安装集群后Master通常自带一个污点,可以通过以下命令查看:
kubectl describe node master-1 | grep -i taint # 输出示例: # Taints: node-role.kubernetes.io/master:NoSchedule
正是这个污点保证了普通业务Pod不会跑到控制平面节点上抢夺资源。只有像CNI网络插件、监控Agent这类系统级组件,才会在自己的Deployment中写入对应的容忍度,从而获得在Master上运行的资格。
三种effect的深度对比与使用
effect决定了污点的执行强度,理解三种模式的差异是正确使用的前提。NoSchedule是最常见的硬性排斥,调度器不会把不能容忍该污点的新Pod调度到节点上,但已经在节点上运行的旧Pod不会被驱逐。PreferNoSchedule则是软性版本,调度器会尽量避免调度,但当集群资源紧张、其他候选节点都不合适时,仍然允许落位,适合表达一种倾向性而非强制性。NoExecute最激进,它不仅阻止新Pod进入,还会把节点上已有的、不能容忍该污点的Pod直接驱逐出去,常用于节点出现故障时的快速隔离。
给节点打污点使用 kubectl taint 命令,语法中的effect紧跟在value后面,用冒号分隔:
# 添加污点:key为dedicated,value为gpu,effect为NoSchedule kubectl taint nodes gpu-node-1 dedicated=gpu:NoSchedule # 添加PreferNoSchedule类型的污点 kubectl taint nodes bigdisk-node dedicated=bigdisk:PreferNoSchedule # 移除污点:在key末尾加一个减号 kubectl taint nodes gpu-node-1 dedicated=gpu:NoSchedule-
NoExecute模式在实际运维中价值很大。假设某台机器内存即将耗尽,运维人员可以立即给它打上 NoExecute 污点,调度器会立刻把上面所有不容忍该污点的Pod驱逐走,由控制器在其他节点重建,实现快速的故障摘除。配合 tolerationSeconds 字段还能实现更精细的优雅处理,后面会详细展开。
tolerations字段写法详解与常见陷阱
容忍度配置在Pod的spec.tolerations中,核心是operator字段的选择。当operator为Equal时,要求key、value、effect三者完全匹配才算容忍;当operator为Exists时,只要节点存在指定的key即可容忍,value是什么都无所谓。如果连key都不写、operator设为Exists,就成了万能容忍,Pod可以无视所有污点出现在任何节点上。此外,effect也可以留空,表示容忍该key下所有类型的effect。
apiVersion: v1
kind: Pod
metadata:
name: gpu-job
spec:
containers:
- name: trainer
image: pytorch/pytorch:latest
tolerations:
# 精确匹配:必须完全等于 dedicated=gpu 且effect为NoSchedule
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
# 存在即匹配:只要节点有priority标签的污点,无论value是什么
- key: "priority"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300这里出现了tolerationSeconds,它专门配合NoExecute使用,含义是Pod在该污点触发驱逐时,最多再坚持300秒,给应用留出保存现场、优雅关闭的时间窗口。如果不设置,NoExecute污点的容忍度默认是永久生效的。
实践中要警惕两个陷阱。一是万能容忍的滥用,有些团队为了图省事给所有Deployment都加上空的Exists容忍,结果污点机制完全失效,专用节点的隔离形同虚设。二是effect不匹配的隐蔽问题:如果节点上的污点是NoExecute,而Pod只写了NoSchedule的容忍度,驱逐时同样会被赶走,排查时要确认三要素是否严格对得上。另外,Kubernetes自带一些系统级污点,比如 node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable,DaemonSet类型的系统组件通常会自带对它们的容忍,避免节点短暂失联时被清场,这点在自定义组件时也要留意补齐。
典型生产场景的组合实践
第一个场景是GPU等昂贵资源的专用化。给所有GPU节点打上 dedicated=gpu:NoSchedule 的污点,只有训练任务的Pod带上对应容忍度才能使用,防止其他服务因为资源余量凑巧被调度进来浪费算力。为了进一步提升确定性,还可以叠加节点亲和性,让容忍度负责准入、亲和性负责倾向,双重保障下调度结果几乎完全可控。
第二个场景是节点故障的自动摘除。可以借助node-problem-detector等工具检测异常,自动给问题节点打上 NoExecute 污点,让业务Pod提前撤离,而不是等到kubelet真正失联、集群标记 unreachable 之后才触发批量驱逐,减少服务中断时间。配合tolerationSeconds设置,可以让非核心业务立即退出、核心业务延迟几分钟观察,形成分层撤离策略。
第三个场景是 Spot 或抢占式实例的容量回收。云厂商通常会在回收Spot实例前给节点加上类似 cloudprovider/spot-preempt 的污点,effect为NoExecute。提前为Pod配置好带tolerationSeconds的容忍度,配合PreemptionPolicy和PriorityClass,就能在实例被回收时有序地完成Pod迁移,把不可控的强杀变成可控的滚动重建。整体来看,污点与容忍度是一个小而精的调度原语,配置简单但语义明确,掌握之后很多看似复杂的资源隔离问题都能迎刃而解。
Kubernetes污点容忍度Pod调度修改时间:2026-09-05 15:22:52