简介:面向VoIP运维与通信系统工程师,这份基于Keepalived为FreeSWITCH设计的高可用方案资源,围绕主备节点、虚拟IP漂移、健康检查与故障切换机制,系统解决通信服务因单点故障引发的中断问题,适合正在建设双机热备或改造现网架构的技术团队。压缩包共4个文件,整体仅38KB,文件类型包括健康检查脚本、SSH辅助脚本、Keepalived安装配置手册以及Visio主备逻辑图,功能定位清晰,可直接复用或二次修改。其中逻辑图完整绘制了从健康检查、状态异常判定到虚拟IP无缝转移的故障切换流程;安装手册则提供Keepalived部署、keepalived.conf参数配置和服务状态验证等详细步骤,配合脚本可快速完成主备节点部署与切换演示。已有665人学习下载,无论是希望熟悉Keepalived高可用机制,还是需要搭建FreeSWITCH双机环境的初中级运维工程师,都能从这套资料中获得清晰的落地指引。 上周刚帮一个做呼叫中心的朋友把fs从单机迁到了keepalived双机热备,迁完以后他问了我一句:这两台机器到底什么情况下会切换,切换以后正在打的电话会不会断?这个问题其实问到了点子上。很多跑fs的人一说高可用就想到keepalived,但真到了业务层面,光有keepalived还远远不够。fs我这里说的是FreeSWITCH,做VoIP、呼叫中心、SIP接入的老哥们应该都不陌生,它承载着SIP注册、呼叫路由、媒体转发这些实时业务,单台机器一旦挂掉,分机全掉线、外呼全失败、正在通话的RTP流直接断。想让fs具备自动切换能力,keepalived是目前成本最低、也最容易落地的一套方案,这篇文章我把整体设计逻辑、配置过程、切换验证和一些踩过的坑都写出来,给正准备做fs高可用的兄弟一个参考。
1. 先搞清楚fs的高可用到底要解决什么问题
1.1 单机fs的痛点和HA的核心目标
fs单机运行是最常见的跑法,尤其在中小呼叫中心、企业通信网关这种场景下,一台物理机或者云主机,装好freeswitch、配好外呼网关、把分机号段建好,业务就能跑起来。但这种模式有一个天然问题:所有注册、呼叫、媒体都集中在一台机器上,机器宕机、网络抖动、磁盘写满、CPU跑满,任何一个环节出问题,对外业务立刻就瘫。我之前帮朋友排查过一次问题,最后发现只是根分区被日志塞满,fs进程还在但写不了CDR,话务平台那边报错一片,这种隐性故障比直接宕机更麻烦。
HA方案的核心目标很清楚:对外提供一个虚拟IP,正常情况下主节点处理所有业务;主节点出现故障后,虚拟IP快速漂移到备用节点,备用节点接过SIP注册和新的呼叫请求,让业务在分钟级甚至秒级内恢复。这里要有一个正确的心理预期:keepalived的VIP漂移是三层网络层的切换,不是应用层的热迁移,对于已经建立的SIP/RTP通话,媒体流已经经过了主节点,主节点挂了,这通电话必然中断,不可能做到无缝保留。所以做fs的HA,第一设计原则是把“快速恢复新业务”作为目标,而不是“不中断现有通话”。
我把各环节的影响整理了一张表,方便大家做方案评审时用:
| 业务环节 | 单机故障影响 | HA切换后的实际表现 |
|---|---|---|
| SIP注册、新呼叫路由 | 全部中断,分机离线 | 备机接管VIP后恢复,注册重新建立 |
| 正在进行的通话 | 直接中断 | 无法无缝保留,需要上层话务策略配合 |
| 录音、计费、CDR | 中断,本地文件可能丢失 | 备机接管后继续写,历史数据需提前同步 |
| 网关对接、API接口 | 全部不可用 | 备机接管后恢复,接口IP不变 |
1.2 为什么要选keepalived而不是heartbeat/corosync
高可用工具圈子里其实有不少选择:heartbeat、corosync+pcs、pacemaker、keepalived都用过。我的结论是,keepalived是最适合fs这种IP漂移需求的东西,没有之一。keepalived基于VRRP协议,核心就干一件事:在多个节点之间漂移一个虚拟IP,同时通过自定义脚本检查服务健康状态。它没有pacemaker那么复杂,不需要定义资源代理、不需要学习集群命令行那套东西,就是“谁健康谁拿VIP,谁挂了VIP立刻走人”。
当然keepalived也有明显的局限:它只管VIP的漂移,不负责进程拉起,也不负责数据同步。fs挂了不会自动重启,写坏的配置不会自动修复,录音文件也不会自动复制到备机。所以完整的fs HA方案必须由三部分组成:keepalived负责VIP漂移,健康检查脚本负责探活,rsync或共享存储负责数据同步。把这三个边界搞清楚,后面踩坑会少很多。很多新手一上来就在keepalived配置里堆一大堆东西,结果切换逻辑乱成一团。
1.3 方案的整体设计思路
先理顺整体逻辑,我的习惯是这样分三步走:第一步先解决VIP漂移,让两台fs共用一个虚拟IP,任何一台挂了VIP能切走;第二步做健康检查,必须确保脚本探测的是fs的真实处理状态,而不是简单的ping通;第三步解决配置、录音、CDR的数据同步问题,避免切到备机后配置不一致、录音对不上。
顺序不能乱。我见过有人先把数据同步做得很复杂,用上各种存储方案,结果keepalived本身还没配利索,等于在一堆沙子上盖房子。先把VIP漂移跑通,再逐步加健康检查和数据同步,每一步验证通过后再进下一步,这是做HA最稳妥的路径。
2. 架构设计与逻辑图拆解
2.1 高可用架构图
标题里提到设计逻辑图,这里我用文字版拓扑图把整体结构画出来:
+------------------+ | 虚拟IP(VIP) | | 192.168.1.100 | +--------+---------+ | +--------------+--------------+ | | +-------+--------+ +-------+--------+ | fs主节点 | | fs备节点 | | 192.168.1.11 | | 192.168.1.12 | | keepalived | | keepalived | | priority 120 | | priority 110 | +-------+--------+ +-------+--------+ | | +--------------+--------------+ | +--------------+--------------+ | 配置/录音/CDR同步方案 | | rsync + NFS + 数据库写入 | +-----------------------------+这个架构里,SIP话机、网关、上层业务平台全部只和VIP通信,不感知后端是哪台fs在服务。主节点正常时VIP在主节点网卡上;主节点故障或者健康检查判死时,VIP漂移到备节点,整个切换对业务调用方是无感的,SIP注册地址、API对接地址都不变。
2.2 主备切换前后的调用链分析
一句话概括切换过程:主节点持有的VIP被释放,ARP通告更新,备节点在相同网卡上配置同一个VIP,并开始接收流量。具体到fs业务:
- 正常状态下,话机注册请求到达VIP,由主节点mod_sofia处理,REGISTER、INVITE等信令全部走主节点;媒体流RTP也经过主节点转接,呼叫全程依赖主节点。
- 主节点发生宕机或fs进程挂掉时,健康检查脚本连续失败,keepalived判定故障,主节点主动降低优先级或直接退出MASTER状态,VIP解绑。
- 备节点在收到主节点停止通告或检测不到主节点后,将VIP绑定到自己的网卡,同时发送免费ARP刷新交换机MAC表。
- 新呼叫到达备节点,话机重新注册到备节点,SIP代理、外呼网关等业务恢复正常。正在进行的呼叫由于SIP Dialog和RTP会话都在主节点上,主节点一旦挂掉,这通电话就断了。
这里要再次强调,SIP是状态协议,FreeSWITCH又是B2BUA,所有媒体流都经过自身转发,keepalived只解决三层VIP漂移,不做Session级别的状态同步。不要把“切换后正在通话不中断”当成方案目标来设计,否则后面验证时一定会失望。
2.3 脑裂问题及防护
VRRP协议本身有优先级和通告机制,正常情况下只有一台机器会持有VIP。但有一种情况需要特别注意:两台机器之间的网络心跳断了,但两台机器上的fs其实都活着,这时候备节点检测不到主节点的VRRP通告,会认为主节点挂了,于是自己也升级为MASTER,两台机器同时持有VIP,这就是脑裂。脑裂最直接的后果是SIP注册请求被随机分发到两台fs,话机一部分注册在主节点,一部分注册在备节点,两台机器上的呼叫状态完全割裂,业务直接乱套。
防护手段主要分几层:第一层,keepalived开启单播模式,不要走组播,因为组播在部分交换机环境不可靠,单播在心跳断了的时候能被更快感知;第二层,配置VRRP认证,防止异常节点加入;第三层,如果条件允许,在备节点加一个“对端存活仲裁”脚本,通过另一条独立心跳路径检查对端是否真的挂了,如果对端还活着但VRRP断联,就主动降低自己的优先级,避免升级为MASTER。对于中小规模fs场景,前两层已经够用,第三层属于增强项。
3. 部署与配置实操
3.1 环境准备
先规划一套典型双机环境,后面配置都基于这套:
| 角色 | IP地址 | 主机名 | 系统版本 |
|---|---|---|---|
| 主节点 | 192.168.1.11 | fs01 | CentOS 7.9 / Ubuntu 22.04 |
| 备节点 | 192.168.1.12 | fs02 | CentOS 7.9 / Ubuntu 22.04 |
| 虚拟IP | 192.168.1.100 | - | - |
| 业务端口 | 5060 UDP/TCP | - | SIP信令 |
keepalived版本建议1.3.5以上,老版本对脚本返回码和weight的处理存在差异。另外,如果跑在云主机上,要提前确认安全组和虚拟交换机是否支持VRRP协议,不支持的话需要改用单播模式,我下面给出的配置默认采用单播,这也是生产环境推荐做法。
3.2 安装keepalived
两台机器执行同样的安装操作:
# CentOS/RHEL yum install -y keepalived systemctl enable --now keepalived # Ubuntu/Debian apt update && apt install -y keepalived systemctl enable --now keepalived装完之后先别急着配,检查一下防火墙,VRRP单播模式下,默认协议号是112,需要放行:
# firewalld firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept' firewall-cmd --reload # 如果是CentOS 7及以前,用iptables的话: iptables -I INPUT -p vrrp -j ACCEPT这一步不做的话,部署完会发现备节点一直收不到主节点的通告,后面排查会比较绕。
3.3 主节点keepalived配置
主节点配置如下,注意priority、unicast_src_ip、unicast_peer这几个关键字段:
! /etc/keepalived/keepalived.conf global_defs { router_id FS_HA_1 vrrp_skip_check_adv_addr script_user root enable_script_security } vrrp_script chk_fs { script "/etc/keepalived/check_fs.sh" interval 3 weight -20 fall 2 rise 1 } vrrp_instance VI_FS { state MASTER interface eth0 virtual_router_id 51 priority 120 advert_int 2 authentication { auth_type PASS auth_pass 123456 } virtual_ipaddress { 192.168.1.100/24 dev eth0 } track_script { chk_fs } unicast_src_ip 192.168.1.11 unicast_peer { 192.168.1.12 } }这里有两个点要重点解释。第一个是vrrp_script块的weight -20,这个参数的意思是:当脚本探测失败时,当前节点优先级扣减20分。主节点优先级120,健康检查失败后优先级变成100,低于备节点的110,于是VIP让给备节点;脚本恢复正常后优先级回到120,如果没开nopreempt,主节点会抢回VIP。第二个是虚拟路由器ID,两台机器必须保持一致,建议按业务网段规划,不要同一网段里多套keepalived共用同一个ID,否则会出现VIP冲突。
3.4 备节点keepalived配置
备节点配置和主节点几乎一样,只需要改三个地方:
! /etc/keepalived/keepalived.conf global_defs { router_id FS_HA_2 vrrp_skip_check_adv_addr script_user root enable_script_security } vrrp_script chk_fs { script "/etc/keepalived/check_fs.sh" interval 3 weight -20 fall 2 rise 1 } vrrp_instance VI_FS { state BACKUP interface eth0 virtual_router_id 51 priority 110 advert_int 2 authentication { auth_type PASS auth_pass 123456 } virtual_ipaddress { 192.168.1.100/24 dev eth0 } track_script { chk_fs } unicast_src_ip 192.168.1.12 unicast_peer { 192.168.1.11 } }说一个特别容易踩的坑:备节点的priority不能只比主节点少一点,要考虑健康检查扣分后的关系。比如主节点priority 120,备节点110,主节点失败后扣20变成100,低于备节点110,这样切换才能发生。如果主节点和备节点设置成120和105,主节点失败后100,105大于100,备节点不会抢占,VIP就卡在主节点上不动了。配置完成后在备节点执行systemctl restart keepalived,然后用ip addr show eth0确认备节点当前没有VIP绑定。
3.5 健康检查脚本编写与应用
健康检查脚本是整个fs HA方案里最关键的环节。很多人偷懒用ping做检查,结果主节点fs进程都僵死了,ping还通,VIP根本不切走。正确的做法是组合检查:第一,fs进程必须存在;第二,fs_cli必须能正常执行status命令并返回有效输出。
我实际在用的脚本如下:
#!/bin/bash # /etc/keepalived/check_fs.sh # 检查freeswitch进程是否存活 if ! pgrep -f "/usr/local/freeswitch/bin/freeswitch" > /dev/null 2>&1; then exit 1 fi # 通过fs_cli检查服务是否可交互,timeout防止命令卡死 out=$(timeout 5 /usr/local/freeswitch/bin/fs_cli -x "status" 2>/dev/null) if echo "$out" | grep -q "FreeSWITCH" && echo "$out" | grep -q "Session Rate"; then exit 0 fi exit 1保存后设置执行权限:
chmod +x /etc/keepalived/check_fs.sh脚本逻辑并不复杂,process检查保证进程没了立刻判死,fs_cli检查保证即使进程还在但服务假死时也能被发现。timeout 5一定要加,我遇到过fs_cli在fs负载极高时无限等待的情况,脚本一旦卡住,keepalived的track_script也会被拖住,整个VRRP状态机就会变得迟钝。
3.6 切换验证流程
配置完成后,验证切换是最重要的一步。我的验证流程是这样的:
第一步,在备节点上确认VIP还没有绑定,在主节点上确认VIP已经出现:
# 主节点执行 ip addr show eth0 | grep 192.168.1.100第二步,用sngrep或者直接tcpdump抓SIP包,模拟一路呼叫先跑起来,然后手动停掉主节点的fs进程:
# 主节点执行 systemctl stop freeswitch第三步,观察备节点是否在几秒内绑定了VIP:
# 备节点执行,应该很快能看到VIP ip addr show eth0 | grep 192.168.1.100第四步,用sip软电话或者sipp工具重新注册到VIP,确认信令能正常到达备节点。注意一点:验证时要故意让fs处于假死状态而不是杀掉进程,比如用gdb把fs进程挂住,或者通过fs_cli执行阻塞操作,这样能验证健康检查脚本的判断逻辑是否有效。
4. 数据与配置同步方案
4.1 配置文件同步策略
fs的配置目录通常在/usr/local/freeswitch/conf,包括dialplan、directory、sip_profiles、gateway等。HA场景下要求主备配置高度一致,否则切换后行为完全对不上。最简单的方案是用rsync做定时同步,我一般是每小时同步一次,同时配合inotify实时同步关键目录。
先写一个同步脚本:
#!/bin/bash # /etc/keepalived/sync_fs_conf.sh # 在主节点执行,同步配置到备节点 rsync -avz --delete \ --exclude '*.log' \ --exclude '*.pid' \ /usr/local/freeswitch/conf/ \ root@192.168.1.12:/usr/local/freeswitch/conf/还有个细节容易忽略:fs的sip_profiles里如果配置了外部SIP地址,这个IP在两台机器上可能是不同的,直接rsync会把主节点的IP同步过去,导致备机切换后SIP信令里的Contact地址不对。我的建议是,对外SIP信令地址尽量用VIP或域名,不要写死实际节点IP,这样rsync同步过去才不会有问题。如果实在要区分内部通信地址,可以把这部分配置单独拆出来,不参与同步。
4.2 注册数据和话单的同步
fs的SIP注册信息默认存在内存和本地db文件里,主节点宕机后,备节点不会自动拥有这些注册状态。这意味着切换后,话机需要重新走REGISTER流程,时间取决于话机的注册周期,常规设置60秒左右的话,最坏情况下切换后一分钟内所有话机完成重新注册。如果业务要求切换后注册状态秒级保留,就需要把mod_sofia的注册数据库切到外部数据库,比如和CDR一起写MySQL或者PostgreSQL,但这会引入数据库可用性问题,复杂度直线上升,中小规模场景一般不推荐。
话单和CDR的写入逻辑相对简单,主备节点同时写入同一套数据库即可。比如计算系统用MySQL存储CDR,两台fs都配置相同的ODBC数据源,谁在处理呼叫谁就写数据库,不存在数据丢失问题。这里要注意的是数据库本身要保持高可用,否则fs虽然切换了,CDR写入还是会失败。
4.3 录音文件的同步
录音文件是fs系统里最占空间的数据,如果只有主节点本地写录音,切换后主节点上的录音文件备机读不到,业务需要调取录音时就缺了。常见做法有三种:第一种,小规模场景推荐rsync定时增量同步,把/usr/local/freeswitch/recordings定时拉到备机;第二种,录音量大时直接把录音目录放到NFS共享存储,两边同时挂载;第三种,录音上传到对象存储,fs本地只做缓存,这是最稳妥的方案。
特别提醒:不要两台fs同时挂载同一块传统共享盘然后都往里面写文件,没有集群文件系统做锁管理的情况下,很容易出现文件写坏的问题。录音这种文件密集写入的场景,要么走NFS但保证同一时刻只有一台fs在写,要么干脆用对象存储,让本地盘只做缓存。
4.4 切换后fs启动顺序
很多坑出现在主节点恢复之后。keepalived默认情况下,主节点重新健康后,因为优先级高会抢回VIP,但如果此时主节点的fs还没来得及完全启动,就会出现VIP回来了但没有进程监听5060端口的情况,业务恢复反而更慢。我建议主节点恢复后不要立即抢回VIP,可以通过配置nopreempt实现:
vrrp_instance VI_FS { state BACKUP nopreempt priority 120 ... }nopreempt参数配合state BACKUP使用,这样主节点恢复后不会主动抢占VIP,而是继续让当前健康的备节点提供服务。等到下一次需要切换时,优先级机制再决定谁接管。同时,如果确实需要主节点恢复后抢回VIP,需要确保fs的启动流程完成后keepalived再启动,可以在systemd里设置依赖关系,等freeswitch服务ready后再启动keepalived。
5. 常见问题与排查技巧实录
5.1 VIP不漂移,该从哪里开始查
VIP不漂移是keepalived HA最常遇到的问题。我的排查顺序是:先看keepalived进程状态和日志,再确认健康检查脚本是否在按预期返回,最后检查VRRP通信是否正常。
systemctl status keepalived journalctl -u keepalived -f ip addr show eth0 | grep 192.168.1.100如果日志里连续出现VRRP通告丢失,先怀疑是否防火墙或者安全组把VRRP协议拦了。检查unicast_src_ip和unicast_peer是否配置正确,很多云主机的虚拟网络不支持组播,必须用单播。virtual_router_id不一致也会导致两台机器互相不认,这个错位比较隐蔽,检查时一定要两台机器都看。
5.2 健康检查脚本误判导致频繁切换
脚本判死条件写得太苛刻,会出现频繁切换的情况。比如我最早做方案时,脚本里检查了fs的Session Rate数值,结果空闲时段Session Rate为0,脚本误判成异常,导致备机频繁接管VIP,SIP话机不停重注册,业务反而更不稳定。后来改成检查进程存活加fs_cli返回状态,问题才解决。
还有一类情况是脚本没有加timeout,fs负载高时命令返回慢,脚本执行时间超过VRRP的通告间隔,导致检查结果滞后。建议脚本内所有命令都加timeout,整体执行时间控制在1秒以内。
5.3 切换后业务恢复慢
很多时候VIP漂移很快,但业务恢复却很慢,原因在于freeswitch启动需要加载大量拨号方案、网关定义和法务配置,在机器性能一般的情况下可能要30秒甚至更久。此时VIP已经在备机上,但5060端口还没有监听,外部呼叫直接失败。
解决思路是“先启动fs,再启动keepalived,最后挂VIP”。我习惯把fs做成systemd服务,并写一个post-start检查脚本,等fs_cli能成功执行sofia status后再拉起keepalived。这样即便在切换场景下,备机的keepalived抢到VIP之前,fs已经完成启动,业务恢复时间主要取决于SIP注册周期和呼叫路由的响应速度。
5.4 双主脑裂的检测与处理
一旦怀疑发生脑裂,第一时间在两台机器上同时执行ip addr show eth0,如果两边都能看到VIP,基本可以确认脑裂已经发生。紧急处理办法是先手动停掉其中一台的keepalived,让VIP收敛到另一台,然后在业务低峰期排查VRRP通信故障根因。
长期防护措施包括:使用单播模式替代组播,增加VRRP认证,配置独立心跳检测脚本,有条件的情况下用两台交换机做链路冗余,避免单点网络故障导致心跳中断。脑裂问题的根因是网络层次的心跳通道不可靠,VRRP本身没有绝对防脑裂的机制,只能通过多重检测降低发生概率。
5.5 正在通话中的会话保持策略
不得不再次面对这个现实:keepalived方案下,主节点宕机后正在通话的会话一定中断。对于呼叫中心这类对通话连续性要求较高的场景,我建议在整体的SIP组网层面做配合。比如把fs接在SIP中继后面,中继网关检测到FS不可用后,将呼叫重新路由到备用FS;或者话机上配置两个SIP账号,一个注册主节点,一个注册备节点,故障时话机自动切换账号重新拨号。
如果业务对通话连续性要求极其严格,退路是FS集群加共享状态数据库,配合负载均衡设备做呼叫级热切换,但这套方案的复杂度和成本都不是普通keepalived能比的,大多数中小企业用本文这套VIP漂移加快速接管方案已经足够。
最后再分享一点个人体会。我最早做fs HA时,以为keepalived是万能的,后来发现真正决定切换效果的是健康检查脚本和启动顺序,VIP本身反而是最不重要的环节。还有一件事值得提醒各位:验证切换不要只在没人打电话的半夜做,最好挑一个非高峰的白天,真实地跑几路呼叫进去,观察切换后的注册重建、CDR写入、录音归档这些行为是否符合预期。高可用不只是技术问题,更是运维习惯问题,干过的人自然懂。
本文还有配套的精品资源,点击获取