k8s-容忍Toleration和污点Taint

k8s-容忍Toleration和污点Taint

1.容忍和污点概念

在 Kubernetes 中,污点(Taints)容忍(Tolerations) 是一对协同工作的机制,用于控制哪些 Pod 可以被调度到特定的 Node(节点)上。

核心概念

  • 污点(Taint):附加在 Node 上的属性。它就像给节点打上了「排斥标记」,默认情况下会拒绝所有不能容忍该污点的 Pod 被调度上来。
  • 容忍(Toleration):配置在 Pod 规范(spec.tolerations)中的规则。它代表 Pod 拥有针对特定污点的「免疫力」,允许(但不强求)该 Pod 调度到带污点的节点。

污点的三要素与效果

一个 Taint 由 key=value:effect 构成,其中 effect 决定了具体的排斥行为:

效果(Effect) 行为说明
NoSchedule 强排斥:若 Pod 未容忍,绝不调度到该节点;已在运行的 Pod 不受影响。
PreferNoSchedule 软排斥:尽量避免调度,但如果资源实在紧张,依然可能调度
NoExecute 驱逐排斥:若 Pod 未容忍,不仅不能调度,已经在节点上运行的 Pod 也会被立即驱逐(可配置 tolerationSeconds 延迟驱逐)。

核心作用

  • 节点隔离:防止普通应用 Pod 侵占关键或特殊用途节点的资源。
  • 避免故障蔓延:在节点出现异常(如网络不可达、磁盘耗尽)时,自动驱逐无法继续运行的业务 Pod。
  • 精细化调度:与节点亲和性(Node Affinity)配合,实现节点与 Pod 的精准双向绑定。

典型应用场景

  • 专有资源隔离(如 GPU / 高内存节点) 将昂贵的 GPU 节点打上 gpu=true:NoSchedule 污点,只有配置了相应 toleration 的深度学习任务才能调度上来,防止普通的 Nginx 或 Web 服务挤占 GPU 节点 CPU/内存。
  • Master / 控制平面节点保护 K8s 集群默认会给 Master 节点打上 node-role.kubernetes.io/control-plane:NoSchedule 污点,确保只有 API Server、CoreDNS 等集群关键组件可以运行,业务容器不会干扰控制面。
  • 基于节点状态的动态驱逐 当节点出现故障时,Node Controller 会自动给节点打上内置污点(如 node.kubernetes.io/unreachable:NoExecutenode.kubernetes.io/not-ready:NoExecute)。普通 Pod 会立即被驱逐并在健康节点重建;若应用需要容忍短暂网络抖动,可在 Toleration 中设置 tolerationSeconds: 300 延迟驱逐。
  • 节点维护与排空(Drain) 节点升级维护前,添加 NoExecute 污点可以平滑将现有业务驱逐到其他节点。

提示:污点是「排斥机制」(拒绝不需要的 Pod),若要让特定 Pod「必须」跑在指定节点上,需结合 Node Affinity(节点亲和性) 共同使用。

2.污点命令格式解析

在 Kubernetes 中,污点(Taints)主要通过 kubectl taint 命令进行管理。它的基本语法结构与 kubectl label 非常相似。

语法结构如下:

1
kubectl taint nodes <节点名称> <key>=<value>:<effect>

**<节点名称>**:目标 Node 的名称(可通过 kubectl get nodes 查看)。

