Kubernetes 污点与容忍度实战:从“打标签”到“调度谜团”全解析
本文通过一个真实的 K8s 集群操作案例,深入剖析 Taints 与 Tolerations 的底层原理,带你彻底搞懂 DaemonSet 的特殊调度行为和
operator: Exists的匹配逻辑。
一、现象:系统组件为何无视污点?
最近在维护一个 K8s 集群时,我遇到了一个值得深入探究的现象:
- 我给三个节点打了污点
node-role.cloud.kubeblocks.io/control-plane=true:NoSchedule - 原本以为新 Pod 不会再调度到这些节点上,但后续新增的 calico-node 和 kube-proxy Pod(注意:这些 Pod 是在打污点之后新建的)依然被调度到了这些节点上
- 更值得思考的是:当我追加一个 NoExecute 污点后,删除 Pod 再重建,它们依然能调度回原节点
这背后的根本原因在于:calico-node 和 kube-proxy 并不是普通 Pod,而是以 DaemonSet 方式部署的系统组件。 DaemonSet 的设计目标就是在每个节点上运行一个 Pod,其调度依据是 DaemonSet 自身的 tolerations 配置,而非单个 Pod 的调度逻辑。只要 DaemonSet 中配置了对应的容忍规则,它就能在节点有污点的情况下持续运行。
带着这个发现,我梳理了整个排查过程。
二、基础概念回顾
2.1 污点(Taints)与容忍度(Tolerations)
| 概念 | 作用 | 配置位置 |
|---|---|---|
| 污点 | 让节点"排斥"某些 Pod | Node 的 .spec.taints |
| 容忍度 | 让 Pod "容忍"节点的污点 | Pod 的 .spec.tolerations |
2.2 污点的三种效果
| Effect | 含义 |
|---|---|
NoSchedule | 新 Pod 不会调度到该节点(已有 Pod 不受影响) |
PreferNoSchedule | 尽量不调度(软性限制) |
NoExecute | 新 Pod 不会调度,且已有 Pod 若不匹配容忍度会被驱逐 |
三、实战操作全记录
3.1 查看节点初始污点
for node in iv-yet8omgwsgcva4fibmvz iv-yet8omgwsgcva4fie1zq iv-yet8omgwsgcva4ficnb8; do
echo -n "$node: "; kubectl get node $node -o jsonpath='{.spec.taints}' && echo
done
输出:
iv-yet8omgwsgcva4fibmvz: [{"effect":"NoSchedule","key":"node-role.cloud.kubeblocks.io/control-plane","value":"true"}]
iv-yet8omgwsgcva4fie1zq: [{"effect":"NoSchedule","key":"node-role.cloud.kubeblocks.io/control-plane","value":"true"}]
iv-yet8omgwsgcva4ficnb8: [{"effect":"NoSchedule","key":"node-role.cloud.kubeblocks.io/control-plane","value":"true"}]
此时每个节点只有 NoSchedule 污点。
3.2 查看系统 Pod 运行状态
kubectl get pod -n kube-system -owide | grep -E "fibmvz|fie1zq|ficnb8"
calico-node-zkrxt 1/1 Running 114s 172.31.53.193 iv-yet8omgwsgcva4fibmvz
kube-proxy-5d7c7 1/1 Running 3s 172.31.53.193 iv-yet8omgwsgcva4fibmvz
这两个 Pod 的 Age 显示它们是在打污点之后新建的,却依然调度到了有污点的节点上。
3.3 查看这些 Pod 的容忍度
kube-proxy 的容忍度:
kubectl get pod kube-proxy-5d7c7 -n kube-system -o yaml | grep -A 10 tolerations
tolerations:
- operator: Exists # 关键:容忍所有污点!
- 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
calico-node 的容忍度:
kubectl get pod calico-node-zkrxt -n kube-system -o yaml | grep -A 20 tolerations
tolerations:
- effect: NoSchedule
operator: Exists # 匹配所有 NoSchedule 污点
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute
operator: Exists # 匹配所有 NoExecute 污点
- 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
3.4 追加 NoExecute 污点
for node in iv-yet8omgwsgcva4fibmvz iv-yet8omgwsgcva4fie1zq iv-yet8omgwsgcva4ficnb8; do
kubectl taint nodes $node node-role.cloud.kubeblocks.io/control-plane=true:NoExecute --overwrite
done
再次查看节点污点:
iv-yet8omgwsgcva4fibmvz: [{"effect":"NoExecute","key":"node-role.cloud.kubeblocks.io/control-plane","value":"true"},{"effect":"NoSchedule","key":"node-role.cloud.kubeblocks.io/control-plane","value":"true"}]
现在每个节点同时拥有 NoSchedule 和 NoExecute 两个污点。
3.5 删除 Pod 验证重新调度
kubectl delete pod calico-node-zkrxt -n kube-system
kubectl delete pod kube-proxy-5d7c7 -n kube-system
查看新 Pod:
kubectl get pod -n kube-system -owide | grep fibmvz
calico-node-m9dfs 1/1 Running 15s 172.31.53.193 iv-yet8omgwsgcva4fibmvz
kube-proxy-56jzk 1/1 Running 2m32s 172.31.53.193 iv-yet8omgwsgcva4fibmvz
关键发现:即使增加了 NoExecute 污点,删除 Pod 后重建,它们依然调度回原节点!
3.6 查看 DaemonSet 级别的容忍度
calico-node DaemonSet:
kubectl get daemonset calico-node -n kube-system -o yaml | grep -A 20 tolerations
tolerations:
- effect: NoSchedule
operator: Exists
- key: CriticalAddonsOnly
operator: Exists
- effect: NoExecute # ⭐ 明确存在
operator: Exists
kube-proxy DaemonSet:
kubectl get daemonset kube-proxy -n kube-system -o yaml | grep -A 20 tolerations
tolerations:
- operator: Exists # 容忍所有污点(NoSchedule + NoExecute + PreferNoSchedule)
四、核心知识点解析
4.1 operator: Exists 的匹配逻辑
| 配置 | 匹配范围 |
|---|---|
operator: Exists(无 effect) | 匹配所有污点(任何 key、value、effect) |
effect: NoSchedule, operator: Exists | 匹配所有 NoSchedule 污点 |
effect: NoExecute, operator: Exists | 匹配所有 NoExecute 污点 |
关键: 当 operator: Exists 时,调度器只检查 effect,完全忽略 key 和 value。
4.2 为什么 calico 和 kube-proxy 能无视 NoExecute 污点?
| 节点污点 | kube-proxy 匹配规则 | calico 匹配规则 |
|---|---|---|
NoSchedule | operator: Exists(所有污点)✅ | effect: NoSchedule, operator: Exists ✅ |
NoExecute | operator: Exists(所有污点)✅ | effect: NoExecute, operator: Exists ✅ |
两个组件都能容忍 NoExecute,因此追加 NoExecute 污点后它们依然可以调度。
4.3 DaemonSet 的特殊性:与普通 Pod 的本质区别
| 对比项 | 普通 Pod(Deployment) | DaemonSet Pod |
|---|---|---|
| 调度依据 | Pod 自身的 tolerations | DaemonSet 的 tolerations |
| 调度目标 | 满足调度条件的任意节点 | 每个节点 |
| 污点影响 | 受污点限制 | 可配置容忍度绕过污点 |
核心结论:DaemonSet 的设计目标就是在每个节点上运行 Pod,因此它的调度逻辑比普通 Pod 更"强势",可以通过 tolerations 配置绕过节点污点。
五、排查方法论总结
# 1. 查看节点污点
kubectl describe node <node-name> | grep Taints
# 2. 查看 Pod 容忍度
kubectl get pod <pod-name> -n <namespace> -o yaml | grep -A 10 tolerations
# 3. ⭐ 查看 DaemonSet 容忍度(关键!调度决策的真正依据)
kubectl get daemonset <ds-name> -n <namespace> -o yaml | grep -A 10 tolerations
重要提示: 排查 DaemonSet 的调度问题时,一定要查看 DaemonSet 本身的 tolerations,而不是只看某个 Pod 的配置。Pod 的 YAML 可能不完整显示所有继承的容忍度,而 DaemonSet 的配置才是调度器决策的真正依据。
六、总结
- DaemonSet 不等于普通 Pod:它以"每个节点一个 Pod"为目标,天生具备绕过污点的能力
- 调度决策看 DaemonSet,不是看 Pod:DaemonSet 控制器的调度依据是自身的 tolerations 配置
operator: Exists是理解容忍度匹配的核心:它只检查 effect,忽略 key/value- NoExecute 污点可以被 DaemonSet 的容忍度绕过:只要配置了
effect: NoExecute, operator: Exists,Pod 就不会被驱逐 - 系统组件(calico、kube-proxy)天生配置了宽泛的容忍度,确保集群网络和代理功能覆盖所有节点
如果觉得有用,欢迎点赞、收藏、转发! 有任何问题,欢迎在评论区交流讨论。
更多推荐


所有评论(0)