简介:这份资源面向运维工程师与VoIP通信系统开发者,聚焦Freeswitch高可用架构的落地实践,解决单点故障导致通信服务中断的问题。包内共4个文件,以sh脚本、txt手册和vsd逻辑图为主,压缩包约38KB,其中脚本承担健康检查与故障切换辅助,手册提供安装配置步骤,Visio图直观呈现主备切换流程。资源围绕Keepalived的VRRP机制展开,涵盖主备节点配置、虚拟IP绑定、自定义健康检查脚本、故障自动转移等关键环节,并给出Debian/Ubuntu与CentOS/RHEL下的安装命令及keepalived.conf参数说明。已有668人学习,适合需要快速搭建Freeswitch HA环境、理解健康检查与VIP漂移逻辑的读者参考,也可作为运维排错与方案设计的辅助材料。
1. 文件服务高可用:keepalived 到底在保什么
某天凌晨两点,某公司内部文件服务(下称 fs)单点宕机,所有依赖它做配置下发和日志归档的业务全部卡死,值班同学手动切了半小时才恢复。事后复盘发现,fs 本身没做任何高可用,全靠一台机器硬扛。这个场景里,keepalived 就是用来解决「一台挂了另一台顶上」的问题:它通过 VRRP 协议在多台 fs 节点之间选举出一个持有虚拟 IP(VIP)的主节点,主节点对外提供服务,备节点持续探测,一旦主节点失联就接管 VIP,客户端无感知。适合谁?适合正在用单机 fs、又不想上重型负载均衡集群的团队。下面从设计逻辑图讲到配置落地,再到踩坑排查,一步步拆开。
2. 先画清楚设计逻辑图:VRRP 状态机与 fs 的挂载点
2.1 为什么 fs 的 HA 不能只靠 keepalived 本身
很多人以为装了 keepalived、配了 VIP 就万事大吉,这是最大的误解。keepalived 只负责「谁持有 VIP」,它不关心 fs 进程是否真的在干活。如果 fs 进程已经僵死但机器还活着,keepalived 默认的检测机制(检查网卡、VRRP 通告)依然认为节点健康,VIP 不会漂移,客户端连上来照样报错。所以设计逻辑图时必须把 fs 的健康检查嵌进 keepalived 的检测链路里,常见做法是用vrrp_script调用一个探测脚本,脚本返回非零就降低优先级,触发切换。
设计逻辑图的核心要素有四层:第一层是 VIP 与 VRRP 实例,决定谁对外;第二层是优先级与抢占策略,决定切换时机;第三层是 fs 健康探测脚本,决定「假活」能否被发现;第四层是 fs 数据一致性,决定切换后备节点能不能直接服务。前三层是 keepalived 的职责,第四层是 fs 自身的职责,两者必须一起设计,否则切换成功但数据是旧的,等于白切。
2.2 一张可落地的逻辑图该包含哪些节点
我一般会画成三列:左侧是客户端与 DNS,中间是 VIP 和两台 fs 节点,右侧是共享存储或同步通道。客户端只认 VIP,不直接写节点 IP;VIP 在主节点上,备节点处于 BACKUP 状态;两台节点之间通过 rsync 或分布式存储保持数据同步;keepalived 的vrrp_script每隔 2 秒执行一次 fs 探测,连续失败 2 次就把优先级从 100 降到 90,备节点优先级 95 就会抢占。
这里有个关键参数:advert_int默认 1 秒,表示主节点每秒发一次 VRRP 通告。备节点在3 * advert_int + skew_time内没收到通告就认为主节点挂了。如果 fs 探测脚本执行时间超过 advert_int,会拖慢通告节奏,所以探测脚本必须轻量,最好在 500 毫秒内返回。
2.3 用配置文件把逻辑图翻译成 keepalived 实例
下面是一份最小可用的 keepalived 配置,主备节点只有state、priority、unicast_src_ip三处不同。先看主节点:
# /etc/keepalived/keepalived.conf 主节点 global_defs { router_id fs_node_01 # 节点标识,主备不能重复 script_user root # 执行探测脚本的用户 enable_script_security # 禁止非 root 执行脚本 } vrrp_script check_fs { script "/etc/keepalived/check_fs.sh" # fs 健康探测脚本 interval 2 # 每 2 秒执行一次 timeout 1 # 超过 1 秒算失败 fall 2 # 连续失败 2 次才降优先级 rise 2 # 连续成功 2 次才恢复 weight -10 # 失败时优先级减 10 } vrrp_instance VI_FS { state MASTER interface eth0 # 绑 VIP 的网卡,必须真实存在 virtual_router_id 51 # 主备必须一致,同网段不能冲突 priority 100 # 主节点初始优先级 advert_int 1 # VRRP 通告间隔 unicast_src_ip 192.168.10.11 # 本机真实 IP unicast_peer { 192.168.10.12 # 对端真实 IP } authentication { auth_type PASS auth_pass fs_ha_2024 # 主备必须一致 } virtual_ipaddress { 192.168.10.100/24 dev eth0 # 对外 VIP } track_script { check_fs # 引用上面的探测脚本 } notify_master "/etc/keepalived/notify.sh master" notify_backup "/etc/keepalived/notify.sh backup" notify_fault "/etc/keepalived/notify.sh fault" }备节点只需改三处:state BACKUP、priority 95、unicast_src_ip 192.168.10.12,unicast_peer改成主节点 IP。virtual_router_id、auth_pass、virtual_ipaddress必须完全一致。
逻辑说明:vrrp_script定义了探测节奏和权重变化,track_script把它挂到实例上,notify_*在状态切换时触发脚本,用来做 fs 服务的启停或告警。参数上,weight -10配合主 100 备 95,意味着主节点探测失败一次降 10 后变成 90,低于备节点的 95,备节点就会抢占。如果不想自动抢占,把备节点state设为 BACKUP 且加nopreempt,但生产环境一般不建议,容易导致故障后无人接管。
2.4 fs 健康探测脚本怎么写才不误判
探测脚本是整套方案里最容易翻车的地方。写得太简单,进程在但服务不可用;写得太重,拖慢 VRRP 通告。我一般用「进程存在 + 端口可连 + 一次轻量读写」三层判断:
#!/bin/bash # /etc/keepalived/check_fs.sh # 返回 0 表示健康,非 0 表示异常 # 第一层:进程是否存在 if ! pgrep -x "fs_server" > /dev/null; then exit 1 fi # 第二层:服务端口是否可连,超时 1 秒 if ! timeout 1 bash -c "echo > /dev/tcp/127.0.0.1/8080" 2>/dev/null; then exit 1 fi # 第三层:一次轻量接口探测,只读不写 code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 1 http://127.0.0.1:8080/health) if [ "$code" != "200" ]; then exit 1 fi exit 0逻辑说明:第一层用pgrep -x精确匹配进程名,避免匹配到同名子进程;第二层用 bash 内置的/dev/tcp做端口探测,不依赖 nc 等外部工具;第三层用 curl 请求健康接口,--max-time 1保证不超时。参数上,timeout 1和--max-time 1都要小于 keepalived 的timeout 1,否则脚本还没返回就被 keepalived 判超时。脚本必须chmod +x,且放在script_user有权限读取的路径。
3. 主备切换的完整落地步骤:从裸机到 VIP 漂移
3.1 两台节点的初始化与网络前提
落地第一步不是装 keepalived,而是确认网络前提。两台 fs 节点必须在同一二层网络,否则 VRRP 通告走不了组播;如果跨网段,必须用unicast_peer指定单播地址,这也是我上面配置里用单播的原因,兼容性更好。确认eth0存在且 IP 固定,关闭 NetworkManager 对 VIP 的干扰,常见做法是在网卡配置里加NM_CONTROLLED=no。
然后同步时间,时间偏差过大会导致 VRRP 状态判断异常。用 chrony 或 ntp 都行,关键是两台节点时间差控制在 1 秒内。接着安装 keepalived,不同发行版包名不同,常见的是keepalived一个包搞定。安装后先别启动,把配置文件写好再启动,否则默认配置可能抢占同网段其他 VRRP 实例。
3.2 数据同步通道的搭建与验证
keepalived 切换的是 VIP,但 fs 的数据必须两边都有。我一般用 rsync 做准实时同步,主节点定时推送到备节点。下面是一个每 30 秒同步一次的脚本:
#!/bin/bash # /etc/keepalived/sync_fs.sh 主节点执行 SRC="/data/fs/" DST="192.168.10.12:/data/fs/" rsync -az --delete --exclude 'tmp/' -e "ssh -o StrictHostKeyChecking=no" "$SRC" "$DST"逻辑说明:-a保留权限和时间戳,-z压缩传输,--delete删除备节点上主节点已删的文件,--exclude 'tmp/'排除临时目录避免同步抖动。参数上,同步频率要和 fs 的写入量匹配,写入量大就缩短间隔或改用 inotify 触发。验证方法是主节点写一个文件,等 30 秒后在备节点ls确认存在,再删掉主节点文件,确认备节点也删掉。
3.3 启动顺序与 VIP 漂移验证
启动顺序有讲究:先起备节点,再起主节点。因为备节点先进入 BACKUP 状态等待通告,主节点起来后发通告,备节点确认主节点优先级更高,保持 BACKUP。如果反过来,主节点先起,备节点后起,备节点可能短暂认为自己是主,造成双主。启动命令用systemctl start keepalived,然后systemctl enable keepalived开机自启。
验证 VIP 漂移:在主节点执行ip addr show eth0,应看到192.168.10.100;然后systemctl stop keepalived模拟故障,在备节点执行同样命令,VIP 应在 3 秒内出现。再用ping 192.168.10.100持续观察,丢包应在 2 到 3 个以内。最后把主节点 keepalived 起回来,确认 VIP 是否按预期漂回(取决于是否配置抢占)。
3.4 切换时 fs 服务的启停钩子
VIP 漂移只是网络层切换,fs 服务本身可能需要在切换时做启停或重载。这就是notify_master、notify_backup、notify_fault的用途。下面是一个 notify 脚本示例:
#!/bin/bash # /etc/keepalived/notify.sh case "$1" in master) systemctl start fs_server # 成为主节点时启动 fs logger "fs HA: became MASTER" ;; backup) systemctl stop fs_server # 成为备节点时停止 fs,避免双写 logger "fs HA: became BACKUP" ;; fault) systemctl stop fs_server logger "fs HA: FAULT state" ;; esac逻辑说明:成为 MASTER 时启动 fs,成为 BACKUP 时停止 fs,这样保证同一时刻只有主节点在写数据,避免双写冲突。参数上,$1是 keepalived 传入的状态名,必须精确匹配。脚本要chmod +x,且路径在配置里写对。注意:如果 fs 启动很慢,notify 脚本会阻塞 keepalived 状态机,建议在脚本里用nohup或 systemd 异步启动。
4. 避坑与排查:那些让切换失效的细节
4.1 双主现象:两台节点同时持有 VIP
现象:客户端偶尔连到备节点,数据出现不一致,ip addr在两台机器上都能看到 VIP。原因通常是防火墙放行了 VRRP 但没放行单播地址,或者virtual_router_id冲突,或者auth_pass不一致导致通告被丢弃。解决:检查iptables -L是否放行 VRRP 协议(协议号 112)和单播端口;确认两台节点virtual_router_id相同且同网段无其他实例占用;用tcpdump -i eth0 vrrp抓包,看通告是否双向可达。
4.2 切换不触发:fs 挂了但 VIP 不漂
现象:手动 kill 掉 fs 进程,VIP 依然在主节点,客户端报错。原因通常是vrrp_script没生效,或者track_script没挂到实例上,或者脚本返回码不对。解决:手动执行check_fs.sh看返回码;用journalctl -u keepalived看是否有脚本执行日志;确认weight设置后主节点降级后的优先级确实低于备节点。常见错误是weight -10但备节点优先级只差 5,降级后还是主节点高。
4.3 VIP 漂移后客户端不恢复
现象:VIP 已经漂到备节点,但客户端仍然连不上。原因通常是 ARP 缓存未更新,或者备节点没启动 fs 服务,或者 VIP 绑定的网卡不对。解决:在客户端arp -d 192.168.10.100清缓存;确认备节点 notify 脚本已启动 fs;确认virtual_ipaddress里的dev和实际网卡一致。如果客户端和 fs 跨网段,还需要在交换机上确认 VIP 的 MAC 地址已更新。
4.4 同步延迟导致切换后数据丢失
现象:切换后客户端读到的文件是几分钟前的版本。原因是 rsync 同步间隔太长,或者切换时最后一次同步还没完成。解决:缩短同步间隔,或在 notify_master 脚本里先执行一次全量同步再启动 fs;对一致性要求高的场景,改用共享存储或分布式文件系统,keepalived 只负责 VIP,数据层单独保证。
4.5 keepalived 日志太少,排查靠猜
现象:出问题时journalctl里只有启动日志,没有状态切换细节。原因是默认日志级别不够。解决:在global_defs里加log_facility或启动参数加-D开启详细日志;也可以在每个 notify 脚本里用logger主动打点,记录状态变化时间。我一般会在 notify 脚本里把date、hostname、$1一起写进日志文件,方便事后对齐时间线。
5. 进阶技巧:让 fs 的 HA 从「能切」到「切得干净」
前面四章解决了「能切」的问题,但生产环境里「切得干净」更重要。所谓干净,是指切换后没有残留连接、没有双写、没有数据回滚。这里分享三个我踩坑后固定下来的习惯。
第一个习惯是给 VIP 加garp_master_delay和garp_master_repeat。默认情况下,节点成为 MASTER 后会发送免费 ARP 更新交换机 MAC 表,但发送次数和间隔不可控。在vrrp_instance里加:
garp_master_delay 1 # 成为 MASTER 后延迟 1 秒发免费 ARP garp_master_repeat 3 # 连续发 3 次这样交换机 MAC 表更新更可靠,客户端 ARP 缓存也能更快刷新。参数上,garp_master_delay不要设太大,否则切换感知变慢;garp_master_repeat设 3 到 5 次足够。
第二个习惯是用track_interface监控额外网卡。如果 fs 节点有多块网卡,其中一块负责数据同步,这块网卡挂了但主网卡还在,keepalived 默认不会切换。加上:
track_interface { eth0 eth1 }任何一块网卡异常都会触发优先级调整。注意track_interface的权重是全局的,和vrrp_script的 weight 叠加,别让总降级幅度超过主备优先级差。
第三个习惯是定期做「无预告切换演练」。我一般每月挑一个低峰期,直接systemctl stop keepalived模拟故障,观察 VIP 漂移时间、fs 启动时间、客户端恢复时间,并记录到表格里。下面是我用的演练记录表:
| 演练项 | 预期值 | 实测值 | 是否达标 |
|---|---|---|---|
| VIP 漂移时间 | ≤ 3 秒 | 2.1 秒 | 是 |
| fs 服务启动时间 | ≤ 5 秒 | 4.3 秒 | 是 |
| 客户端恢复时间 | ≤ 10 秒 | 8.7 秒 | 是 |
| 数据丢失量 | 0 文件 | 0 文件 | 是 |
| 双主出现 | 无 | 无 | 是 |
这张表比任何监控都直观,因为它是端到端的。监控只能告诉你 VIP 在哪,演练才能告诉你客户端到底能不能用。
最后一个技巧是关于nopreempt的取舍。很多教程建议加nopreempt防止主节点恢复后抢回 VIP,但我的血泪经验是:如果 fs 主节点恢复后数据比备节点旧,抢回 VIP 会导致数据回滚。所以要么不加nopreempt,让主节点恢复后抢回并立即做一次全量同步;要么加nopreempt,但必须在主节点恢复后手动确认数据一致再决定是否切回。我一般选前者,因为自动化程度高,但同步脚本必须足够健壮。
这套方案我用了几年,最大的教训是:keepalived 的配置本身不难,难的是 fs 健康探测的准确性和数据同步的可靠性。探测脚本写得太松,切换不触发;写得太紧,网络抖动就误切。同步间隔太短,带宽压力大;太长,切换后数据旧。这些参数没有标准答案,只能根据实际写入量和网络质量调。希望帮到你。
本文还有配套的精品资源,点击获取