原创

高可用架构(一):Keepalived

温馨提示:
本文最后更新于 2026年03月25日,已超过 3 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

 

一、背景与价值

在分布式系统和微服务架构盛行的今天,服务的高可用性(High Availability, HA)已成为系统设计的核心指标之一。没有任何硬件或软件是永远不宕机的,因此,我们需要一种机制来确保当主节点发生故障时,备用节点能够迅速接管服务,将停机时间缩短到秒级甚至毫秒级。

Keepalived 正是这样一个轻量级、功能强大的开源工具,它常被用于解决单点故障问题,是构建高可用集群的“瑞士军刀”。

二、核心概览

Keepalived 是一个基于 VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)实现的负载均衡和高可用解决方案。它最初是为 LVS(Linux Virtual Server)负载均衡器设计的心跳检测工具,但随着时间的推移,它的功能已经远远超出了 LVS 的范畴,目前广泛用于各类服务(如 Nginx、MySQL、Redis)的主备高可用部署。

核心功能:

  1. 健康检查(Health Check): 实时监控后端服务或本地进程的状态。
  2. 故障转移(Failover): 当检测到主节点故障时,自动将虚拟 IP(VIP)漂移到备用节点。
  3. 负载均衡(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 的应用层协议,主要基于以下网络层级协议特性的原因:

  1. 工作层级不同(最关键原因)
    • 目标: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)无法做到的。
  1. 标准化与兼容性
    • VRRP 是 IETF 标准协议(RFC 5798)。网络设备(交换机、路由器)原生支持识别和处理 VRRP 报文。
    • 如果使用自定义的 TCP/UDP 协议,中间的网络设备(特别是二层交换机)可能无法理解其意图,且在处理 IP 地址接管(ARP 更新)时需要额外的非标准逻辑。
  1. 效率与开销
    • VRRP 使用组播(Multicast)发送心跳,无需建立连接(无握手过程),开销极小,检测速度快。
    • TCP 需要三次握手建立连接,维护状态复杂;UDP 虽然无连接,但缺乏标准的组播选举机制,需要自行实现复杂的防脑裂和选举算法。

总结:Keepalived 使用 VRRP 是因为只有网络层/链路层协议才能直接控制 IP 地址的归属权。TCP/UDP 是在 IP 地址确定之后才进行数据传输的,无法解决“IP 地址由哪台机器持有”这个根本问题。

3. 故障切换全流程

切换过程依赖于 VRRP 的状态机和心跳机制:

  1. 正常状态
    • Master:定期(默认每 1 秒)向组播地址 224.0.0.18 发送 VRRP 通告报文(Advertisement),声明自己存活且优先级。
    • Backup:监听该组播地址。只要收到 Master 的通告,就保持备份状态,不抢占 VIP。
  1. 故障检测
    • 如果 Master 宕机(进程挂掉、服务器断电)或 网络链路中断,它将停止发送通告。
    • Backup 节点启动一个计时器(默认为 3 * 间隔 + 偏移量,通常约 3-4 秒)。如果在计时器超时前仍未收到 Master 的通告,判定 Master 失效。
  1. 选举与接管
    • Backup 节点将自己提升为 Master 状态。
    • 新 Master 在自己的网卡上绑定虚拟 IP (VIP)
    • 新 Master 发送免费 ARP (Gratuitous ARP) 广播,通知局域网内的交换机和主机:“这个 VIP 对应的 MAC 地址现在变了(或者在这个端口)”,更新交换机的 MAC 地址表和主机的 ARP 缓存。
    • 流量随即切换到新的主节点。
  1. 故障恢复:
    • 如果原 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

注意

  1. 云环境限制:在阿里云、AWS、腾讯云等公有云的 VPC 环境中,底层网络设备通常不支持组播(Multicast)或协议号 112 的透传。因此,原生的 VRRP/Keepalived 往往无法直接在公有云的内网中工作。
    • 解决方案:使用单播模式(Unicast)配置 Keepalived(需修改 keepalived.conf 指定 unicast_peer),并确保安全组允许协议 112 的单播通信。
  1. 交换机配置:如果是物理交换机环境,确保连接 Keepalived 节点的交换机端口允许组播流量通过,有时需要配置 igmp snooping 相关选项以避免组播报文被泛洪或丢弃。

四、VRRP 连通性测试与故障排查

最准确的方法是使用 tcpdump 抓包,直接看有没有 VRRP 数据包在两台机器之间传输。

1.tcpdump 工具安裝

确保两台服务器(假设为 Node-ANode-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是静态优先级的工作流程,流程解析:

  1. 检测阶段:Keepalived 仅检查网卡链路层状态。因为网卡没拔,它认为节点 A 是健康的。
  2. 决策阶段:节点 A 继续以 优先级 100 发送心跳。节点 B 收到后,发现 100>90100>90 ,乖乖保持备份状态。
  3. 结果:VIP 死死绑定在节点 A。流量进来后,因为 Nginx 挂了,直接报错。高可用失效

3. 配置 track_script

配置 track_script是动态优先级的工作流程,流程解析:

  1. 检测阶段:Keepalived 定期调用脚本。脚本发现 Nginx 挂了,返回错误码。
  2. 判定阶段:达到 fall 阈值后,Keepalived 判定业务故障。
  3. 降权阶段:节点 A 的优先级从 100 自动降至 50
  4. 切换阶段
    • 节点 A 发送心跳:“我现在优先级是 50”。
    • 节点 B 收到心跳,对比自身优先级 90。
    • 因为 90>5090>50 ,节点 B 立即抢占成为 Master,并绑定 VIP。
  1. 结果:流量被引导至节点 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
}
正文到此结束