K8s学习笔记(二十) 亲和性、污点、容忍、驱逐

K8s 的亲和性、污点(Taint)、容忍(Toleration)和驱逐(Eviction)都是控制 Pod 调度与节点关系的核心机制,但作用方向和场景不同。简单说:

  • 亲和性:Pod "主动选择" 节点(或其他 Pod);
  • 污点:节点 "主动排斥" Pod(给节点打 "排斥标签");
  • 容忍:Pod "接受被排斥"(告诉节点 "我能忍受你的排斥");
  • 驱逐:节点或 K8s "强制移除" Pod(因资源不足或节点异常)。

1 亲和性与反亲和性

K8s 的亲和性(Affinity)和反亲和性(Anti-Affinity)是控制 Pod 调度位置的核心机制,简单说就是:

  • 亲和性:让 Pod "尽量" 或 "必须" 跑到某些节点 / 其他 Pod 附近;
  • 反亲和性:让 Pod "尽量" 或 "必须" 远离某些节点 / 其他 Pod 附近。

通过这两种机制,能实现更灵活的调度策略(比如 "把前端 Pod 和后端 Pod 放同一节点减少延迟""避免同一应用的多个副本挤在同一节点以防单点故障")。

1.1 2 个核心维度

亲和性 / 反亲和性的配置,主要从 "作用对象""约束强度" 两个维度区分:

作用对象 含义 典型场景
节点亲和性 Pod 对节点的 "偏好"(基于节点标签) 只跑在有 SSD 的节点、只跑在特定机房的节点
Pod 亲和性 / 反亲和性 Pod 对其他 Pod 的 "偏好"(基于其他 Pod 标签) 前后端 Pod 放同一节点、同应用副本分散在不同节点
约束强度 含义 后果
硬约束(Required) 必须满足,不满足则 Pod 调度失败 若没符合条件的节点,Pod 会一直处于 Pending 状态
软约束(Preferred) 尽量满足,不满足也能调度到其他节点 优先按规则调度,实在不行就 "妥协"

1.2 节点亲和性(NodeAffinity):控制 Pod 跑在哪些节点

节点亲和性是 Pod 通过节点的标签 来选择节点,比如节点有disk=ssdregion=beijing标签,Pod 可以指定 "只跑在带这些标签的节点上"。

1.2.1 硬约束(必须满足)示例

需求:Pod 必须调度到 "有env=prod标签" 且 "没有disk=hdd标签" 的节点上。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-required
spec:
  containers:
  - name: demo
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬约束
        nodeSelectorTerms:  # 节点选择条件(满足任意一个term即可)
        - matchExpressions:  # 表达式列表(需同时满足所有表达式)
          - key: env          # 节点标签的key
            operator: In      # 操作符:In(在列表中)、NotIn(不在列表中)、Exists(存在该标签)等
            values: ["prod"]  # 标签的value列表
          - key: disk
            operator: NotIn
            values: ["hdd"]
1.2.2 软约束(尽量满足)示例

需求:优先把 Pod 调度到 "有performance=high标签" 的节点,若没有则调度到其他节点。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-preferred
spec:
  containers:
  - name: demo
    image: nginx
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:  # 软约束(可配置多个,有权重)
      - weight: 80  # 权重(0-100),数值越高优先级越高
        preference:
          matchExpressions:
          - key: performance
            operator: In
            values: ["high"]
      - weight: 20  # 次要偏好:有`zone=zone1`标签的节点
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values: ["zone1"]
关键说明:
  • nodeSelectorTerms是 "或" 关系(满足任意一个 term 即可);
  • matchExpressions内部是 "与" 关系(必须满足所有表达式);
  • 操作符Exists只需节点有该标签即可,无需指定values(如key: env, operator: Exists)。

1.3 Pod 亲和性 / 反亲和性:控制 Pod 与其他 Pod 的位置关系

Pod 亲和性 / 反亲和性是通过其他 Pod 的标签 来决定调度位置,比如 "和app=backend的 Pod 放同一节点"(亲和),或 "不和其他app=frontend的 Pod 放同一节点"(反亲和)。

核心依赖拓扑域(topologyKey):定义 "什么范围算'附近'"(如同一节点、同一可用区、同一机房)。常见拓扑域:

  • kubernetes.io/hostname:同一节点(最常用);
  • topology.kubernetes.io/zone:同一可用区;
  • topology.kubernetes.io/region:同一区域。
1.3.1 Pod 亲和性(PodAffinity)示例

需求:让前端 Pod(app=frontend)尽量和后端 Pod(app=backend)调度到同一节点(减少网络延迟)。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: frontend-pod
  labels:
    app: frontend
spec:
  containers:
  - name: frontend
    image: nginx
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:  # 软亲和(尽量满足)
      - weight: 100
        podAffinityTerm:
          labelSelector:  # 目标Pod的标签(匹配app=backend的Pod)
            matchExpressions:
            - key: app
              operator: In
              values: ["backend"]
          topologyKey: kubernetes.io/hostname  # 拓扑域:同一节点
1.3.2 Pod 反亲和性(PodAntiAffinity)示例

