FreeSWITCH高可用实战:keepalived双机热备与VIP漂移切换方案 📅 发布时间:2026/9/7 8:07:02 👁 浏览次数: 简介面向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、corosyncpcs、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.11fs01CentOS 7.9 / Ubuntu 22.04备节点192.168.1.12fs02CentOS 7.9 / Ubuntu 22.04虚拟IP192.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-rulerule protocol valuevrrp 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主节点失败后100105大于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 21; 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/ \ root192.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脚本误判成异常导致备机频繁接管VIPSIP话机不停重注册业务反而更不稳定。后来改成检查进程存活加fs_cli返回状态问题才解决。还有一类情况是脚本没有加timeoutfs负载高时命令返回慢脚本执行时间超过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写入、录音归档这些行为是否符合预期。高可用不只是技术问题更是运维习惯问题干过的人自然懂。本文还有配套的精品资源点击获取