上一篇文章讲了用 Keepalived 加 Nginx 搭双机高可用,但只讲了"怎么配",没讲"为什么能这样工作"。如果你不理解 Keepalived 背后的 VRRP 协议,遇到"双主""VIP 不漂移""切换后客户端访问失败"这类问题时就会无从下手,只能靠猜。这篇文章专门把原理讲透:Keepalived 到底是什么,VRRP 是怎么选出主节点的,虚拟 IP 为什么能在两台机器之间漂移,抢占和非抢占有什么区别,以及生产环境里最常见的几个坑。
文章会从协议层面解释清楚主备切换的完整过程,然后给出可以在两台虚拟机里复现的实验步骤,包括用 tcpdump 抓 VRRP 通告、用 ip a 观察 VIP、手动制造故障验证漂移。命令区分 RHEL/CentOS/Rocky 与 Debian/Ubuntu 两套发行版。读懂这篇,你再回头看主备配置,就能明白每个参数为什么这么写。
Keepalived 到底解决什么问题
Keepalived 最早是为 Linux 虚拟服务器(LVS)做健康检查而生的,后来逐渐演变成通用的高可用工具。它主要干两件事。第一件是管理虚拟 IP 的漂移,通过 VRRP 协议在一组服务器里选出一个主节点,把 VIP 绑到主节点上,主节点故障时把 VIP 交给别的节点。第二件是健康检查,持续探测本机或后端服务是否正常,不正常时调整优先级,触发或放弃 VIP 持有权。
换句话说,Keepalived 把"谁对外服务"这件事自动化了。没有它的时候,运维要靠人工改 DNS、改负载均衡配置来切换,慢且容易出错;有了它,整个切换在几秒内完成,而且不需要人干预。
Keepalived 常和 Nginx、HAProxy、LVS 搭配使用。它本身不处理业务流量,只负责"把门牌号交给谁"。所以它通常部署在流量入口的最外层,是整套高可用架构里最先接触客户端的组件。
VRRP 协议:主备是怎么选出来的
VRRP(Virtual Router Redundancy Protocol,虚拟路由器冗余协议)是 Keepalived 的技术基础,工作在 IP 层,协议号 112。它把一组物理服务器虚拟成一台"虚拟路由器",这台虚拟路由器对外提供一个或多个虚拟 IP,内部则通过选举产生一个主节点来真正响应这些 IP。
工作过程大致是这样。每个参与 VRRP 的节点都有优先级(priority,0 到 255),并周期性地向组播地址 224.0.0.18 发送 VRRP 通告报文。优先级最高的节点成为 MASTER,持有 VIP 并负责响应 ARP 和流量。其余节点处于 BACKUP,只听不说,同时监控 MASTER 的通告。如果在一段时间内收不到 MASTER 的通告(通常是 3 倍 advert_int,即 Master_Down_Interval),BACKUP 就会认为 MASTER 挂了,重新选举,优先级最高的 BACKUP 升级为新的 MASTER 并接管 VIP。
通告默认走组播,目的地址是 224.0.0.18,源地址是物理网卡地址,协议 112。这里有个关键点:虚拟 IP 在物理上是配在主节点的网卡上的,但当主备切换时,新主节点会发送免费 ARP(Gratuitous ARP),告诉交换机"这个 IP 的 MAC 现在是我",交换机据此更新转发表,流量就流向了新主节点。这就是"漂移"的本质——不是 IP 地址真的移动,而是 IP 的归属和对应的 MAC 映射发生了切换。
组播与单播
默认的组播模式在传统物理机房很好用,但在云环境和跨网段场景经常失效,因为云平台的虚拟网络往往不转发组播。这时需要用单播模式,让节点直接把 VRRP 通告发到对端的物理 IP,绕过组播。配置方式是显式声明 unicast_src_ip 和 unicast_peer:
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
unicast_src_ip 192.0.2.12
unicast_peer {
192.0.2.11
}
virtual_ipaddress {
192.0.2.100/24
}
}
单播模式下没有组播的自动发现,对端列表必须写全,任何一台节点漏配都会导致它收不到通告。多节点(三台以上)的集群里尤其要注意这一点。
抢占与非抢占:谁先起来谁当主?
VRRP 里有个容易混淆的概念:抢占模式(preempt)。默认情况下抢占是开启的,意思是"只要我的优先级最高,我就要当主"。这会带来一个现象:主节点故障、备节点接管后,主节点恢复了,因为它的优先级更高,会立刻把 VIP 抢回来。每次抢回都是一次切换,业务会有几百毫秒到几秒的抖动。
非抢占模式则是"先到先得"。配置 nopreempt 后,一旦某个节点成为 MASTER,即使更高优先级的节点后来恢复,也不会主动抢夺,除非当前 MASTER 真的挂了。非抢占适合"不希望频繁切换、宁可让恢复的节点当备"的场景。配置示例:
vrrp_instance VI_1 {
state BACKUP
nopreempt # 关闭抢占
priority 100
interface eth0
virtual_router_id 51
virtual_ipaddress {
192.0.2.100/24
}
}
注意,非抢占模式下通常建议所有节点的 state 都写成 BACKUP,否则可能出现初始状态与抢占行为不一致的混乱。state MASTER 只是声明初始角色,真正的角色由 priority 和抢占策略在运行时决定。
state、priority 与权重的关系
很多人以为 state MASTER 就等于"这台一定是主"。其实不是。Keepalived 启动时,如果 state MASTER 就会先进入 MASTER 状态并发送通告,但如果发现网络里已经有一个优先级更高的 MASTER,它会退让成 BACKUP。所以 state 只是初始值,运行时以 priority 为准。
priority 是选举的核心,范围 0 到 255。数值越大优先级越高。主节点配 100、备节点配 90 是最常见的做法。配合健康检查的 weight 参数,可以实现"应用故障但机器没死"时的自动降级。例如 weight -20 表示检查失败时优先级减 20,主节点从 100 降到 80,低于备节点的 90,于是备节点接管。
需要特别注意一种边界情况:当权重为负且主节点优先级较低时才有效。如果主节点配 100、备节点配 90,而权重是 -20,减完变成 80,确实低于 90,能触发切换。但如果备节点是 85,主节点减完是 80,也低于 85,同样能切。反过来,如果权重绝对值不够大,减完仍然高于备节点,就不会切换。这是权重计算最常见的配置错误。
VIP 漂移与客户端感知
VIP 漂移对客户端是否"透明",取决于切换是否发生 ARP 更新以及客户端缓存是否及时失效。理想情况下,主备切换时新主节点会立即发送免费 ARP,交换机更新表项,后续流量走新主。客户端如果还缓存着旧 MAC,可能会短暂不通,直到 ARP 缓存过期或收到免费 ARP 更新。
可以用下面的命令观察漂移过程。在主节点和备节点分别查看 VIP 归属:
ip -4 addr show eth0 | grep 192.0.2.100
用 tcpdump 在同一网段抓 VRRP 通告,可以看到周期性的协议号 112 报文:
sudo tcpdump -i eth0 -n vrrp
sudo tcpdump -i eth0 -n proto 112
切换瞬间会看到免费 ARP 广播,可专门观察:
sudo tcpdump -i eth0 -n arp
如果切换后客户端仍偶发失败,多半是 ARP 缓存问题。可以在客户端查看缓存:
ip neigh show | grep 192.0.2.100
必要时清理特定表项(风险提示:清理会影响该地址的转发,请确认影响范围):
sudo ip neigh del 192.0.2.100 dev eth0
notify 脚本:把切换事件用起来
Keepalived 提供了丰富的状态钩子,让你在切换时执行自定义动作,比如写日志、发告警、摘除后端节点、刷新缓存。常用的钩子有 notify_master、notify_backup、notify_fault、notify_stop、notify。声明方式是在 vrrp_instance 里加一行:
notify_master "/etc/keepalived/notify.sh master"
notify_backup "/etc/keepalived/notify.sh backup"
notify_fault "/etc/keepalived/notify.sh fault"
脚本通过位置参数接收状态,第一个参数是 master、backup 或 fault,后面还会带上实例名和优先级,可以根据需要拼装信息:
#!/usr/bin/env bash
# /etc/keepalived/notify.sh
STATE=$1
INSTANCE=$2
PRIORITY=$3
logger -t keepalived-notify "state=$STATE instance=$INSTANCE priority=$PRIORITY"
# 可在此调用告警接口,注意加超时,避免阻塞状态机
exit 0
脚本一定要快、要幂等、要能容错。不要在通知脚本里做长时间的网络调用,否则会拖慢 Keepalived 的状态机,甚至导致误判。
常见问题:脑裂、拦截与双主
什么是脑裂?
脑裂指的是主备两个节点都认为自己该当主,或者因为收不到对方通告而各自成为 MASTER,结果两个节点同时持有 VIP、同时对外响应,造成 IP 冲突、连接错乱和 ARP 混乱。脑裂的根因通常是通告不通——组播被屏蔽、防火墙拦了 112 协议、心跳链路断了。
防护手段有几层。一是改用 unicast 单播,明确指定对端;二是放行 VRRP 协议;三是部署独立的仲裁或心跳链路,避免业务链路故障导致误判;四是在云环境优先使用云平台提供的高可用方案或私有网络内的单播。还可以通过 notify 脚本在进入 MASTER 时做二次确认。
防火墙拦了 VRRP 怎么办?
RHEL/CentOS/Rocky 系用 firewalld 显式放行 VRRP:
# RHEL / CentOS / Rocky 系
sudo firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
sudo firewall-cmd --reload
Debian/Ubuntu 系如果启用了 ufw,一般默认不拦 VRRP,但若自定义了规则,需要在 /etc/ufw/before.rules 里放行,改完重新加载:
# Debian / Ubuntu 系
sudo ufw reload
风险提示: 修改防火墙前,务必确认 SSH 端口不受影响,并保留一个可用的控制台会话,避免把自己锁在外面。
为什么会出现双主?
除了通告不通,virtual_router_id 冲突是另一个常见原因。如果同网段有两个无关的 Keepalived 集群,恰好用了相同的 virtual_router_id,它们会互相"看到"对方并参与选举,导致混乱。规划时给每个集群分配不同的 ID。同理,auth_pass 不一致会导致认证失败,在旧版本里可能表现为各自为主。
VIP 一直不漂移,可能是什么原因?
先确认备节点真的收到了通告(抓包验证),再确认权重计算是否真的让优先级反超。很多时候是 weight 设置得太小,或者 track_script 根本没生效。可以手动执行脚本看返回码,也可以临时把备节点优先级调高验证漂移能力。
Keepalived 会接管业务流量吗?
不会。Keepalived 只管 VIP,业务流量由主节点上的 Nginx、HAProxy 或 LVS 处理。Keepalived 挂了,如果 VIP 还在、Nginx 还在运行,业务可能仍然可用;反过来 Nginx 挂了但 Keepalived 没检测到,VIP 不会漂移,业务就中断了。所以健康检查脚本必须覆盖真正影响业务的服务。
总结
- Keepalived 是通过 VRRP 协议实现虚拟 IP 自动漂移的工具,负责"谁对外服务",不处理业务流量。
- VRRP 用组播或单播通告选举主节点,优先级最高者持有 VIP,切换靠免费 ARP 更新交换机映射。
state只是初始角色,真正决定主备的是 priority 与抢占策略;nopreempt可抑制频繁切换。- 健康检查配合
weight能让应用故障也触发漂移,权重数值要确保减完后低于备节点。 - 脑裂、防火墙拦截 VRRP、双主是高发问题,云环境优先用单播并做好通告链路的冗余。
企业QQ咨询




