简介:Keepalived 1.2.13源码包面向运维工程师、网络架构学习者及高可用实践者,围绕VRRP与LVS实现服务故障切换,适合研究主备切换、健康检查与负载均衡配置。压缩包共188个文件、约334KB,以62个C头文件和61个C源文件为核心,辅以autoconf模板、示例配置、man手册与安装脚本,兼顾源码阅读、编译部署和样例参考。已有369人学习下载。包内可见VRRP实例、虚拟服务器组及健康检查等配置线索,也能结合日志和抓包思路理解排错;1.2.13在VRRP实现、性能与配置选项上的改动,可通过源码逐项对照,适合搭建实验环境、验证版本特性并梳理高可用部署流程。包体还保留变更日志、版本号文件与许可证信息,便于追溯发布背景、核对版本差异与编译参数细节,并辅助二次编译与旧项目维护。
1. 两台机器一个 VIP:为什么还有人翻 keepalived-1.2.13
前阵子给一个内部系统做双机热备,两台机器、一个虚拟 IP,对外只暴露一个地址。真正让人半夜爬起来的问题从来不是 IP 漂不过去,而是 IP 漂过去了、进程没起来,流量照样打空。keepalived-1.2.13.tar.gz 就是应付这类场景的老家伙:用 VRRP 在几台机器之间选主,主机出问题时备机接管虚拟 IP,还能挂 vrrp_script 把“端口通不通、进程在不在”变成漂移的判据。它适合自建机房、没有独立负载均衡设备、又不想引入太重组件的场景,也适合想把 VIP 漂移这件事彻底摸透的人——源码亲手编一遍,比看十篇配置教程都扎实。代价是 1.2.13 年头够久,依赖、语法、边界条件都得自己捋一遍,下面的内容就按这个顺序来。
2. 从 tar.gz 到可执行文件:1.2.13 的依赖、configure 与产物核对
2.1 源码包和仓库包的选择边界
多数发行版仓库里能直接装到 keepalived,版本往往比 1.2.13 高。这两条路不是谁替代谁,而是适用面不同。仓库包的好处是省事,包管理器会连带把 init 脚本、sysconfig、日志轮转都装好,升级也方便;坏处是版本和配置语法绑死在你装的那一版上,网上找来的示例配置经常因为多了一行vrrp_strict或者version 2就直接启动失败。
源码包的价值在于可控。configure 阶段可以关掉用不上的模块,比如纯 VIP 漂移根本不需要 LVS 那套 IPVS 代码,--disable-lvs --disable-lvs-syncd一关,编译依赖少一大截,产出的二进制也不用去碰内核头文件。另外同一份二进制推到十几台机器上,行为完全一致,不会出现“这台是 2.0、那台是 2.2”的排错地狱。1.2.13 属于 1.2 系列的后期版本,配置语法相对宽松,vrrp_instance、vrrp_script、notify_master这些关键指令都在,做双机主备够用。需要提前知道的是,这一版只讲 VRRPv2,认证是明文口令,别指望它承担安全职责。
2.2 依赖清单与 configure 参数
先把依赖摆清楚。下表是我在干净机器上编译前会逐个确认的东西,缺哪个补哪个:
| 依赖 | 用途 | 缺失时的表现 |
|---|---|---|
| gcc、make | 编译与链接 | configure 直接报找不到 C 编译器 |
kernel-devel(版本必须匹配uname -r) | 提供net/ip_vs.h等头文件,供 LVS 部分编译 | configure 找不到内核目录,或 make 阶段编译 IPVS 相关文件报错 |
| openssl-devel | SSL_GET 健康检查、部分摘要工具 | 不做 HTTPS 探测可以不装,但要留意 configure 摘要里的 SSL 支持项是否被关掉 |
| iproute2 | 运行期用ip addr看 VIP 落点 | 不影响运行,排错时抓瞎 |
| ipvsadm | 只有用 virtual_server 做四层转发才需要 | 纯 VRRP 场景无影响 |
装完之后解包、进目录、跑 configure。我一般把配置文件目录单独指到/etc/keepalived,这一步不做,后面 systemd 和 init 脚本都会找不到配置:
# 基础编译工具 yum install -y gcc make # 内核头文件:版本号必须和 uname -r 一致,否则路径对不上 yum install -y kernel-devel-$(uname -r) kernel-headers-$(uname -r) # 只有需要 SSL_GET 检查时才装 yum install -y openssl-devel tar -zxf keepalived-1.2.13.tar.gz cd keepalived-1.2.13 # 只做 VIP 漂移:关掉 LVS,省掉内核头依赖 ./configure \ --prefix=/usr/local/keepalived \ --sysconfdir=/etc/keepalived \ --disable-lvs \ --disable-lvs-syncd三个参数逐个说清楚。--prefix决定安装根目录,二进制落在sbin/keepalived,示例配置和 init 脚本落在etc/下面;--sysconfdir决定运行时去哪读keepalived.conf,默认值在 prefix 下面,改到/etc/keepalived是为了符合大多数人的运维习惯,也方便配置管理工具统一推送;--disable-lvs和--disable-lvs-syncd是配套的,关掉 LVS 之后就不需要--with-kernel-dir指内核源码路径了。如果你确实要用 virtual_server 做转发,把这两行换成--with-kernel-dir=/usr/src/kernels/$(uname -r),并确认那个目录真实存在。
configure 跑完别急着 make,先看输出末尾的摘要。里面有几行值得逐个确认:Use IPVS framework是 Yes 还是 No,Use VRRP framework必须是 Yes,Use SSL support决定你能不能写 SSL_GET 检查。摘要和你预期不一致,说明依赖没装全或者参数写错了,这时候 make 下去大概率是白等一场。
2.3 make、install 与产物核对
编译本身没什么花活,机器核数多就并行跑:
make -j"$(nproc)" make install # 核对产物 ls -l /usr/local/keepalived/sbin/keepalived ls -l /etc/keepalived/keepalived.conf # 确认二进制能跑、版本对不对 /usr/local/keepalived/sbin/keepalived -vmake install会把二进制装到 prefix 的sbin/,把示例配置装到 sysconfdir,通常还带一个bin/genhash,那个是给 HTTP 检查生成摘要用的,纯 VIP 场景用不上。init 脚本不会自动进/etc/init.d,得自己从源码目录keepalived/etc/init.d/keepalived拷过去,同时把keepalived/etc/sysconfig/keepalived拷到/etc/sysconfig/,启动参数从那里读。
现在多数机器用 systemd,直接写一个 unit 更省事:
[Unit] Description=Keepalived VRRP After=network.target [Service] Type=forking # -D 输出详细日志,-f 显式指定配置文件,避免读错路径 ExecStart=/usr/local/keepalived/sbin/keepalived -D -f /etc/keepalived/keepalived.conf ExecReload=/bin/kill -HUP $MAINPID PIDFile=/var/run/keepalived.pid KillMode=process [Install] WantedBy=multi-user.targetType=forking是因为 keepalived 自己会 daemon 化;KillMode=process防止停止服务时把子进程漏掉;ExecReload用 HUP 信号触发配置重载,改完配置不用整机重启,切换窗口小很多。启动之后用ps -ef | grep keepalived看,正常情况下能看到父进程加一个 VRRP 子进程。日志走 syslog 的 daemon facility,一般落在/var/log/messages或/var/log/syslog,-D打开后能看到每个实例进 MASTER、BACKUP 的时间点,后面排查全靠它。
3. VRRP 选主参数:state、priority、virtual_router_id 填错的三种后果
3.1 VRRP 报文与状态机:切换快慢由谁决定
VRRP 干的事说白了就一句话:一组机器共享一个虚拟 IP,同一时刻只有一台对外宣告自己是主人。实现方式是组播通告,默认目的地址 224.0.0.18,IP 协议号 112。每台机器按advert_int的间隔往外发 VRRP Advertisement,报文里带着自己的 priority 和 virtual_router_id。priority 最高的那台进入 MASTER 状态并持有 VIP,其余停在 BACKUP 状态听通告。
切换时间不是随便定的。BACKUP 上跑着一个 master_down_interval 定时器,算法是3 × advert_int + skew_time,其中 skew_time 约等于(256 - priority) / 256秒。按默认advert_int 1算,主挂了之后备机大概 3 秒出头接管,priority 越低等得越久,这是为了减少多台备机同时抢主的概率。想快就把 advert_int 调小,但代价是通告流量上升,网络稍有拥塞就容易误判,我一般不动这个值,1 秒在局域网里够稳。
状态机是 INIT → BACKUP → MASTER 这套。刚启动时先进 BACKUP,等一个 master_down_interval 收不到通告才升 MASTER,所以主机重启后不会立刻把 VIP 抢回去,除非配了抢占。抢占是默认行为:主机恢复、开始发通告,发现自己的 priority 比当前 MASTER 高,就直接把 VIP 抢回来。很多业务不喜欢这种“恢复即抖动”,后面第 6 章会讲怎么用 nopreempt 和 preempt_delay 把这件事压住。
3.2 主备两份配置的差异点
下面这份是主机侧的完整可用配置,备机只需要改三处:
global_defs { router_id node_a # 日志标识,两台不能重名,否则排错时完全分不清谁是谁 vrrp_mcast_group4 224.0.0.18 # 默认组播地址,没有特殊需求不要改 } vrrp_instance VI_1 { state MASTER # 初始角色,实际选主结果由 priority 决定 interface eth0 # 物理网卡名,不能写成 eth0:0 这种子接口 virtual_router_id 51 # 1-255,同一广播域内必须唯一 priority 150 # 备机写 100,差值决定切换是否干脆 advert_int 1 # 通告间隔,单位秒 authentication { auth_type PASS auth_pass Abc12345 # VRRPv2 只取前 8 位,写再长也只有前 8 位生效 } virtual_ipaddress { 192.168.10.100/24 dev eth0 } }备机侧把router_id改成 node_b,state改成 BACKUP,priority改成 100,其余三项和主机保持完全一致。这里有个容易被忽略的点:virtual_router_id和auth_pass必须两边严格一致,否则通告会被对方丢弃,双方都认为对面死了,结果就是双主。interface也要确认是真实承载 VIP 的那张网卡,bond 或 VLAN 子接口场景下写错名字,VIP 会绑到错误设备上,业务侧看到的是“VIP 在但连不通”。
参数取值上有几个边界值要记住。priority有效范围是 1 到 254,0 保留给“主动放弃 MASTER”的场景,255 保留给 IP 地址拥有者,普通配置不要碰这两个值。virtual_router_id是 1 到 255,同一二层网络里如果有别的团队也在跑 keepalived,撞号概率不低,上线前先跟网络侧确认一遍。
3.3 启动后的三看验证法
配置写完不要直接上生产,先在三看里过一遍:看 VIP 落点、看通告报文、看故障切换耗时。
# 第一看:VIP 到底绑在哪台机器、哪张网卡上 ip -4 addr show dev eth0 | grep 192.168.10.100 # 第二看:抓 10 个 VRRP 通告,协议号 112 tcpdump -i eth0 -nn -c 10 'proto 112' # 第三看:在主上停服务,备机上循环观察 VIP 出现的时间 systemctl stop keepalived while true; do date +%T; ip -4 addr show dev eth0 | grep -c 192.168.10.100; sleep 0.5; done第一看的输出应该是主机上有192.168.10.100/24,备机上没有。第二看如果只在一台机器上抓到包,说明组播没通,先查交换机或防火墙;正常应该看到源地址是各自主机的物理 IP、目的地址 224.0.0.18 的报文,间隔稳定在 1 秒左右。第三看是验证切换时间最直接的办法,从停服务到备机出现 VIP 的时间应该接近 3 秒,明显超出说明通告有丢包,或者备机的advert_int和主机不一致。
日志里对应的关键行有三条:Entering BACKUP STATE、Entering MASTER STATE、VRRP_Instance(VI_1) Received advert with higher priority。前两条告诉你角色变化的时间点,第三条说明发生了抢占,前面那行会带上对方的 priority,对着看就知道是谁抢了谁。
4. 健康检查落地:用 vrrp_script 把 VIP 漂移绑到进程存活上
4.1 VRRP 只保证机器活着,不保证服务活着
这是新手最容易翻车的地方。VRRP 判断“对方死没死”的唯一依据是收不收得到通告,只要机器还在、网卡还在、keepalived 进程还在,通告就一直发。至于上面的 nginx 是不是已经挂了、后端数据库连接是不是全断了,VRRP 一无所知。结果就是:VIP 纹丝不动地待在一台已经不能提供服务的机器上,业务全量报错,监控告警响了十分钟,运维上去手动重启才发现问题。
keepalived 提供的解法是vrrp_script。它允许你挂一个任意脚本,脚本返回 0 代表健康、非 0 代表异常,keepalived 按固定间隔去跑这个脚本,并根据结果调整本机的 priority,priority 掉到比备机低,VIP 自然就漂走了。这套机制的好处是判据完全自定义:可以是端口探测、可以是进程数、可以是调一次业务健康接口、甚至可以是查一次磁盘水位。1.2.13 还带 virtual_server 那一套 LVS 健康检查,但那是给四层转发用的,纯 VIP 场景用不上,也别把两套混着配,排错时会非常痛苦。
4.2 写一个能用的 vrrp_script 检查脚本
脚本要满足三个条件:快、绝对路径、退出码明确。下面这个是最小可用版本:
#!/bin/bash # 检查本机 web 服务的健康接口,2 秒超时 # 退出码 0 = 健康,非 0 = 异常,keepalived 只看退出码 code=$(curl -s -o /dev/null -m 2 -w '%{http_code}' http://127.0.0.1:80/health) if [ "$code" = "200" ]; then exit 0 fi exit 1逻辑很简单,但每行都有讲究。用127.0.0.1而不是对外域名,是为了避开 DNS 和外部链路带来的抖动,健康检查必须只反映本机服务的真实状态。-m 2限制总耗时,防止脚本卡死拖垮整个检查周期。-w '%{http_code}'只取状态码,把响应体丢掉,减少内存和 IO 开销。判断条件别用curl -f,那个在 3xx 重定向时行为不符合直觉,显式比对 200 更可控。
脚本放好之后必须chmod +x,路径写绝对路径,这一点没有商量余地——keepalived 启动时的环境变量和你登录 shell 的环境变量完全不是一回事,PATH里没有/usr/local/bin是家常便饭。
4.3 track_script 与 weight 的算术关系
脚本本身不会自动生效,要挂到实例上:
vrrp_script chk_web { script "/usr/local/bin/chk_web.sh" # 绝对路径,必须可执行 interval 2 # 每 2 秒跑一次 weight -40 # 失败一次,本机 priority 减 40 fall 3 # 连续失败 3 次才判定为故障 rise 2 # 连续成功 2 次才恢复 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 150 advert_int 1 authentication { auth_type PASS auth_pass Abc12345 } virtual_ipaddress { 192.168.10.100/24 dev eth0 } track_script { chk_web } }weight是这里唯一需要动脑子的参数。它的含义是“脚本失败时给 priority 加多少分”,负值就是扣分。主机的 priority 是 150,备机是 100,差值是 50,weight -40扣完还有 110,仍然比备机高,VIP 不会漂。这种情况下健康检查形同虚设,日志里连个错误都不会有,你会以为它工作得很好。正确写法是让weight的绝对值超过主备差值,比如-60,扣完变成 90,低于备机的 100,切换才会发生。
fall和rise是防抖用的。fall 3表示连续失败 3 次才认定故障,配合interval 2就是至少 6 秒才触发一次切换,能过滤掉偶发的超时。rise 2表示连续成功 2 次才恢复,防止服务刚起来还不稳定就急着抢回来。这两个值调小会让切换更灵敏但也更容易抖,调大会让故障发现变慢,我的习惯是interval 2、fall 3、rise 2,大多数业务能接受。
4.4 notify 脚本:漂移之后还要做什么
VIP 漂过去不代表万事大吉。有些场景需要配合动作:清一下 ARP 缓存让网关立刻更新、把本机的应用配置从“备”切到“主”、给监控系统推一条状态变更、或者重启一个依赖 VIP 存在的服务。这些用 notify 系列脚本做:
vrrp_instance VI_1 { # ... 前面的配置省略 notify_master "/usr/local/bin/on_master.sh" notify_backup "/usr/local/bin/on_backup.sh" notify_fault "/usr/local/bin/on_fault.sh" }#!/bin/bash # on_master.sh:本机刚接管 VIP 时执行 # $1 是 keepalived 传进来的状态字符串:MASTER / BACKUP / FAULT echo "$(date '+%F %T') become $1" >> /var/log/keepalived-notify.log # 通知网关刷新 ARP,部分老交换机不做 gratuitous ARP 的转发 arping -c 3 -I eth0 -s 192.168.10.100 192.168.10.1脚本有三个硬性约束:必须可执行、必须快速返回、不能阻塞。keepalived 是同步调用 notify 脚本的,脚本卡住不退出,整个状态机就跟着卡住。所以脚本里别写sleep、别做耗时的数据库操作、别调需要交互的命令,要做重活就丢到后台或者交给独立的 agent 处理。脚本里的$1是状态名,1.2.13 传的是大写字符串,别拿它跟小写去比。
5. 避坑与排查:五类翻车现场的现象、原因与处置
5.1 起不来与配置解析类
第一条:启动即退出,日志里只有一行“Unable to open configuration file”。现象是systemctl start之后立刻变成 failed,ps里什么都没有。原因几乎都是配置文件路径不对:编译时--sysconfdir没改,默认去 prefix 下面的 etc 找,而你按习惯把配置放在了/etc/keepalived/keepalived.conf。处置办法是启动时用-f显式指定路径,或者重新 configure 一遍把 sysconfdir 指对。判断配置本身有没有语法问题时,先用keepalived -t -f /etc/keepalived/keepalived.conf做一次静态检查,这个版本认这个参数就直接给结论;如果它不认,就前台跑keepalived -n -d -f ...把错误直接打到终端上,比翻日志快得多。
第二条:配置检查通过,但启动后报“Unknown keyword”。现象是进程起来又退出,日志里明确指到某一行的指令不认识。原因是把高版本的配置抄到了 1.2.13 上,vrrp_strict、garp_master_repeat、version 2这类 1.3 之后才出现的东西这一版没有。处置办法是把不认识的指令逐行删掉或者注释掉,每次只改一处,改完跑一次-t。我见过最离谱的一次是有人把整段virtual_server抄过来,但编译时用了--disable-lvs,那一段直接变成非法配置,报错信息还指在别的行上,绕了很久。
5.2 漂移与双主类
第三条:两台机器同时持有 VIP,客户端连接时通时不通。这是最典型也最危险的双主。现象是ip addr在两边都能看到/32的 VIP,ping 时通时断,抓包能看到两个源地址都在发通告。原因有三类:virtual_router_id撞号,同网段里还有别的实例用了同一个 ID;auth_pass不一致,两边互相把对方的通告当非法包丢掉,都以为对面死了;网络设备把组播掐了,两边谁也收不到谁。处置顺序是先对配置,把 VRID、auth_pass 前 8 位、advert_int 三个值逐字比对;配置一致还双主,就用tcpdump -i eth0 -nn 'proto 112'在两边各抓一次,看能不能收到对方的包。收不到就是网络问题,要么放行协议号 112,要么改单播。
第四条:主机恢复后不抢回 VIP,或者抢回瞬间业务抖一下。现象是主修好上线了,VIP 还在备机上待着,或者刚抢回来又掉回去。原因是nopreempt被误用了,这个参数生效有前提,不是写上就管用;另一种情况是没配preempt_delay,主机一恢复就抢,此时业务还没完全就绪,VIP 抢回来等于把流量引到一个半死的服务上。处置办法是主机侧加preempt_delay 300,让它在恢复后等 5 分钟再抢,给业务留出预热时间;确实不想让它抢回,就用nopreempt,同时确认state写的是 BACKUP,具体条件在第 6 章展开。
5.3 健康检查与脚本类
第五条:服务明明挂了,VIP 却一动不动。现象是 nginx 进程已经没了,主机上curl 127.0.0.1直接拒绝连接,但 VIP 稳稳待着,日志里一条切换记录都没有。原因最常见的是 weight 算术没算对,weight -40配上 50 的主备差值,扣完还是比备机高,脚本跑得再勤也没用。其次是脚本没有执行权限,keepalived 调它的时候直接返回 126,但这个失败被fall计数吃掉了,日志里看不出来;再其次是脚本里用了相对路径或依赖PATH,手动执行正常,被 keepalived 调用就报 command not found。处置办法是三步走:先在主机上手动执行脚本echo $?确认返回 0;再把weight的绝对值调到大于主备 priority 差值;最后把脚本里的所有命令换成绝对路径。验证是否真的生效,看日志里有没有VRRP_Script(chk_web) failed这一行,有这行说明脚本被调到了,没有就是根本没跑起来。
6. 单播与 nopreempt:两个参数决定漂移听不听话
组播在跨网段、云上 VPC、或者交换机开了组播抑制的环境里经常不通,表现就是两边互相收不到通告,最后变成双主。改单播是最干脆的解法,1.2.13 这一版已经支持:
vrrp_instance VI_1 { # ... 前面的配置省略 unicast_src_ip 192.168.10.11 # 本机物理 IP unicast_peer { 192.168.10.12 # 对端物理 IP,多台就写多行 } }改单播之后就不用管 224.0.0.18 了,防火墙只需要放行两两之间的协议号 112。注意unicast_src_ip必须写本机真实网卡上的地址,写 VIP 会导致通告发不出去。
nopreempt的生效条件比很多人以为的严格,配置不对就是白写:
| 条件 | 说明 |
|---|---|
state必须是 BACKUP | 写 MASTER 时 nopreempt 不生效,这是最常见的失效原因 |
| 两台 priority 不能相同 | 相同会导致选主结果不确定,nopreempt 也救不了 |
| 只影响“抢回”,不影响“接管” | 主机挂了备机照样接管,只是主机恢复后不主动抢 |
与preempt_delay二选一 | 想延迟抢回用 preempt_delay,想彻底不抢用 nopreempt |
我一般的做法是:核心业务用preempt_delay 300,让主机恢复后等五分钟再接管,既保留了主机优先的语义,又给了业务预热时间;只有主备硬件配置差距很大、备机性能明显更强时,才用nopreempt让它一直待在备机上。从那以后我每次配完这两个参数,都会在一台机器上手动 stop 再
本文还有配套的精品资源,点击获取