Asterisk 和 FreeSWITCH 是两款主流的开源软交换平台,前者以模块化和广泛的协议兼容性著称,后者则在高并发和媒体处理方面表现突出。传统方式在物理机或虚拟机上直接安装时,经常需要处理不同系统依赖、C 库版本冲突以及配置漂移等问题。容器化可以将两个平台分别封装在独立的镜像中,通过卷挂载和网络隔离实现快速部署与回滚。不过容器化语音系统并不只是打包,SIP 信令与 RTP 媒体流的端口映射、音频设备访问以及配置持久化都需要专门设计。

镜像选择与构建思路
对于 Asterisk,官方并没有提供维护的 Docker 镜像,但社区中有多个成熟选择,例如 andrius/asterisk 和 watsco/asterisk。这些镜像通常基于 Debian 或 Alpine,内置了常用编解码器模块和启动脚本。FreeSWITCH 也有官方社区镜像,适合快速验证。选择镜像时需要关注三个点:是否包含所需的音频编解码器,是否以非 root 用户运行,以及是否便于注入自定义配置。
如果对安全要求较高,建议基于官方基础镜像自行构建。下面是一个简化后的 Asterisk 镜像构建示例,重点展示如何从 Debian 安装 asterisk 并保持前台运行。
FROM debian:bookworm-slim RUN apt-get update && apt-get install -y --no-install-recommends asterisk && rm -rf /var/lib/apt/lists/* COPY asterisk.conf /etc/asterisk/asterisk.conf COPY pjsip.conf /etc/asterisk/pjsip.conf EXPOSE 5060/udp 10000-20000/udp CMD ["asterisk", "-f"]
这段 Dockerfile 使用 asterisk -f 让进程在前台运行,避免容器启动后立即退出。实际生产环境中还应该挂载配置目录,而不是把配置直接拷贝进镜像,这样可以更方便地修改拨号计划而无需重新构建镜像。
构建 FreeSWITCH 镜像的思路类似,但依赖更多。FreeSWITCH 通常需要编译安装,如果不想等待编译,可以选用已经编译好的社区镜像。无论选择哪种方式,都建议固定镜像标签,避免 latest 标签在升级时带入未预期的变更。
Docker Compose 编排实战
使用 Docker Compose 可以同时管理 Asterisk 和 FreeSWITCH 两个服务,并把它们接入同一个自定义网络。下面是一个完整的编排文件示例,包含 UDP 端口映射、配置目录挂载以及重启策略。
version: "3.9"
services:
asterisk:
image: andrius/asterisk:latest
container_name: asterisk
restart: unless-stopped
ports:
- "5060:5060/udp"
- "10000-10100:10000-10100/udp"
volumes:
- ./asterisk-conf:/etc/asterisk
- ./asterisk-sounds:/var/lib/asterisk/sounds
environment:
- TZ=Asia/Shanghai
network_mode: bridge
freeswitch:
image: watsco/freeswitch:latest
container_name: freeswitch
restart: unless-stopped
ports:
- "5080:5080/udp"
- "16384-16484:16384-16484/udp"
volumes:
- ./freeswitch-conf:/etc/freeswitch
- ./freeswitch-sounds:/usr/share/freeswitch/sounds
environment:
- TZ=Asia/Shanghai
network_mode: bridge
需要注意的是,UDP 端口映射在 Docker 中会经过一次 iptables NAT 转换,如果容器没有正确获取到公网地址,SIP 消息中的 SDP 会携带私有地址,导致单通或无法建立媒体流。因此上面的端口映射方案适合测试环境,生产环境通常需要配合 externip 或 localnet 参数修正。
两个服务都使用了 network_mode: bridge 这一默认模式,容器之间可以通过服务名互相访问。比如 Asterisk 可以通过 freeswitch:5080 转发 SIP 消息。如果不需要容器间通信,可以去掉自定义网络,使用默认 bridge。
网络模式与媒体流处理
SIP 信令默认使用 5060 端口(UDP/TCP),RTP 媒体流则通常占用一段连续的 UDP 端口范围。在容器化环境中,最直接的部署方式是使用 host 网络模式,让容器直接共享宿主机的网络栈。这种方式避免了 NAT 带来的地址转换问题,Asterisk 和 FreeSWITCH 可以像原生安装一样绑定公网 IP 和端口。
下面对比一下 bridge 网络和 host 网络对媒体流的影响。bridge 模式下,容器外部访问 5060 端口时会经过 DNAT,但 SIP 协议在应用层携带了 IP 和端口信息,如果应用没有感知到被映射后的地址,就会在 SDP 中写入容器内部 IP,导致远端无法回连 RTP 流。解决办法是在配置文件中设置 externip 为宿主机公网地址,并设置 localnet 为容器子网,让 Asterisk 或 FreeSWITCH 对来源地址做判断。
下面是 Asterisk 的 pjsip.conf 中常用的网络配置片段,供参考:
[transport-udp] type=transport protocol=udp bind=0.0.0.0:5060 external_media_address=203.0.113.10 external_signaling_address=203.0.113.10 local_net=172.16.0.0/12
FreeSWITCH 的 SIP 配置位于 XML 文件中,需要修改 <param name="ext-sip-ip" value="auto"/> 和 <param name="ext-rtp-ip" value="auto"/> 等参数。在容器中建议将这些参数设置为实际的公网 IP,或者通过环境变量注入。对于内网通信,使用 host 模式加上 auto-nat 也能工作,但跨主机和公网场景必须显式指定。
另外,RTP 端口范围在容器中应该尽量缩小并连续映射,否则容易出现丢包或对称性错误。Asterisk 默认使用 10000-20000,FreeSWITCH 默认使用 16384-32768,如果使用 bridge 映射,建议映射完整范围。
持久化、升级与 Kubernetes 注意事项
容器化的一个重要原则是配置与镜像分离。Asterisk 的配置目录通常是 /etc/asterisk,录音和语音文件在 /var/lib/asterisk;FreeSWITCH 的配置目录是 /etc/freeswitch,录音文件在 /var/lib/freeswitch。应该把这些路径通过卷挂载到宿主机或网络存储,避免容器重建后数据丢失。
升级时,可以先用相同配置启动新版本容器,验证通话功能正常后再滚动替换旧容器。对于 Docker Compose,可以执行 docker compose pull && docker compose up -d,但建议先备份配置目录。如果配置格式在新版本中有较大变化,应在测试环境先验证。不要同时升级 Asterisk 和 FreeSWITCH,因为两者可能需要配合调试。
在 Kubernetes 中部署软交换时,情况更复杂。由于 SIP 和 RTP 依赖固定的 IP 和大量 UDP 端口,一般建议使用 hostNetwork: true 让 Pod 直接使用节点网络,避免 Service 的负载均衡干扰 SIP 会话。这就意味着每个节点只能运行一个实例,并且需要通过节点选择器固定调度。健康检查可以使用 SIP 端口探针,或者执行 asterisk -rx "core show channels" 这类命令来判断进程是否正常。
对于需要高可用的场景,不能简单依靠 Kubernetes 的副本数来扩展,因为 SIP 通话状态是粘性的。可以借助外部数据库或内存共享方案将会话状态外置,但这会引入新的复杂度。更务实的做法是为每个语音服务单独部署,使用 DNS SRV 或负载均衡器做故障转移。
容器化 Asterisk 与 FreeSWITCH 能够显著降低部署成本和环境冲突,但需要仔细处理网络、配置持久化和升级策略。理解 SIP 与 RTP 的工作方式后,再结合 Docker Compose 或 Kubernetes 的特性,可以构建出稳定且易于维护的语音平台。
容器化AsteriskFreeSWITCH修改时间:2026-08-27 06:23:41