Keepalived虚拟路由配置详解:从VRRP原理到生产故障切换实践
做运维的人最怕半夜收到告警短信。服务挂了可以重启但备机顶不上来、虚拟 IP 漂不过去那就不只是重启的问题了。Keepalived 是这类高可用方案里用得最多、也最容易上手的工具它基于 VRRP 虚拟路由冗余协议让两台服务器共享一个虚拟 IP实现故障秒级切换。这篇文章围绕“Keepalived 虚拟路由配置”这条主线把从原理、方案选型、配置文件拆解、故障切换实测到生产排障的完整过程都过一遍适合刚接触高可用的小白也适合正在搭 Nginx 双机、MySQL 主主这类场景的运维和开发同学。文中所有配置和命令都是我在真实环境里验证过的可以直接照着抄。1. 场景切入什么情况下需要虚拟路由1.1 单点故障是可用性的头号敌人先聊一个最朴素的问题你的服务是什么架构如果只有一台服务器上面跑了 Nginx、MySQL、Redis那这台机器就是整个系统的单点。它宕机业务就停。上双机是常规解法但双机解决的是“机器挂了怎么办”的问题随之而来的新问题是用户的请求到底打到哪台机器上方案有很多最省事的操作是改 DNS但这会引入缓存和 TTL 时间切换以分钟甚至小时计。假如把切换做到秒级让两台机器共用一个 IP平时这个 IP 挂在一台机器上这台机器挂了IP 自动漂到另一台机器上用户无感知这就是虚拟路由要干的事。1.2 VRRP 协议到底在做什么Keepalived 的核心是 VRRPVirtual Router Redundancy Protocol虚拟路由冗余协议这套协议的思路在思科、华为这些网络设备里的 HSRP、VRRP 中非常成熟Keepalived 把它搬到了 Linux 服务器上。看我画的这个逻辑两台机器组成一个虚拟路由器对外表现为一个虚拟 IPVIP。其中一台是 Master负责实际转发流量另一台是 Backup处于待命状态。Master 每隔 1 秒默认往组播地址 224.0.0.18 发送 VRRP 通告报文Backup 收到报文就知道 Master 活着。如果 Backup 连续 3 个通告周期没收到 Master 的报文就认定 Master 挂了自己升级为 Master绑定 VIP同时发出免费 ARP 更新交换机上的 MAC 表。整个过程大致 3 到 5 秒业务影响范围很小。VRRP 报文走的是 IP 协议号 112不是 TCP也不是 UDP这个细节后面配置防火墙时会反复用到。1.3 常见组合Keepalived 很少单独出现实际项目中Keepalived 几乎总是和具体业务一起出现。最典型的是 Nginx 双机Keepalived 管 VIPNginx 管请求进入再配合健康检查脚本保证“不仅机器活着服务也得活着”MySQL 主主复制或主从复制也会用到 KeepalivedVIP 指向当前可写的库主库挂了自动切到备库Redis 哨兵之前的老方案也靠它托管 VIP。Keepalived 本身不产生流量它就是那个“门牌号”的管理员。2. 方案选型为什么最终选 Keepalived2.1 高可用方案横向对比市面上的高可用方案不止 Keepalived 一个我在项目里也试过 Heartbeat 和 Pacemaker Corosync各有各的脾气。简单说一下我的结论。方案原理适合场景上手难度我的评价KeepalivedVRRP 虚拟路由IP 漂移双机热备、LVS、Nginx/MySQL 场景低配置简单切换快生态成熟Heartbeat心跳 资源脚本老牌双机热备中活少文档老旧新项目不太推荐Pacemaker Corosync分布式资源管理器集群、多节点、复杂资源约束高功能强悍但学习成本高小规模用不上云厂商的 SLB / HAVIP云平台自带云原生环境低依赖厂商跨云不方便如果你的需求就是“两台机器抢一个 IP故障切换”Keepalived 是最直接的选择。Pacemaker 能管几十台机器的资源编排但杀鸡用牛刀配置复杂后反而容易埋雷。2.2 Keepalived 的三个组成部分Keepalived 虽然不是特别大的项目但它内部结构清晰主要包含三块核心调度框架、VRRP 栈和健康检查模块。VRRP 栈负责虚拟路由状态机决定当前谁是 Master 谁是 Backup健康检查模块负责周期性地探测业务进程探测失败的节点会被降低优先级核心框架负责把这两件事串联起来并在状态切换时触发通知脚本。理解了这三个模块再看配置文件就会清楚很多vrrp_instance管状态机vrrp_script管健康检查notify系列参数管切换事件回调。2.3 脑裂概念先打个预防针做高可用一定要懂脑裂。脑裂Split Brain指两台机器互不知晓对方状态都认为自己是 Master于是同时绑定同一个 VIP导致网络冲突、ARP 混乱、请求被两台机器同时响应。VRRP 靠组播报文防脑裂Master 和 Backup 一直在互相确认。但网络分区、交换机故障、防火墙把 VRRP 报文丢了都有可能导致脑裂。预防手段包括开启单播模式绕过组播限制配置nopreempt减少争抢健康检查脚本里做双盘互斥或者依赖第三方仲裁等。后面第五节和第六节我会详细讲怎么测、怎么排。3. 环境准备与安装3.1 网络规划与参数约定安装之前先把机器规划清楚。下面是我这次实验的环境你可以按自己的网段替换。机器A192.168.1.10Nginx 已启动机器B192.168.1.11Nginx 已启动虚拟IPVIP192.168.1.100操作系统Ubuntu 22.04CentOS 7 的命令我会顺带标注网卡名eth0规划时注意几点。VIP 必须和两台机器在同一广播域否则漂过去也ping不通VIP 不要和已有真实 IP 冲突也别落在 DHCP 分配池里两台机器的防火墙必须放行 VRRP 协议不然 Master 和 Backup 完全无法发现对方。第三个问题最容易踩后面单独说。3.2 安装步骤与版本说明Keepalived 的安装非常标准包管理器和编译安装都行生产环境我通常用发行版自带的包方便 systemd 管理。# Ubuntu / Debian apt update apt install keepalived -y # CentOS / RHEL yum install keepalived -y安装完成后先看版本keepalived --version。Keepalived 1.x 和 2.x 的配置差异不大2.x 对 IPv6、单播报文支持更好我建议直接用 2.x。源码编译装的话依赖库主要是 OpenSSL 和 popt编译参数里加--enable-sha1即可不过生产环境直接用 yum/apt 就够了省心。3.3 防火墙放行 VRRP这一步必须做对VRRP 报文协议号是 112用 iptables 放行时不能写 TCP 端口很多人在这里翻车。# Ubuntu / CentOS 通用iptables iptables -I INPUT -p 112 -d 224.0.0.18 -j ACCEPT # CentOS 7 如果用 firewalld firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p 112 -d 224.0.0.18 -j ACCEPT firewall-cmd --runtime-to-permanent如果两台机器之间只有单播模式要把目标地址换成对端 IPiptables -I INPUT -p 112 -s 192.168.1.11 -j ACCEPT。另外记得放行 SSH 和业务端口别把正常的服务访问也拦了。修改防火墙后先用keepalived -t做一次配置语法检查再启服务。4. 配置文件逐段拆解4.1 主节点完整配置Keepalived 的配置文件在/etc/keepalived/keepalived.conf核心就是global_defs、vrrp_script和vrrp_instance三段。先看主节点 A 的完整配置global_defs { router_id LVS_DEVEL_A enable_script_include } vrrp_script check_nginx { script /etc/keepalived/scripts/check_nginx.sh interval 2 weight -20 rise 2 fall 2 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 150 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.1.100/24 dev eth0 label eth0:1 } track_script { check_nginx } notify_master /etc/keepalived/scripts/notify.sh MASTER notify_backup /etc/keepalived/scripts/notify.sh BACKUP notify_fault /etc/keepalived/scripts/notify.sh FAULT }逐行说参数router_id只是标识符两台机器要不同vrrp_script定义了一个健康检查脚本间隔 2 秒跑一次连续成功 2 次rise 2才认为恢复连续失败 2 次fall 2才判业务异常state MASTER表示本机初始角色实际“谁说了算”最终还是看priorityvirtual_router_id是虚拟路由 ID两台机器必须一致范围 1 到 255advert_int是通告间隔默认 1 秒authentication里的auth_pass两台机器也必须一致virtual_ipaddress就是要绑定的 VIP我顺手加了 eth0:1 的标签方便用ip addr区分。4.2 备节点配置差异机器 B 的配置文件基本照抄 A差异只有三处router_id改成 LVS_DEVEL_Bstate改成 BACKUPpriority改成 100。优先级低所以平时他不会抢 VIP。注意virtual_router_id、auth_pass、interface必须和主节点完全一致只要有一个人不一致两台机器就形同陌路各绑各的 VIP这就是脑裂的常见起因。再说一个很多人忽略的细节两个节点都建议加上nopreempt并且把state都写成 BACKUP。nopreempt的意思是非抢占Master 故障恢复后不会立刻把 VIP 抢回来业务不会因为主备切换折腾两遍。如果不加这个参数Master 一恢复就抢回 VIP每次都发生一次闪断生产环境不推荐这样。具体的对照效果我在第五节测试部分展开。4.3 健康检查脚本与 weight 优先级计算vrrp_script是一个独立的脚本脚本退出码为 0 表示健康非 0 表示故障。Nginx 场景我推荐双保险既检查进程又检查 HTTP 响应#!/bin/bash # /etc/keepalived/scripts/check_nginx.sh if ! systemctl is-active --quiet nginx; then exit 1 fi code$(curl -s -o /dev/null -w %{http_code} --connect-timeout 3 http://127.0.0.1/healthz) if [ $code ! 200 ]; then exit 1 fi exit 0MySQL 场景可以换成mysqladmin pingRedis 场景可以用redis-cli ping套路都一样。这里有个关键概念weight。脚本失败时本机 VRRP 优先级会减掉weight的绝对值。我的配置里 Master 优先级 150脚本失败后变成 130Backup 优先级 100依然比 130 低所以 Master 的 Nginx 挂了VIP 不会漂移这就错了。解决办法有两个要么把 Backup 的优先级抬到 120 以上要么把 Master 的weight改成 -60失败后 150 - 60 90低于 Backup 的 100Backup 才会接管。我只做了一件事把主节点weight -60改成和priority联动。实操建议写成weight -60配合priority 150这样脚本失败阈值清晰Backup 兜底逻辑明确。weight的范围是 -254 到 254别超出。4.4 notify 脚本切换事件要让监控系统知道故障切换发生时不能只靠人工看要把状态变化推给监控系统。notify脚本会在状态切换时被调用第一个参数就是当前状态MASTER、BACKUP 或 FAULT。我一般写一个通用脚本#!/bin/bash # /etc/keepalived/scripts/notify.sh TYPE$1 now$(date %F %T) echo $now [notify] state$TYPE host$(hostname) /var/log/keepalived-notify.log # 这里是可选动作调用内部监控系统的 webhook发送告警 # curl -s -X POST -H Content-Type: application/json \ # -d {\title\:\keepalived 切换\,\state\:\$TYPE\,\host\:\$(hostname)\} \ # http://monitor.example.com/webhook生产环境里每次切换都要有记录、有告警。切换本身不可怕可怕的是切换了你不知道。5. 启动验证与故障切换实测5.1 语法检查与启动服务配置文件写完后先做语法检查keepalived -t -f /etc/keepalived/keepalived.conf看到输出包含Configuration file ... syntax ok就说明配置没问题。然后启动服务并设置开机自启systemctl enable --now keepalived systemctl status keepalived启动后立刻用ip addr show dev eth0看 VIP 在哪台机器上预期是 MasterA 机器绑定 192.168.1.100。提示改配置后不要直接systemctl restart keepalived生产环境这个动作会造成 VIP 短暂丢失。标准操作是先keepalived -t语法检查再systemctl reload keepalived。如果一定要重启建议在服务低峰期操作。5.2 正常状态下的验证手段服务稳定后从 B 机器看日志正常情况每分钟都有一条 VRRP 报文接收记录journalctl -u keepalived -f再看 A 机器的日志会周期性地输出Sending gratuitous ARP on eth0 for 192.168.1.100之类的内容说明 Master 在正常通告和刷新 ARP。还可以抓包确认 VRRP 报文tcpdump -i eth0 vrrp -nn能看到 A 机器周期性地往 224.0.0.18 发包源 IP 是 192.168.1.10。这一步能同时确认防火墙放行生效了。5.3 模拟故障切换停服务、停进程、拔网线我在实验室里把三种故障都模拟了一遍记录如下。第一种停掉 Master 上的 Nginx。A 机器上执行systemctl stop nginx健康检查脚本 2 秒后探测到失败Master 优先级降到 90B 机器收到低优先级通告后立即升级。整个过程从停 Nginx 到 VIP 漂移实测 3 到 4 秒。期间从客户端持续 ping 192.168.1.100会看到丢 3 到 4 个包这是正常的切换不是零丢包。第二种直接停掉 Master 上的 Keepalived 进程。更暴力B 机器在连续收不到 VRRP 通告约 3 秒后主动接管VIP 漂移完成。这个场景模拟的是整机宕机。第三种拔掉 Master 的网线。效果和第二种类似因为网线一断VRRP 报文就发不出去了Backup 同样在约 3 秒后接管。我建议这套测试每个月做一次尤其是业务上线后拿真实流量试试切换是否符合预期。5.4 抢占与非抢占模式对比实测我用两组配置做了对比结论很明显。默认抢占模式下Master 恢复后 1 秒内就把 VIP 抢回来客户端看到的是 VIP 连续切换两遍业务抖动被拉长。开启nopreempt后Master 恢复成 BACKUP 角色VIP 继续留在 Backup 上系统更稳定。代价是优先级失去意义主备关系长期取决于“谁能一直活着”对能力不对称的机器要慎重。非抢占模式要求两台机器的state都写成 BACKUP不然会出现双 Master 竞争。6. 常见问题排查实录6.1 虚拟 IP 起不来或飘错节点VIP 起不来的原因通常有三个。第一网卡名写错了interface配置里如果写了不存在的网卡服务能起来但不会绑定 VIP用ip link show查看真实网卡名再核对第二VIP 和某台机器的真实 IP 冲突或者已经被别的服务占用用ip addr看看第三配置语法有错误但没检查出来keepalived -t能拦住大部分。VIP 起在错误的节点上优先检查priority和virtual_router_id。两台机器的virtual_router_id必须一致不一致时两台机器各建各的虚拟路由器谁也管不了谁。其次确认备节点的interface和 VIP 所属网段匹配如果备节点接口在别的网段VIP 漂过去也没法对外提供服务。6.2 两台机器同时持有 VIP双 Master 脑裂出现双 Master 是最棘手的情况症状是ip addr两台机器都能看到 VIP业务访问时通时不通。排查步骤很固定。第一步看日志。两台机器同时journalctl -u keepalived如果双方都输出Entering MASTER STATE说明 VRRP 报文根本没互通。第二步检查防火墙重点就是前面说的协议 112 是否放行组播地址 224.0.0.18 是否被拦。第三步用tcpdump -i eth0 vrrp抓包任何一方都收不到对端报文就是网络问题。第四步检查交换机端口是否开启了组播过滤、IGMP Snooping 等策略有些交换机默认丢弃组播这种情况必须改用单播模式。单播模式配置很简单在vrrp_instance里加两行unicast_src_ip 192.168.1.10 unicast_peer { 192.168.1.11 }备节点反过来写。云环境、跨交换机场景我强烈建议直接用单播少跟组播较劲。6.3 健康检查脚本导致的频繁切换有一种坑很隐蔽脚本本身写得不严谨导致 Keepalived 每几分钟就切一次。最常见的问题是脚本里用了 curl 而 curl 依赖的 DNS 解析超时或者脚本没设置超时时间进程卡住Keepalived 判定失败。解决方法是所有外部命令都加上超时参数脚本内部最后要明确exit 0或exit 1不要依赖默认返回码。另外interval不建议小于 1 秒脚本 0.5 秒内跑不完的话探测周期太短只会放大噪声。我还遇到过脚本权限的问题Keepalived 以 root 运行一般还好但如果你用了普通用户执行去限制脚本没执行权限所有节点都会进入 FAULT 状态。记得chmod x并先用 root 手跑一遍脚本确认返回码正确。6.4 常见问题速查表现象可能原因排查命令 / 处理方式两台机器都不绑定 VIPvrrp_instance未启用 / 无有效通告看日志确认priority是否过低且 script 失败Master 正常但 VIP 在 Backup优先级配置颠倒对比两台prioritystate与nopreempt切换时丢包超过 10 秒交换机 ARP 表刷新慢增加garp_repcount检查端口 STPVRRP 日志没有输出防火墙拦截协议 112tcpdump -i eth0 vrrp/ 放行防火墙健康检查失败但服务正常脚本探测依赖未配置手动执行脚本观测返回码和耗时服务恢复了 VIP 不回来开了nopreempt这是预期行为不是故障syslog 大量BAD VRRP认证密码不一致 / VRID 不一致核对auth_pass和virtual_router_id7. 生产环境经验与个人体会7.1 我在真实环境踩过的几个坑第一次搭 Keepalived 时我把两台机器的auth_pass写成了带特殊字符的 16 位密码结果两边日志狂刷认证失败。VRRP 的 PASS 认证本身不防伪造它只是防误加入密码别搞太复杂8 位以内数字字母组合足够两台机器一致就行。第二次遇到的问题是 VIP 在备机上但备机的 Nginx 还没起来客户端全部报错。这就是健康检查脚本只查了 Keepalived 自身没有查业务的后果。后来我把track_script绑定到 Nginx 的健康检查并且把脚本放到两台机器上都执行一遍确认两边 Nginx 都正常才让 VIP 漂移。第三个坑是云环境。部分公有云不支持组播我把配置改成单播后一切正常这个经验我建议所有云上部署的同学直接照做。还要注意云平台的“HAVIP”或“虚拟 IP”产品可能自带一些网络策略Keepalived 的 VIP 如果和云平台自己的 IP 冲突只能使用平台提供的功能。7.2 Keepalived 和监控告警体系的配合之前说过 notify 脚本要接入告警这里再深一层。切换告警不仅要告诉值班人员“发生了切换”还要能区分是主动维护还是故障切换。我在 notify 脚本里加了一个判断如果脚本是在手工维护期间触发会带上maintenance标记不进入告警如果是故障触发才推送。这个逻辑用很简单的方式就能实现在 /tmp 放一个维护标记文件notify 脚本读一下就行。另外监控系统不要只盯 VIP要两个真实 IP 一起盯。如果只监控 VIPMaster 挂了之后 VIP 漂到备份服务正常监控根本不会发现这次故障也就不会触发根因分析。我把两台机器的 Keepalived 进程状态、VIP 归属、业务探活都放进了一个监控面板每次切换都留痕。7.3 扩展方向VRRPv3、多实例与自动化到这里 Keepalived 虚拟路由的配置已经可以支撑一个典型的双机热备项目了但它能做的远不止这些。VRRPv3 支持 IPv6 和更大的通告间隔粒度双栈环境下值得用一个 Keepalived 进程里可以定义多个vrrp_instance比如同时管理业务 VIP 和管理网 VIP互不干扰还可以和 LVS 结合Keepalived 本来就是为 LVS 设计的VIP 后面的真实服务器列表用virtual_server配置实现四层负载均衡。自动化方面我习惯用 Ansible 管理两台机器的配置文件和健康检查脚本把角色、优先级、VIP 这些变量抽出来新环境上线就是跑一遍 playbook 的事。记住每次变更要过一遍keepalived -t并且做一次切换演练这台就稳了。