
Keepalived的主备切换和虚拟IP漂移这话题我前后折腾过不少回。以前搭LVSKeepalived做负载均衡后来给MySQL、Nginx做高可用也用它。说句实话配置本身不难难的是理解它为什么会这么工作。最近又帮朋友排查一次双主脑裂问题顺手把所有配置重头过了一遍发现很多细节之前没吃透。这篇就当是给同样搞运维、搞架构的朋友一份复习笔记把Keepalived虚拟路由配置从原理到实战、从脚本到抓包全部捋一遍避免大家少走我之前踩过的坑。先说清楚Keepalived到底解决什么问题。它最核心的用途是给一组服务器提供一个虚拟IPVIP这组服务器对外只有一个地址当其中一台挂了VIP自动漂移到另一台业务基本无感知。底层依赖的是VRRP协议也就是虚拟路由冗余协议。凡是想给业务加一层高可用能力、又不希望引入太复杂方案的团队基本都会先用它。它也适合个人学习毕竟安装配置轻量、验证方式直观一台虚拟机都可以玩起来。1. 重学之前先想清楚Keepalived到底在解决什么问题1.1 从一次真实故障说起前阵子朋友公司的内部系统突然不可访问后台一看跑应用的那台服务器因为内存故障重启了。按理说系统做了负载均衡前面挂了两台Nginx结果访问还是全断。排查后发现问题很简单Nginx确实部署了两台但客户端访问的地址写死了第一台的IP第一台一挂流量根本没地方去。这就是典型的有冗余硬件没有冗余入口场景。Keepalived要解决的痛点就在这里即使后端有两台甚至更多服务节点对外也只需要暴露一个虚拟IP这个VIP由多台机器共同持有但同一时刻只有一台真正生效。持有VIP的节点挂了另外一台自动顶上整个切换过程对客户端完全透明。1.2 VRRP协议VIP是怎么漂起来的虚拟IP能漂移靠的是VRRP协议。协议原理可以简化成一个选举机制一组路由器或服务器共同组成一个虚拟路由器对外表现为一个IP地址。这组成员里会选出一个Master负责响应针对VIP的请求其余都是Backup时刻监听Master的状态。Master会周期性发送VRRP通告报文通告里携带自己的优先级。一旦Backup在设定时间内收不到通告就会认为Master挂了然后根据优先级重新选举优先级最高的变成新MasterVIP随即绑定到新节点上。这里有个关键点这个组里的每个节点都配置了VIP但只有Master节点的VIP是生效的Backup节点上VIP只是配置存在、并没有绑定到网卡。说句大白话VIP不是谁的地址在漂移而是谁有资格对外宣称自己持有这个地址在切换。理解了这层后面的配置细节就很好想了。1.3 Keepalived与LVS的关系很多人看到Keepalived的名字以为它只能配合LVS使用。其实它最初确实是为LVS设计的负责给负载均衡器做高可用后来因为健康检查模块做得很成熟慢慢也成了通用的高可用方案。所以现在经常能看到KeepalivedNginx、KeepalivedMySQL这种组合。这里要补一个基本认识Keepalived内部主要由三个模块构成core核心控制、check健康检查、vrrpVRRP协议栈。check模块负责检查后端服务的存活状态vrrp模块负责虚拟IP的漂移核心控制模块把两者串起来。配置Keepalived时写的那些配置块本质上就是在配置这三个模块的行为。2. 配置文件核心拆解每一段配置背后的逻辑2.1 global_defs全局定义里容易忽略的坑先看最常见的全局配置段global_defs { router_id LVS_DEVEL enable_script_security }router_id只是一个标识不一定非得叫这个名字但集群内建议保持唯一方便排查日志时区分节点。很多人忽略的是enable_script_security这个参数它的作用是对健康检查脚本做更多安全检查限制脚本的运行环境。如果不需要执行外部脚本可以不开但如果用到了脚本建议开启。我见过不开这个选项导致某些环境下脚本执行异常的案例虽然不常见但排查起来很费劲。全局配置里还有一个用得比较多的参数是通知邮件配置类似notification_email和smtp_server。坦白讲生产环境我基本不配置这两个字段因为邮件通知延迟大、容易丢远不如把状态信息打到日志里、由监控系统收集。如果确实要用邮件提醒建议把SMTP服务器指向内部邮件网关别直接用公网SMTP否则认证和延迟都够你喝一壶的。2.2 vrrp_instanceVIP是怎么绑上去的这是Keepalived配置里的核心先看基础模板vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1234 } virtual_ipaddress { 192.168.1.100/24 dev ens33 label ens33:1 } }逐行拆开看state MASTER表示本节点初始状态是Master。这里有一个常见的误解以为state写什么就是什么。实际上state只影响启动时的初始状态节点起来后会通过VRRP报文协商最终状态由优先级决定。也就是说即使某个节点state写的是MASTER如果另一个节点priority更高它也会成为Backup。interface ens33指定VRRP报文从哪个网卡发出去。这个必须和VIP绑定的网卡一致如果机器是多网卡写错会影响通信。virtual_router_id 51虚拟路由ID范围是0到255。同一个VRRP组内的所有节点这个值必须一致。另外注意同一台机器上如果有多个vrrp_instance不同实例的virtual_router_id不能重复否则会冲突。priority 100优先级范围0到255越大越容易成为Master。主备节点之间优先级设置要拉开差距比如Master设为100Backup设为90避免因轻微抖动导致频繁切换。advert_int 1通告间隔单位是秒。Master每隔1秒发送一次VRRP广播。Backup端有一个Master Down定时器默认3倍的通告间隔也就是3秒内收不到通告就切换。生产环境建议不要把advert_int调得过小比如0.2秒虽然能加快切换速度但会增大网络报文量和CPU占用。virtual_ipaddress块是真正配置VIP的地方可以跟多个VIP。注意dev ens33指定VIP绑定的网卡label ens33:1是配置一个虚拟子接口名。不写label不影响功能但排查IP时不容易区分所以建议加上。2.3 健康检查脚本让Keepalived真正感知业务故障只做VRRP切换还不够因为Keepalived默认只检测节点本身是不是活着它不会自动感知Nginx挂了或者MySQL进程僵死。这时候就需要给vrrp_instance配置跟踪脚本vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 weight -20 fall 2 rise 2 } vrrp_instance VI_1 { # ... 其他配置 track_script { check_nginx } }这里的关键是weight参数。它表示脚本失败时对优先级的影响。刚才配置的Master优先级是100脚本失败后权重减20优先级变成80低于Backup的90于是自动让位给Backup。这里有一个很关键的细节如果Backup节点也配置了同样的脚本它的优先级也会被扣所以主备切换后的新Master必须保证它的脚本是成功的才能稳定持有VIP。脚本本身要写成这样#!/bin/bash if pgrep -x nginx /dev/null; then exit 0 else exit 1 fi有两点要注意脚本必须有执行权限chmod x别忘了脚本的退出码必须明确是0或1Keepalived只根据退出码判断成功失败。2.4 状态通知脚本故障切换的精确记录这个功能我强烈推荐配置特别是维护的机器数量多了以后。它能在节点状态变化时触发外部脚本把关键信息记录下来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 }我一般会在notify脚本里把当前时间、主机名、切换后的状态写到一个自定义日志文件同时把VIP信息也带上。这样事后追溯故障时间线非常方便。最理想的状态是每次切换都有据可查而不是等用户反馈刚才断了几分钟才发现问题。3. 实操双机热备Nginx的完整配置过程3.1 环境准备与安装先明确实验环境两台CentOS 7虚拟机网卡都是ens33IP分别是192.168.1.10和192.168.1.11VIP规划为192.168.1.100两台机器上都装了Nginx。安装Keepalived直接用包管理器yum install -y keepalived nginx systemctl enable keepalived nginx安装完成后先确认一下版本keepalived --version如果版本太老早期1.2.x有些参数语法不同现在主流发行版默认都是1.3.x或1.4.x基本上不用太担心。装好之后先启动一下Nginx确保业务端口是通的systemctl start nginx curl -I http://127.0.0.13.2 MASTER节点配置在192.168.1.10上编辑/etc/keepalived/keepalived.confglobal_defs { router_id LB_MASTER enable_script_security } vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 weight -20 fall 2 rise 2 } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1234 } virtual_ipaddress { 192.168.1.100/24 dev ens33 label ens33: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 }这里解释一下为什么auth_pass只写了1234。VRRP认证本身是明文传输这个功能更多是防止误配置导致的路由器ID冲突而不是真正的安全加密。所以设置一个能区分的密码就够了别投入精力去搞复杂的密码策略。3.3 BACKUP节点配置在192.168.1.11上同样编辑配置文件差异点有三个router_id、state、priorityglobal_defs { router_id LB_BACKUP enable_script_security } vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 weight -20 fall 2 rise 2 } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 51 priority 90 advert_int 1 authentication { auth_type PASS auth_pass 1234 } virtual_ipaddress { 192.168.1.100/24 dev ens33 label ens33: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 启动验证IP漂移与抓包确认两台节点都准备好后先启动Keepalived然后到MASTER节点查看IP情况systemctl start keepalived ip addr show ens33正常情况下MASTER节点的ens33上会多出一个ens33:1接口地址是192.168.1.100。BACKUP节点上不会出现这个地址。用外部机器或另一台设备去ping这个VIP能通就说明入口正常。进一步验证VRRP通信在MASTER节点上抓包tcpdump -i ens33 vrrp -n能看到源IP是本机地址、目标IP是224.0.0.18VRRP组播地址的报文内容里包含virtual_router_id和priority。这组报文正常情况下每1秒出现一次。如果抓不到包优先检查防火墙是否放行了VRRP协议。很多系统默认会放行VRRP但有些环境需要手动添加规则后面第4节会展开讲。4. 切换测试与故障演练把坑全部踩一遍4.1 手动切换测试配置完成之后必须做一次主动切换测试验证方案真实有效。方法是直接停止MASTER节点的Keepalived服务systemctl stop keepalived然后在BACKUP节点上执行ip addr show ens33如果配置正确几秒内VIP就会出现在BACKUP上。再用外部机器ping一下VIP应该是通的。这里有一个容易踩的坑如果你用的是虚拟机而且之前用ssh连接的是VIP切换后旧连接会断掉这是正常现象客户端重新连接即可。把MASTER节点的Keepalived重新启动等到VIP自动回到MASTER再确认一次。这样双向切换都验证过才算完成基本演练。4.2 脑裂的产生原因与对策脑裂是双机热备方案里最需要警惕的问题。简单说两台节点都认为自己是Master同时持有VIP导致外部请求被分散到两个节点甚至出现IP冲突。造成脑裂最常见的原因是VRRP组播报文被阻断了比如防火墙把224.0.0.18的流量挡了或者两台机器之间网络隔离。Backup收不到Master的通告等到超时时间一过就自动切换成Master而此时原来的Master其实还活着。排查脑裂有两个高效方法。第一在两端分别执行ip addr show如果两个节点都有VIP基本可以断定脑裂。第二同时查看两端的系统日志tail -f /var/log/messages | grep Keepalived或者journalctl -u keepalived -f如果看到两端都出现Entering MASTER STATE那就是脑裂了。防止脑裂最直接的办法是确保VRRP组播报文能正常互通。如果网络环境不允许组播可以把组播改成单播配置也很简单vrrp_instance VI_1 { unicast_src_ip 192.168.1.10 unicast_peer { 192.168.1.11 } }在MASTER节点上unicast_src_ip写本机IPunicast_peer写对端IPBACKUP节点则反过来。配置完成后VRRP通告不再走组播而是定向发送到对端单播地址能有效规避组播被阻断的问题。另一个控制脑裂风险的手段是设置nopreempt模式。正常情况下如果Backup因为收不到Master通告而变成Master等原Master恢复后会因为优先级更高而重新抢占导致VIP再次漂移中间可能产生两次切换。如果希望旧Master恢复后不自动抢占保持当前Master不变可以在配置里加vrrp_instance VI_1 { nopreempt }注意nopreempt模式下各节点的初始state必须都设为BACKUP否则不生效。这个模式也适合对切换敏感、希望尽量减少抖动次数的场景。4.3 常见问题排查清单我习惯把平时遇到的问题整理成一张速查表排查时按表逐步核对效率高很多现象可能原因排查与解决方法VIP没有出现在任何节点配置文件语法错误keepalived -t -f /etc/keepalived/keepalived.conf检查配置两台节点互相抢占优先级差值太小拉大优先级差距建议20以上Backup收不到通告防火墙拦截放行VRRP或改用单播模式切换正常但业务不通脚本没生效手动运行脚本检查退出码和权重配置日志不断报错dropping packetvirtual_router_id或认证密码不一致核对两端router_id、认证信息启动失败Cannot open file配置文件路径写错用绝对路径重试检查文件是否存在这里特别提醒一下Keepalived配置改完以后不要直接重启服务就完事。先用配置检查命令验证语法再实际操作。另外查看状态信息可以使用systemctl status keepalived -l日志里如果出现VRRP_Script(check_nginx) succeeded或failed这样的字眼说明脚本已经被正常调度结合它就能判断优先级变化的具体原因。4.4 抓包验证中的细节最后补充一个抓包排查的技巧。VRRP报文默认发往224.0.0.18这个组播地址端口号是112。抓包时如果只抓了eth0网卡可能看不到报文需要指定VRRP协议tcpdump -i ens33 proto 112 -n -vv或者直接抓组播地址也行。抓包后重点看报文的priority字段和virtual_router_id字段两端的virtual_router_id必须一致否则即使网络通也会因为ID不匹配导致报文被丢弃。当年我就犯过这个错两台机器明明都是同样配置却一直在通告和丢弃之间循环日志里全是dropping VRRP packet排查到最后才发现一台机器上的virtual_router_id写错了。再说一个小细节如果你用的是VMware虚拟机来做实验记得把网卡模式设置为桥接或NAT中能互通的方式而且最好关掉同一虚拟化平台自身的MAC地址漂移防护功能否则切换时VIP对应的MAC地址变化可能被虚拟交换机过滤掉导致外部访问不通。这个问题在物理机上反而不容易出现虚拟化环境下却非常隐蔽我第一次在VMware里测试时就卡在这里半小时。5. 脚本自检与调优让切换更平稳5.1 健康检查脚本的写法要点前面提到check_nginx.sh只做了进程检查实际生产中可以做得更细致。比如Nginx进程活着但端口没有监听进程检查就判断不出来。更靠谱的脚本会同时检查端口#!/bin/bash if pgrep -x nginx /dev/null curl -sf -o /dev/null http://127.0.0.1/; then exit 0 else exit 1 fi这里的curl -sf会静默访问本地Nginx首页如果HTTP返回异常或连接失败curl退出码非0脚本返回1Keepalived就会认为节点不健康。需要注意的是脚本超时时间要控制好。Keepalived执行脚本时默认没有严格超时限制脚本里最好自己加上超时逻辑避免Nginx卡死时curl长时间挂起。可以给curl加--max-time 3参数3秒没响应就直接判定失败。我个人的经验是脚本越简单越稳定。复杂的脚本逻辑比如还要检测数据库连接、检查磁盘空间会让故障切换的判定变得不可控建议把核心业务相关的检查拆分成多个独立的vrrp_script块而不是一个大而全的脚本。不同检查项设置不同的weight一旦重要的检查失败优先级下降幅度更大这样更精细地控制切换行为。5.2 日志切分与轮转Keepalived默认会把日志写到/var/log/messages里在日志量大的时候信息会很散。实际上可以通过修改启动参数把日志单独输出到一个文件。多数发行版使用systemd管理服务方法是编辑service文件加参数# /etc/systemd/system/keepalived.service.d/override.conf [Service] ExecStart ExecStart/usr/sbin/keepalived --log-file/var/log/keepalived.log --log-detail --log-facility7然后systemctl daemon-reload systemctl restart keepalived这样Keepalived的运行日志会集中写到/var/log/keepalived.log排查时就一个文件不用再大海捞针翻messages了。文件轮转可以交给logrotate配置一个简单的keepalived轮转规则即可。5.3 不重启服务的情况下重载配置很多人改完配置习惯直接systemctl restart keepalived这在生产环境里其实是有风险的因为重启服务会导致VIP短暂释放然后重新绑定中间会有几十毫秒到一秒的空窗期。更好的做法是用reload方式加载新配置systemctl reload keepalivedKeepalived会平滑地重新读取配置尽量保持VIP不中断。当然涉及比较底层的参数变更比如VRRP协议本身的变化reload可能不生效这时才需要考虑重启。保险起见维护窗口内先做一次切换演练确认方案没问题再执行变更。6. 集群环境下的扩展思路双机热备是Keepalived最常见的用法但如果你有更多节点比如三台机器做高可用配置思路也基本一致。虚拟路由器组里可以配置多个节点每个节点拥有不同的优先级比如A节点100、B节点90、C节点80。正常情况下A是MasterA挂掉后B接替B也挂掉后C接替。这里要注意实际工作环境中我一般建议不要把优先级跨度拉得太大要给紧急修复预留空间否则A恢复后瞬间抢回流量可能出现抖动。还有一种用法是同时配置多个vrrp_instance为一组服务提供多个VIP。比如三台机器第一个实例让A是Master第二个实例让B是Master这样既能实现高可用又能把流量分摊到两台机器上避免备机常年闲置。这是互为主备的标准玩法在Keepalived里就是多定义几个instance每个instance设置不同的router_id和priority组合比如实例VI_1A节点priority 100B节点priority 90VIP1实例VI_2A节点priority 90B节点priority 100VIP2这样两个VIP分别由不同节点承载两台机器都在干活资源利用率能提升不少。不过前提是业务本身可以水平拆分否则两个VIP都指向同一套业务流量分配还是得靠上层负载设备再决策。把Keepalived玩熟了之后你会发现这套高可用方案的覆盖面其实很广。从最顶层的Nginx负载均衡到中间层的MySQL主主复制再到底层的Redis哨兵前置代理都可以用Keepalived做入口漂移。它不解决数据同步问题只负责“把入口切到健康节点”这一件事所以和各类数据同步组件搭配起来特别顺手。根据我个人经验用Keepalived一定要养成先抓包、再看日志、最后查配置的排查习惯。很多诡异问题都是三层之间互相干扰导致的先从网络层确认VRRP报文通了再往上分析问题会快得多。还有一个小技巧维护时给每个节点配置独立的终端会话一边ping VIP一边操作切换这样切换过程是秒级还是毫秒级都能直观感受得到不用完全依赖日志去脑补。