污点 及 容忍度

污点介绍

taint,中文译作"污点"。taint是k8s中用于标记Node局限性的,它可让Pod与Node产生排斥性。

查看调度情况

文件 pod-alpine.yaml

 apiVersion: v1
 kind: Pod
 metadata:
   name: pod-alpine
   namespace: default
   labels:
     app: myapp
 spec:
   restartPolicy: Always
   containers:
   - name: container-alpine
     image: alpine:3.7
     command: ["/bin/sh", "-c", "while true;do sleep 3600;done;"]

操作

 # 创建pod
 kubectl apply -f pod-alpine.yaml
 ​
 # 查看pod
 kubectl get pod pod-alpine -o wide
 ​
 # 查看node的taint
 kubectl describe node <nodename> | grep Taints

节点

Taint

含义

master

node-role.kubernetes.io/master:NoSchedule

不允许普通 Pod 调度上来

node1

<none>

可以正常调度

node2

<none>

可以正常调度

这意味着什么

  • master 节点带 NoSchedule 污点:这是 Kubernetes 的标准做法,防止业务 Pod 跑到控制平面节点上。普通 Pod(没有对应 Toleration)不会调度到 master。

  • node1 / node2 没有污点:任何 Pod 都可以调度到这两个节点上。

创建和删除Taint

创建节点Taint的格式为:

 kubectl taint node <nodename> key=[value]:effect

删除节点Taint的格式为:

 kubectl taint node <nodename> key=[value]:effect-

命令格式详解

添加污点

 kubectl taint node <nodename> key=[value]:effect

部分

说明

例子

key

污点的键(必填)

node-role.kubernetes.io/master

[value]

污点的值(可选,用 = 连接)

gpu=true

effect

效果(必填,用 : 连接)

NoSchedule

effect 三种取值:

effect

含义

NoSchedule

不容忍的 Pod 不会被调度上来(已运行的不动)

PreferNoSchedule

尽量不调度,但没地方去时也可以上

NoExecute

不容忍的 Pod 不仅不调度,已在运行的也会被驱逐

示例:

 # 带 value 的
 kubectl taint node node1 gpu=true:NoSchedule
 ​
 # 不带 value 的(key 后面直接跟 :effect)
 kubectl taint node node1 dedicated:NoSchedule


删除污点

 kubectl taint node <nodename> key=[value]:effect-

关键就是末尾的 -,表示删除。删除时必须完整写对 key、value、effect,才能精确匹配删掉。

示例:

 # 删除你 master 上的那个污点
 kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
 ​
 # 删除带 value 的
 kubectl taint node node1 gpu=true:NoSchedule-

⚠️ 注意:- 要紧贴在 effect 后面,中间不能有空格。

实验要求

1.为node1定义污点: taint=:PreferNoSchedule
2.创建pod并查看实际调度的节点
3.为node2定义污点: taint=:NoSchedule
4.重复第2步骤
5.修改node1污点: taint=:NoExecute
6.清除2个节点的所有污点

实验操作

1-
kubectl taint node node1 taint=:PreferNoSchedule

2-
kubectl apply -f pod-alpine.yaml
watch -n 1 kubectl get po pod-alpine -o wide

3-
kubectl taint node node2 taint=:NoSchedule
kubectl delete -f pod-alpine.yaml

4-
kubectl apply -f pod-alpine.yaml

5-
kubectl taint node node1 taint=:PreferNoSchedule-
kubectl taint node node1 taint=:NoExecute

6-
kubectl taint node node1 taint=:NoExecute- && kubectl taint node node2 taint=:NoSchedule-

Taint与pod调度(定向调度)

通过诸如"nodeName"、"nodeSelector"、"affinity"等手段可以实现Pod的定向调度。定向调度代表Pod对节点的偏好,而taint代表着节点对Pod的偏好。当这两个设置出现冲突的时候,又是怎样的结果?

完整的对照表:

Pod 调度方式

PreferNoSchedule (软性排斥)

NoSchedule (硬性排斥)

NoExecute (绝对排斥 + 驱逐)

nodeName

✅ 可调度 (无视污点)

✅ 可调度 (无视污点)

❌ 被驱逐 (若没有容忍度)

nodeSelector

✅ 可调度 (无其他节点时)

❌ Pending

❌ Pending

affinity-硬策略

✅ 可调度 (无其他节点时)

❌ Pending

❌ Pending

affinity-软策略

⚠️ 可能调度 (权重决定)

❌ Pending

❌ Pending

