单台Nginx做反向代理,能解决后端应用的水平扩展问题,但引出了一个新的单点:如果这台Nginx所在机器抖动、重启或内核崩溃,所有请求都会失败。Keepalived引入到Nginx层,本质上是给负载均衡器自己加一层主备冗余。两台Nginx同时在线,对外只暴露一个虚拟IP,主节点出现异常时备节点自动顶上去,整个切换过程不需要修改DNS,也不需要客户端重新建立连接。

一、先看清单点故障在哪里
很多环境下,Nginx被配置成入口网关,后面挂着多个应用节点,例如192.168.10.21和192.168.10.22分别运行Java服务。流量经过Nginx时,根据upstream里的策略分发到不同节点。这个方案在应用层实现了冗余,但Nginx自身变成了最脆弱的一环。只要负责反向代理的机器需要停机维护、升级内核或发生硬件故障,即使后端服务全部健康,外部请求依然无法进入。
Keepalived解决这个问题的思路来自VRRP协议。VRRP可以在多台路由器或服务器之间选举出一个主设备,主设备持有虚拟IP和虚拟MAC地址,其他设备处于备份状态。主设备周期性发送通告报文,备份设备在超时后认为主设备失效,随即接管虚拟IP。由于虚拟IP是独立的,客户端始终访问同一个地址,后端完全不知道入口已经切换到了另一台物理机。
实际部署时,至少需要两台Linux服务器,每台都安装Nginx和Keepalived。为了便于后文说明,这里假设节点A的物理IP是192.168.10.11,节点B的物理IP是192.168.10.12,虚拟IP是192.168.10.100。两台Nginx的配置保持一致,后端应用池也指向同一组应用节点。Keepalived只负责决定哪台机器持有虚拟IP,一旦持有虚拟IP的机器上的Nginx进程异常,Keepalived要么尝试拉起Nginx,要么主动降低自己的优先级让虚拟IP漂移到对端。
二、Nginx侧:让负载均衡和健康检查先跑通
在配置Keepalived之前,登录节点A和节点B,先确认两台机器上的Nginx都能独立访问后端。最简单的反向代理配置如下,重点在upstream块和proxy_pass。示例中定义了一个名为backend的服务器组,包含两个应用节点,并设置了权重和被动健康检查阈值。
upstream backend {
server 192.168.10.21:8080 weight=3 max_fails=2 fail_timeout=15s;
server 192.168.10.22:8080 weight=1 max_fails=2 fail_timeout=15s;
keepalive 32;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
}这里的max_fails和fail_timeout是开源Nginx自带的被动健康检查机制。比如max_fails=2表示在fail_timeout规定的时间内,如果某个后端节点连续失败两次,Nginx就会暂时把它摘除,不再转发请求。这种方式依赖真实请求触发,不像主动健康检查那样定时探测,但胜在无需额外模块,配置简单,适合大多数业务场景。如果业务对故障感知要求更高,可以编译第三方模块nginx_upstream_check_module实现主动探测,不过那会增加维护成本,通常不推荐一上来就引入。
配置完成后使用nginx -t检查语法,再执行nginx -s reload。此时在两台机器上分别访问http://192.168.10.11和http://192.168.10.12,都应该能看到后端返回的内容。这就是Keepalived介入前需要达到的状态:任何一台Nginx单独工作都能正常转发。很多人跳过这一步直接配Keepalived,导致切换后VIP虽然漂移了,但备机自己的Nginx配置其实不能转发,最终依然访问失败。
三、Keepalived侧:用VRRP让虚拟IP自动漂移
Keepalived的配置分为全局定义、检测脚本和VRRP实例三部分。先在节点A上编辑/etc/keepalived/keepalived.conf,配置为MASTER角色,优先级设高一些。关键是要关联一个检测Nginx进程的脚本,否则Keepalived只能感知机器是否宕机,无法感知Nginx是否崩溃。
global_defs {
router_id LB_NODE_A
}
vrrp_script chk_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -20
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}
virtual_ipaddress {
192.168.10.100/24 dev eth0
}
track_script {
chk_nginx
}
}节点B的配置结构相同,只需要把router_id改为LB_NODE_B,state改为BACKUP,priority改为90即可。两台机器的virtual_router_id必须一致,否则会被认为是不同的VRRP组,无法互通。认证密码也需要完全一致,不然心跳报文会被丢弃。
检测脚本的内容很简单,先检查nginx进程是否存在,如果不存在则尝试启动一次,等待两秒后再次检查。如果仍然失败,脚本返回非零退出码,Keepalived会按照weight -20的规则把本机优先级从100降到80。此时对端节点B优先级为90,自然就能抢占虚拟IP。脚本如下:
#!/bin/bash
if ! killall -0 nginx >/dev/null 2>&1; then
systemctl start nginx
sleep 2
if ! killall -0 nginx >/dev/null 2>&1; then
exit 1
fi
fi
exit 0注意脚本必须赋予执行权限,例如chmod +x /etc/keepalived/check_nginx.sh。Keepalived默认以root权限运行,能够执行systemctl start nginx。如果脚本中使用了systemctl,需要确保Keepalived进程能够调用该命令。在一些精简版系统里,如果Keepalived被限制在非特权用户下运行,脚本会执行失败,通常检查日志会发现权限拒绝。
关于抢占行为,默认情况下MASTER恢复后会自动抢回虚拟IP,这在某些场景会导致短暂抖动。如果希望避免频繁切换,可以在两边的vrrp_instance里都加上nopreempt参数,并且把节点A设置为state BACKUP、优先级仍为100,让最初启动的那台成为实际主节点。但这种模式对排障要求更高,生产环境需要根据网络质量评估。
四、切换验证与生产避坑指南
配置完成后,先启动节点A的Keepalived,再启动节点B。在节点A上执行ip addr show eth0,应该能看到除了192.168.10.11之外,还多出一个192.168.10.100。使用客户端访问http://192.168.10.100,能正常获得后端响应。此时虚拟IP确实绑定在节点A上。
接下来做故障模拟:在节点A上手动停掉Nginx,例如systemctl stop nginx。由于Keepalived的检测脚本间隔为2秒,脚本会先尝试启动Nginx,如果启动失败才会降低优先级。因此为了看到漂移,应当先把Nginx彻底禁用,例如systemctl stop nginx后立即改掉nginx.service或删除执行权限,制造无法恢复的状态。约2到4秒后,在节点B上执行ip addr show eth0,能看到虚拟IP已经转移到节点B。客户端再次访问192.168.10.100仍然正常,说明切换成功。
生产环境需要额外注意几个细节。VRRP默认使用组播地址224.0.0.18,如果两台机器之间有防火墙或云平台安全组,必须放行对应的VRRP协议流量。部分虚拟化环境禁止组播,此时可以在Keepalived配置中改用单播,通过unicast_src_ip和unicast_peer指定两边的物理IP。这样虽然绕开了组播限制,但配置文件更依赖准确的IP,迁移时容易出错。
另一个容易踩坑的地方是脑裂。当心跳链路中断时,两边都认为自己是主节点,同时持有虚拟IP,会导致间歇性访问失败。预防脑裂的常见做法包括使用独立的私网心跳、设置合理的advert_int和preempt_delay,以及在交换机上配置端口隔离。真正的强一致需要借助仲裁节点或云厂商的浮动IP能力,Keepalived本身只能提供尽力而为的高可用。对大多数中小型业务来说,只要网络稳定,Nginx加Keepalived的双机热备已经足够降低入口故障。
最后要强调一点,Keepalived解决的是入口层的高可用,不等于业务系统整体高可用。如果后端数据库、缓存或应用节点本身没有冗余,仍然可能在其他位置出现单点。把负载均衡层做扎实之后,应该继续审视整个请求链路,从DNS解析、Nginx入口、应用服务到数据库连接,每一层都具备冗余能力,整个系统才算真正具备抗故障能力。
Nginx负载均衡Keepalived双机热备高可用修改时间:2026-09-27 21:55:55