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=ssd
或region=beijing
标签,Pod 可以指定 “只跑在带这些标签的节点上”。
1.2.1 硬约束(必须满足)示例
需求:Pod 必须调度到 “有env=prod
标签” 且 “没有disk=hdd
标签” 的节点上。
apiVersion: v1
kind: Pod
metadata:name: node-affinity-required
spec:containers:- name: demoimage: nginxaffinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 硬约束nodeSelectorTerms: # 节点选择条件(满足任意一个term即可)- matchExpressions: # 表达式列表(需同时满足所有表达式)- key: env # 节点标签的keyoperator: In # 操作符:In(在列表中)、NotIn(不在列表中)、Exists(存在该标签)等values: ["prod"] # 标签的value列表- key: diskoperator: NotInvalues: ["hdd"]
1.2.2 软约束(尽量满足)示例
需求:优先把 Pod 调度到 “有performance=high
标签” 的节点,若没有则调度到其他节点。
apiVersion: v1
kind: Pod
metadata:name: node-affinity-preferred
spec:containers:- name: demoimage: nginxaffinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution: # 软约束(可配置多个,有权重)- weight: 80 # 权重(0-100),数值越高优先级越高preference:matchExpressions:- key: performanceoperator: Invalues: ["high"]- weight: 20 # 次要偏好:有`zone=zone1`标签的节点preference:matchExpressions:- key: zoneoperator: Invalues: ["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
)调度到同一节点(减少网络延迟)。
apiVersion: v1
kind: Pod
metadata:name: frontend-podlabels:app: frontend
spec:containers:- name: frontendimage: nginxaffinity:podAffinity:preferredDuringSchedulingIgnoredDuringExecution: # 软亲和(尽量满足)- weight: 100podAffinityTerm:labelSelector: # 目标Pod的标签(匹配app=backend的Pod)matchExpressions:- key: appoperator: Invalues: ["backend"]topologyKey: kubernetes.io/hostname # 拓扑域:同一节点
1.3.2 Pod 反亲和性(PodAntiAffinity)示例
需求:让app=redis
的多个 Pod必须分散在不同节点(避免单点故障,适合有状态服务)。
apiVersion: v1
kind: Pod
metadata:name: redis-pod-1labels:app: redis
spec:containers:- name: redisimage: redisaffinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution: # 硬反亲和(必须满足)- labelSelector: # 目标Pod的标签(匹配其他app=redis的Pod)matchExpressions:- key: appoperator: Invalues: ["redis"]topologyKey: kubernetes.io/hostname # 拓扑域:不同节点(即不能和同标签Pod在同一节点)
如果再创建redis-pod-2
(同标签),K8s 会自动把它调度到和redis-pod-1
不同的节点。
1.4 关键注意事项
- 硬约束慎用:硬约束(Required)如果没有满足条件的节点,Pod 会一直 Pending,建议先用软约束观察调度情况,再逐步调整为硬约束。
- Pod 亲和性开销更高:Pod 亲和性 / 反亲和性需要扫描集群中其他 Pod 的标签,大规模集群可能增加调度器负担,节点亲和性更轻量。
- 拓扑域选择:
- 想 “同节点” 用
kubernetes.io/hostname
; - 想 “同可用区但不同节点” 用
topology.kubernetes.io/zone
(适合平衡性能和可用性)。
- 想 “同节点” 用
- 标签一致性:确保目标节点或 Pod 的标签正确(比如用
kubectl get nodes --show-labels
查看节点标签)。
1.5 验证调度结果
配置后,用以下命令查看 Pod 被调度到哪个节点,验证是否符合预期:
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 运行” 的污点:
# 格式: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 中配置对应的容忍:
apiVersion: v1
kind: Pod
metadata:name: gpu-pod
spec:containers:- name: gpu-appimage: nvidia/cuda # 假设这是一个需要GPU的应用tolerations: # 容忍配置- key: "gpu" # 匹配污点的keyoperator: "Equal" # 匹配方式:Equal(精确匹配value)或Exists(只要key存在,忽略value)value: "yes" # 匹配污点的valueeffect: "NoSchedule" # 匹配污点的effect
3.2 灵活配置方式
-
忽略 value:只要节点有
gpu
这个 key 的污点,不管 value 是什么都容忍:tolerations: - key: "gpu"operator: "Exists" # 只要key存在就容忍effect: "NoSchedule"
-
容忍所有污点:(谨慎使用,可能导致 Pod 被调度到不合适的节点)
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:
- 优先驱逐资源使用超过 limits的 Pod(“违规者” 先被清);
- 再按Pod 优先级(PriorityClass) 驱逐:低优先级 Pod 先被驱逐;
- 同优先级下,驱逐资源请求比例最高的 Pod(如请求 1 核却用了 2 核的 Pod)。
4.3 关键配置:驱逐阈值
可在kubelet
配置中自定义驱逐阈值(如kubelet-config.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
配置 “最多再留多久”: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 实战验证命令
- 查看节点污点:
kubectl describe node <节点名> | grep Taint
- 给节点打污点:
kubectl taint nodes <节点名> key=value:effect
- 移除节点污点:
kubectl taint nodes <节点名> key:effect-
(末尾加 “-”) - 查看 Pod 的容忍配置:
kubectl describe pod <pod名> | grep Tolerations
- 手动驱逐 Pod:
kubectl delete pod <pod名> --grace-period=0 --force
(强制立即驱逐)
7 总结
- 想让 Pod “选节点”:用亲和性;
- 想让节点 “拒 Pod”:给节点打污点,再让允许的 Pod 配置容忍;
- 想让 Pod “被赶走”:要么触发节点自动驱逐(资源不足),要么用
NoExecute
污点强制驱逐。