测试文件 pod-alpine.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-alpine
  namespace: default
  labels:
    app: myapp
spec:
  nodeName: node1  				# 定向调度
  restartPolicy: Always
  containers:
  - name: container-alpine
    image: alpine:3.7
    command: ["/bin/sh", "-c", "while true;do sleep 3600;done;"]

操作(可以观察 pod 启动后分配 工作节点 及 状态 )

# 设置node1的taint
kubectl taint node node1 taint=:PreferNoSchedule

# 启动pod做测试(查看在那个工作节点启动)
kubectl apply -f pod-alpine.yaml
watch -n 1 kubectl get po pod-alpine -o wide
kubectl delete -f pod-alpine.yaml

# 换一个taint (NoSchedule)
kubectl taint node node1 taint=:PreferNoSchedule-
kubectl taint node node1 taint=:NoSchedule
kubectl apply -f pod-alpine.yaml
kubectl taint node node1 taint=:NoSchedule-

# 换最后一个taint (NoExecute)
kubectl delete -f pod-alpine.yaml
kubectl taint node node1 taint=:NoExecute
kubectl apply -f pod-alpine.yaml
kubectl taint node node1 taint=:NoExecute-

容忍度

什么是Toleration toleration,中文译作"容忍”。与taint刚好相反,某些时候我们需要在有污点的节点上运行Pod,这种无视污点的调度方式称呼为容忍(即使节点存在污点,也能容忍下来,依旧让其运行Pod)。

容忍度匹配策略

精确匹配:

spec:
  # 定义容忍策略
  tolerations:
    # Equal精确匹配,表示 key、value、effect必须写全
  - operator: Equal
    # 污点的键。如果设置成 key: "" 代表任意的键值对
    key: taint
    # 污点的值。
    value: ""
    # 污点的效果,如果设置成 effect: "" 代表所有策略
    effect: NoSchedule

模糊匹配:

spec:
  # 定义容忍策略
  tolerations:
    # Exists模糊匹配,表示 key、value、effect满足其中之一即可
  - operator: Exists
    # 污点的键。如果设置成 key: "" 代表任意的键值对
    key: taint
    # 污点的效果,如果设置成 effect: "" 代表所有效果
    effect: NoSchedule

容忍度测试pod (pod-alpine.yaml)

apiVersion: v1
kind: Pod
metadata:
  name: pod-alpine
  namespace: default
  labels:
    app: myapp
spec:
  # 定义容忍策略
  tolerations:
    # Equal精确匹配
  - operator: Equal
    key: taint
    value: ""
    effect: NoSchedule
  restartPolicy: Always
  containers:
  - name: container-alpine
    image: alpine:3.7
    command: ["/bin/sh", "-c","while true;do sleep 3600;done;"]

实验步骤

- 1. 为node1定义污点: taint=:PreferNoSchedule
- 2. 为node2定义污点: taint=:NoSchedule
- 3. 创建pod并查看实际调度的节点
- 4. 修改pod 的容忍为: operator: Exists  effect: NoSchedule
     修改node2污点: taint=:NoExecute
- 5. 再次创建pod并查看实际调度的节点
- 6. 清除2个节点的所有污点

实验操作

1-
kubectl taint node node1 taint=:PreferNoSchedule

2-
kubectl taint node node2 taint=:NoSchedule

3-
kubectl  apply -f pod-alpine.yaml 
watch -n 1 kubectl get po pod-alpine -o wide

4-
vim pod-alpine.yaml 编辑配置文件修改内容
kubectl taint node node2 taint=:NoSchedule-
kubectl taint node node2 taint=:NoExecute

5-
kubectl  apply -f pod-alpine.yaml

6-
kubectl taint node node1 taint=:PreferNoSchedule-
kubectl taint node node2 taint=:NoExecute-

亲和及反亲和

亲和介绍

Affinity,中文译作“亲和”。亲和性调度用于表达 Pod 对 Node 的偏好(节点亲和),或 Pod 对其他 Pod 的偏好(Pod 亲和)。与污点“节点排斥 Pod”的方向相反,亲和是“Pod 主动靠近节点或其他 Pod”。

亲和分为两类:

类型

作用对象

解决的问题

节点亲和(nodeAffinity)

Pod 与 Node

Pod 想调度到哪些节点上

Pod 亲和/反亲和(podAffinity / podAntiAffinity)

Pod 与 Pod

Pod 想和哪些 Pod 在一起,或不想和哪些 Pod 在一起