需求:让app=redis的多个 Pod必须分散在不同节点(避免单点故障,适合有状态服务)。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: redis-pod-1
  labels:
    app: redis
spec:
  containers:
  - name: redis
    image: redis
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬反亲和(必须满足)
      - labelSelector:  # 目标Pod的标签(匹配其他app=redis的Pod)
          matchExpressions:
          - key: app
            operator: In
            values: ["redis"]
        topologyKey: kubernetes.io/hostname  # 拓扑域:不同节点(即不能和同标签Pod在同一节点)

如果再创建redis-pod-2(同标签),K8s 会自动把它调度到和redis-pod-1不同的节点。

1.4 关键注意事项

  1. 硬约束慎用:硬约束(Required)如果没有满足条件的节点,Pod 会一直 Pending,建议先用软约束观察调度情况,再逐步调整为硬约束。
  2. Pod 亲和性开销更高:Pod 亲和性 / 反亲和性需要扫描集群中其他 Pod 的标签,大规模集群可能增加调度器负担,节点亲和性更轻量。
  3. 拓扑域选择:
    • 想 "同节点" 用kubernetes.io/hostname
    • 想 "同可用区但不同节点" 用topology.kubernetes.io/zone(适合平衡性能和可用性)。
  4. 标签一致性 :确保目标节点或 Pod 的标签正确(比如用kubectl get nodes --show-labels查看节点标签)。

1.5 验证调度结果

配置后,用以下命令查看 Pod 被调度到哪个节点,验证是否符合预期:

bash 复制代码
kubectl get pods -o wide  # 查看Pod的NODE列

如果 Pod 处于 Pending 状态,用kubectl describe pod <pod名>查看事件,通常会提示 "没有满足亲和性条件的节点",可据此调整标签或约束规则。

1.6 总结

类型 作用对象 典型场景 核心配置字段
节点亲和性 节点(标签) 只跑在 SSD 节点、特定区域节点 nodeAffinity
Pod 亲和性 其他 Pod(标签) 前后端 Pod 同节点、同可用区 podAffinity + topologyKey
Pod 反亲和性 其他 Pod(标签) 同应用副本分散在不同节点、不同可用区 podAntiAffinity + topologyKey

2 污点(Taint):节点主动 "拒绝" Pod

节点可以通过 "污点" 主动排斥 Pod,就像给节点贴了 "禁止入内" 的标签。只有 Pod 明确表示 "我能忍受这个污点"(即配置了对应的容忍),才能被调度到该节点。

2.1 污点的核心组成

每个污点由 3 部分构成:key=value:effect,其中:

  • key=value:污点的标识(类似标签);
  • effect:污点的 "排斥效果",决定如何排斥 Pod,有 3 种类型:
effect 类型 作用
NoSchedule 节点不会调度新 Pod 到该节点(已运行的 Pod 不受影响)。
PreferNoSchedule 尽量不调度新 Pod 到该节点(软约束,实在没节点时也能调度)。
NoExecute 不仅不调度新 Pod,还会驱逐已运行在该节点上、且没有对应容忍的 Pod。

2.2 给节点打污点(操作示例)

假设给节点node-1打一个 "只允许 GPU 相关 Pod 运行" 的污点:

bash 复制代码
# 格式:kubectl taint nodes <节点名> <key>=<value>:<effect>
kubectl taint nodes node-1 gpu=yes:NoSchedule

此时,没有容忍这个污点的 Pod 会被node-1拒绝调度。

2.3 常见场景
  • 给 GPU 节点打污点,避免普通 Pod 占用 GPU 资源;
  • 给 master 节点打默认污点(node-role.kubernetes.io/master:NoSchedule),防止普通 Pod 调度到 master;
  • 给故障节点打NoExecute污点,快速驱逐其上的 Pod。

3 容忍(Toleration):Pod "忍受" 节点的污点

容忍是 Pod 的配置,用于告诉 K8s:"我能忍受节点的某个 / 某些污点,请允许我调度到该节点"。

3.1 容忍的配置示例(YAML)

要让 Pod 能调度到带有gpu=yes:NoSchedule污点的节点,需在 Pod 中配置对应的容忍:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  containers:
  - name: gpu-app
    image: nvidia/cuda  # 假设这是一个需要GPU的应用
  tolerations:  # 容忍配置
  - key: "gpu"        # 匹配污点的key
    operator: "Equal" # 匹配方式:Equal(精确匹配value)或Exists(只要key存在,忽略value)
    value: "yes"      # 匹配污点的value
    effect: "NoSchedule"  # 匹配污点的effect

3.2 灵活配置方式

  • 忽略 value:只要节点有 gpu这个 key 的污点,不管 value 是什么都容忍:

    yaml 复制代码
    tolerations:
    - key: "gpu"
      operator: "Exists"  # 只要key存在就容忍
      effect: "NoSchedule"
  • 容忍所有污点:(谨慎使用,可能导致 Pod 被调度到不合适的节点)

    yaml 复制代码
    tolerations:
    - operator: "Exists"  # 不指定key和effect,容忍所有污点

3.3 污点 + 容忍的经典组合

