
1. 为什么单台Nginx扛不住真实流量很多团队在项目初期都是单台Nginx打天下配置简单、维护省心直到某天凌晨被告警电话叫醒——Nginx进程挂了整个业务入口全断。这种事故我经历过不止一次后来复盘发现单点Nginx的问题远不止进程崩溃这么简单。单台Nginx的脆弱性体现在三个层面。第一是硬件层面网卡、内存、磁盘任何一个部件故障都会导致服务不可用这不是调优能解决的。第二是软件层面Nginx虽然以稳定著称但配置热加载失败、worker进程异常退出、连接数打满等情况依然会发生。第三是运维层面系统内核升级、安全补丁安装、证书更换这些操作都需要重启服务单机环境下每次操作都是一次业务中断风险。有人会说那用DNS轮询或者LVS不就行了DNS轮询的问题在于故障切换依赖TTL缓存客户端可能几分钟内还在往挂掉的节点发请求而且它没法做健康检查。LVS确实强但配置复杂度高对于中小规模业务来说有点杀鸡用牛刀。Keepalived配合Nginx双机主备这个方案恰好卡在一个甜点位上——足够简单又能实现秒级故障切换还能顺带解决IP漂移问题。这个方案的核心思路是两台服务器都装Nginx和Keepalived同一时刻只有一台MASTER持有虚拟IPVIP对外提供服务另一台BACKUP待命。MASTER上的Keepalived通过VRRP协议持续向BACKUP发送心跳一旦BACKUP收不到心跳就认为MASTER挂了自己接管VIP成为新的MASTER。整个过程对客户端完全透明因为它们访问的一直是那个VIP。适合参考这套方案的人群很明确手里有两台服务器、业务入口是Nginx、希望实现高可用但不想引入太复杂组件的运维或后端开发。如果你只有一台机器或者业务已经上了Kubernetes那这套方案可能不是最优解。但如果你正处在单机扛不住、集群又太重的尴尬阶段双机主备就是那个刚刚好的选择。2. VRRP协议与Keepalived的工作机制拆解2.1 VRRP到底在干什么VRRP全称Virtual Router Redundancy Protocol翻译过来叫虚拟路由冗余协议。名字听着唬人本质其实很简单多台机器组成一个组共同虚拟出一个IP地址同一时刻只有一台机器响应这个IP的请求。打个比方就像公司前台。平时只有A前台在岗访客都找A。A突然请假了B前台立刻顶上访客还是找前台这个位置不需要知道具体是谁在坐。VIP就是那个前台的位置MASTER和BACKUP就是A和B。VRRP组内通过多播地址224.0.0.18发送通告报文默认每1秒发一次。BACKUP如果连续3秒3倍通告间隔没收到MASTER的通告就启动选举流程。优先级高的成为新MASTER优先级相同则比较IP地址大小。这个3倍间隔的设计是为了容忍网络抖动避免误切换。2.2 Keepalived的角色与状态机Keepalived是VRRP协议在Linux上的实现但它比纯VRRP多了健康检查功能。这是它比LVS自带VRRP更实用的地方——Keepalived不仅能检测对方机器是否活着还能检测本机的Nginx进程是否正常。Keepalived的状态机有三个核心状态MASTER、BACKUP、FAULT。MASTER持有VIP并持续发通告BACKUP收通告并随时准备接管FAULT是健康检查失败后的状态此时会主动放弃VIP。状态转换的触发条件包括收到更高优先级的通告、心跳超时、健康检查脚本返回失败。这里有个容易踩的坑Keepalived默认只检查自己的心跳不检查Nginx。也就是说如果Nginx进程挂了但Keepalived还在跑MASTER依然持有VIP但用户请求全部502。所以必须配置vrrp_script来监控Nginx状态这是整套方案里最关键的配置项之一。2.3 抢占模式与非抢占模式的选择Keepalived默认是抢占模式原MASTER恢复后会重新抢回VIP。这在某些场景下是好事比如你希望性能更强的机器一直当主力。但抢占会导致一次额外的切换如果原MASTER反复重启就会反复抢占造成VIP频繁漂移。非抢占模式nopreempt下原MASTER恢复后不会抢回VIP而是保持BACKUP状态直到当前MASTER出问题。这个模式适合两台机器配置对等、不希望频繁切换的场景。配置方式是在vrrp_instance里加一行nopreempt但注意这个参数只在state为BACKUP的节点上生效。我的建议是如果两台机器配置完全一样用非抢占模式更稳如果一台明显强于另一台用抢占模式让强机当主力。这个选择没有绝对对错取决于你的实际部署情况。3. 双机环境的前置准备与网络规划3.1 机器规划与IP分配动手之前先把IP规划清楚这是后面所有配置的基础。假设你有两台服务器规划如下角色主机名物理IPVIP优先级MASTERnginx-01192.168.1.101192.168.1.100100BACKUPnginx-02192.168.1.102192.168.1.10090VIP必须和物理IP在同一网段否则VRRP通告无法正常收发。优先级数值越大越优先MASTER设100、BACKUP设90是常见做法留出空间方便以后加机器。注意VIP不要和DHCP地址池重叠否则可能出现IP冲突。如果内网有DHCP服务器务必把VIP排除在地址池之外。3.2 系统参数调整两台机器都需要做几项系统层面的调整否则Keepalived可能无法正常工作。第一项是关闭防火墙对VRRP的拦截。VRRP使用多播地址224.0.0.18和协议号112很多默认防火墙规则会把它挡掉。最省事的做法是直接放行# 如果使用firewalld firewall-cmd --permanent --add-rich-rulerule protocol valuevrrp accept firewall-cmd --reload # 如果使用iptables iptables -A INPUT -p vrrp -j ACCEPT iptables -A OUTPUT -p vrrp -j ACCEPT第二项是开启IP转发。虽然纯Nginx场景下不一定需要但如果后续要做七层转发或者配合其他组件这个参数迟早要开echo net.ipv4.ip_forward 1 /etc/sysctl.conf sysctl -p第三项是关闭NetworkManager对VIP的干扰。某些发行版的NetworkManager会接管网卡配置导致Keepalived添加的VIP被清除。稳妥做法是在网卡配置里加上NM_CONTROLLEDno或者直接停用NetworkManager。3.3 Nginx的安装与基础配置两台机器的Nginx配置必须保持一致否则切换后行为不一致会出问题。安装方式看个人习惯yum/apt装或者源码编译都行关键是版本要统一。Nginx的核心配置里有几项和双机主备直接相关。首先是worker进程数建议设为CPU核心数其次是连接数上限根据业务量调整最重要的是日志格式要能区分请求最终落到了哪台机器方便排查问题log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for upstream_addr$upstream_addr; access_log /var/log/nginx/access.log main;配置完成后用nginx -t检查语法然后启动服务。两台机器都确认Nginx能正常响应请求后再进入Keepalived的配置环节。4. Keepalived配置文件的逐项拆解4.1 全局配置段Keepalived的主配置文件通常在/etc/keepalived/keepalived.conf。全局段主要定义邮件通知和路由规则双机主备场景下最需要关注的是通知功能global_defs { notification_email { opsexample.com } notification_email_from keepalivedexample.com smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id nginx-01 }router_id建议设为唯一值通常用主机名。这个字段会出现在日志里方便定位是哪台机器发的通告。邮件通知功能在切换时能及时告警但需要本机有邮件服务如果没配邮件服务这部分可以省略改用其他监控手段。4.2 健康检查脚本定义这是整套配置里最关键的段落。vrrp_script定义了一个检查脚本Keepalived会按指定间隔执行它根据返回码决定是否切换状态vrrp_script chk_nginx { script /etc/keepalived/check_nginx.sh interval 2 weight -20 fall 2 rise 2 }逐项解释interval 2表示每2秒执行一次weight -20表示脚本失败时优先级减20这样MASTER优先级从100降到80低于BACKUP的90触发切换fall 2表示连续失败2次才判定为故障rise 2表示连续成功2次才恢复。对应的检查脚本内容#!/bin/bash # 检查Nginx进程是否存在 if ! kill -0 $(cat /var/run/nginx.pid) 2/dev/null; then exit 1 fi # 进一步检查Nginx是否能正常响应 if ! curl -s -o /dev/null -w %{http_code} http://127.0.0.1/health | grep -q 200; then exit 1 fi exit 0脚本要加执行权限chmod x /etc/keepalived/check_nginx.sh。这里我建议同时检查进程和端口响应因为进程活着但服务卡死的情况并不少见。如果只检查进程Nginx假死时Keepalived不会切换用户照样访问不了。4.3 vrrp_instance实例配置实例配置定义了VRRP组的具体行为MASTER和BACKUP的配置只有state和priority两处不同vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 123456 } virtual_ipaddress { 192.168.1.100/24 } track_script { chk_nginx } notify_master /etc/keepalived/notify.sh master notify_backup /etc/keepalived/notify.sh backup notify_fault /etc/keepalived/notify.sh fault }virtual_router_id是VRRP组的标识同一组内两台机器必须一致不同组之间不能重复。advert_int 1是通告间隔默认1秒。authentication是简单密码认证防止误加入其他VRRP组密码最多8位。notify_master等脚本在状态切换时触发可以用来做告警、日志记录或者调整其他服务。比如切换时给监控系统发个事件或者更新DNS记录这些都可以在notify脚本里实现。4.4 配置文件的差异对比把两台机器的配置差异整理成表格部署时对照检查配置项MASTER节点BACKUP节点stateMASTERBACKUPpriority10090router_idnginx-01nginx-02virtual_router_id5151virtual_ipaddress192.168.1.100/24192.168.1.100/24auth_pass123456123456除了这几项其他配置必须完全一致。部署时建议先配好一台用scp复制到另一台再改差异项这样能避免手误导致的配置不一致。5. 启动验证与故障切换实测5.1 服务启动与状态确认两台机器都配置好后先启动MASTER的Keepalived再启动BACKUP的systemctl start keepalived systemctl enable keepalived启动后在MASTER上执行ip addr show eth0应该能看到VIP 192.168.1.100已经挂在网卡上。在BACKUP上执行同样的命令不应该看到VIP。同时用systemctl status keepalived确认服务状态正常日志里没有报错。查看Keepalived日志可以用journalctl -u keepalived -f正常运行时MASTER会周期性打印VRRP_Instance(VI_1) Sending gratuitous ARPBACKUP会打印VRRP_Instance(VI_1) Entering BACKUP STATE。5.2 手动触发切换的完整过程验证切换最直接的方式是停掉MASTER的Keepalived# 在MASTER上执行 systemctl stop keepalived然后在BACKUP上观察几秒内应该能看到VIP漂移过来。用ip addr show eth0确认VIP出现同时日志里会打印VRRP_Instance(VI_1) Transition to MASTER STATE。此时用另一台机器ping VIP应该能正常通。访问VIP上的Nginx服务也应该正常响应。整个过程通常在3秒内完成具体取决于advert_int和fall的配置。5.3 模拟Nginx故障的切换测试停Keepalived测的是进程级故障更贴近真实场景的是Nginx挂掉但Keepalived还在跑。这时候就要靠健康检查脚本了# 在MASTER上停掉Nginx systemctl stop nginx等待几秒取决于interval和fall的乘积MASTER的优先级会因为脚本失败而降低BACKUP检测到优先级变化后接管VIP。此时在MASTER上ip addr应该看不到VIP了而BACKUP上能看到。这个测试很关键因为它验证的是健康检查脚本是否真正生效。我见过不少配置脚本路径写错、权限不对、或者脚本逻辑有bug导致Nginx挂了Keepalived却毫无反应这种配置等于白做。5.4 切换后的状态恢复测试完成后把MASTER的Nginx和Keepalived重新启动。如果是抢占模式MASTER会重新抢回VIP如果是非抢占模式VIP会留在BACKUP上直到BACKUP出问题。恢复后要检查几件事MASTER的VIP是否回来抢占模式、Nginx是否正常响应、日志里有没有异常报错。如果发现VIP没回来检查优先级配置和抢占模式设置。6. 生产环境踩过的坑与优化建议6.1 脑裂问题的成因与防范脑裂是双机主备最危险的情况两台机器都认为自己是MASTER同时持有VIP导致IP冲突和请求混乱。成因通常是心跳链路中断比如网线松动、交换机故障、或者防火墙误拦了VRRP报文。防范脑裂有几个手段。第一是用独立网卡跑心跳业务网卡和心跳网卡分开避免业务流量拥塞影响心跳。第二是配置unicast_peer把多播改成单播减少对网络设备的依赖unicast_src_ip 192.168.1.101 unicast_peer { 192.168.1.102 }第三是加双活检测脚本在notify脚本里检查对端是否可达如果不可达就主动降级。这些手段组合使用能把脑裂概率降到很低。6.2 VIP漂移后ARP缓存不更新有些场景下VIP漂移了但客户端还是往旧机器发请求原因是ARP缓存没更新。Keepalived在切换时会发送免费ARPgratuitous ARP来刷新周边设备的ARP表但某些交换机或客户端可能不响应。解决办法是在notify脚本里手动触发ARP更新#!/bin/bash case $1 in master) arping -c 3 -A -I eth0 192.168.1.100 ;; esacarping -A发送ARP应答包强制刷新对端ARP缓存。这个操作在切换后执行一次即可能显著减少切换后的请求丢失。6.3 健康检查脚本的常见陷阱健康检查脚本写不好整套高可用就是摆设。我踩过的坑包括脚本里用了相对路径导致找不到命令、curl超时时间没设导致脚本卡死、脚本返回码逻辑写反了。几个实用建议脚本里所有命令用绝对路径curl加--max-time 2限制超时脚本开头加set -e让错误及时暴露测试时手动执行脚本确认返回码符合预期。另外脚本的检查逻辑要贴合业务比如你的Nginx主要做反向代理那就检查后端是否可达而不是只检查Nginx进程。6.4 日志与监控的配套建设双机主备上线后没有配套监控等于裸奔。至少要监控三项VIP当前在哪台机器、Keepalived进程状态、Nginx健康检查结果。VIP位置可以通过在每台机器上跑脚本检测ip addr里有没有VIP然后上报到监控系统。Keepalived状态可以用systemctl is-active keepalived判断。健康检查结果可以解析Keepalived日志里的关键字。告警策略上VIP漂移事件必须告警因为每次漂移都意味着一次故障。但要注意区分正常维护和异常切换避免维护窗口期被告警轰炸。可以在notify脚本里加个标记维护期间不触发告警。7. 从双机主备到更复杂场景的延伸双机主备解决了可用性问题但它有个天然局限BACKUP平时是闲置的资源利用率只有50%。业务量增长后你可能会想能不能让两台机器同时干活。这就引出了双机双活的概念。Nginx本身支持负载均衡两台机器可以同时持有VIP需要DNS轮询或者上层负载均衡器配合或者用Keepalived的lvs_sync_daemon做连接同步。但双活的复杂度明显上升会话保持、数据一致性、脑裂处理都要重新设计。另一个延伸方向是多机集群。Keepalived支持多个BACKUP节点优先级依次递减MASTER挂了之后优先级最高的BACKUP接管。这种架构适合对可用性要求极高的场景但要注意VRRP通告在多节点下的风暴问题。不过话说回来方案选型要看实际需求。如果你的业务量单台Nginx就能扛住双机主备已经足够如果单台扛不住那要考虑的就不只是高可用还有负载均衡和水平扩展这时候可能Kubernetes或者专业的负载均衡器更合适。技术方案没有银弹适合当前阶段的才是最好的。我个人在实际操作中的体会是双机主备这套方案的价值不在于技术多先进而在于它用最小的复杂度解决了最核心的问题。配置一次稳定运行几年这种性价比是很多复杂方案比不了的。但前提是配置要扎实健康检查要可靠监控要到位否则高可用就是纸面上的高可用。