网站半夜挂了,打电话叫醒你的时候,最怕的不是服务起不来,而是不知道哪台机器还能顶上。我早年维护一个电商项目时就遇到过这种事:数据库主机内存故障,重启之后还是断断续续,业务方在群里催,开发在等数据库,整个晚上都在被动救火。后来我们痛定思痛,给核心节点都上了高可用,而Keepalived就是当时用得最多的那套方案。
Keepalived这个名字在老运维圈子里几乎等于“VIP漂移”的代名词。它基于VRRP协议实现,做的是主机层面的故障切换,配合Nginx、HAProxy这类负载均衡组件一起用,可以解决“入口IP挂了没人管”的问题。这篇文章不打算只贴配置文件,而是想把我从规划、部署到踩坑的整个过程都写出来。内容包括Keepalived的底层原理、它与Nginx/HAProxy/LVS的定位区分、双机热备的完整搭建步骤、配置项选举逻辑,以及我在生产环境里碰到过的真实问题和排查方法。无论你是刚入门的小白,还是想给现有系统补高可用的老手,这篇文章应该能帮你少走不少弯路。
1. Keepalived是什么:从VRRP、VIP、健康检查三个词理解高可用
很多人第一次看到Keepalived,以为它是某种负载均衡软件,其实不是。Keepalived本身并不转发业务流量,它的核心使命只有一个:保证你定义的“虚拟IP”始终落在某一台健康的机器上。为了讲清楚这件事,我习惯把它拆成三个关键词:VRRP协议、虚拟IP(VIP)、健康检查。
1.1 VRRP协议:高可用的底层机制
VRRP,全称Virtual Router Redundancy Protocol,虚拟路由冗余协议。它最早是网络设备上的标准协议,用来解决“默认网关挂了怎么办”的问题。以前企业网络里通常会配两台路由器,一台主一台备,通过VRRP让两台路由器共享一个虚拟IP。主路由器正常时,虚拟IP由它持有,所有流量走它;一旦主路由器失联,备份路由器会在几秒内接管虚拟IP,继续转发流量。整个过程对内网终端完全透明,终端根本感知不到网关发生了切换。
Keepalived做的事情,本质上就是把网络层的这套思想搬到了服务器层面。它在一组服务器上运行VRRP协议,让这些服务器“竞聘”同一个虚拟IP的持有权。谁持有VIP,谁就是当前的服务入口。这个协议在设计上非常巧妙,它依靠组播报文来交换状态,不需要额外的集中式协调组件,因此部署起来非常简单,两台机器就能组成一个高可用组。
这里有一个新手容易混淆的点:VRRP里的“主”和“备”并不是按IP大小或MAC地址来排序的,而是按优先级(priority)来确定的。优先级越高,成为MASTER的倾向越强。这个选举机制是Keepalived一切行为的基础,后面我会在配置部分详细展开。
1.2 VIP漂移:业务无感知的关键
虚拟IP(VIP)是Keepalived对外暴露的“门面”。正常状态下,VIP绑定在MASTER节点的网卡上;当MASTER故障时,BACKUP节点通过VRRP协议感知到主节点失联,立刻在自己的网卡上配置这个VIP,并发送免费ARP(Gratuitous ARP)通知局域网内的交换机更新MAC地址表。这一系列动作完成后,访问VIP的流量就会自动转向新的节点,客户端几乎感知不到任何中断。
理解VIP漂移,最直观的方式是把它想成酒店的“前台电话”。住客打总机号码找前台,不管今天值班的是张经理还是李经理,总机号码不变;哪位经理在岗,电话就转给谁。服务器故障就是“经理突然请假”,Keepalived的任务就是在几十秒内安排另一位经理坐到前台,同时保证外面的电话线还是同一根。
VIP漂移有几个特点需要特别注意。第一,它是在二层网络内生效的,VIP和真实服务器必须在同一个广播域,否则ARP通知无法正常工作。第二,漂移过程依赖VRRP报文的心跳检测,所以主备之间必须能正常组播通信,防火墙不能阻断VRRP协议。第三,VIP并不是只漂一次就完事,当原MASTER恢复后,如果配置允许抢占,VIP还可能再漂回去,这就会产生“抖动”。
1.3 健康检查:Keepalived不是只做IP漂移
如果Keepalived只是一个只会发VRRP报文的工具,那它和路由器上的协议栈也没什么区别。真正让它适合做服务高可用的,是它自带的健康检查机制。Keepalived可以周期性地执行我们定义的检查脚本,比如:Nginx是否存活、后端端口是否响应、进程是否僵死。只有当检查失败时,该节点才会主动降低自己的优先级,让出MASTER地位。
正是这种“软检查+硬心跳”的组合,让Keepalived能处理一大部分真实故障。比如Nginx进程挂掉但机器还活着,如果没有健康检查,VRRP层的主备状态完全正常,VIP不会漂移,用户访问还是落在故障机器上。但如果我们配置了vrrp_script检查Nginx进程,一旦发现Nginx挂了,就触发优先级下降,VIP漂移到备用节点,用户恢复访问,整个过程不需要人工介入。
在生产环境里,我们通常还会在健康检查脚本里做“双重验证”:先检查进程是否存在,再尝试或检查本地端口是否监听。只有这两关都过了,才判定节点是健康的。这里涉及到脚本写法和超时控制,细节我会在第三章实操部分给出完整的示例。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Keepalived与Nginx、HAProxy、LVS之间的定位关系
很多初学者会把Keepalived和Nginx、HAProxy、LVS混在一起谈,因为它们在架构图中经常一起出现。实际上这几类组件的角色完全不同:Nginx、HAProxy、LVS解决的是“流量怎么分发”的问题,Keepalived解决的是“入口怎么保障”的问题。理解这个边界,才能设计出清晰的高可用架构。
2.1 四个组件的职责边界
先给一个我常用的对照表,你可以把它当作记忆锚点:
| 组件 | 工作层级 | 核心职责 | 是否能做健康检查 | 是否处理业务流量 |
|---|---|---|---|---|
| LVS | 四层(内核态) | 基于Linux内核的IPVS模块做负载均衡,性能极高 | 可配合Keepalived实现 | 是 |
| HAProxy | 四层/七层 | 反向代理和负载均衡,支持TCP和HTTP | 自带健康检查 | 是 |
| Nginx | 七层 | 反向代理、HTTP服务、静态资源服务 | 自带被动健康检查 | 是 |
| Keepalived | 三层/二层 | 基于VRRP维护VIP,实现节点级高可用 | 通过脚本/模块检查服务存活 | 否 |
从这个表里可以看到,Keepalived是唯一一个不直接处理业务流量的组件。它更像是一个“VIP管家”,谁的状态好,谁就持有VIP,至于VIP后面到底是什么服务在监听,Keepalived并不关心。可以是Nginx,可以是HAProxy,也可以是一个普通的Java应用端口。
LVS和Keepalived的渊源尤其深。早期很多生产环境是LVS做四层负载,Keepalived做器高可用,两台LVS设备一主一备,后面的真实服务器池由LVS统一调度。这种架构现在已经比较少见了,因为Nginx和HAProxy在功能和易用性上更胜一筹,但如果你维护过老系统,多半见过“LVS+Keepalived”这对组合。
2.2 常见组合:Keepalived + Nginx
当前最主流的用法,还是Keepalived + Nginx。Keepalived对外提供一个VIP,VIP指向两台Nginx节点;两台Nginx各自作为反向代理,把请求转发给后端的应用服务器集群。客户端只需要访问VIP,完全不感知后端有多少台Nginx。
这套组合的优势在于:Nginx负责七层的路由和分发,Keepalived负责节点故障时的快速切换。两者分工明确,互不干扰。当一台Nginx节点宕机时,Keepalived检测到Nginx进程异常,VIP漂移到另一台继续服务;当整个机房断网时,VRRP报文丢失,备用节点接管VIP。前者是服务级故障,后者是节点级故障,Keepalived都能覆盖。
需要注意的是,Keepalived本身不会去区分“Nginx进程起了但配置错误”这类状态。它只能执行我们所写的检测脚本,根据返回值判断健康与否。所以检测脚本的质量,直接决定了故障切换的准确性。这也是我后面要强调的重点:不要迷信Keepalived的默认配置,一定要自己写好检测脚本。
2.3 部署前必须想清楚的高可用架构设计
在动手敲命令之前,我建议你先把架构想清楚。部署Keepalived最怕的,不是命令敲错,而是压根不知道自己想要什么。有几个问题需要提前回答:
- 你要保护的是什么服务?是Nginx、数据库、还是自研应用?这决定了
vrrp_script的检测内容。 - 主备两台机器是否在同一个二层网络?VIP的网段是否和业务网段一致?如果跨网段,VRRP的生效范围会受限。
- 故障切换的目标时间是多久?Keepalived的
advert_int默认是1秒,结合vrrp_script的检测周期,一般切换时间在2到5秒之间。如果业务要求秒级以内的切换,你需要更精细的调优。 - 主备的硬件配置和负载能力是否接近?如果一台机器扛不住全部流量,即使切换成功,业务也会因容量不足而失败。
这些问题在设计阶段解决,远比部署后再返工要省事得多。我在第一次部署Keepalived时就没有想清楚第二点,结果调试了很久才意识到VIP和业务网段不一致,导致测试流量始终无法到达。
3. 环境部署:从零开始搭建Keepalived双机热备
接下来进入实操环节。我会用一个最小化的双机热备场景,把Keepalived的安装、配置、验证过程完整走一遍。实验环境不一定需要真实服务器,两台虚拟机就够了。
3.1 实验环境规划和拓扑规划
我这次实验用的配置如下,你可以直接照搬,只要保证两台机器网络互通即可:
| 节点 | 主机名 | IP地址 | 角色 |
|---|---|---|---|
| 主节点 | lb01.mydomain.local | 192.168.10.11 | MASTER |
| 备节点 | lb02.mydomain.local | 192.168.10.12 | BACKUP |
| 虚拟IP | 无 | 192.168.10.100 | VIP |
操作系统是Rocky Linux 9(CentOS系列的发行版都可以),两台机器上都安装了Nginx,用来模拟真实业务入口。如果你手头没有现成的Nginx,用yum install nginx装一个即可,不需要特殊配置,只要Nginx能启动并监听80端口就行。
拓扑上,两台机器位于同一个网段192.168.10.0/24,主备之间通过二层组播通信。这里有一个前提条件:实验网络里不能让其他Keepalived实例干扰你的VRRP组,否则可能会发生VIP被莫名抢占的问题。后面讨论多实例时会展开。
3.2 安装Keepalived与Nginx
安装非常简单,使用epel仓库就能搞定。Rocky Linux默认带有epel-release,执行以下命令:
bash复制# 在主备两台机器上执行
yum install -y keepalived nginx
systemctl enable --now nginx
安装完成后,可以先看一下Keepalived的版本:
bash复制keepalived --version
正常情况下会显示类似Keepalived v2.2.8 (07/25, 2022)的版本号。这一步的目的是确认安装包里自带的配置语法,因为不同版本的Keepalived配置项有细微差异,比如vrrp_script的script参数在不同版本中可能要求使用绝对路径。
打开Keepalived的默认配置文件看看:
bash复制cat /etc/keepalived/keepalived.conf
默认文件通常只有几行,包含global_defs和一个简单的vrrp_instance。我们不会直接用它,而是会覆盖成自定义配置。
3.3 编写Nginx存活检测脚本
健康检查脚本是整个故障切换能否生效的关键。这里我提供一个我常用的脚本,内容不复杂,但对很多生产场景足够用:
bash复制#!/bin/bash
# /etc/keepalived/check_nginx.sh
if [ "$(systemctl is-active nginx)" == "active" ]; then
exit 0
fi
# 部分情况下systemd状态不正确,再兜底检查进程
if pgrep -x nginx > /dev/null 2>&1; then
exit 0
fi
exit 1
这个脚本的逻辑是:先查systemd状态,如果Nginx服务是active就直接判定健康;如果systemd状态不对,再用pgrep检查进程是否存在。双保险的原因是,某些被手动启动的Nginx进程systemd并不知道,只查systemd可能误判。
脚本给足执行权限:
bash复制chmod +x /etc/keepalived/check_nginx.sh
然后手动跑一遍,确认返回码是正确的:
bash复制/etc/keepalived/check_nginx.sh
echo $?
如果Nginx正在运行,应该输出0。这一步看似多余,但非常重要。我见过不少同事把脚本写好后直接从别处复制过来,结果脚本里有Windows换行符,keepalived执行时直接报“No such file or directory”。
如果你想让检查更严格,可以把脚本从“进程检查”升级为“端口探测”,用curl或nc试连80端口。不过要注意,探测频率过高会对业务端口产生额外压力。我的经验是端口探测的间隔不要少于2秒,否则后端服务在高峰期可能被自身的健康检查拖垮。
3.4 配置主节点Keepalived
现在开始写主节点的/etc/keepalived/keepalived.conf。先强调一下,配置文件修改前一定要备份:
bash复制cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
主节点配置如下:
bash复制global_defs {
router_id LB_LVS_MASTER
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -20
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
192.168.10.100/24 dev ens33
}
track_script {
check_nginx
}
}
这里每个参数都值得展开讲一遍:
router_id只是本机标识,可以随意起名,但最好不要带中文或特殊字符;vrrp_script块定义了一个名为check_nginx的检查脚本,每2秒执行一次,weight -20表示脚本失败时本机优先级降低20;fall 2表示连续失败2次才真正判负,这样能减少偶发抖动导致的误切换;rise 1表示只要成功1次便重新标记为健康。
vrrp_instance VI_1是实例定义。state MASTER表示该节点在正常情况下希望成为主节点;interface ens33必须改成你机器上实际网卡的名字,可以用ip a查看;virtual_router_id 51是虚拟路由ID,主备节点必须一致,且同一网段不同实例不能冲突;priority 100是初始优先级,另一台备节点必须比这个值低;advert_int 1是VRRP通告间隔,单位秒,一般不建议调太低,除非你对切换时间有极高要求。
3.5 配置备节点Keepalived
备节点配置和主节点几乎一模一样,只有两处不同:state改成BACKUP,priority调低一些。我这里设置priority 90,这样正常情况下VIP会一直掌握在主节点手里。
bash复制global_defs {
router_id LB_LVS_BACKUP
}
vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -20
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
192.168.10.100/24 dev ens33
}
track_script {
check_nginx
}
}
为什么备节点的priority要低?因为VRRP的选举规则是优先级高的当MASTER。如果两台机器都是priority 100,那么当主节点短暂故障恢复后,可能会出现双方同时认为自己是MASTER的情况,也就是传说中的脑裂。一定要保证主备的优先级有明确差值,这是生产环境的第一原则。
另外,auth_pass建议长度不超过8位,且主备必须一致。Keepalived对认证信息不一致的情况不会报错,但VRRP报文会被对端丢弃,导致两边互不可见,VIP长时间无法漂移。
3.6 启动验证:如何确认VIP成功漂移
配置写完,分别启动两台机器的Keepalived服务:
bash复制# 主备都执行
systemctl enable --now keepalived
然后查看主节点上VIP是否已经绑定:
bash复制ip addr show ens33
正常情况下,主节点的ens33网卡上会多出一个192.168.10.100地址。如果你发现VIP没有出现,先检查日志:
bash复制tail -f /var/log/messages
或者使用journalctl系统日志:
bash复制journalctl -u keepalived -f
日志中如果出现Entering MASTER state,说明主节点已经进入主状态;如果出现Entering BACKUP state,说明它认为自己是备节点。确认VIP没问题后,我们来做一次故障切换测试:停掉主节点的Nginx服务。
bash复制systemctl stop nginx
大约2秒左右,主节点的脚本检测会连续失败两次,触发优先级下降,VRRP重新选举,VIP会漂移到备节点。这时候在备节点上执行ip addr show ens33,你就能看到VIP已经绑上去了。客户端此时如果访问192.168.10.100,流量已经由备节点的Nginx接管。
这就是一次完整的高可用切换。你可能觉得过程很丝滑,但我在第一次做这个测试时,等了十几秒都没漂移,最后发现是防火墙把VRRP报文给拦了。
4. 核心配置项深度解析与选举机制
配置能用是一回事,能调优是另一回事。这一节我从原理出发,把配置里的关键决策点讲透,这样你以后碰到非标准场景时也能自己判断怎么改。
4.1 VRRP实例与优先级是如何决定主备的
VRRP实例通过virtual_router_id标识,主备节点使用相同的ID,就形成了一个虚拟路由器。在这个虚拟路由器内部,所有节点周期性发送VRRP通告报文,报文中携带自己的优先级。每个节点收到对端的通告后,会进行一次比较:如果对方优先级比自己高,自己就进入BACKUP状态;如果自己优先级更高,继续保持或切换为MASTER。
默认情况下,VRRP允许抢占。也就是说,即使当前MASTER正在正常工作,一旦有一个优先级更高的节点加入,这个高优先级节点会立刻抢占MASTER角色。抢占机制在某些场景下很实用,比如主节点硬件检修回来后,我们希望它自动重新接管;但在另一些场景下很烦人,比如主节点只是临时抖动,恢复后它又抢回VIP,导致切换两次,业务出现两段中断。
了解这一点后,你就能明白为什么priority的差值不能太小。如果主节点是100,备节点是99,那个位数的优先级优势在故障恢复时几乎起不到缓冲作用,任何一次网络抖动都可能触发角色互换。我建议主备差值至少10以上,最好20。
4.2 state、priority、nopreempt的配合使用
配置里state MASTER / state BACKUP写的是“期望状态”,但Keepalived实际运行状态完全由VRRP选举决定。很多人以为state MASTER就一定是MASTER,其实不准确。如果备节点priority更高,它一样会成为MASTER。
如果你希望主节点故障恢复后不要立刻抢回VIP,可以在两个节点的vrrp_instance里都加上nopreempt。注意,nopreempt通常要求所有节点的state都配置为BACKUP,否则在某些版本的Keepalived里不生效。这样做的好处是减少不必要的切换,代价是主节点恢复后无法自动回切,需要人工干预或等待下次故障。
生产环境里到底要不要开启nopreempt,我的经验是看业务容忍度。如果业务对两次切换之间的短暂中断完全无法接受,那就开启nopreempt,让当前MASTER一直提供服务,直到它再次故障。如果业务可以接受几秒钟的中断,那么保持默认的抢占模式,让主节点自动回切,运维管理更省心。
4.3 multicast与单播模式的选择
VRRP默认使用组播地址224.0.0.18、协议号112来发送通告。绝大多数局域网环境都支持组播,所以默认配置即可工作。但有些云平台、容器网络或交换机配置会禁用组播,这时候Keepalived的Heartbeat就会完全失效,两台机器互相感知不到对方,进而出现双MASTER的脑裂。
遇到这种情况,可以在实例配置里改为单播模式:
bash复制# 主节点
unicast_src_ip 192.168.10.11
unicast_peer {
192.168.10.12
}
# 备节点
unicast_src_ip 192.168.10.12
unicast_peer {
192.168.10.11
}
unicast_src_ip指定本机发送VRRP报文使用的源地址,unicast_peer填写对端IP。改成单播后,Keepalived不再依赖组播,只要TCP/IP层能互通,VRRP报文就能送达。这在云服务器和容器化环境里几乎是必配项。
使用单播模式时,还需要在防火墙里放行VRRP协议。与组播模式需要放行目的地址224.0.0.18不同,单播模式只需要放行对端IP发来的VRRP协议数据包。放行规则我会在下一节专门说。
4.4 通知脚本:让故障切换对运维系统可见
Keepalived支持在状态切换时调用外部脚本,这就是notify_master、notify_backup、notify_fault三个配置项。它们的作用是在切换的瞬间执行一条命令,把状态变化通知到监控系统、写入数据库或者触发报警。
我的一个实际做法是写一个简单的通知脚本,把切换信息写入日志并调用企业微信群机器人接口:
bash复制#!/bin/bash
# /etc/keepalived/notify.sh
log_file="/var/log/keepalived-state.log"
echo "$(date) 状态切换为: $1" >> $log_file
curl -s -X POST 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的key' \
-H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"Keepalived on $(hostname) switched to $1\"}}" > /dev/null 2>&1
然后在全局配置或实例里引用:
bash复制vrrp_instance VI_1 {
...
notify_master "/etc/keepalived/notify.sh MASTER"
notify_backup "/etc/keepalived/notify.sh BACKUP"
notify_fault "/etc/keepalived/notify.sh FAULT"
}
这样每次VIP发生漂移,监控系统都能第一时间收到消息。对于7×24小时的业务来说,这条通知链路比任何事后排查都重要。我在实际运维中,把通知时间精确到秒,再配合监控平台的告警策略,基本能做到“切换发生时,运维人员已经在看日志了”。
5. 实际部署中的常见问题与排查套路
这一节是实战价值最高的部分。我把自己和身边同事踩过的坑集中整理出来,做成一张速查表,再挑几个典型问题详细讲。
5.1 问题速查表
| 现象 | 可能原因 | 排查命令/办法 |
|---|---|---|
| VIP一直不出现 | 防火墙拦截VRRP、keepalived未启动、配置语法错误 | ip a、systemctl status keepalived、journalctl -u keepalived、keepalived -t -f /etc/keepalived/keepalived.conf |
| 主备都有VIP | 组播不通、优先级一致、VRRP报文被丢弃、网络隔离 | 抓包看VRRP报文、比较两边配置 |
| 主节点Nginx挂了但VIP不漂移 | 健康检查脚本失败、权重配置不对、脚本路径错误 | 手动执行脚本看返回码、检查track_script配置 |
| 切换后业务仍不通 | VIP绑定成功但服务监听失败、ARP缓存未更新 | 检查新MASTER端口监听、执行arping -I ens33 -c 3 192.168.10.100 -U |
| 需要访问VIP上的端口但telnet超时 | 防火墙未放行VIP对应端口 | firewall-cmd --list-all、ss -lntp确认监听地址 |
Keepalived日志频繁出现VRRP_Instance\(VI_1\) Dropping received VRRP packet |
auth_pass不一致、virtual_router_id不一致、ip版本不对 | 对比两侧配置文件 |
5.2 脑裂问题的排查与预防
脑裂是Keepalived最危险的故障之一,表现为两台机器同时持有VIP,客户端流量在两台机器之间漂移不定,或者负载均衡策略完全混乱。脑裂的根源,就是主备之间无法正常通信,导致双方都认为对方失联,于是各自抢占MASTER角色。
排查脑裂的第一步,是确认两台机器之间VRRP报文是否可达。如果使用组播模式,装好tcpdump后抓包:
bash复制tcpdump -i ens33 vrrp -n
如果能看到对端周期性地发送VRRP报文,说明二层组播正常;如果只能看到自己发送的报文,却没有对端的,说明组播被阻断或者对端keepalived没有正常工作。可以尝试切换为单播模式,绕开组播问题。
预防脑裂的思路有三层。第一,配置合理的优先级差值和nopreempt策略;第二,部署监控来检测“同一时间内VRRP状态为MASTER的节点数量”,一旦超过1就立刻报警;第三,条件允许时使用隔离机制,比如在检测到脑裂时自动禁用业务端口,牺牲可用性保一致性。不过这一层比较复杂,大多数场景做到前两层就够了。
5.3 检测脚本导致的误切换
vrrp_script的检测脚本如果写得不好,会带来比不做高可用更严重的后果。我见过一个案例:某团队把脚本写成了一个一次性执行的命令,结果Nginx检测脚本在进程刚启动的前两秒里总是失败,导致Keepalived频繁降权重,VIP在主备之间来回跳,业务间歇性不可用。
避免误切换的要点有几个:
- 脚本必须可重复执行,不能有“只跑一次”的副作用。
- 脚本执行时间要短,尽量不要超过
interval的值。比如interval 2,脚本执行时长如果超过2秒,Keepalived可能出现积压,影响检测精度。 - 合理使用
fall和rise。fall 2表示连续2次失败才认为故障,rise 1表示恢复1次即认为健康。前者可以减少抖动导致的误切换,后者可以加快恢复速度。 - 脚本内部不要用
sleep长时间挂起,不要写交互式命令。
另外,脚本的输出会被记录到系统日志里,所以不要在脚本里频繁echo无关内容,否则系统日志会被刷爆。这是一个看起来很傻但经常发生的问题。
5.4 防火墙和SELinux对VRRP的影响
在CentOS/RHEL/Rocky系列系统上,firewalld默认只是拦截外部访问,但你如果在部署时不小心把防火墙策略加严了,VRRP报文就会被挡在门外。放行VRRP协议的命令如下:
bash复制# 组播模式
firewall-cmd --permanent --add-rich-rule='rule protocol value="112" accept'
firewall-cmd --reload
# 单播模式,假设对端IP是192.168.10.12
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p vrrp -s 192.168.10.12 -j ACCEPT
firewall-cmd --reload
命令里的protocol value="112"就是VRRP的协议号。如果你用的是iptables,也要相应放行。这个环节经常被忽略,因为本地测试时Keepalived日志不会报错,实际上VIP却始终无法漂移,排查半天才发现是防火墙的问题。
SELinux同样可能干扰Keepalived脚本执行。如果你发现脚本在命令行手动执行没问题,但被Keepalived调用时就失败,先检查SELinux:
bash复制getenforce
如果是Enforcing模式,看看是否有AVC拒绝日志:
bash复制ausearch -m avc -ts recent
出现拒绝记录时,可以临时调整脚本文件的SELinux上下文,或者直接放行对应策略。生产环境我建议保持SELinux开启,但针对Keepalived做好策略定制,而不要为了图省事直接setenforce 0。
6. 案例延伸:容器化与前后端分离项目里的Keepalived实践
这几年云原生和容器化越来越普及,很多人问Keepalived是不是已经过时了。我的观点很明确:只要你的业务还在用虚拟机或者物理机做入口节点,Keepalived就是最成熟、最稳妥的高可用方案之一。但如果你的环境已经全面容器化,那就要重新考虑它的落点。
6.1 Java前后端分离项目中Keepalived的落点
以常见的Java前后端分离项目为例,比如RuoYi分离版,部署时通常会有Nginx做前端静态资源服务与后端接口反向代理,后端是Spring Boot应用打包成Jar包或Docker容器。这种架构里,高可用的关键点往往在Nginx这一层,而不是后端Java服务本身。
为什么?因为Nginx是用户请求进入后端的第一道关口。如果Nginx挂了,前端页面直接无法访问,后端再稳定也无济于事。所以在这类项目里,Keepalived加Nginx双机热备是最常见的入场方案:两台Nginx节点共享一个VIP,后端Java服务做双节点负载。Keepalived在这里守护的是Nginx进程,而不是Java进程。
如果你用的是RuoYi这种自带Nginx配置的前后端分离项目,有一个小细节需要注意:Nginx配置里的upstream地址要写成后端服务的内网IP或域名,不要写成VIP自身。否则VIP漂移后,Nginx转发请求的地址可能因为网络变化而失效。我在一次部署中,就是因为写成了本机IP,导致主备切换后后端连接全部失败。
6.2 Docker场景下Keepalived放在宿主机还是容器里
如果业务通过docker-compose部署,后端Java和Nginx都跑在容器里,那Keepalived应该放在哪里?我的建议是放在宿主机上,而不是容器里。原因有三点:
第一,Keepalived需要直接操作网卡和VRRP组播,容器内的网络隔离会让这些操作变得很别扭。第二,如果容器或docker-compose项目重启,容器里的Keepalived生命周期会跟着变化,高可用能力反而不稳定。第三,Keepalived守护的终极目标是“宿主机上承载的业务是否正常”,这个判断放在宿主机上最直观。
具体操作方式是:宿主机安装Keepalived,VIP绑定在宿主机的物理网卡上,然后通过docker run -p 80:80或docker-compose的端口映射,把容器内的Nginx端口映射到宿主机的80端口。Keepalived的健康检查脚本检测宿主机的80端口,也就是容器映射出来的端口。这样VIP漂移后,流量会导到另一台宿主机的Nginx容器上。
容器化场景下还有一个常见问题:docker-compose默认创建的网络可能和宿主机网络不在同一网段,但VRRP报文还是从宿主机物理网卡发出的,所以一般没问题。只是要确保Keepalived检测的是宿主机端口,而不是容器内部端口。如果你用docker exec去检测容器状态,会引入更多复杂度,不建议在Keepalived脚本里这么做。
6.3 生产环境部署的几条经验判断
最后分享几条我在生产环境部署Keepalived时沉淀下来的经验,不太像标准的教程内容,但确实能帮你在关键时刻做决策。
第一,优先把Keepalived部署在专职的入口节点上,而不是和应用服务混部在同一台机器上。混合部署虽然省机器,但一旦物理机宕机,后端应用和VIP同时失效,高可用效果大打折扣。第二,主备节点之间最好有独立的带外网络,专门跑VRRP报文,这样可以避免业务流量拥塞时影响心跳,降低脑裂概率。第三,每次修改配置文件后,一定要用keepalived -t -f /etc/keepalived/keepalived.conf做一次语法校验,再重载服务。如果配置有问题,重载会导致Keepalived直接退出,风险非常高。
还有一点是关于健康检查的周期。很多文章会把advert_int和interval调得特别低,比如0.1秒、0.5秒,但这会显著增加网络负载和系统开销。生产环境一般advert_int 1、interval 2就够了,故障切换时间在2到5秒之间。云环境和高性能网络里可以再压缩一点,但不要为了追求极致而在网络不稳的链路上冒险。
我在实际使用中发现,Keepalived最容易被低估的价值,不是你故障时它切得有多快,而是它让运维人员的“恢复预案”变得极简单:默认情况下,你只需要处理一台机器的服务状态,VIP会自动落到别处。故障恢复后,回切也是自动的。把这条机制纳入日常变更流程,系统变更的窗口期会缩短很多。
最后再分享一个小技巧:测试Keepalived切换时,不要一上来就直接执行systemctl stop keepalived,这样测的只是进程退出场景,真实故障往往是网线断开、机房断电、服务假死。更贴近生产的方式,是临时用iptables把VRRP报文丢掉,制造“网络不通但进程正常”的假象,这样才能验证你的健康检查和切换策略在真实故障下是否靠谱。我后来做任何高可用验收,都会加上这条网络隔离测试,它能暴露出来的问题,比单纯停服务要现实得多。
