健康检查本身不引发脑裂但设计不当会加剧误判,需解耦健康检查与高可用决策、统一后端状态判定、禁用自动切回、加固心跳链路、统一可观测打标。
在分布式 Nginx 集群中,健康检查本身不会直接引发脑裂,但若设计不当,会加剧脑裂发生后的误判——比如双主状态下两个节点各自“认为自己健康”,却对同一后端做出不一致的探测结论,导致流量分发混乱、故障掩盖或雪崩式重试。避免这类误判,关键在于让健康检查逻辑与高可用决策解耦,并强化状态一致性边界。
健康检查必须独立于 VIP 状态运行
很多团队把 Keepalived 的 vrrp_script 和 Nginx 健康检查混为一谈:例如用一个脚本既查 Nginx 进程又查后端连通性,再通过 weight 调整 priority。这会造成“VIP 主从”和“服务可用性”强绑定,一旦心跳中断,两节点各自执行本地检查,极易得出矛盾结论(如 A 认为后端 OK、B 认为超时),进而触发非预期切换或拒绝切换。
正确做法是:
- Nginx upstream 内部的健康检查(如 health_check 或 nginx_upstream_check_module)只负责后端节点状态,不参与 VIP 抢占决策
- Keepalived 的 vrrp_script 仅用于判断本机 Nginx 服务是否可响应(例如访问 /nginx_status 或轻量 /ping),不探任何后端
- 后端健康状态由统一 /healthz 接口暴露,所有 Nginx 实例共用同一套判定逻辑,避免因本地网络抖动导致差异化判断
禁用自动切回,防止震荡放大误判
脑裂恢复期最危险的不是“谁当主”,而是“谁先抢回”。若 MASTER 节点刚恢复网络就立刻 preemption,而此时其 Nginx 可能尚未完成后端重连或连接池重建,健康检查尚未稳定收敛,就会把未就绪状态当作“正常”上报,造成流量打到半死节点。
应强制设置:
- BACKUP 节点配置 nopreempt,MASTER 恢复后不自动夺权
- vrrp_script 中记录 last_switch_time,两次切换间隔 ≥ 300 秒,给健康检查留出至少 2–3 个完整探测周期(如 interval=10s,则需 20–30s 才能确认后端真实恢复)
- 切换后首次探测失败,立即降权并暂停切回尝试,而非等待下一轮定时检查
用单播 + 协议白名单加固心跳链路,减少“假失联”
80% 的脑裂诱因是 VRRP 心跳丢包,而非服务崩溃。组播被交换机过滤、防火墙拦截 IP 协议号 112、WAF 误杀健康探针,都会让 Keepalived 误判对方宕机,从而双双升主。此时若健康检查路径又被同一条链路拦截,就会形成双重误判闭环。
必须做到:
- 关闭组播,改用 unicast_peer 配置直连单播心跳,绕过交换机转发依赖
- iptables/nftables 显式放行 -p 112,云平台安全组允许自定义协议 112
- 为健康检查单独开辟第二通道:例如用另一张网卡直连,运行独立的 curl 检查脚本,结果仅用于日志告警,不参与决策,但可辅助人工快速识别是网络问题还是服务问题
所有检查结果统一打标并接入可观测体系
没有上下文的健康状态就是噪声。同一个后端,在 A 节点返回 200,在 B 节点超时,到底是后端真挂了,还是 A 节点路由异常?若指标无标签、无时间对齐、无链路关联,就无法归因。
务必确保:
- Nginx 的 log_format 中包含 $upstream_addr、$upstream_response_time、$upstream_http_x_request_id
- /healthz 接口响应日志打标 instance=nginx-web-01、role=master、upstream=api-svc
- Prometheus 采集时,对 keepalived_state、nginx_upstream_servers、/healthz_latency 分别加 job 和 instance 标签,故障时可一键筛选“同一 upstream 下所有 master 节点的检查结果对比”