给节点打污点,再让特定 Pod 配置容忍,实现 "节点专属 Pod":

  • 节点node-gpu打污点:gpu=yes:NoSchedule
  • 只有带gpu=yes容忍的 Pod(如 AI 训练 Pod)才能调度到node-gpu
  • 普通 Pod 因没有容忍,被node-gpu拒绝,避免占用 GPU 资源。

4 驱逐(Eviction):强制移除 Pod

驱逐是 K8s 在节点资源不足、节点异常等情况下,将 Pod 从节点上 "赶走" 的机制,确保集群稳定。

4.1 驱逐的两种类型

类型 触发原因 示例场景
节点触发驱逐 节点资源紧张(内存 / 磁盘不足)、节点状态异常(如磁盘损坏)。 节点内存使用率达 95%,驱逐低优先级 Pod 释放资源。
用户触发驱逐 管理员手动执行命令,主动移除 Pod。 节点维护前,手动驱逐其上的所有 Pod。

4.2 自动驱逐的核心逻辑(节点压力)

K8s 节点的kubelet会监控资源使用,当达到 "驱逐阈值" 时,按以下顺序驱逐 Pod:

  1. 优先驱逐资源使用超过 limits的 Pod("违规者" 先被清);
  2. 再按Pod 优先级(PriorityClass) 驱逐:低优先级 Pod 先被驱逐;
  3. 同优先级下,驱逐资源请求比例最高的 Pod(如请求 1 核却用了 2 核的 Pod)。

4.3 关键配置:驱逐阈值

可在kubelet配置中自定义驱逐阈值(如kubelet-config.yaml):

yaml 复制代码
evictionHard:  # 硬阈值:达到后立即驱逐
  memory.available: "100Mi"  # 内存可用量低于100Mi时驱逐
  nodefs.available: "10%"    # 节点磁盘可用量低于10%时驱逐
evictionSoft:  # 软阈值:达到后等待一段时间再驱逐
  nodefs.available: "15%"
evictionSoftGracePeriod:  # 软阈值等待时间
  nodefs.available: "5m"   # 磁盘可用量低于15%后,等待5分钟再驱逐

4.4 NoExecute 污点与驱逐的关系

当节点添加NoExecute污点时,会触发驱逐:

  • 没有对应容忍的 Pod 会被立即驱逐;

  • 有对应容忍的 Pod,可通过 tolerationSeconds配置 "最多再留多久":

    yaml 复制代码
    tolerations:
    - key: "node.kubernetes.io/unreachable"  # 节点不可达的污点(K8s自动添加)
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 300  # 节点不可达后,最多再留300秒(5分钟)再被驱逐

5 核心机制对比与配合使用

机制 作用方向 典型场景 与其他机制的配合
亲和性 Pod 主动选择节点 让 Pod 跑在 SSD 节点、同可用区 与污点 + 容忍配合:先通过亲和性选节点,再用容忍通过节点的污点检查
污点 节点主动排斥 Pod 保护 GPU 节点、隔离故障节点 必须配合容忍才能让 Pod 调度到该节点
容忍 Pod 接受节点的排斥 允许 GPU 应用调度到带 GPU 污点的节点 单独使用无效,需配合节点的污点
驱逐 强制移除 Pod 节点资源不足时保稳定、节点维护前清 Pod NoExecute污点会触发驱逐,驱逐优先级受 Pod 优先级影响

6 实战验证命令

  1. 查看节点污点:kubectl describe node <节点名> | grep Taint
  2. 给节点打污点:kubectl taint nodes <节点名> key=value:effect
  3. 移除节点污点:kubectl taint nodes <节点名> key:effect-(末尾加 "-")
  4. 查看 Pod 的容忍配置:kubectl describe pod <pod名> | grep Tolerations
  5. 手动驱逐 Pod:kubectl delete pod <pod名> --grace-period=0 --force(强制立即驱逐)

7 总结

  • 想让 Pod "选节点":用亲和性;
  • 想让节点 "拒 Pod":给节点打污点,再让允许的 Pod 配置容忍;
  • 想让 Pod "被赶走":要么触发节点自动驱逐(资源不足),要么用NoExecute污点强制驱逐。
相关推荐
舰长1154 小时前
k8s 持久化存储方案-NFS
云原生·容器·kubernetes
kuniqiw4 小时前
远程处理器协议框架学习
学习
海梨花4 小时前
【八股笔记】SSM
java·开发语言·笔记·后端·面试·框架
钊气蓬勃.4 小时前
深度学习笔记:入门
人工智能·笔记·深度学习
hmbbcsm4 小时前
python学习之路(二)
学习
冷崖5 小时前
定时器的学习(二)
linux·c++·学习
想唱rap5 小时前
C++ string类的使用
开发语言·c++·笔记·算法·新浪微博
ajsbxi5 小时前
【Redis】缓存读/写操作流程
redis·笔记·spring·缓存·bootstrap
好奇龙猫5 小时前
【学习AI-相关路程-mnist手写数字分类-一段学习的结束:自我学习AI-复盘-代码-了解原理-综述(5) 】
人工智能·学习·分类