服务端阅读 05月31日 16:34
Kubernetes 亲和性和反亲和性如何控制 Pod 调度?
Kubernetes 亲和性和反亲和性,本质上是在回答“Pod 应该靠近谁、远离谁、只能去哪里”。nodeAffinity 关心节点标签,比如把需要 SSD 的服务放到 disk=ssd 的节点;podAffinity 关心已有 Pod,比如把网关和同可用区缓存放近一点;podAntiAffinity 则常用于把同一应用副本打散,避免一个节点故障带走所有实例。真正落地时不要把所有规则都写成 required,硬约束越多,调度失败的概率越高;通常是“必须满足资源和隔离,性能偏好用 preferred”。怎么用节点亲和性限制 Pod 去指定节点?节点亲和性比 nodeSelector 更灵活,支持 In、NotIn、Exists、Gt、Lt 等操作符。比如只允许 Pod 调度到带有 nodepool=ssd 的节点,并优先选择同区域节点,可以这样写:apiVersion: apps/v1kind: Deploymentmetadata: name: apispec: replicas: 2 selector: matchLabels: app: api template: metadata: labels: app: api spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: nodepool operator: In values: ["ssd"] preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: topology.kubernetes.io/zone operator: In values: ["cn-east-1a"] containers: - name: api image: nginx:1.25这里的 requiredDuringSchedulingIgnoredDuringExecution 是硬门槛,不满足就 Pending;preferredDuringSchedulingIgnoredDuringExecution 是加分项,不满足仍可调度。后半段的 IgnoredDuringExecution 很容易被误解:它表示 Pod 调度成功后,如果节点标签后来被改掉,Kubernetes 不会主动驱逐这个 Pod。这个边界很重要,亲和性解决的是“调度时放哪里”,不是“运行中持续纠偏”。如果要给节点补标签,先用命令确认标签是否稳定存在:kubectl label node node-1 nodepool=ssdkubectl get nodes -L nodepool,topology.kubernetes.io/zone不要把临时标签当成长期调度依据。比如临时给节点打 debug=true 后忘记清理,后续 Pod 可能被错误吸到这批节点上。更稳的做法是把节点池、可用区、实例规格这类由平台维护的标签作为调度依据,业务自己维护的标签则要有变更流程。追问required 和 preferred 应该怎么取舍?required 适合合规、安全、硬件能力这类不能妥协的条件,比如 GPU 任务必须上 GPU 节点,数据库实例必须上本地盘节点。preferred 适合性能优化和成本优化,比如优先同可用区、优先 SSD、优先空闲节点,但没抢到也允许服务先跑起来。踩坑最多的是把高可用诉求也全写成 required,节点数量一少、标签不齐或滚动升级时,Pod 会长时间 Pending。实际项目里建议先用 preferred 观察调度结果,再把确实不能退让的条件收紧成 required。Pod 亲和性和反亲和性常用在哪些场景?Pod 亲和性适合把强依赖、低延迟通信的组件放近,比如计算服务和本地缓存希望在同一可用区,减少跨区访问延迟。Pod 反亲和性更常见,用来把同一 Deployment 的副本分散到不同节点或不同可用区,降低单点故障影响。边界是它依赖目标 Pod 的 label,如果 label 写错或 selector 太宽,会出现“亲和到不该亲和的 Pod”的情况。生产环境里通常给业务 label 保持稳定,避免把版本号、临时灰度标签拿来做调度依据。topologyKey 选 hostname 还是 zone?kubernetes.io/hostname 表示按节点维度分散,适合防止同一个应用的多个副本落在一台机器上。topology.kubernetes.io/zone 表示按可用区维度分散,适合云上多可用区容灾,但跨区会带来网络延迟和流量成本。取舍点在于你要防的是“单机故障”还是“可用区故障”。如果副本数小于可用区数量,强制 zone 反亲和可能导致某些副本调度不上,这时 preferred 往往比 required 更稳。如何排查亲和性导致的 Pending?先看事件,调度器通常会直接告诉你哪些节点不满足规则:kubectl describe pod <pod-name>kubectl get nodes --show-labelskubectl describe node <node-name> | grep -A5 Taints如果事件里出现 didn't match Pod's node affinity,说明节点标签和 nodeAffinity 对不上;如果出现 didn't match pod affinity/anti-affinity rules,就要检查目标 Pod 的 label 和 topologyKey。另一个常见坑是污点和亲和性叠加:亲和性允许去某节点,但没有 toleration,调度器仍然不会放过去。排查时不要只盯 affinity 字段,资源不足、污点、PVC 绑定模式也可能同时参与过滤。亲和性需要和污点容忍一起用吗?需要,但两者解决的问题不同。亲和性是在“吸引”或“偏好”某些节点,污点是节点主动“拒绝”不合适的 Pod,容忍度则是 Pod 表示自己可以接受这个拒绝条件。比如专用 GPU 节点通常会打 taint,只有声明 toleration 且有 GPU nodeAffinity 的任务才能上去。边界是 toleration 不是调度目标,它只代表“允许去”,真正想让 Pod 去哪里仍要靠 nodeSelector、nodeAffinity 或资源请求。