硬亲和与软亲和:与污点的 effect 类似,亲和也有强弱之分:

类型

字段

含义

硬亲和

requiredDuringSchedulingIgnoredDuringExecution

必须满足,不满足则 Pending

软亲和

preferredDuringSchedulingIgnoredDuringExecution

尽量满足,不满足也能调度

IgnoredDuringExecution 的含义与容忍度一致:调度时生效,运行后若标签变化不再匹配,不会驱逐已有 Pod。

节点亲和(nodeAffinity)

节点亲和根据 Node 的标签 约束 Pod 可以调度到哪些节点,是 nodeSelector 的增强版,语法更丰富,且支持软策略。

节点亲和匹配策略

硬亲和(required):

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd

软亲和(preferred):

spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd

weight 范围 1-100,权重越高,调度器越倾向于选择满足条件的节点。

操作符说明

operator

含义

In

标签值在列表中

NotIn

标签值不在列表中

Exists

存在指定 key 的标签

DoesNotExist

不存在指定 key 的标签

Gt / Lt

标签值大于/小于指定值

Pod 亲和及反亲和

Pod 亲和/反亲和基于 节点上已有 Pod 的标签 来约束新 Pod 的调度位置,而非节点自身的标签。

核心概念:

  • 拓扑域(topologyKey):用节点标签的 key 来划分范围。例如 kubernetes.io/hostname 表示以单个节点为范围,topology.kubernetes.io/zone 表示以可用区为范围。

  • LabelSelector:筛选参照的 Pod 标签。

语义:

  • Pod 亲和:如果节点上已有满足条件的 Pod,则新 Pod 应该 调度到该节点(或同一拓扑域)。

  • Pod 反亲和:如果节点上已有满足条件的 Pod,则新 Pod 不应该 调度到该节点(或同一拓扑域)。

Pod 亲和匹配策略

硬亲和(required):

spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - backend
        topologyKey: kubernetes.io/hostname

软亲和(preferred):

spec:
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - backend
          topologyKey: kubernetes.io/hostname

Pod 反亲和匹配策略

硬反亲和(required):

spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - web
        topologyKey: kubernetes.io/hostname

软反亲和(preferred):

spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - web
          topologyKey: kubernetes.io/hostname

拓扑域理解

topologyKey

拓扑域范围

典型用途

kubernetes.io/hostname

单个节点

Pod 分散到不同节点(反亲和)

topology.kubernetes.io/zone

可用区

Pod 集中到同一可用区(亲和)

topology.kubernetes.io/region

区域

跨区域调度约束

⚠️ 注意:使用 Pod 反亲和时,集群中每个节点都必须具有 topologyKey 指定的标签,否则可能导致意外调度行为。Pod 亲和/反亲和计算开销较大,不建议在数百节点的集群中使用。

实验要求

1. 为node1打标签:disk=ssd
2. 创建pod,配置节点硬亲和:必须调度到 disk=ssd 的节点
3. 查看pod调度到的节点
4. 为node2打标签:disk=hdd
5. 修改pod为节点软亲和:偏好 disk=ssd
6. 再次创建pod,观察调度结果
7. 清除node1和node2的标签

实验操作

1-
kubectl label node node1 disk=ssd

2-
cat > pod-nodeaffinity.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity
  namespace: default
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disk
            operator: In
            values:
            - ssd
  restartPolicy: Always
  containers:
  - name: container-alpine
    image: alpine:3.7
    command: ["/bin/sh", "-c", "while true;do sleep 3600;done;"]
EOF
kubectl apply -f pod-nodeaffinity.yaml
watch -n 1 kubectl get po pod-nodeaffinity -o wide

3-
# 观察pod是否运行在 node1 上

4-
kubectl label node node2 disk=hdd

5-
cat > pod-nodeaffinity.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity
  namespace: default
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        preference:
          matchExpressions:
          - key: disk
            operator: In
            values:
            - ssd
  restartPolicy: Always
  containers:
  - name: container-alpine
    image: alpine:3.7
    command: ["/bin/sh", "-c", "while true;do sleep 3600;done;"]
EOF

6-
kubectl delete -f pod-nodeaffinity.yaml
kubectl apply -f pod-nodeaffinity.yaml
watch -n 1 kubectl get po pod-nodeaffinity -o wide
# 预期:优先调度到 node1(disk=ssd),若无资源则可能调度到 node2

7-
kubectl label node node1 disk-
kubectl label node node2 disk-
kubectl delete -f pod-nodeaffinity.yaml