高可用架构(一):Keepalived
一、背景与价值
在分布式系统和微服务架构盛行的今天,服务的高可用性(High Availability, HA)已成为系统设计的核心指标之一。没有任何硬件或软件是永远不宕机的,因此,我们需要一种机制来确保当主节点发生故障时,备用节点能够迅速接管服务,将停机时间缩短到秒级甚至毫秒级。
Keepalived 正是这样一个轻量级、功能强大的开源工具,它常被用于解决单点故障问题,是构建高可用集群的“瑞士军刀”。
二、核心概览
Keepalived 是一个基于 VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)实现的负载均衡和高可用解决方案。它最初是为 LVS(Linux Virtual Server)负载均衡器设计的心跳检测工具,但随着时间的推移,它的功能已经远远超出了 LVS 的范畴,目前广泛用于各类服务(如 Nginx、MySQL、Redis)的主备高可用部署。
核心功能:
- 健康检查(Health Check): 实时监控后端服务或本地进程的状态。
- 故障转移(Failover): 当检测到主节点故障时,自动将虚拟 IP(VIP)漂移到备用节点。
- 负载均衡(Load Balancing): 虽然不如 Nginx 或 HAProxy 功能丰富,但它内置了简单的四层负载均衡能力(配合 LVS)。
三、VRRP 协议工作原理
1. VRRP 机制详解
VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)是一种容错协议,旨在提高网络默认网关的可用性。
- 核心概念:它将多台物理路由器(或运行该协议的服务器,如 Keepalived)组合成一个虚拟路由器。
- 对外表现:这个虚拟路由器拥有一个独立的 虚拟 IP 地址 (VIP) 和 虚拟 MAC 地址。局域网内的主机只需将默认网关配置为这个 VIP。
- 工作机制:
- Master(主节点):组内优先级最高的设备成为 Master,负责响应发往 VIP 的流量,并定期发送 VRRP 通告报文。
- Backup(备节点):其他设备处于 Backup 状态,监听 Master 的通告。
- 故障切换:如果 Master 宕机或网络中断,Backup 节点在超时未收到通告后,会自动选举新的 Master 接管 VIP,从而实现网关的高可用,用户端几乎无感知。
2. 技术选型
Keepalived 选择 VRRP 而不是基于 TCP/UDP 的应用层协议,主要基于以下网络层级和协议特性的原因:
- 工作层级不同(最关键原因):
- 目标:Keepalived 的核心任务是管理虚拟 IP (VIP) 的漂移,确保网关或服务入口 IP 始终可达。这属于网络层的功能。
- TCP/UDP:属于传输层(Layer 4)。它们依赖于 IP 地址已经存在且路由可达。如果用 TCP/UDP 来做主备切换,当主节点宕机时,IP 地址本身可能还绑定在主节点上(或者需要复杂的机制去移动 IP),客户端依然会尝试连接那个可能已失效的 IP。
- VRRP:VRRP 是一种网络层协议(IP Protocol 112),直接封装在 IP 报文中,不经过 TCP 或 UDP。它利用链路层的组播能力,在局域网内宣告“谁拥有这个虚拟 IP”。正因为它是网络层协议,它能直接操作网卡的 IP 别名和 ARP 表,实现 IP 的物理漂移,这是传输层协议(TCP/UDP)无法做到的。
- 标准化与兼容性:
- VRRP 是 IETF 标准协议(RFC 5798)。网络设备(交换机、路由器)原生支持识别和处理 VRRP 报文。
- 如果使用自定义的 TCP/UDP 协议,中间的网络设备(特别是二层交换机)可能无法理解其意图,且在处理 IP 地址接管(ARP 更新)时需要额外的非标准逻辑。
- 效率与开销:
- VRRP 使用组播(Multicast)发送心跳,无需建立连接(无握手过程),开销极小,检测速度快。
- TCP 需要三次握手建立连接,维护状态复杂;UDP 虽然无连接,但缺乏标准的组播选举机制,需要自行实现复杂的防脑裂和选举算法。
总结:Keepalived 使用 VRRP 是因为只有网络层/链路层协议才能直接控制 IP 地址的归属权。TCP/UDP 是在 IP 地址确定之后才进行数据传输的,无法解决“IP 地址由哪台机器持有”这个根本问题。
3. 故障切换全流程
切换过程依赖于 VRRP 的状态机和心跳机制:
- 正常状态:
- Master:定期(默认每 1 秒)向组播地址 224.0.0.18 发送 VRRP 通告报文(Advertisement),声明自己存活且优先级。
- Backup:监听该组播地址。只要收到 Master 的通告,就保持备份状态,不抢占 VIP。
- 故障检测:
- 如果 Master 宕机(进程挂掉、服务器断电)或 网络链路中断,它将停止发送通告。
- Backup 节点启动一个计时器(默认为 3 * 间隔 + 偏移量,通常约 3-4 秒)。如果在计时器超时前仍未收到 Master 的通告,判定 Master 失效。
- 选举与接管:
- Backup 节点将自己提升为 Master 状态。
- 新 Master 在自己的网卡上绑定虚拟 IP (VIP)。
- 新 Master 发送免费 ARP (Gratuitous ARP) 广播,通知局域网内的交换机和主机:“这个 VIP 对应的 MAC 地址现在变了(或者在这个端口)”,更新交换机的 MAC 地址表和主机的 ARP 缓存。
- 流量随即切换到新的主节点。
- 故障恢复:
- 如果原 Master 恢复,且配置了 preempt(抢占模式,默认开启),它会发现自己是更高优先级的,于是重新发送通告并抢回 Master 角色,触发又一次切换。若配置为 nopreempt,则保持当前状态直到再次故障。
4. 通信特征
VRRP不使用端口号。
- 协议类型:VRRP 不是基于 TCP 或 UDP 的,因此不存在端口号的概念。
- 协议号:它直接封装在 IP 包中,使用的 IP 协议号是 112。
- 通信方式:使用 组播 (Multicast)。
- 组播地址:224.0.0.18 (IPv4) 或 FF02::12 (IPv6)。
- TTL:通常为 255,确保只在本地链路传播,不被路由器转发到外网。
5. 网络配置实战
由于 VRRP 不使用端口,防火墙规则不能基于端口(如 --dport 80)配置,而必须基于协议号和组播地址。
在 Linux (iptables/firewalld) 上的配置示例:
场景:允许本机接收和发送 VRRP 报文,以确保主备切换正常。
方法一:使用 firewalld
# 添加 VRRP 协议到公共区域
sudo firewall-cmd --permanent --add-protocol=vrrp
# 重载防火墙以生效
sudo firewall-cmd --reload
# 验证配置
sudo firewall-cmd --list-all
方法二:使用 ufw
修改/etc/ufw/before.rules文件
# 允许 VRRP 协议
-A ufw-before-input -p vrrp -j ACCEPT
# 或者使用协议号写法,以防某些版本不认识 'vrrp' 关键字
# -A ufw-before-input -p 112 -j ACCEPT
然后重启ufw 服务
# 重载规则
sudo systemctl restart ufw
# 验证规则
sudo ufw status verbose
注意:
- 云环境限制:在阿里云、AWS、腾讯云等公有云的 VPC 环境中,底层网络设备通常不支持组播(Multicast)或协议号 112 的透传。因此,原生的 VRRP/Keepalived 往往无法直接在公有云的内网中工作。
- 解决方案:使用单播模式(Unicast)配置 Keepalived(需修改 keepalived.conf 指定 unicast_peer),并确保安全组允许协议 112 的单播通信。
- 交换机配置:如果是物理交换机环境,确保连接 Keepalived 节点的交换机端口允许组播流量通过,有时需要配置 igmp snooping 相关选项以避免组播报文被泛洪或丢弃。
四、VRRP 连通性测试与故障排查
最准确的方法是使用 tcpdump 抓包,直接看有没有 VRRP 数据包在两台机器之间传输。
1.tcpdump 工具安裝
确保两台服务器(假设为 Node-A 和 Node-B)都安装了 tcpdump:
# CentOS/Kylin/RedHat
sudo yum install -y tcpdump
# Ubuntu/Debian
sudo apt-get install -y tcpdump
2.VRRP 报文抓包
1. 在“备节点” (Backup) 上执行抓包命令
假设你们的网卡名称是 eth0(请用 ip addr 确认实际网卡名,可能是 ens33, bond0 等)。
# -n: 不解析域名(显示IP更快)
# -i eth0: 监听网卡
# -v: 显示详细信息
# "vrrp": 只过滤 VRRP 协议的数据包
sudo tcpdump -n -i eth0 -v vrrp
保持这个窗口打开,不要关闭。
2. 观察现象
- 情况 A:通了 (正常)
你会看到屏幕不断滚动输出类似以下内容(通常每 1 秒一条):
15:20:01.123456 IP 192.168.1.10 > 224.0.0.18: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
- 解读:
- 192.168.1.10 是主节点的真实 IP。
- 224.0.0.18 是 VRRP 专用的组播地址。
- vrid 51 必须和你配置文件里的
virtual_router_id一致。 - prio 100 是主节点的优先级。
- 结论:网络通畅,防火墙未拦截,Keepalived 工作正常。
- 情况 B:不通 (异常)
屏幕一片空白,或者很久才跳一下无关的包。
- 结论:心跳包被拦截了,或者主节点没发出来。这会导致脑裂。
3.连通性异常排查
如果抓不到包,请按顺序检查以下三点:
1. 检查防火墙 (最常见原因)
VRRP 使用的是 协议号 112,不是 TCP 也不是 UDP,普通的端口放行规则无效。
- 临时测试法:在两台机器上暂时关闭防火墙。
sudo systemctl stop firewalld # CentOS/Kylin
sudo ufw disable # Ubuntu
关闭后,再看备节点的 tcpdump 是否有包。
- 如果有包了 -> 说明是防火墙问题。
- 永久修复方法 ,配置防火漆允许 VRRP 协议
2. 检查云安全组/交换机策略
- 如果是云服务器 (阿里云、腾讯云、AWS 等):大多数云厂商的安全组默认禁止组播 (Multicast)。
- 现象:防火墙关了也没包。
- 解决:必须修改 keepalived.conf,将默认的组播改为 单播 (Unicast)。
- 配置示例:
vrrp_instance VI_1 {
# ... 其他配置
unicast_src_ip 192.168.1.10 # 本机内网IP
unicast_peer {
192.168.1.11 # 对端内网IP
}
}
- 如果是物理机/虚拟机:检查中间交换机是否禁用了组播功能。
3. 检查网卡配置
- 确认 keepalived.conf 中的 interface eth0 填写的网卡名称,和你们实际通信的网卡名称是否一致?
- 确认两台机器是否在同一个子网(掩码一致)?
五、业务级高可用
1.track_script
track_script 是 Keepalived 实现“业务级高可用”的核心配置块。它的核心作用是:将外部脚本的执行结果(成功/失败)与 VRRP 实例的优先级(Priority)动态绑定。如果没有它,Keepalived 只是一个“网卡存活检测器”;有了它,Keepalived 就变成了一个“业务健康检测器”。
一个标准的配置结构如下:
# 1. 定义脚本逻辑
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2 # 每2秒检查一次
fall 2 # 连续2次失败才认定失败 (防抖动)
rise 1 # 连续1次成功即认定恢复
weight -50 # 【关键】失败时优先级降低50分
}
# 2. 在实例中引用
vrrp_instance VI_1 {
state MASTER
priority 100 # 初始优先级
track_script {
check_nginx # 【关键】这里名字必须和上面定义的 vrrp_script 名字一致
}
# ... 其他配置
}
2. 不配置 track_script
不配置 track_script是静态优先级的工作流程,流程解析:
- 检测阶段:Keepalived 仅检查网卡链路层状态。因为网卡没拔,它认为节点 A 是健康的。
- 决策阶段:节点 A 继续以 优先级 100 发送心跳。节点 B 收到后,发现 100>90100>90 ,乖乖保持备份状态。
- 结果:VIP 死死绑定在节点 A。流量进来后,因为 Nginx 挂了,直接报错。高可用失效。
3. 配置 track_script
配置 track_script是动态优先级的工作流程,流程解析:
- 检测阶段:Keepalived 定期调用脚本。脚本发现 Nginx 挂了,返回错误码。
- 判定阶段:达到
fall阈值后,Keepalived 判定业务故障。 - 降权阶段:节点 A 的优先级从 100 自动降至 50。
- 切换阶段:
- 节点 A 发送心跳:“我现在优先级是 50”。
- 节点 B 收到心跳,对比自身优先级 90。
- 因为 90>5090>50 ,节点 B 立即抢占成为 Master,并绑定 VIP。
- 结果:流量被引导至节点 B,服务恢复。
六、常见故障与生产最佳实践
1.脑裂故障
脑裂 (Split-Brain) 是指在 Keepalived(或任何高可用集群)中,主节点 (Master) 和 备节点 (Backup) 同时认为自己是 Master,导致两者都持有虚拟 IP (VIP),进而引发 IP 冲突、数据写入混乱或服务不可用的严重故障。
1. 心跳网络中断
这是最典型的原因。Keepalived 依赖 VRRP 心跳包来确认对方是否存活。如果心跳链路断了,但业务网络是通的,备节点收不到主节点的心跳,就会误以为主节点挂了,从而抢占 VIP。
- 具体表现:
- 主节点活着,业务正常。
- 备节点活着,业务正常。
- 结果:两台机器上都绑定了 VIP。客户端请求可能被随机分配到任意一台,如果涉及写操作(如数据库),会导致数据不一致。
- 常见诱因:
- 防火墙拦截:主/备节点的防火墙(firewalld, iptables, 云安全组)丢弃了 VRRP 协议包(协议号 112)。
- 交换机/路由器故障:连接两台服务器的交换机端口故障、VLAN 配置错误,或者中间网络设备禁止了组播 (224.0.0.18)。
- 网线/网卡松动:专门用于心跳的网卡物理链路断开。
- 云环境限制:公有云(阿里云/AWS等)底层网络默认禁止组播,导致心跳包发不出去或收不到。
2. 主节点负载过高
主节点并没有挂,但因为 CPU 100%、内存耗尽或 IO 阻塞,导致系统无法及时发送或处理 VRRP 心跳包。
- 机制:
- Keepalived 默认每隔 1 秒 (advert_int 1) 发送一次心跳。
- 备节点通常在 3 * advert_int (即 3 秒) 内收不到心跳,就判定主节点死亡。
- 如果主节点只是“卡”了 4 秒,然后恢复了,但在这 4 秒内,备节点已经抢占了 VIP。
- 结果:主节点恢复后,如果配置了抢占模式 (nopreempt 未开启),它会立刻抢回 VIP,导致 VIP 在短时间内频繁跳动,甚至出现短暂的双主窗口期。
3. 配置错误
人为配置失误也是高频原因。
- Virtual Router ID (VRID) 不一致:
- 如果主备配置的 virtual_router_id 不同,它们属于不同的组,互不理睬。如果此时 VIP 配置相同,两者都会绑定 VIP,直接形成脑裂。
- 优先级 (Priority) 配置错误:
- 如果两台机器都配置了相同的最高优先级(例如都是 100),且都配置了 nopreempt (非抢占),在某些特定启动顺序下,可能两者都维持 Master 状态。
- 认证密码不一致:
- 如果 auth_pass 不一致,接收方会丢弃对方的心跳包(视为非法包),效果等同于心跳中断,触发备节点抢占。
4. 网络延迟过大
在跨机房或广域网 (WAN) 部署 Keepalived 时,网络延迟可能超过 VRRP 的超时阈值。
- 场景:
- 主备节点跨越不同城市。
- 网络抖动导致心跳包延迟到达(例如平时 10ms,突然变成 4000ms)。
- 备节点在超时时间内没收到包,判定主节点挂掉,发起抢占。
- 注意:Keepalived 默认设计用于局域网 (LAN),不建议直接用于高延迟的跨机房场景,除非大幅调大 advert_int 和 timeout。
5. 健康检查脚本误判
如果你配置了 vrrp_script 来检测业务(如 Nginx),脚本逻辑有问题也可能导致脑裂。
- 场景:
- 主节点的业务进程(如 Nginx)其实活着,但检测脚本因为权限问题、路径错误或逻辑 Bug 返回了“失败”。
- Keepalived 根据脚本结果,主动降低了主节点的优先级。
- 备节点发现主节点优先级低于自己,于是抢占 VIP。
- 关键点:如果此时主节点的网络心跳还能发出去(只是优先级低了),而备节点也认为自己优先级高,在特定的配置组合下(特别是非抢占模式配置不当),可能出现状态混乱。
2.生产环境最佳实践
在现代云环境和复杂网络中,强烈建议放弃组播,改用单播 (Unicast)。单播是点对点通信,不依赖组播地址,穿透性更强,配置更可控。
在keepalived.conf中新增配置
# 填写【本机】的真实物理 IP 地址
unicast_src_ip 192.168.1.52
# 填写【对端】的真实物理 IP 地址
unicast_peer {
192.168.1.62
}
- 本文标签: Java 高可用
- 本文链接: https://xiaolanzi.cyou/article/86
- 版权声明: 本文由卓原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权
