keepalived 1.2.13 源码编译与 VRRP 高可用配置
简介Keepalived 1.2.13 源码包面向运维工程师、系统管理员及高可用架构学习者围绕 VRRP 与 LVS 实现服务故障切换和负载均衡适合搭建双机热备实验、研读健康检查逻辑及排查主备切换问题。压缩包共 188 个文件约 334KB以 62 个 .h 头文件和 61 个 .c 源文件为主体便于追踪 VRRP 状态机、检查器与配置解析的实现同时包含 .in 配置模板、init 启动脚本、pem 证书样例以及构建脚本与说明文档可辅助理解编译、部署及样例配置结构。已有 369 人学习下载说明其对入门与进阶读者均具参考价值。通过源码与配套样例读者可梳理 Keepalived 的模块划分、健康检查类型、虚拟服务器组配置方式并积累故障排查与高可用方案设计思路。1. 一个 2013 年的 tar.gz 值不值得拆keepalived-1.2.13 的定位与适用面机房搬迁那次一台跑着老业务的机器必须在不停机的前提下换到新主机上系统源里能装的只有 1.2 系列的包最后从源码目录里翻出来的就是 keepalived-1.2.13.tar.gz。这个包解决的问题很具体用 VRRP 协议在几台机器之间选出一台持有虚拟 IP主节点挂了备节点在几秒内接管顺带还能给 IPVS 后面的真实服务器做端口级健康检查服务不通就把它从转发列表里摘掉。它不是新东西但在存量系统、离线环境、需要交叉编译到特定内核的场景里这个版本仍然是很多人的基线。适合三类人要维护老配置却读不懂keepalived.conf里那些关键字的人、要在没有外网源的环境里从源码编出可执行文件的人、以及想知道主备切换到底为什么抖的人。2. 从 tar.gz 到可执行文件configure 开关与目录落点源码包拿到手的第一件事不是./configure而是先确认这个版本的开关清单。keepalived 的构建体系是 autotools不同小版本之间开关增减很随意网上抄来的编译命令经常在这台机器上直接报“unrecognized option”。我的习惯是先解包、进目录、把 help 打出来过滤一遍再决定参数。2.1 摸清依赖边界再动手tar -xzf keepalived-1.2.13.tar.gz cd keepalived-1.2.13 # 先看这个版本到底认哪些开关别照抄博客 ./configure --help | grep -Ei sysconf|lvs|vrrp|snmp|libnl|kernel这条命令的输出决定了后面所有事。--disable-lvs、--disable-vrrp、--enable-snmp这几个开关在 1.2 系列里一直存在而 netlink 相关的开关在不同小版本里名字会变先grep一遍比事后对着报错猜要快得多。基础依赖只有三样gcc、make、和目标内核版本匹配的头文件。netlink 和 openssl 的头文件缺失时configure会在末尾打印checking for ... no然后直接退出把最后 20 行日志看完基本就知道缺什么。# 典型的最小构建三个参数各管一件事 ./configure \ --prefix/usr/local/keepalived \ --sysconf/etc \ --localstatedir/var三个参数的分工值得说清楚因为后面所有的路径问题都从这里来。--prefix决定二进制和文档装到哪主程序落在/usr/local/keepalived/sbin/keepalived。--sysconf/etc决定编译进程序的配置文件搜索路径最终是/etc/keepalived/keepalived.conf这个路径是硬编码进二进制的改了就得重编。--localstatedir/var决定 PID 文件位置落到/var/run/keepalived.pid写 systemd unit 时要用到它。如果这台机器只做纯 VRRP 的 VIP 漂移、不打算跑 IPVS加--disable-lvs能少编进去一堆东西也少一层内核模块依赖。反过来如果编译阶段卡在 netlink 相关头文件上先看--help里有没有--disable-libnl一类的开关有就关掉走内置实现没有就去装对应发行版的 libnl 开发包别硬改源码。make -j$(nproc) 21 | tee build.log make installtee这一步不是形式主义。老代码在新版 gcc 上编译报错往往埋在几千行输出中间而且如果这个版本的 Makefile 默认带了-Werror一行“警告”就能让整个构建中断最后一行通常是cc1: all warnings being treated as errors。真遇到了make CFLAGS-O2 -g -Wno-error覆盖一下就能过去不用去改 Makefile。2.2 装完先核对目录落点路径内容由谁决定/usr/local/keepalived/sbin/keepalived主程序--prefix/etc/keepalived/keepalived.conf主配置--sysconf/var/run/keepalived.pidPID 文件--localstatedir/usr/local/keepalived/etc/rc.d/init.d/keepalived自带的 init 脚本样例--prefix/usr/local/keepalived/etc/sysconfig/keepalived启动参数文件--prefix装完先跑keepalived -v确认版本串对得上再看/etc/keepalived/下有没有文件。有些版本会把样例装成keepalived.conf.sample有些直接装成keepalived.confls一眼就能分辨。配置文件不存在时程序不是报“找不到配置”而是按内置默认值把进程拉起来并保持空转这是新手最容易误判“装成功了”的地方。源码树里的keepalived/etc/keepalived/keepalived.conf就是官方样例包含 VRRP 和 IPVS 两大段完整示例值得留着当字典翻比在网上搜半截配置可靠。2.3 进程托管手写 systemd unit 的几个关键行1.2.13 不带 systemd unit也没有 sd_notify 支持所以只能用Typeforking配 PID 文件。[Unit] DescriptionKeepalived VRRP daemon Afternetwork-online.target Wantsnetwork-online.target [Service] Typeforking PIDFile/var/run/keepalived.pid EnvironmentFile-/etc/sysconfig/keepalived ExecStart/usr/local/keepalived/sbin/keepalived -D -f /etc/keepalived/keepalived.conf ExecReload/bin/kill -HUP $MAINPID KillSignalSIGTERM Restarton-failure RestartSec2 [Install] WantedBymulti-user.target-D是详细日志调试期必开稳定后可以去掉。EnvironmentFile前面的减号表示文件不存在也不报错避免老机器上没有/etc/sysconfig/keepalived时 unit 直接启动失败。KillSignalSIGTERM是有意写死的收到 TERM 时 keepalived 会走正常的“下线”流程主动发一个优先级为 0 的通告让备机立刻接管并且把 VIP 从网卡上摘干净如果被 KILL 掉VIP 会残留备机虽然能接管但主节点恢复后容易出现地址冲突。提示systemctl reload触发的是 SIGHUP进程会重读配置并重算 VRRP 状态线上执行时主备可能瞬断一次变更窗口要挑好。3. VRRP 实例配置六个必填字段和三个影响切换行为的参数VRRP 的核心就一句话多台机器共用一个虚拟路由器 ID按优先级选出 MasterMaster 定期发通告备机一段时间收不到通告就认为对方死了自己顶上。配置文件的难点不在语法而在每个字段到底影响哪一步。3.1 一个能跑起来的 vrrp_instanceglobal_defs { router_id node_a # 只用于日志识别和告警不参与选举 } vrrp_instance VI_1 { state MASTER # 初始状态最终身份由优先级和抢占决定 interface eth0 # 发通告和挂 VIP 的网卡 virtual_router_id 51 # 同一广播域内必须唯一取值 1-255 priority 150 # 数值大者胜 advert_int 1 # 通告间隔单位秒两端必须一致 authentication { auth_type PASS auth_pass kp2024ab # PASS 模式最多 8 位超了会被截断 } virtual_ipaddress { 192.0.2.100/24 dev eth0 } }备节点的配置除三处外完全相同router_id改成node_b、state改成BACKUP、priority改成 100。virtual_router_id必须一字不差advert_int必须一致认证密码必须一致这三项里任何一项对不上两边都会认为对方不存在各自举着 VIP 当 Master。六个必填字段里最容易被忽视的是interface。它同时决定了两件事VRRP 通告从哪张网卡发出去、VIP 挂到哪张网卡上。双网卡的机器上如果通告走eth0而业务在eth1就会出现“切换正常但业务不通”的现象排查时先ip -4 addr show看 VIP 落在哪张卡上。authentication里的密码长度是个硬限制PASS 模式只取前 8 个字符配了 12 位而两端只有一端“看起来”改过实际上截断后仍然相同反而掩盖了配置不一致的问题。3.2 认证、通告间隔与抢占三个决定切换行为的参数advert_int决定故障感知速度。默认 1 秒备机大约 3 个通告周期收不到就切换也就是 3 秒左右。往小调能更快但网络抖一下就误切实际接管时间还不如稳定在 3 秒。priority决定谁当选。主备之间建议至少差 20留出脚本权重的调整空间。差值只有 1 的时候一次脚本权重变化就能让两边来回抢日志里会看到状态周期性翻转很难查。nopreempt决定原主恢复后是否抢回。默认行为是抢占主节点恢复、优先级重新变高就把 VIP 抢回来。在业务进程启动慢的机器上这个行为很致命——主节点刚恢复keepalived 起来了但后端服务还没 readyVIP 已经抢回来业务直接断一下。常见做法是备节点上加nopreempt并且state必须写BACKUP写 MASTER 时nopreempt不生效让 VIP 留在当前持有者手里直到它真的出问题。注意nopreempt只影响“恢复后是否抢回”不影响故障时的接管别把它当成防止脑裂的手段。如果环境里交换机默认关闭组播、或者跨网段部署VRRP 通告根本发不出去。较新的 1.2 小版本支持单播配置块里加unicast_peer { 192.0.2.11 }并把interface指向互通的那张卡即可。这个版本的配置解析器是逐行读的先写上去跑一次日志里没有unknown keyword就说明支持。3.3 三件套验证抓包、地址、日志配置写完不要急着当它生效三层验证依次做一遍五分钟能省掉后面两小时。# 第一层通告有没有真的发出去、发给了谁 tcpdump -i eth0 -nn -c 10 proto 112 # 第二层VIP 到底挂在哪张卡上 ip -4 addr show dev eth0 # 第三层状态机怎么走的 tail -f /var/log/messages | grep -i keepalived第一条命令的预期输出是每秒一条来自本机、目的地址224.0.0.18的包源地址是本机真实 IPTTL 255。看到 TTL 不是 255说明中间有设备在转发或者做了代理VRRP 的选举逻辑会被打乱。一条都抓不到问题在interface或组播跟优先级无关。第二条命令用来确认 VIP 的掩码和网卡。VIP 挂上了但掩码是/32同网段的其他机器就认为它不在本地子网报文会走网关绕一圈回不来。第三条命令的日志里Entering MASTER STATE和Entering BACKUP STATE是状态迁移的标志VRRP_Instance(VI_1) Received advert with higher priority表示收到了更高优先级的通告、主动让位。这几行是后面所有排障的起点。4. 健康检查vrrp_script 的权重算术与 real_server 的摘除逻辑VIP 漂移只解决“机器活着但 keepalived 挂了”的情况。业务进程卡死、端口不响应、后端服务已经挂了keepalived 默认是不知道的VIP 照样在主节点上挂着流量照样打进来。这一层要靠vrrp_script和real_server的健康检查补上。4.1 vrrp_script 的权重要算不能拍vrrp_script chk_gateway { script /etc/keepalived/chk_gateway.sh interval 2 # 每 2 秒执行一次 fall 3 # 连续失败 3 次才判定为失败 rise 2 # 连续成功 2 次才判定为恢复 weight -60 # 失败时优先级减 60 } vrrp_instance VI_1 { # ... 前面的字段省略 track_script { chk_gateway } }vrrp_script的语义是“算出一个有效优先级”而不是“直接触发切换”。有效优先级的算法是脚本判定为成功时有效优先级等于配置的priority判定为失败时等于priority weightweight 为负就是减。所以切的不是脚本是优先级。这就带来第一个坑weight 的绝对值必须大于主备的优先级差否则脚本失败也不会切。主 150、备 100、差 50weight 写-30失败后有效优先级 120 仍然高于备机的 100日志里能看到脚本失败但状态一动不动。写成-60失败后 90 低于 100才会真正切换。#!/bin/bash # /etc/keepalived/chk_gateway.sh # 退出码 0 表示健康非 0 表示异常keepalived 只看退出码 curl -s -o /dev/null -m 2 http://127.0.0.1:8080/healthz脚本本身有三条纪律。第一必须有可执行权限chmod 755否则 keepalived 调用时直接失败日志里是Cannot execute script。第二必须自己带超时curl -m 2或timeout 2脚本卡住会把检查线程挂死interval形同虚设。第三检查的必须是本机真实状态——查127.0.0.1或本地业务端口不要去 ping 网关外网抖动会导致全线切换。fall和rise是抗抖动用的。fall 3意味着连续三次失败配合interval 2最坏情况下要 6 秒才判定失败加上 VRRP 自身的 3 秒整体接管时间接近 10 秒。这个数字要在上线前算清楚并写进预案别等到故障时才发现比预期慢一倍。提示部分 1.2 小版本的vrrp_script不认timeout关键字写上去只会在日志里报unknown keyword然后跳过脚本的自我保护必须自己做。4.2 real_server 健康检查端口级摘除VIP 后面挂真实服务器时健康检查的粒度要更细谁挂了摘谁不动 VIP。virtual_server 192.0.2.100 80 { delay_loop 6 # 每 6 秒轮询一次所有 real_server lb_algo wrr # 加权轮询 lb_kind DR # 直接路由模式响应不回经过调度器 persistence_timeout 50 # 会话保持 50 秒 protocol TCP real_server 192.0.2.11 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 connect_port 80 } } real_server 192.0.2.12 80 { weight 1 HTTP_GET { url { path /healthz status_code 200 } connect_timeout 3 nb_get_retry 2 } } }TCP_CHECK只验证三次握手端口开着就算健康进程假死但监听还在的情况它看不出来。HTTP_GET会真的发一个请求并校验状态码代价是多一点开销但对“进程活着但不干活”这类故障有效。两种混用很常见无状态服务用TCP_CHECK有依赖的服务用HTTP_GET。delay_loop和nb_get_retry的组合决定了摘除延迟。6 秒轮询、3 次重试、每次间隔 3 秒最坏情况下要接近 15 秒才会把一台坏掉的机器摘掉。这个期间流量还在往它身上打所以后端服务的健康检查接口本身要足够轻别做数据库查询。DR 模式有一个前置条件容易漏真实服务器上要把 VIP 配到 loopback 并关掉 ARP 响应否则它自己会去应答 VIP 的 ARP 请求流量直接绕过调度器。# 在每台 real_server 上执行只加地址不发布 ARP ip addr add 192.0.2.100/32 dev lo sysctl -w net.ipv4.conf.all.arp_ignore1 sysctl -w net.ipv4.conf.all.arp_announce2 sysctl -w net.ipv4.conf.lo.arp_ignore1 sysctl -w net.ipv4.conf.lo.arp_announce2这四个 sysctl 要写进/etc/sysctl.conf重启后依然生效。只改all不改lo的话在某些内核上 loopback 的默认值会覆盖全局设置表现为“配置都写了但 ARP 还是应答”。4.3 notify 钩子把状态机变成可观测事件notify_*系列指令在状态迁移时执行外部脚本是排查切换时间线最有效的手段。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_stop /etc/keepalived/notify.sh stop }#!/bin/bash # /etc/keepalived/notify.sh STATE${1:-unknown} # 配置里写死的状态比依赖 keepalived 传参稳 INSTANCE${2:-VI_1} TS$(date %F %T) # 1) 落盘给监控采集器读 echo $STATE /var/run/keepalived.${INSTANCE}.state # 2) 写系统日志方便和 keepalived 自身日志对齐时间线 logger -t keepalived-notify $INSTANCE - $STATE at $TS # 3) 只在接管时做业务侧动作比如清会话、摘上游 if [ $STATE master ]; then echo $TS take over /var/log/keepalived-notify.log fi脚本里有两条经验。状态直接用配置里写死的参数不依赖 keepalived 追加的参数顺序版本之间这个顺序变过。脚本里绝对不要手动ip addr addVIP——keepalived 自己会加重复加会报RTNETLINK answers: File exists而且下线时容易删不干净留下地址冲突的隐患。5. 避坑与排查老版本 keepalived 最容易踩的五个坑这一节的每一条都对应一次真实的排查过程按“现象 → 原因 → 解决”记遇到时可以直接对号。5.1 双主两台机器同时举着 VIP现象ip addr在两台机器上都能看到192.0.2.100客户端访问时好时坏ARP 表在两台机器的 MAC 之间反复切换交换机日志里有 MAC 漂移告警。原因有三个层次。最常见的是virtual_router_id冲突——同一广播域里还有另一组 VRRP 用了 51两组通告互相干扰选举结果随机。其次是认证不匹配一端auth_pass改过而另一端没改两边都认为对方的通告非法各自认为自己是唯一 Master。再隐蔽一点的是advert_int不一致一端 1 秒一端 2 秒收通告的超时判断全乱。还有一种情况是交换机开了组播抑制或者端口隔离两台机器根本收不到对方的通告这种在容器化或云上环境里尤其常见。解决先tcpdump -i eth0 -nn proto 112在两端同时抓看能不能抓到对方的通告。抓不到就是二层或组播问题抓得到但状态还是双主就逐字段比对virtual_router_id、auth_type、auth_pass、advert_int四项。环境不支持组播的改用unicast_peer走单播。配置核对完还有双主就要在交换机侧确认这两个端口在同一个 VLAN 且没有隔离策略。5.2 VIP 加上了但业务不通现象切换正常VIP 也能 ping 通但通过 VIP 访问服务全部超时直连真实 IP 一切正常。原因VIP 加在哪张网卡、掩码多长、后端有没有做 ARP 抑制三件事里必有一件错。VIP 挂到eth0而业务监听在eth1的地址上服务压根没监听这个地址。或者 VIP 掩码写成/32同网段机器认为它不在本地子网回包走了默认网关。再或者用了 DR 模式但真实服务器没关 ARP 响应回包直接绕过了调度器客户端收到的是真实服务器的源地址三次握手对不上。解决ip -4 addr show确认 VIP 的设备和掩码ss -lntp确认业务进程监听的地址。如果业务只能绑本机已有地址、启动时 VIP 还没挂上加net.ipv4.ip_nonlocal_bind1允许绑定非本地地址否则进程会直接启动失败。DR 模式下回到 4.2 节那四条 sysctl一台一台核对别只看一台。5.3 脚本权重设置了但切不回去现象主节点业务进程挂掉vrrp_script日志里明明打了失败VIP 却没有转移主节点继续当 Master。原因weight 的绝对值小于主备优先级差有效优先级仍然高于备机。或者fall设得太大故障持续时间不够计数器还没到阈值就恢复了状态机根本没触发。还有一种情况是脚本检查的对象不对——脚本里curl的是外网地址外网抖动导致全线误判但故障当时外网是通的脚本返回 0永远不会失败。解决把优先级算术写在纸上priority weight算出来的数必须低于备机的priority。把fall/rise和interval乘一遍算出最坏判定时间写进切换预案。脚本里只检查本机状态任何依赖外部网络的检查都应该放到监控系统里不要放在 VRRP 的判定链路上。5.4 配置写错了却“启动成功”现象systemctl start返回 0进程在跑但某个配置块完全没生效比如track_script写了却没起作用。原因这个版本的配置解析器遇到不认识的关键字时会在日志里打一行Unknown keyword然后跳过这一行继续解析进程照样启动、照样跑退出码是 0。systemctl status看不出任何异常只有翻/var/log/messages才能发现。解决每次改完配置先grep -i unknown keyword /var/log/messages再grep -i keepalived看有没有Configuration file相关的告警。更稳的做法是前台跑一次/usr/local/keepalived/sbin/keepalived -n -l -d -f /etc/keepalived/keepalived.conf-n不 fork、-l输出到终端、-d把解析后的完整配置 dump 出来。dump 出来的内容和你写的对不上问题就找到了。这个版本不一定支持较新版本里的-t配置检查选项如果-n之类的开关报 usage按它打印的选项列表挑。提示配置 dump 是排查配置类问题最快的手段比逐行读文件可靠得多因为它展示的是解析器真正理解的内容。5.5 老代码在新环境上的编译与 PID 文件问题现象make在中途报错错误信息是某个函数隐式声明或者最后一行是cc1: all warnings being treated as errors。装完之后systemctl stop停不掉进程或者停了之后重启提示 PID 文件已存在。原因老代码里的部分写法在新版 gcc 下会触发警告而某些版本的 Makefile 默认带-Werror警告即错误。PID 文件问题则来自--localstatedir没指对或者进程被 KILL 掉时没来得及清理/var/run/keepalived.pidunit 里的PIDFile指向一个过期文件systemd 会认为服务状态异常。解决编译阶段用make CFLAGS-O2 -g -Wno-error覆盖不要改源码。编译前确认./configure --help里列出的开关别照抄网上的命令行。PID 问题先确认PIDFile路径和--localstatedir一致再看单元文件里是不是缺了Restarton-failure手工清理残留 PID 文件时一定先ps确认没有 keepalived 进程否则新进程会因为绑定 VIP 冲突而反复退出。6. 把切换行为测出来三轮破坏性验证和一份时间线配置写完、日志干净只能说明它跑起来了不能说明它会在该切的时候切。我一般会做三轮破坏性验证每轮都掐着秒表看切换耗时最后得到一份可以写进预案的时间线。第一轮验证进程级故障。在主节点上systemctl stop keepalived预期是立刻发出优先级 0 的通告、备机秒级接管notify_stop和notify_master两个钩子都会触发。# 主节点执行同时另开一个窗口在备机上看状态文件 systemctl stop keepalived watch -n0.5 cat /var/run/keepalived.VI_1.state这一轮应该最快通常在 1 秒以内因为走的是主动下线而不是超时判定。如果这一轮就要等好几秒说明notify脚本里有阻塞操作keepalived 在等脚本返回。第二轮验证链路级故障。用防火墙只丢弃 VRRP 通告模拟交换机策略变更或链路单向故障业务流量不受影响。# 主节点上只丢 VRRP别用默认的 DROP 全部否则连不上机器 iptables -I INPUT -p 112 -j DROP # 观察备机日志接管后把规则删掉观察原主是否抢回 iptables -D INPUT -p 112 -j DROP这一轮的耗时应该接近advert_int × 3配置成 1 秒就是 3 秒左右。测出来的数字明显大于 3 秒去查advert_int两端是否一致。规则删掉之后原主抢回的时间点正好用来验证nopreempt有没有生效——加了nopreempt的备节点不会把 VIP 还回去。第三轮验证业务级故障。主节点上停掉业务进程但保留 keepalived看vrrp_script触发切换需要多久。# 停业务观察脚本失败计数到 fall 的次数后是否切换 systemctl stop your-service grep -i chk_gateway /var/log/messages | tail -20耗时应该是interval × fall加上 VRRP 的判定时间再加几秒余量。三轮测完把三个数字填进一张表故障类型触发条件预期耗时实测进程退出systemctl stop keepalived1 秒内待测链路中断丢弃 proto 112advert_int × 3待测业务假死端口不响应interval × fall 数秒待测三行都填完这份表就是故障时的判断依据实际切换时间超出表里的数字说明有额外的阻塞没到时间就切了本文还有配套的精品资源点击获取