Keepalived 高可用集群,这四个字背后,是我两年前一次凌晨三点被监控电话吵醒的血泪教训。当时一套跑了快一年的业务系统,Nginx入口节点静悄悄地宕了,整个前端入口中断了一个多小时才被值班同事发现。那次之后我彻底想明白了一件事:单点再怎么加固,都扛不住硬件老化、机房断电、内核panic这种不讲理的事故,高可用集群不是“大厂才需要”的东西,而是一个正经业务系统的基本盘。而Keepalived,就是我在对比了多套方案之后最终选定的那个“把VIP安稳地漂来漂去”的组件。
这篇文章不打算写成一页页的官方文档翻译,而是把Keepalived高可用集群从原理、选型、配置、验证、排错这几条线完整串一遍,重点讲清楚那些文档里不会明说的设计动机和坑。如果你正在为内网网关、数据库前置、负载均衡入口、DNS服务这类场景搭建双机热备,这篇文章可以直接当作业指导书来用。
1. 为什么我最终选了Keepalived:高可用方案的选型逻辑
先别急着敲安装命令,我们先说清楚一件事:Keepalived在整套高可用架构里到底扮演什么角色。它最核心的能力只有三个——通过VRRP协议把一组机器组织成一个虚拟路由器、对外提供一个可漂移的虚拟IP(VIP)、用自定义脚本探测业务健康状态并据此调整角色。换句话说,它解决的是“一台机器挂了,IP能不能自动换到另一台机器上继续干活”这个最基础也最关键的问题。
当年我面临的选择其实不少,这里把常见方案放在一起对比过,结论会更立体:
| 方案 | 核心机制 | 适合场景 | 上手成本 |
|---|---|---|---|
| Keepalived | VRRP虚拟路由冗余 + 健康检查脚本 | 双机热备、Nginx/LVS/网关入口,轻量直接 | 低 |
| HAProxy自带健康检查 | 只做负载均衡和后端探测,不做VIP漂移 | 必须搭配Keepalived或其他VIP方案使用 | 低 |
| Heartbeat | 资源代理 + 集群消息传递 | 老牌方案,配置复杂,社区活力一般 | 中高 |
| Pacemaker + Corosync | 完整集群资源管理器(CRM) | 多节点、多资源、复杂约束的强一致性场景 | 很高 |
重点说下为什么没有选Pacemaker这类重方案。如果只是双机主备、资源就一个VIP加两个服务,用Pacemaker属于典型的“杀鸡用牛刀”。它的资源约束语法、fencing机制、DC选举逻辑,光是理解成本就够喝一壶,而且配置文件的维护压力并不小。Keepalived的哲学是“一个主配置文件搞定一切”,vrrp_instance里既能定义VIP,又能挂脚本做健康检查,还能在角色切换时触发notify脚本,心智负担小得多,出问题也好排查。
另一个促使我选Keepalived的关键因素,是它对网络拓扑的侵入性极低。你不需要在交换机上做任何改动(除非要手动绑MAC),只靠VRRP多播包就能完成节点间的协商。对很多没有网络设备权限、只能在服务器层面想办法的团队来说,这是非常大的自由度。
当然,Keepalived也不是万能的。比如它无法帮你自动拉起一台新的应用服务器,也无法解决两台机器之间的网络彻底隔离下的状态决策问题(这就是脑裂,后面专门说),更不负责分布式锁之类的强一致事务。认清边界比盲目崇拜重要。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. VRRP协议与VIP漂移:Keepalived高可用的底层逻辑
Keepalived的底层灵魂是VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议)。这个协议的目的很简单:把多台路由器(在咱们的场景里就是多台Linux服务器)虚拟成一台“虚拟路由器”,对外只暴露一个虚拟IP,由其中一台MASTER实际持有并响应ARP请求,其他机器作为BACKUP待命。MASTER挂了,BACKUP顶上,整个过程对客户端来说是无感的。
VRRP本质上是个基于多播的选举协议,默认组播地址是224.0.0.18,协议号112。节点之间通过周期性发送VRRP报文来宣告自己的存活状态和优先级。这里有几个关键点值得展开:
- 优先级(priority)决定角色:范围是1到254,数值越大越优先成为MASTER。两台机器优先级不同,就能实现“A为主、B为备”的固定主备模式;如果优先级相同,则比较IP地址大小来决定谁是MASTER。
- 抢占(nopreempt)机制:默认情况下,一个BACKUP如果发现自己的优先级比当前MASTER高,会立刻抢占成为MASTER。这在计划内切换、RMA维护场景中很有用;但如果不希望频繁抢占(比如希望“先到先得”,谁活着谁继续干),可以打开nopreempt。
- 发送间隔与master_down:MASTER默认每秒发送一次VRRP通告(advert_int 1s),BACKUP如果在3个通告间隔内没收到MASTER的报文,就判死对方并进入MASTER状态。这个3倍规则不是随便定的,是为了兼容网络抖动和报文丢失,但又不能等太久。
VIP漂移的本质,是MASTER变更后在局域网内重新广播 gratuitous ARP(免费ARP),告诉所有交换机、客户端“这个虚拟IP的MAC地址变了,请刷新ARP缓存”。这一步如果出现问题,客户端的TCP连接就会一直往旧MAC上发,表现为服务“假死”。后面排错部分我会重点说这个。
Keepalived还引入了VRRP通告优先级选举与通告间隔联动的机制。比如MASTER在通告里会带上自己的优先级,BACKUP收到通告后与自己本地的优先级比较,如果发现自己更高,并且配置允许抢占,它就会主动切换。这个设计让“优雅的主备切换”成为可能——你只需要在MASTER上把服务停掉或者把权重调低,它在下一轮通告时就会“自降身份”,让BACKUP平滑上位。
另外还有个和脑裂相关的点:VRRP依赖节点之间的多播通信,但如果网络设备不支持多播,或者跨了三层网络部署,端口就不通了。好在Keepalived支持单播模式(unicast_source_ip/unicast_peer配置项),可以绕过多播限制,在两端明确指定彼此的IP进行直连通信。这在一些云厂商VPC里尤其重要,因为很多VPC网络默认禁掉组播。
3. 从零搭建一套双机热备集群:关键配置与验证步骤
理论部分聊得差不多,直接上一套可复用的主备配置。这部分实操我会把每一个关键参数为什么这么写都交代清楚,方便你照抄的同时也能按需调整。
3.1 环境规划
我这次以最常见的“两台Nginx + Keepalived部署在同一个二层网络”为例,参数如下:
| 角色 | 主机名 | 业务IP | Role | 优先级 |
|---|---|---|---|---|
| 主节点 | lb01 | 192.168.1.11 | MASTER | 120 |
| 备节点 | lb02 | 192.168.1.12 | BACKUP | 100 |
| 虚拟IP | - | 192.168.1.200 | VIP | - |
两个节点都安装Keepalived和Nginx,VIP最终绑定在MASTER的eth0上。客户端和上层交换机只认识192.168.1.200这个地址,无需关心它到底在当前物理在哪个节点。
3.2 安装Keepalived
这里以CentOS/RHEL 7/8/9系为例,Ubuntu/Debian系换成apt即可,配置文件路径和格式一致:
bash复制# CentOS/RHEL 系
yum install -y keepalived nginx
# Ubuntu/Debian 系
apt update && apt install -y keepalived nginx
# 验证版本
keepalived --version
注意一点:Keepalived依赖内核的IPVS模块才能做LVS转发,但如果只是做VIP漂移和高可用探活,不配置LVS虚拟服务器,这个依赖不是必须的。不过在编译安装时还是建议把相关依赖(openssl-devel、popt-devel、libnl3-devel)装全,避免某些功能编译不进去。
3.3 主节点(MASTER)配置文件
bash复制# /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_MASTER_01 # 每个节点必须不同,建议用主机名或明确标识
enable_script_security # 开启脚本安全,要求脚本路径带绝对路径
vrrp_skip_check_adv_addr # 跳过收到的VRRP通告地址检查,减少资源消耗
vrrp_strict # 严格模式,遵守VRRP规范,与iptables联动
vrrp_garp_interval 0 # 免费ARP报文间隔,0表示尽量快
vrrp_gna_interval 0
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2 # 每2秒执行一次探活
timeout 3 # 脚本执行超过3秒视为失败
fallback 2 # 连续2次失败才认为节点不健康
rise 1 # 连续1次成功就恢复健康
weight -20 # 健康失败时,优先级扣减20(见下文分析)
}
vrrp_instance VI_1 {
state MASTER # 初始状态
interface eth0 # 承载VIP的网卡
virtual_router_id 51 # 同一组VRRP实例必须一致,范围1-255
priority 120 # 主节点优先级
advert_int 1 # VRRP通告间隔,单位秒
nopreempt # 如果不想让高优先级节点回来时立即抢占,开此项
unicast_src_ip 192.168.1.11 # 单播模式下本机IP
unicast_peer {
192.168.1.12 # 单播模式下对端IP
}
authentication {
auth_type PASS # 最简单的密码认证
auth_pass 8d3f9a2c # 8位以内仅字母数字的密码,同一组实例必须一致
}
virtual_ipaddress {
192.168.1.200/24 dev eth0 label eth0:1 # VIP绑定到eth0的子接口eth0:1
}
track_script {
check_nginx # 引用上面的探活脚本
}
notify_master "/etc/keepalived/notify.sh master"
notify_backup "/etc/keepalived/notify.sh backup"
notify_fault "/etc/keepalived/notify.sh fault"
}
3.4 备节点(BACKUP)配置文件差异
备节点的配置整体几乎一模一样,差异只在四处:router_id改成LVS_BACKUP_01,state改成BACKUP,priority改成100,unicast_src_ip和unicast_peer互换。其余部分保持一致。注意这里的nopreempt两个节点要同时对齐状态,如果主节点开了nopreempt而备节点没开,会导致备节点永远不会主动抢占,主节点恢复后也切不回来。
track_script里的weight参数值得多说两句。我配的是weight -20,意思是:如果check_nginx脚本执行失败,节点优先级自动降低20。这样设计的好处是,当MASTER的Nginx挂了但Keepalived进程还活着时,它的优先级会从120掉到100,低于BACKUP的100(取决于配置判定方式),于是BACKUP会主动抢占MASTER角色,VIP漂移过去。如果脚本返回失败但你没有weight,Keepalived进程本身不会死,MASTER角色也不会让出去,那这台机器就如同“脑子活着但手断了”,业务照样中断。
3.5 健康检查脚本
bash复制#!/bin/bash
# /etc/keepalived/check_nginx.sh
if [ "$(systemctl is-active nginx 2>/dev/null)" = "active" ]; then
exit 0
fi
# 此处也可以改为实际请求探测,如 curl -s http://127.0.0.1/healthz
# 如果依赖的服务是TCP端口,可以用 nc -z -w 2 127.0.0.1 80
exit 1
脚本写好后给它可执行权限,并确保Keepalived服务以root或指定用户能正常执行它,enable_script_security开启时尤其注意绝对路径。
3.6 启动与验证
bash复制systemctl enable --now keepalived
systemctl status keepalived
在主节点上查看VIP是否绑定成功:
bash复制ip addr show eth0
# 期望看到 inet 192.168.1.200/24 scope global secondary eth0:1
ip addr show eth0:1
然后模拟一次主节点故障(直接systemctl stop keepalived),观察VIP是否快速漂移到备节点:
bash复制# 备节点上执行,大约3-5秒后应该能看到VIP出现
watch -n1 ip addr show eth0
# 同时可以用tcpdump抓VRRP报文,确认选举过程
tcpdump -i eth0 vrrp -n
这里有一个常见的验证误区:只看VIP有没有出现在备节点,不看流量是否真的能通。建议在切换后立刻从第三方机器执行 ping 192.168.1.200 和 curl http://192.168.1.200,确认业务可用才算切换成功。
4. 高可用架构里的配角与主角:Keepalived和HAProxy到底各管什么
这个热词里问得最多的问题,就是把Keepalived和HAProxy当成同类软件比来比去。它俩根本不是同一层的东西,就像拿电梯和走廊比谁更适合搬家:电梯负责“把人从一层搬到另一层”,走廊负责“进屋之后怎么分流”,两者配合起来才是完整的垂直交通方案。
为了把这件事讲透,我拉了一张对比表:
| 维度 | Keepalived | HAProxy |
|---|---|---|
| 核心功能 | 虚拟IP漂移、节点健康探测、VRRP选举 | 四层/七层负载均衡、请求转发、后端被动健康检查 |
| 工作层级 | 网络层(VIP/ARP/路由冗余) | 传输层(TCP)与HTTP层 |
| 是否解决单点 | 是,直接解决 | 否,HAProxy自身不上VIP反而需要外部提供高可用 |
| 是否做流量分发 | 可以配合LVS做ipvs转发,但本身不分发HTTP流量 | 是,支持加权轮询、最少连接、URI哈希等策略 |
| 典型组合 | 上层提供VIP,下端驱动LVS | 业务入口,后接真实服务器池 |
你看,二者天然是互补关系。生产中最经典的一套架构,正是“LVS(或HAProxy)+ Keepalived”:
- 客户端访问VIP
192.168.1.200 - Keepalived保证VIP始终绑定在一台健康的负载均衡器上
- 这台负载均衡器(HAProxy或LVS)再把请求分发给后端的真实应用服务器
如果只看Keepalived本身,它其实也内置了对LVS的支持——这就是它名字里“LVS”的由来。配置文件里可以定义virtual_server配置IPVS转发规则,Keepalived负责激活和维护IPVS表项。不过如果你们已经接了LVS在跑,Keepalived在这套架构里就专心扮演“健康检查 + VIP漂移”的角色,不需要额外配置virtual_server。这两个用法一个是“完整方案”,一个是“分工协作”,理解区别很重要。
当有人问“有了HAProxy为什么还要Keepalived”时,本质上是混淆了负载均衡能力和高可用保障这两个维度。HAProxy本身再强壮,也只是单进程单机版,机器宕了VIP没人接管,请求还是全断。Keepalived就是那根最后的安全绳。
当然,如果你一定要把两者当竞争对手来看,唯一的交集点是:Keepalived内置的IPVS/健康检查也能完成一部分流量分发,但它不会解析HTTP请求头、没法做七层路由正则匹配。所以网上“Keeepalived vs HAProxy”的争论,其实是在拿“飞机上的航电系统”和“客舱服务系统”比谁更高级,脱离场景谈优劣没有意义。
5. 生产环境中踩过的坑:脑裂、ARP缓存与脚本探活误判
配置跑起来简单,真正难的是跑起来之后遇到的那些“似懂非懂”的奇怪问题。我把这几年在生产环境里遇到过的典型问题整理出来,每一个都附上了排查思路和解决方式,照方抓药即可。
5.1 脑裂:两个节点同时持有VIP
这是所有高可用集群的头号事故。造成脑裂的原因通常是节点之间的VRRP通信中断(例如交换机端口隔离了VLAN、网线松了、iptables挡了组播/单播),但各自与业务网络的连接还正常。由于BACKUP收不到MASTER的通告,它也会升级为MASTER,结果同一个VIP在两个节点上同时出现,请求在交换机层面直接飘忽不定。
排查手段很直接:
bash复制# 两个节点同时执行
ip addr show eth0 | grep 192.168.1.200
# 如果两边都显示VIP,基本确诊脑裂
# 查看VRRP通信状态
keepalived -t -f /etc/keepalived/keepalived.conf # 配置语法检查
journalctl -u keepalived | grep VRRP
# 重点看是否持续出现 "Entering MASTER STATE" 或 "Master" 记录
解决思路有几个层面:首要的是把VRRP通信的链路和路径固定下来。比如用独立的网卡/网段承载VRRP报文,不要让业务流量把心跳链路打爆;在防火墙上显式放行协议112(如果用了单播就放行对应端口,默认端口号为112)。其次是部署监控:写一个脚本定期检查两个节点上VIP是否都绑定了,发现异常立刻告警。对于内部无状态服务,这个兜底基本管够。在某些严格要求不脑裂的场景,可能还需要引入第三节点仲裁或使用STONITH(比如通过IPMI强制关机),但这已经超出Keepalived本身的能力范围,属于集群治理的深水区。
5.2 VIP漂移后客户端连接假死:问题出在ARP缓存
明明VIP已经漂移到备节点,但业务侧反馈部分客户端仍然无法访问。这类问题的根因通常是客户端或交换机的ARP缓存没有及时刷新。VIP的MAC地址从旧主机变更为新主机后,有些设备的ARP缓存老化时间很长(比如默认300秒),在这段时间内所有发往VIP的包都会送到旧宿主机的MAC上,自然全部丢失。
讲一下为什么Keepalived已经发了免费ARP,还是会发生这种现象。免费ARP只能保证“收到这个报文的设备”更新缓存,但三层交换机、路由器对ARP更新的策略并不一致:很多设备为了防ARP欺骗,会忽略并非自己发出的免费ARP,或者只在固定老化时间后重新学习。所以问题不完全在Keepalived端,而在网络设备的学习策略。
实操层面的解决思路有两个方向:
- 调内核ARP相关参数,尽量在VIP漂移时主动发送更多免费ARP:
bash复制# 在keepalived.conf里设置
vrrp_garp_interval 0
vrrp_gna_interval 0
- 在交换机上修改连接VIP的动态ARP缓存老化时间(例如华为设备是
arp expire-time 60)。如果控制不了网络设备,就在VIP切换后通过登录受影响主机手动清除ARP缓存验证(临时手段)。
备节点上也需要提前调整内核参数,特别是当VIP从主节点漂移过来时,备节点要能正确响应针对VIP的ARP请求,否则也会出现“VIP在但ping不通”的现象:
bash复制# /etc/sysctl.conf 中添加(以eth0承载VIP为例)
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.eth0.arp_announce = 2
sysctl -p
arp_ignore=1表示只回应目标IP为本网卡接口地址的ARP请求;arp_announce=2表示始终使用本接口的最佳本地地址作为ARP报文源地址。这两个参数在LVS负载均衡场景下尤其重要,很多人VIP能漂移、但服务始终不通,查了很久才发现是内核默认ARP行为导致VIP的ARP请求应答不给力。
5.3 探活脚本“误杀”节点:权重与连续次数的正确踩法
Keepalived的vrrp_script探活非常香,但也容易因为配置不严谨把人坑了。最常见的误杀场景是这样:脚本里写了一个curl -sf http://127.0.0.1/healthz,而业务接口在系统启动初期还没就绪,探活失败一次,触发weight扣20,默认就抢占了。等到业务真正就绪后,脚本检测到成功又恢复优先权,又切回去,结果VIP在两个节点之间来回蹦,客户端连接稳定性极差。
我这里提供一个相对稳的写法思路:
- 脚本检测命令设置合理超时,例如
curl --connect-timeout 2 --max-time 3,防止命令卡死拖垮节点; - 使用
fallback 2、rise 1这类连续次数参数,把“瞬时抖动”过滤掉; - 如果业务启动慢,可以在脚本里加一个
grace冷却期,比如检测到失败后先sleep再重试; - 一定要收尾处输出标准退出码(exit 0/exit 1),Keepalived只认退出码,不认pid文件、不认TCP连接数;
- 权重值不要远小于主备优先级差,否则“扣完也还是比BACKUP高”,脚本永不失位,就失去了探活的意义。
在我实际维护的集群里,曾出现探活脚本本身写得不对,导致Keepalived进程因为脚本权限问题退出,整机VIP全丢的案例。那一刻才真正体会到,高可用系统里每一个可被软件控制的环节,都是潜在的单点。
5.4 从日志里还原事故现场:排查Command Line
Keepalived的日志分散在journalctl -u keepalived和/var/log/messages里(视系统而定)。排错时最核心的动作不是看配置语法,而是看日志里的状态机变化:
bash复制# 查看最近的状态切换记录
journalctl -u keepalived --since "10 minutes ago" | grep -E "VRRP|state|script"
有一个关键日志是VRRP_Instance(VI_1) transitioning to MASTER,它意味着该节点认为原MASTER失联了。紧接着往往会出现VRRP_Instance(VI_1) Received lower prio advert之类信息,说明有更低优先级的节点在宣告自己是MASTER,这其实就是主备切换的整个证据链条。跟着这条线索,你就能判断是脚本挂了、网络断了还是谁手动重启了Keepalived,而不是瞎猜。
6. 生产实践中的三个补充建议:监控、演练与规划
前面五个部分把原理、配置、对比、排错都讲完了,最后这条线是我觉得真正拉开运维质量差距的地方。
第一个建议是给Keepalived状态加上主动监控。很多人只监控vip ping通不通,这是远不够的。我现在的做法是:用Zabbix/Prometheus分别采集主备两个节点上的keepalived进程状态、VIP绑定数量、VRRP状态变化次数、脚本探活耗时等指标,一旦发现“双活”“脚本探活持续失败”就发出告警。另外,一台机器如果连续多次进入MASTER状态,往往说明网络不太稳定,要给这种“状态抖动”加个阈值。
第二个建议是主动做切换演练,而不是等故障上门。每半年左右,挑一个业务低峰期,执行一次systemctl stop keepalived,观察VIP漂移耗时、业务恢复耗时、日志是否正常,顺便清一遍ARP缓存问题。演练记录写得多了,你会积累出每台机器的“切换DNA”,真出问题时不会慌。
第三个建议是规划VIP分组与多实例,而不是只做一个vrrp_instance。如果你的入口需要同时支持内外网、多个业务段,完全可以在Keepalived里配置多个vrrp_instance,每个实例有自己的VIP、自己的网卡、自己的探活脚本。这样可以做成“互为主备”:节点A承载VIP1、节点B在VIP2上也有漂移能力,两台机器均摊流量,避免一台长期闲置一台长期疲劳。多实例之间router_id可以相同,但virtual_router_id必须全局唯一,这里踩过坑的人都懂。
根据我个人经验,Keepalived的稳定性上限其实取决于你的监控和演练意识,而不只是这份配置写得有多精致。高可用不是上了软件就真的高可用的,它是一个需要持续投入、动态维护的过程。每次看到一个项目因为主备“意外切换”而事故,十有八九是缺了演练这一步。趁现在系统还健康,把监控补上,把演练排进日历,才能真正睡得着觉。
