FreePBX 17 Docker Compose 部署指南
Docker Compose 部署 FreePBX 17 实战:escomputers/freepbx:17-nofail2ban + MariaDB 10.11 双容器编排(栈 freepbx-17-stack)、php install 数据库初始化、Web 向导、sipsettings/bulkhandler 补装、ionCube 加载器安装、分机批量导入与 sipsak 注册验证、三卷持久化与等价 docker run(2026-10-26 实测)。
FreePBX 17 Docker Compose 部署指南
目标:用 Docker Compose 一键拉起 FreePBX 17 + Asterisk + MariaDB 双容器栈,完成数据库初始化、Web 向导、模块补装、ionCube 加载器安装与 SIP 分机注册验证。全流程已在 Docker Engine 29.9.2 + Compose v5.6.1 环境实测通过(栈名
freepbx-17-stack)。
一、环境信息
- 宿主操作系统:Linux x86_64(实测 Debian 12 bookworm)。
- Docker Engine:29.9.2(实测
docker --version→Docker version 29.9.2, build a82e528)。 - Docker Compose:v5.6.1(实测
docker compose version→Docker Compose version v5.6.1)。 - 镜像清单(实测):
escomputers/freepbx:17-nofail2ban(3.21GB)——FreePBX 17 全家桶;mariadb:10.11(459MB)——数据库。
- Compose 栈名:
freepbx-17-stack;容器名freepbx-17-stack-freepbx-1、freepbx-17-stack-db-1(Compose 按「栈名-服务名-序号」自动生成)。 - 专用网络:
freepbx-17-net(10.89.72.0/24,固定 IP:db=10.89.72.30、freepbx=10.89.72.40);SIP 测试客户端 10.89.72.50。 - 宿主端口映射:8137→80(Web HTTP)、8140→443(HTTPS)、5062→5060/udp(SIP 信令)、16384-16484/udp(RTP 媒体)。
- 容器内软件版本(实测):FreePBX framework 17.0.33.2(模块在线升级后)、Asterisk 21.10.2(镜像自带)、PHP 8.2.31、MariaDB 10.11、Apache 2.4.65、NodeJS 18.20.4。
二、镜像选型说明
FreePBX 官方不提供容器镜像,社区镜像中escomputers/freepbx维护活跃、开箱即用。选型对照如下:
| 维度 | escomputers/freepbx | tiredofit/freepbx |
|---|---|---|
| 基础系统 | debian:bookworm-slim | debian(s6-overlay 多进程管理) |
| 内置组件 | Asterisk + FreePBX + PHP + Apache + MariaDB 客户端 | Asterisk + FreePBX + PHP + Apache(数据库外置) |
| 定制性 | 单一 entrypoint,结构简单 | 环境变量驱动,可配置项多、较复杂 |
| 上手成本 | 低(适合快速验证/中小部署) | 中高(适合需要深度定制的场景) |
| 实测结论 | 本文采用,17 全家桶完整可用 | 未深入实测 |
选择 escomputers/freepbx 的理由:镜像把 LAMP + Asterisk + FreePBX 源码(/usr/local/src/freepbx)全部打齐,entrypoint 脚本(/usr/local/src/entrypoint.sh)负责拉起 cron、postfix、Asterisk 与 Apache,数据库初始化步骤清晰可控。标签选择 17-nofail2ban(不带 fail2ban 的 17 变体),安全防护交给宿主层统一做,容器内组件更精简。
💡 说明:该镜像的组件版本以镜像发布时为准(README 标注 FreePBX 17.0.21 / Asterisk 21.10.2 / MariaDB 10.11.14 / Apache 2.4.65 / NodeJS 18.20.4)。FreePBX 框架版本可通过
fwconsole ma upgradeall在线升级到模块市场最新(实测升级到 17.0.33.2);Asterisk 大版本跟随镜像,容器内不能换。
镜像不支持的组件:DAHDI 通道(PCI 语音卡直通场景不适用,需物理机安装,见 《Debian 12 安装 FreePBX 17 指南》)。
三、前置条件
- Docker 与 Compose 插件已安装:
docker --version
docker compose version
Docker version 29.9.2, build a82e528
Docker Compose version v5.6.1
- 创建专用外部网络(固定 IP 需要;不固定 IP 可省略此步并把 compose 里的网络定义改为普通内部网络):
docker network create freepbx-17-net --subnet 10.89.72.0/24
创建成功会输出网络 ID,并可用 docker network ls | grep freepbx-17-net 复核。
- 规划部署目录(compose 与配置文件集中存放):
mkdir -p /opt/freepbx-17-stack && cd /opt/freepbx-17-stack
- 端口占用检查(8137/8140/5062/16384-16484 需空闲):
ss -lntup | grep -E ':(8137|8140|5062)\b'
(无输出即端口空闲)
四、部署步骤
4.1 编写 docker-compose.yaml
在 /opt/freepbx-17-stack/docker-compose.yaml 写入(与实测文件一致):
name: freepbx-17-stack
volumes:
mysql_data:
var_data:
etc_data:
networks:
defaultnet:
external: true
name: freepbx-17-net
services:
db:
restart: always
image: mariadb:10.11
environment:
MARIADB_ROOT_PASSWORD: q1w2e3r4
MARIADB_DATABASE: asterisk
MARIADB_USER: freepbxuser
MARIADB_PASSWORD: q1w2e3r4
volumes:
- mysql_data:/var/lib/mysql
- ./my.cnf:/etc/mysql/conf.d/zz-freepbx.cnf:ro
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
defaultnet:
ipv4_address: 10.89.72.30
freepbx:
restart: always
image: escomputers/freepbx:17-nofail2ban
volumes:
- var_data:/var
- etc_data:/etc
networks:
defaultnet:
ipv4_address: 10.89.72.40
ports:
- "8137:80/tcp"
- "8140:443/tcp"
- "5062:5060/udp"
- "16384-16484:16384-16484/udp"
depends_on:
- db
4.2 编写 MariaDB 配置与初始化 SQL
my.cnf(FreePBX 对 SQL 模式有要求,必须放宽默认严格模式):
[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION
💡 说明:MariaDB 默认
sql_mode包含STRICT_TRANS_TABLES等严格项,FreePBX 的模块安装与 CDR 写入对空值/隐式转换敏感,遗留严格模式会报错。NO_ENGINE_SUBSTITUTION是 FreePBX 官方安装脚本实际采用的 SQL 模式,容器场景同样适用。文件名带zz-前缀保证它在 MariaDB 官方镜像的默认配置之后加载。
init.sql(首次初始化时建库并授权):
CREATE DATABASE IF NOT EXISTS asterisk;
CREATE DATABASE IF NOT EXISTS asteriskcdrdb;
GRANT ALL PRIVILEGES ON `asterisk`.* TO 'freepbxuser'@'%';
GRANT ALL PRIVILEGES ON `asteriskcdrdb`.* TO 'freepbxuser'@'%';
FLUSH PRIVILEGES;
⚠️ 注意:
init.sql挂载到 MariaDB 官方镜像的/docker-entrypoint-initdb.d/,仅在 mysql_data 卷为空(首次启动)时执行。复用旧卷重建栈时不会重跑,需按十、10.4 节手动补授权。
4.3 启动栈
docker compose up -d
实测状态:
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
freepbx-17-stack-db-1 mariadb:10.11 "docker-entrypoint.s…" db 3 hours ago Up 3 hours 3306/tcp
freepbx-17-stack-freepbx-1 escomputers/freepbx:17-nofail2ban "/usr/bin/env bash /…" freepbx 3 hours ago Up 2 hours 0.0.0.0:16384-16484->16384-16484/udp, [::]:16384-16484->16384-16484/udp, 0.0.0.0:8137->80/tcp, [::]:8137->80/tcp, 0.0.0.0:8140->443/tcp, [::]:8140->443/tcp, 0.0.0.0:5062->5060/udp, [::]:5062->5060/udp
验证数据库就绪:
docker exec freepbx-17-stack-db-1 mariadb -u root -pq1w2e3r4 -e 'show databases;'
Database
asterisk
asteriskcdrdb
information_schema
mysql
performance_schema
sys
asterisk 与 asteriskcdrdb 两个库已由 init.sql 建好,即可进入关键的第 4.4 步。
4.4 FreePBX 数据库初始化(关键步骤)
镜像的 entrypoint 不会自动安装 FreePBX 数据库(entrypoint 只拉起 cron/postfix/Asterisk/Apache),首次部署必须手动执行框架安装。进入容器,在框架源码目录执行:
docker exec -it freepbx-17-stack-freepbx-1 bash -c 'cd /usr/local/src/freepbx && php install -n --dbuser=freepbxuser --dbpass=q1w2e3r4 --dbhost=db'
参数说明:-n 非交互模式;--dbuser/--dbpass 使用 init.sql 授权的 freepbxuser;--dbhost 用 Compose 网络中的 db 服务名(固定 IP 场景写 10.89.72.30 亦可)。
执行完成后验证框架可用(实测回显):
docker exec freepbx-17-stack-freepbx-1 fwconsole --version
FW Console - FreePBX Utility 17.0.33.2
docker exec freepbx-17-stack-freepbx-1 asterisk -V
Asterisk 21.10.2
⚠️ 注意:此时 Web 界面已可访问但尚未创建管理员账号,需先完成 4.5 的初始化向导。
4.5 Web 初始化向导(创建 admin 账号)
浏览器访问 http://<宿主IP>:8137/,首次进入强制进入初始化向导,按表单填写(与裸机安装的向导一致):
| 表单字段 | 说明 | 实测填写值 |
|---|---|---|
| Username | 管理员用户名 | admin |
| Password1 / Password2 | 管理员密码(两次一致) | q1w2e3r4 |
| 管理员邮箱 | admin@example.lan | |
| System Identifier | 系统标识 | pbx-compose-01 |
| Automatic Module Updates | 自动模块更新 | Disabled(生产建议手动) |
| Automatic Security Updates | 安全更新自动安装 | Enabled |
| Update Every / Update Period | 更新检查周期/时段 | Daily / Nightly |
提交后进入管理后台。验证账号落库(实测回显):
docker exec freepbx-17-stack-db-1 mariadb -u freepbxuser -pq1w2e3r4 asterisk -e 'select username from ampusers;'
username
admin
4.6 补装批量导入等模块
该镜像内置模块为精简开源集(实测 fwconsole ma list | grep -c Enabled 仅 14 个),批量导入分机所需的 bulkhandler 与 SIP 参数配置所需的 sipsettings 均需在线补装(需可访问 mirror.freepbx.org):
docker exec -it freepbx-17-stack-freepbx-1 fwconsole ma downloadinstall sipsettings
docker exec -it freepbx-17-stack-freepbx-1 fwconsole ma downloadinstall bulkhandler
docker exec freepbx-17-stack-freepbx-1 fwconsole reload
💡 说明:
downloadinstall会自动解析并安装依赖——sipsettings 会连带装上 filestore 与 backup(实测 sipsettings 17.0.6.10、filestore 17.0.3.2、backup 17.0.13),bulkhandler 实测装到 17.0.7.1。安装中途卡住多为到 mirror.freepbx.org 的网络不稳,重跑同一命令即可续装。
补装后的模块版本抽样(实测回显):
| backup | 17.0.13 | Enabled | GPLv3+ | Sangoma |
| bulkhandler | 17.0.7.1 | Enabled | GPLv3+ | Sangoma |
| core | 17.0.18.54 | Enabled | GPLv3+ | Sangoma |
| filestore | 17.0.3.2 | Enabled | AGPLv3 | Sangoma |
| framework | 17.0.33.2 | Enabled | GPLv2+ | Sangoma |
| sipsettings | 17.0.6.10 | Enabled | AGPLv3+ | Sangoma |
4.7 安装 ionCube 加载器(可选但建议)
镜像未内置 ionCube Loader,未安装时部分依赖加密的模块不可用。安装步骤(实测):
cd /tmp
wget https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz
tar -xzf ioncube_loaders_lin_x86-64.tar.gz
拷贝 PHP 8.2 对应的 loader 进容器(PHP 8.2 的扩展目录为 /usr/lib64/php/20220829):
docker cp ioncube/ioncube_loader_lin_8.2.so freepbx-17-stack-freepbx-1:/usr/lib64/php/20220829/ioncube_loader_lin_8.2.so
写入 CLI 与 Apache 两套 PHP 的加载配置:
docker exec freepbx-17-stack-freepbx-1 bash -c 'echo "zend_extension=/usr/lib64/php/20220829/ioncube_loader_lin_8.2.so" > /etc/php/8.2/cli/conf.d/00-ioncube.ini && echo "zend_extension=/usr/lib64/php/20220829/ioncube_loader_lin_8.2.so" > /etc/php/8.2/apache2/conf.d/00-ioncube.ini'
重启 freepbx 服务使 loader 生效,并验证:
docker compose restart freepbx
docker exec freepbx-17-stack-freepbx-1 php -m | grep -i ioncube
ionCube Loader
the ionCube PHP Loader
loader 文件校验(实测 sha256):
27563b15c28e63800c74defe39fad06229b68267734187bf088f47a9170c985e ioncube_loader_lin_8.2.so
⚠️ 注意:
docker cp的文件位于容器可写层、不在数据卷内,容器重建(docker compose down后重新up)会丢失,需重做本节(根治方案见十一、11.5)。
4.8 创建分机并验证 SIP 注册
用 bulkhandler 批量导入分机 2001:
docker exec freepbx-17-stack-freepbx-1 bash -c 'printf "extension,name,tech,secret,voicemail,ringtimer,noanswer,recording,outboundcid,sipname,id,devicetype,user,description\n2001,Sales-01,pjsip,q1w2e3r4,disabled,0,,inherit,,2001,,fixed,,Sales desk extension\n" > /tmp/ext.csv'
docker exec -it freepbx-17-stack-freepbx-1 fwconsole bulkimport --type=extensions --replace /tmp/ext.csv
Importing bulk extensions
确认端点生成:
docker exec freepbx-17-stack-freepbx-1 asterisk -rx 'pjsip show endpoints' | grep 'Endpoint:.*2001'
Endpoint: 2001/2001 Unavailable 0 of inf
在测试客户端用 sipsak 发起注册(客户端 IP 10.89.72.50,PBX 容器 IP 10.89.72.40):
sipsak -U -s sip:2001@10.89.72.40 -H 10.89.72.50 --auth-username=2001 --password=q1w2e3r4
PBX 侧确认注册落地(实测回显):
docker exec freepbx-17-stack-freepbx-1 asterisk -rx 'pjsip show contacts'
Contact: <Aor/ContactUri..............................> <Hash....> <Status> <RTT(ms)..>
Contact: 2001/sip:2001@10.89.72.50:55571 ce7f0fc0b2 NonQual nan
Objects found: 1
注册链路打通,Compose 部署全流程完成。
五、数据持久化说明
栈使用三个命名卷(docker volume ls 实测输出):
local freepbx-17-stack_etc_data
local freepbx-17-stack_mysql_data
local freepbx-17-stack_var_data
| 卷 | 挂载点(容器内) | 存放内容 | 重要性 |
|---|---|---|---|
| freepbx-17-stack_mysql_data | /var/lib/mysql | 两个业务库(asterisk 配置数据 + asteriskcdrdb 通话记录) | 最高(删库即丢全部配置与话单) |
| freepbx-17-stack_etc_data | /etc | Asterisk 与 FreePBX 全部配置文件 | 高(可用 fwconsole 从库重建,但耗时) |
| freepbx-17-stack_var_data | /var | 录音、话单 spool、日志、模块二进制、cron 数据 | 高(含录音/话单原始文件) |
要点:
- 三个卷均为命名卷,
docker compose down不删除卷;docker compose down -v会连数据一起删除,生产环境禁止随手加-v。 - ionCube loader 与手工放进容器的其他文件(如 /tmp/ext.csv)不在卷内,重建即丢。
- 备份示例(配置库 + 通话记录库导出到宿主当前目录):
docker exec freepbx-17-stack-db-1 mariadb-dump -u root -pq1w2e3r4 --databases asterisk asteriskcdrdb > freepbx-db-backup.sql
六、环境变量表
仅 db 服务使用环境变量(mariadb 官方镜像约定),freepbx 服务的 entrypoint 不读取任何环境变量:
| 变量 | 实测值 | 说明 |
|---|---|---|
| MARIADB_ROOT_PASSWORD | q1w2e3r4 | root 密码,运维/备份用 |
| MARIADB_DATABASE | asterisk | 首次初始化自动创建的库名(init.sql 亦会创建,双保险) |
| MARIADB_USER | freepbxuser | 业务账号(FreePBX 连库用) |
| MARIADB_PASSWORD | q1w2e3r4 | 业务账号密码,需与 4.4 节 php install --dbpass 传值一致 |
⚠️ 注意:修改 MARIADB_* 环境变量对已初始化的卷不生效(root 密码、用户只在首次建库时写入),改完需清卷重建或手动在库内改密。
七、配置参数说明
7.1 compose 关键参数逐项
| 参数 | 取值 | 说明 |
|---|---|---|
name | freepbx-17-stack | 显式指定栈名,决定容器/卷/网络资源命名前缀,避免随目录名漂移 |
restart: always | 两服务均开启 | 宿主重启或进程崩溃后自动拉起 |
networks.defaultnet.external | freepbx-17-net | 复用预建外部网络并固定 IP;固定 IP 不是必需项,删去 ipv4_address 后走 DHCP 亦可(服务名 db 的 DNS 解析不受影响) |
depends_on | freepbx 依赖 db | 只保证启动顺序(db 先起),不保证 MariaDB 已就绪——php install 失败时等几秒重试即可 |
ports | 见 7.2 | Web/SIP/RTP 三类端口映射 |
volumes | 三卷 + 两个 bind 挂载 | 见第八章 |
7.2 端口映射与 RTP 范围
| 映射 | 用途 | 说明 |
|---|---|---|
| 8137:80/tcp | Web 管理台 HTTP | 浏览器访问 http://宿主IP:8137 |
| 8140:443/tcp | Web 管理台 HTTPS | 镜像自带自签证书,浏览器会告警 |
| 5062:5060/udp | SIP 信令 | 宿主侧用 5062 避让宿主上可能存在的其他 PBX |
| 16384-16484:16384-16484/udp | RTP 媒体 | 见下方说明 |
RTP 范围说明(实测):容器内 Asterisk 默认 RTP 范围为 10000-20000(asterisk -rx 'rtp show settings' 输出 Port start: 10000 / Port end: 20000)。本次映射只开放 16384-16484 共 101 个 UDP 端口,原因是 Docker 大段 UDP 端口映射会生成海量转发规则、消耗宿主资源(镜像 README 引用的上游问题 moby#11185)。要让媒体流完全走通宿主 NAT,应在 FreePBX Web 界面 Settings → Asterisk SIP Settings → RTP Settings 中把 RTP Port Start/End 改为 16384/16484 并 Apply Config,使实际媒体范围与映射段一致。
7.3 SQL 模式
my.cnf 中 sql_mode=NO_ENGINE_SUBSTITUTION 与 FreePBX 官方安装脚本一致,避免严格模式导致模块安装与 CDR 写入报错(详见 4.2 节说明)。
八、配置文件挂载
| 挂载 | 方向 | 作用 |
|---|---|---|
./my.cnf:/etc/mysql/conf.d/zz-freepbx.cnf:ro | 宿主→db 容器 | 注入 FreePBX 兼容的 SQL 模式;zz- 前缀保证加载顺序在镜像默认配置之后 |
./init.sql:/docker-entrypoint-initdb.d/init.sql:ro | 宿主→db 容器 | 首次建库授权;仅空卷首启执行(见 4.2 节 ⚠️) |
mysql_data:/var/lib/mysql | 命名卷 | 数据库文件持久化 |
var_data:/var | 命名卷 | freepbx 容器数据持久化 |
etc_data:/etc | 命名卷 | freepbx 容器配置持久化 |
💡 说明:
/etc、/var整目录挂卷是 FreePBX 容器化的事实做法——FreePBX 的运行时状态分散在/etc/asterisk、/var/lib/asterisk、/var/spool/asterisk、/var/www/html等处,整目录挂卷比逐个子目录挂载可靠。ionCube loader 位于/usr/lib64/php/(不在上述卷内),是唯一需要重建后重做的文件。
九、等价 docker run
不用 Compose 时,以下两条 docker run 与 compose 定义完全等价(逐条给出,先 db 后 freepbx):
docker network create freepbx-17-net --subnet 10.89.72.0/24
docker run -d --name freepbx-17-stack-db-1 \
--restart always \
--network freepbx-17-net --ip 10.89.72.30 \
-e MARIADB_ROOT_PASSWORD=q1w2e3r4 \
-e MARIADB_DATABASE=asterisk \
-e MARIADB_USER=freepbxuser \
-e MARIADB_PASSWORD=q1w2e3r4 \
-v freepbx-17-stack_mysql_data:/var/lib/mysql \
-v /opt/freepbx-17-stack/my.cnf:/etc/mysql/conf.d/zz-freepbx.cnf:ro \
-v /opt/freepbx-17-stack/init.sql:/docker-entrypoint-initdb.d/init.sql:ro \
mariadb:10.11
docker run -d --name freepbx-17-stack-freepbx-1 \
--restart always \
--network freepbx-17-net --ip 10.89.72.40 \
-p 8137:80/tcp -p 8140:443/tcp \
-p 5062:5060/udp \
-p 16384-16484:16384-16484/udp \
-v freepbx-17-stack_var_data:/var \
-v freepbx-17-stack_etc_data:/etc \
escomputers/freepbx:17-nofail2ban
要点:--ip 固定 IP 依赖 --network 指向的用户自定义网络;两个卷需先 docker volume create(或省略让 Docker 自动创建)。后续的 4.4-4.8 步骤与 Compose 场景完全相同。
十、常用运维命令
10.1 日常状态与日志
docker compose ps # 栈内容器状态
docker compose logs -f freepbx # 跟踪 freepbx 容器日志(Apache/Asterisk 启动输出)
docker compose restart freepbx # 重启 Web/PBX 容器(ionCube 安装后必做)
docker compose down # 停栈(保留三个数据卷)
10.2 容器内 fwconsole 操作
FreePBX 全部 CLI 能力都在 freepbx 容器内:
docker exec freepbx-17-stack-freepbx-1 fwconsole --version
docker exec freepbx-17-stack-freepbx-1 fwconsole ma list # 模块清单
docker exec freepbx-17-stack-freepbx-1 fwconsole ma upgradeall # 模块在线升级
docker exec freepbx-17-stack-freepbx-1 fwconsole reload # 应用配置
docker exec freepbx-17-stack-freepbx-1 asterisk -rx 'pjsip show endpoints'
10.3 数据库备份与恢复
# 备份(配置库 + 通话记录库)
docker exec freepbx-17-stack-db-1 mariadb-dump -u root -pq1w2e3r4 --databases asterisk asteriskcdrdb > freepbx-db-backup.sql
# 恢复
docker exec -i freepbx-17-stack-db-1 mariadb -u root -pq1w2e3r4 < freepbx-db-backup.sql
10.4 复用旧卷时手动补授权
栈重建但卷保留时,init.sql 不会重跑;若此时新换了 MARIADB_USER/MARIADB_PASSWORD,需手动补授权:
docker exec freepbx-17-stack-db-1 mariadb -u root -pq1w2e3r4 -e "GRANT ALL PRIVILEGES ON \`asterisk\`.* TO 'freepbxuser'@'%' IDENTIFIED BY 'q1w2e3r4'; GRANT ALL PRIVILEGES ON \`asteriskcdrdb\`.* TO 'freepbxuser'@'%' IDENTIFIED BY 'q1w2e3r4'; FLUSH PRIVILEGES;"
10.5 数据卷备份
docker run --rm -v freepbx-17-stack_var_data:/data:ro -v /opt/freepbx-17-stack:/backup alpine tar czf /backup/var-data.tar.gz -C /data .
十一、常见问题(FAQ)
11.1 php install 报数据库连接失败
现象:4.4 节的 php install -n --dbuser=freepbxuser --dbpass=q1w2e3r4 --dbhost=db 报连不上 db 或授权被拒。排查顺序:
docker compose ps确认 db 容器 Up;docker exec freepbx-17-stack-db-1 mariadb -u freepbxuser -pq1w2e3r4 -e 'select 1;'验证业务账号可用;- 若报
Access denied,多半是 mysql_data 卷非空导致 init.sql 未执行(见 10.4 节手动补授权),或 MARIADB_PASSWORD 与 php install 的--dbpass不一致。
11.2 Web 界面部分模块报 ionCube 相关错误
现象:模块市场或部分模块页面提示 ionCube/loader 缺失。原因:镜像未内置 ionCube Loader(实测 php -m | grep -i ioncube 无输出)。处理:按 4.7 节安装 loader 并 docker compose restart freepbx,复查 php -m 出现 ionCube Loader 即恢复。
11.3 手改 /etc/asterisk 下的配置文件被自动覆盖
现象:直接编辑 /etc/asterisk/rtp_additional.conf 等文件,fwconsole reload 后改动丢失,文件恢复为 FreePBX 生成的值。实测该文件头部即有声明:
; Do NOT edit this file as it is auto-generated by FreePBX. ;
原因:FreePBX 17 的模块(BMO 架构)以数据库中的设置为准,reload 时自动重生成这些配置文件。处理:改动必须走 Web 界面对应模块(如 RTP 范围在 Settings → Asterisk SIP Settings → RTP Settings),修改后 Apply Config。确需追加自定义参数时使用 FreePBX 的自定义文件(如 extensions_custom.conf、pjsip_custom.conf),这类文件不受重生成影响。
11.4 SIP 注册成功但通话无音频/单向音频
原因:RTP 媒体端口范围与宿主映射段不一致(容器默认 10000-20000,仅映射了 16384-16484),超出映射段的媒体包被宿主丢弃。处理:按 7.2 节把 FreePBX 的 RTP Port Start/End 改为 16384/16484 并 Apply Config;或者反过来扩大映射段覆盖实际 RTP 范围(注意大段 UDP 映射的资源开销,见 7.2 节)。
11.5 容器重建后 ionCube 又丢了
原因:loader 经 docker cp 写入容器可写层,不在 /var、/etc 数据卷内,docker compose down && up 即丢。根治方案:基于该镜像构建自己的镜像层,把 loader 与 ini 固化进去:
FROM escomputers/freepbx:17-nofail2ban
COPY ioncube_loader_lin_8.2.so /usr/lib64/php/20220829/ioncube_loader_lin_8.2.so
COPY 00-ioncube.ini /etc/php/8.2/cli/conf.d/00-ioncube.ini
COPY 00-ioncube.ini /etc/php/8.2/apache2/conf.d/00-ioncube.ini
docker build -t freepbx17-ioncube .
然后把 compose 中 freepbx 服务的 image: 改为 freepbx17-ioncube,重建后永久生效。
11.6 fwconsole ma downloadinstall 长时间卡住
原因:模块市场 mirror.freepbx.org 网络不稳(容器内 DNS 或出口带宽问题)。处理:Ctrl+C 中断后重跑同一命令可续装;多次失败时检查容器 DNS(docker exec freepbx-17-stack-freepbx-1 cat /etc/resolv.conf)与到 mirror.freepbx.org 的连通性。
11.7 端口 5062 收到注册但宿主防火墙拦截 RTP
现象:注册正常、拨打回铃音正常但接通后无声音。排查:宿主防火墙(ufw/firewalld/云安全组)需放行映射的 UDP 段 16384-16484;仅放行 TCP 的 Web 端口不够,RTP 全部走 UDP。
十二、参考链接
- escomputers/freepbx 镜像主页:https://hub.docker.com/r/escomputers/freepbx
- MariaDB 官方镜像文档:https://hub.docker.com/_/mariadb
- FreePBX 官网:https://www.freepbx.org/
- FreePBX 官方安装脚本仓库(裸机安装路径):https://github.com/FreePBX/sng_freepbx_debian_install
- ionCube loaders 下载页:https://www.ioncube.com/loaders.php
- Docker 大段端口映射上游问题:https://github.com/moby/moby/issues/11185
- 专栏前篇 《FreePBX 17 介绍与选型指南》、《Debian 12 安装 FreePBX 17 指南》