**<key>**:污点的键名(支持带有域名前缀,如 [example.com/gpu](https://example.com/gpu))。

**<value>**:污点的值(可为空,空值时语法为 <key>:<effect>)。

**<effect>**:污点生效策略,仅支持三种:

  • NoSchedule:不容忍则不调度。
  • PreferNoSchedule:尽量不调度。
  • NoExecute:不容忍则驱逐并拒绝新调度。

例如:给GPU节点添加一个特殊资源的污点

1
kubectl taint nodes k8s-node01 gpu=true:NoSchedule

2.1添加一个污点

给节点添加污点,一个节点可以有多个污点,多个污点时,key可以重复,effect不可以重复

1
2
# 添加一个污点
kubectl taint nodes k8s-node01 ssd=true:NoExecute
1
2
# 添加一个同名不同影响度的污点:
kubectl taint node k8s-node01 ssd=true:PreferNoSchedule
1
2
# 添加一个不包含 value 的污点
kubectl taint node k8s-node01 ssd:NoSchedule
1
2
# 同时添加多个污点
kubectl taint node k8s-node01 taint02=value02:PreferNoSchedule taint03=value03:NoSchedule
1
2
# 同时为多个节点添加污点
kubectl taint node k8s-node02 k8s-master01 taint02=value02:PreferNoSchedule taint03=value03:NoSchedule
1
2
# 同时添加所有节点(也可以基于 Label)
kubectl taint node taint04=value04:PreferNoSchedule --all

2.2删除一个污点

删除污点(和label类似)

1
2
# 基于key删除,相同key的污点可以有多个,如果不精确指定会删除所有key是ssd的污点
kubectl taint node k8s-master01 ssd-
1
2
# 基于key+Effect删除
kubectl taint node k8s-master01 ssd:NoExecute-
1
2
# 基于完整格式删除
kubectl taint node k8s-master01 ssd=true:NoExecute-

2.3修改污点

如果某个 key 和 effect 已经存在,修改其 value 需要加上 --overwrite 参数:

1
kubectl taint nodes node-1 dedicated=high-memory:NoSchedule --overwrite

2.4查看节点上的污点

1
kubectl get nodes k8s-master01 -o go-template --template {{.spec.taints}}
1
kubectl describe nodes k8s-master01|grep Taints -A 10

3.常见内置污点

Kubernetes 会在节点出现特定条件或生命周期事件时,由控制器自动为节点打上内置污点。这些内置污点通常带有标准的前缀(如 node.kubernetes.io/)。自己在配置污点时不要使用内置污点的这些key

内置污点(Key) 典型 Effect 触发条件与作用
node-role.kubernetes.io/control-plane NoSchedule 控制面节点(Master)专用污点,防止普通业务 Pod 调度到控制节点。
node.kubernetes.io/not-ready NoExecute 节点处于非就绪状态(Ready: False),kubelet 心跳超时或未就绪时触发。
node.kubernetes.io/unreachable NoExecute 控制平面无法与节点通信(NodeController 判定节点失联)。
node.kubernetes.io/unschedulable NoSchedule 节点被设置为不可调度(执行 kubectl cordon 时自动打上)。
node.kubernetes.io/memory-pressure NoSchedule 节点节点内存不足(达到驱逐阈值)。
node.kubernetes.io/disk-pressure NoSchedule 节点根分区或容器运行时镜像分区磁盘空间不足。
node.kubernetes.io/pid-pressure NoSchedule 节点上分配的进程 ID(PID)耗尽。
node.kubernetes.io/network-unavailable NoSchedule 节点网络插件(CNI)尚未配置完成或网络不可达。
node.cloudprovider.kubernetes.io/uninitialized NoSchedule 云厂商控制器(CCM)尚未初始化完成该节点时自动打上。

4.容忍配置解析

在 Kubernetes 中,容忍规则主要通过 keyoperatorvalueeffect 的组合来决定匹配的严格程度。

4.1完全匹配(精确匹配)

要求 keyvalueeffect 必须与节点上的污点完全一致

1
2
3
4
5
tolerations:
- key: "tier"
operator: "Equal"
value: "frontend"
effect: "NoSchedule"

⚙️ 参数机制

  • operator: "Equal":指示需要精确比对键和值。
  • value: "frontend":指定具体要匹配的值。
  • effect: "NoSchedule":限定只容忍 NoSchedule 类型的效果。

📌 适用场景:只有带有 tier=frontend:NoSchedule 污点的节点才会被容忍;如果节点上的污点值是 backend,或者效果是 NoExecute,则无法匹配。

假设三个节点的污点设置如下,Pod 能够成功容忍并调度到哪几个节点上:成功容忍并调度到节点3

🖥️ 节点 1:的污点:tier=backend:NoSchedule
🖥️ 节点 2:的污点:tier=frontend:NoExecute
🖥️ 节点 3:的污点:tier=frontend:NoSchedule

4.2键名匹配(Key 存在即可)

只要节点存在指定的 key,无论其 value 是什么,都可以容忍。

1
2
3
4
tolerations:
- key: "gpu"
operator: "Exists"
effect: "NoSchedule"

⚙️ 参数机制

  • operator: "Exists":只检查键名是否存在。
  • value必须留空Exists 模式下不能指定值。

📌 适用场景:只要节点打了 gpu 键的污点(例如 gpu=v100:NoSchedulegpu=a100:NoSchedule),该 Pod 都能容忍并调度上去,effect参数也需要匹配。

假设三个节点的污点设置如下,Pod 能够成功容忍并调度到哪几个节点上:成功容忍并调度到节点1

  • 🖥️ 节点 1gpu=tesla:NoSchedule
  • 🖥️ 节点 2gpu=rtx4090:NoExecute
  • 🖥️ 节点 3storage=ssd:NoSchedule

4.3忽略 Effect 的匹配(不完全/宽泛匹配)

指定了 key(及可选的 value),但**省略了 effect**。

1
2
3
4
5
6
7
8
9
10
tolerations:
- key: "node-role"
operator: "Equal"
value: "storage"
# effect 字段被省略

# 或者
tolerations:
- key: "node-role" #只匹配key的值即可。
operator: "Exists"

⚙️ 参数机制

  • effect 省略:系统默认匹配该键值下的所有 effect 类型(包括 NoSchedulePreferNoScheduleNoExecute)。

📌 适用场景:节点只要满足 node-role=storage,不论其生效策略是禁止调度还是立即驱逐,Pod 都会全部容忍。

4.4容忍所有污点(通配所有)

无条件容忍节点上的任何污点。

1
2
3
tolerations:
- operator: "Exists"
# key、value、effect 全部省略

⚙️ 参数机制

  • key 为空 且 operator: "Exists":代表“只要有任何污点存在就匹配”。
  • 配合 effect 省略,便构成了对所有 key、所有 value、所有 effect 的全盘容忍。

📌 适用场景:系统级守护进程(如某些必须运行在每一个节点上的 DaemonSet,例如 CNI 网络插件、监控 Agent)。

4.5容忍一段时间的匹配(驱逐缓冲)

主要用于应对节点突发故障或网络抖动,避免 Pod 被立刻驱逐。

1
2
3
4
5
tolerations:
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300

⚙️ 参数机制

  • effect: "NoExecute"tolerationSeconds 只能在该效果下生效。
  • tolerationSeconds: 300:节点出现 unreachable 污点后,Pod 不会被立即踢走,而是继续在节点上存活 300 秒(5 分钟)。

📌 适用场景:有状态服务或重建代价较高的应用。如果节点在 5 分钟内恢复网络,Pod 就不需要重建;若 5 分钟后仍未恢复,控制器才会将其驱逐并重新调度。

要触发这个倒数计时,通常不是在「建立 Pod 时节点就已经有污点」,而是在Pod 运行过程中,节点动态被打上了这个污点

来看一个典型的生产环境场景:

  • 📦 Pod 设定(完全匹配 + 缓冲 60 秒)

    1
    2
    3
    4
    5
    6
    tolerations:
    - key: "maintenance"
    operator: "Equal"
    value: "true"
    effect: "NoExecute"
    tolerationSeconds: 60
  • 🔄 触发的完整时间线

    1. 🟢 平时:节点健康,上面没有 maintenance=true:NoExecute 这个污点。Pod 顺利调度并正常运行。
    2. 🛠️ 节点维护开始(T = 0 秒): 维护人员执行命令,给节点打上污点: kubectl taint nodes k8s-node01 maintenance=true:NoExecute
    3. 🔍 系统检查与计时启动
      • 节点上其他普通 Pod(没有容忍的)→ 立即被全部驱逐
      • 你的 Pod 检查规则:keyvalueeffect 完全匹配
      • 系统发现你设定了 tolerationSeconds: 60,于是启动 60 秒倒数计时器
    4. 60 秒内: 你的 Pod 获得这 60 秒的安全时间,可以用来处理尚未完成的请求、储存日志或做优雅关机(Graceful Shutdown)。
    5. 🛑 满 60 秒(T = 60 秒): 如果此时节点上的维护污点还在,倒数结束,系统正式将你的 Pod 强制驱逐并在其他节点重建。

5.实战-主节点禁止调度

在生产环境中,Kubernetes 的主节点除了部署系统组件外,不推荐再部署任何服务,此时可以通过添加污点来禁止调度,NoSchedule并不会影响以及部署的pod。

1
kubectl taint node k8s-master01 k8s-master02 k8s-master03  node-role.kubernetes.io/control-plane:NoSchedule

也可以添加NoExecute类型的污点,此时不容忍该污点的pod会被驱逐重建:

1
kubectl taint node k8s-master01 k8s-master02 k8s-master03  node-role.kubernetes.io/control-plane:NoExecute

6.实战-新节点禁止调度

当 Kubernetes 集群添加新节点时,通常情况下不会立即调度 Pod 到该节点,新添加的节点需要经过完整 的可用性测试之后才可以调度 Pod,此时也可以使用污点先临时禁止该节点的调度:

1
kubectl taint node k8s-node04 new-node=true:NoSchedule

同样的道理,比如在禁止调度之前已经有 Pod 部署在该节点,可以进行驱逐:

1
kubectl taint node k8s-node04  new-node=true:NoExecute

待新节点测试完毕后,在允许该节点可以进行调度,删除之前添加的污点:

1
kubectl taint node k8s-node04  new-node-

7.实战-节点维护流程

当 Kubernetes 的节点需要进行下线维护时,此时需要先把该节点的服务进行驱逐和重新调度。

此时需要根据实际情况判断是直接驱逐还是选择重新调度,比如某个 Pod 只有一个副本, 或者某个服务比较重要,就不能直接进行驱逐,而是需要先把节点关闭调度,然后在进行服务的 重新部署。

先把需要维护的节点添加一个污点,假设k8s-node03要维护:

1
2
# 先为k8s-node03添加污点,NoSchedule表示新pod如果没有容忍不在调度到node03上,但是node03上已有的不影响
kubectl taint node k8s-node03 maintain:NoSchedule

假设redis-7b68666f59-skvm9比较重要,使用rollout restart触发更新

1
kubectl rollout restart deployment  -n project-prod redis

再次查看该pod,已经部署到k8s-node02节点上了:

1
kubectl get po -n project-prod -owide	

接下来如果没有重要服务,即可对该节点的pod进行驱逐:

1
kubectl taint node k8s-node03 maintain:NoExecute

驱逐后,即可按照预期对节点进行维护,维护完成后可以删除污点,恢复调度:

1
kubectl taint node k8s-node03 maintain-

除了自定义污点,也可以使用 kubectl 快捷指令将节点设置为维护状态:

1
kubectl cordon k8s-node03

此时节点会被标记一个 SchedulingDisabled 状态,但是已经运行在该节点的 Pod 不受影响:

1
2
3
4
5
6
7
8
9
10
[root@k8s-master01 ~]# kubectl cordon k8s-node03
node/k8s-node03 cordoned
[root@k8s-master01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master01 Ready <none> 639d v1.31.4
k8s-master02 Ready <none> 639d v1.31.4
k8s-master03 Ready <none> 639d v1.31.4
k8s-node01 Ready <none> 639d v1.31.4
k8s-node02 Ready <none> 639d v1.31.4
k8s-node03 Ready,SchedulingDisabled <none> 639d v1.31.4

然后再根据服务的重要性使用rollout restart 来滚动更新重要的服务,使其重新调度到其他节点,然后再使用快捷指令驱逐节点上其他的pod:

1
kubectl drain k8s-node03 --ignore-daemonsets --delete-emptydir-data

维护结束使用快捷指令恢复调度:

1
kubectl uncordon k8s-node03
1
2
3
4
5
6
7
8
9
10
[root@k8s-master01 ~]# kubectl uncordon k8s-node03
node/k8s-node03 uncordoned
[root@k8s-master01 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master01 Ready <none> 639d v1.31.4
k8s-master02 Ready <none> 639d v1.31.4
k8s-master03 Ready <none> 639d v1.31.4
k8s-node01 Ready <none> 639d v1.31.4
k8s-node02 Ready <none> 639d v1.31.4
k8s-node03 Ready <none> 639d v1.31.4
命令 作用 对现有Pod的影响 典型场景
cordon 仅禁止新Pod调度 无影响,继续运行 维护前准备、观察节点负载自然下降
drain 禁止调度 + 驱逐现有Pod 会被优雅终止并迁移 正式下线节点、系统升级重启
uncordon 解除禁止调度 - 维护完成,恢复节点使用

8.实战-节点特殊资源保留

当 Kubernetes 中存储特殊节点时,应该尽量保持不需要特殊资源的 Pod 不要调度到这些节点 上,此时可以通过污点进行控制。

比如包含了 GPU 的节点不能被任意调度:

1
kubectl taint node k8s-node03 gpu=true:NoSchedule

具有其它特殊资源,尽量不要调度:

1
kubectl taint node k8s-node02 ssd=true:PreferNoSchedule 

9.实战-调度到具有污点的节点

在生产环境中,经常根据实际情况给节点打上污点,比如特殊资源节点不能随意调度、主节 点不能随意调度,但是需要特殊资源的服务还是需要调度到该节点,一些监控和收集的服务还是 需要调度到主节点,此时需要给这些服务添加合适的容忍才能部署到这些节点。

比如上述添加的 GPU 污点:

1
kubectl taint node k8s-node03 gpu=true:NoSchedule

如果某个服务需要 GPU 资源,就需要添加容忍才能部署至该节点。此时可以添加如下的容 忍配置至 Pod 上:

1
2
3
4
tolerations:
- key: "gpu"
operator: "Exists"
effect: "NoSchedule"

完整配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 需要注意的是节点有污点,pod有容忍并不代表pod一定会调度到该节点上
cat my-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app #定义的pod标签时app=myapp
spec:
tolerations:
- key: "gpu"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: app
image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx:latest
1
2
3
4
5
6
7
8
9
10
[root@k8s-master01 21-taints]# kubectl create -f my-app.yaml
deployment.apps/my-app created
[root@k8s-master01 21-taints]# kubectl get pod
NAME READY STATUS RESTARTS AGE
my-app-bb85fcbfc-rrtmw 1/1 Running 0 6s

#可以看到此时pod调度到了k8s-node02节点
[root@k8s-master01 21-taints]# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-app-bb85fcbfc-rrtmw 1/1 Running 0 11s 172.16.58.205 k8s-node02 <none> <none>

接下来给节点添加一个标签,并配置节点选择器,让pod调度到k8s-node03:

1
2
3
# 为  k8s-node03  添加标签
[root@k8s-master01 21-taints]# kubectl label nodes k8s-node03 gpu=A100
node/k8s-node03 labeled

资源完整配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cat  my-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app #定义的pod标签时app=myapp
spec:
nodeSelector:
gpu: "A100" #定义的node标签时gpu=true
tolerations:
- key: "gpu"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: app
image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx:latest

创建资源并查看pod所在的节点:

1
2
3
4
5
6
7
8
9
10
11
[root@k8s-master01 21-taints]# kubectl replace -f my-app.yaml
deployment.apps/my-app replaced
[root@k8s-master01 21-taints]# kubectl get po
NAME READY STATUS RESTARTS AGE
my-app-6759d86778-khf95 0/1 ContainerCreating 0 2s
my-app-bb85fcbfc-rrtmw 1/1 Running 0 5m12s

# 可以看到此时pod已经部署在k8s-node03上
[root@k8s-master01 21-taints]# kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-app-6759d86778-khf95 1/1 Running 0 6s 172.16.135.164 k8s-node03 <none> <none>

10.实战-专用节点隔离

一个 Kubernetes 集群,很常见会有一些专用的节点,比如 ingress、gateway、storage 或者 多租户环境等。这些节点通常不建议和其他服务交叉使用,所以需要利用污点和容忍将这些节点 隔离起来。

比如选择一批节点作为 ingress 入口的节点:

1
kubectl label node k8s-node02 ingress=true

添加一个污点,不让其他服务部署

1
kubectl taint node k8s-node02 ingress=true:NoSchedule

更改 Ingress 的部署资源,添加容忍和节点选择器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kubectl edit -n ingress-nginx daemonset.apps/ingress-nginx-controller
spec:
template:
spec:
nodeSelector:
ingress: "true"
kubernetes.io/os: linux
tolerations:
- effect: NoSchedule
key: ingress
operator: Exists



11.实战-节点宕机快速恢复服务

当 Kubernetes 集群中有节点故障时,Kubernetes 会自动恢复故障节点上的服务,但是默认 情况下,节点故障时五分钟才会重新调度服务,此时可以利用污点的 tolerationSeconds 快速恢 复服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app #定义的pod标签时app=myapp
spec:
tolerations:
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 10 #10秒后pod迁移到其他节点
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 10 #10秒后pod迁移到其他节点
containers:
- name: app
image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx:latest