K8s 亲和性与反亲和性策略:避免核心服务单机扎堆的容灾实践
在 Kubernetes 集群运维中,很多团队对多副本容灾存在一个巨大的认知盲区:他们认为只要在 Deployment 的 YAML 配置中写上replicas: 10,系统就天然具备了“抗单点硬件故障”的强悍容灾能力。
然而,在多次大促备战的突发故障演练中,我们曾目睹过令人震惊的“全军覆没”惨剧:
某个至关重要的支付核心服务部署了 12 个 Pod 副本。当机房内某一台高配物理宿主机(64 核 256GB)因为电源模块损坏意外宕机时,该支付服务的所有 12 个 Pod 竟然在同一秒内全部阵亡,导致整个支付链路瞬间不可用长达数分钟。
深入排查后发现,Kubernetes 的默认调度器(Kube-Scheduler)在调度 Pod 时,首要考量的是哪个 Node 节点的空闲资源(CPU / Memory Request)最充裕。当一批新 Pod 集中创建时,资源最充裕的那台大规格物理宿主机被调度器连续“塞入”了该服务的所有副本。
在大促备战中,如果不配置严密的Pod 反亲和性(Anti-Affinity)与故障域拓扑隔离,配置再多的副本数也只是“把所有鸡蛋放在同一个篮子里”。
默认调度的致命“扎堆”陷阱
在一个拥有数十台物理节点的 Kubernetes 集群中:
- 物理节点之间可能存在规格差异(如部分新采购的 64 核节点资源极其空闲);
- 当 HPA 触发批量扩容或滚动更新时,Kube-Scheduler 采用 LeastRequestedPriority 算法评分;
- 评分结果使得所有副本集中被调度到同一台物理宿主机、或者同一个网络交换机(Top of Rack, ToR)下的机架上。
一旦发生:
- 宿主机内核 Kernel Panic 或硬件断电;
- 物理机万兆网卡损坏或被网络攻击;
- 宿主机上的 Docker / Containerd 守护进程死锁;
部署在该物理机上的所有副本将无一幸免,集群外部的监控报警瞬间亮起红灯。
[物理宿主机 Node-01 (64C 256G)] -> 硬件突然断电! |-- Pod-1 (支付核心) [死亡] |-- Pod-2 (支付核心) [死亡] |-- ... |-- Pod-12(支付核心) [死亡] ===============================================> 支付服务可用率瞬间跌为 0% !工业级防扎堆:Pod 反亲和性硬隔离与软隔离配置
为了确保同一个核心微服务的副本绝对分散在不同的物理故障域,必须在 Deployment 的podAntiAffinity中配置拓扑分布规则。
Kubernetes 提供了两种反亲和性级别:
- 硬反亲和(
requiredDuringSchedulingIgnoredDuringExecution):刚性硬限制。如果集群中找不到满足“物理机隔离”条件的空闲节点,调度器宁可让 Pod 处于Pending状态,也坚决不允许与同名 Pod 挤在同一台物理机上; - 软反亲和(
preferredDuringSchedulingIgnoredDuringExecution):柔性权重偏好。尽量分散调度,若资源极度紧张时允许适度妥协。
生产级核心交易服务 YAML 模板(结合物理机与可用区双重隔离)
apiVersion: apps/v1 kind: Deployment metadata: name: pay-core-service namespace: trade spec: replicas: 10 template: metadata: labels: app: pay-core-service spec: affinity: # 1. Pod 反亲和性:严禁同一物理机与同一可用区过度扎堆 podAntiAffinity: # 硬性限制:单台物理宿主机上最多只允许运行 1 个 pay-core 副本! requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - pay-core-service topologyKey: "kubernetes.io/hostname" # 物理主机名拓扑域 # 软性偏好:尽量将 Pod 均匀分散到不同的同城可用区 (AZ) preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - pay-core-service topologyKey: "topology.kubernetes.io/zone" # 可用区拓扑域进阶:拓扑分布约束(Topology Spread Constraints)
在 Kubernetes 1.19+ 中,官方推荐使用更加灵活的拓扑分布约束(TopologySpreadConstraints)。相比传统反亲和性的“一刀切禁止”,拓扑分布约束允许精细控制各物理域之间的“最大副本倾斜差值(maxSkew)”。
例如,要求 12 个副本在 3 个可用区之间严格均匀分布,任何两个可用区之间的 Pod 数量差不能超过 1:
spec: topologySpreadConstraints: - maxSkew: 1 # 各拓扑域之间的最大副本倾斜差值不超过 1 topologyKey: "topology.kubernetes.io/zone" whenUnsatisfiable: DoNotSchedule # 强制执行均匀分布 labelSelector: matchLabels: app: pay-core-service节点亲和性(nodeAffinity):核心交易与边缘业务物理隔离
除了 Pod 之间的互斥,在大促期间还必须防范“核心交易 Pod 与耗尽 I/O 的大数据报表 Pod 挤在同一宿主机”的互相伤害。
通过为物理机打上专用标签(如node-role.kubernetes.io/tier: core-oltp),并配合nodeAffinity与污点容忍度(Taints and Tolerations):
spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-role.kubernetes.io/tier operator: In values: - core-oltp # 核心交易 Pod 只能调度至专属的高频 NVMe 交易节点池在大促备战的容灾体系中,高可用从来不是靠运气保证的。通过严密的物理拓扑隔离、拓扑分布约束与宿主机反亲和性配置,系统才能真正做到“任凭单机风吹雨打,核心服务岿然不动”。