feat(agent-call): add repeatable Asterisk bootstrap

This commit is contained in:
2026-09-13 14:30:00 +08:00
parent 5c9165cb4b
commit d30314c7d1
6 changed files with 363 additions and 1 deletions
@@ -120,6 +120,8 @@ Mock多实例、固定端口和媒体关联方式存在简化,顺序成功不
推荐初版:A/B分别运行在独立Linux主机,Asterisk使用host network;中间件通过管理网访问ARI,媒体组件与节点双向可达。这样避免将仓库同一Docker私网中的音频可达性误认为真实外网可达性。
单个真实 SIP 测试 Cell 的可重复 ECS UserData 安装流程已落地于`docs/真实SIP_Asterisk快速部署_v1.0.md`,脚本为`deploy/asterisk_bootstrap.sh`和`deploy/build_asterisk_userdata.py`;本文件保留手工部署与验收边界,不把示例参数当成已授权生产线路。
同机运行两个host-network节点会争用5060、8088和RTP端口,不允许直接照搬。若必须同机双节点,需另行设计完整端口/地址及NAT映射,不能只改变ARI端口。
| 流向 | 放通与限制 |
+132
View File
@@ -0,0 +1,132 @@
# 真实 SIP/Asterisk 快速部署(v1.0)
本流程用于在北京 `cn-beijing` 使用固定 EIP `123.56.71.98` 部署单个真实 SIP 测试 Cell。默认只计划,不创建资源;只有明确执行 `--apply` 才会创建/绑定 ECS。
> 这是单路联调底座,不是生产 1000 路容量方案。竞价实例使用 `SpotAsPriceGo` 时可能被回收并中断通话。
## 1. 已落地脚本
| 文件 | 用途 |
| --- | --- |
| `deploy/render_asterisk.py` | 校验并生成 `http.conf`、`ari.conf`、`pjsip.conf`、`rtp.conf`、`extensions.conf` |
| `deploy/asterisk_bootstrap.sh` | ECS UserData:安装 Docker、拉取 digest 镜像、安装配置、启动并检查 Asterisk |
| `deploy/build_asterisk_userdata.py` | 将渲染配置安全嵌入 UserData;输出包含 ARI 密钥,必须放在 600 权限的本地目录 |
| `deploy/aliyun_host.py` | 北京 ECS/EIP 只读计划与显式 `--apply` 创建/绑定,支持 `SpotAsPriceGo` |
| `deploy/asterisk.sh` | 本地 Compose 配置检查;只有 `up` 才启动服务 |
## 2. 准备本地受限目录
不要把 ARI 密钥、SIP 密码、SSH 私钥写入仓库、命令日志或聊天。以下目录已被 `.gitignore` 忽略:
```bash
set -euo pipefail
umask 077
mkdir -p .local/agent-call
openssl rand -hex 32 > .local/agent-call/ari-password
chmod 600 .local/agent-call/ari-password
```
准备 `.local/agent-call/asterisk.json`。下面只放线路参数,不放密码;`from_user`/`caller_id` 必须按供应商规则确认:
```json
{
"public_ip": "123.56.71.98",
"transport": "udp",
"local_net": "172.16.0.0/12",
"ari_bind": "127.0.0.1",
"primary": {
"host": "61.132.228.221",
"port": 5060,
"auth_mode": "ip",
"register": false,
"from_user": "BD93205882",
"caller_id": "BD93205882 <BD93205882>"
},
"backup": null
}
```
生成一次性 UserData。镜像必须是已批准的 immutable digest:
```bash
python3 deploy/build_asterisk_userdata.py \
--config .local/agent-call/asterisk.json \
--image 'git.ipao.vip/rogee/asterisk-real@sha256:0f5e3cd0e9a86bc9dc4750929abee9ba1faae11ed39739f3e75343d39d837e41' \
--ari-password-file .local/agent-call/ari-password \
--output .local/agent-call/asterisk-user-data.sh
bash -n .local/agent-call/asterisk-user-data.sh
```
## 3. ECS 配置与部署
先查询固定 EIP、全部现有实例和项目标签;不要复用无项目归属的实例。使用专用 VSwitch、安全组、SSH KeyPair,安全组只允许供应商 SIP/RTP 来源:
```bash
python3 deploy/aliyun_host.py \
--config .local/agent-call/aliyun.json \
--state .local/agent-call/host-state.json
```
创建配置 `.local/agent-call/aliyun.json` 时至少填写:
```json
{
"region": "cn-beijing",
"public_ip": "123.56.71.98",
"project_tag": "agent-call",
"image_id": "ubuntu_24_04_x64_20G_alibase_20260828.vhd",
"instance_type": "ecs.e-c1m1.large",
"vswitch_id": "<approved-vswitch-id>",
"security_group_id": "<dedicated-security-group-id>",
"key_pair_name": "<approved-key-pair>",
"spot_strategy": "SpotAsPriceGo",
"system_disk_category": "cloud_essd",
"system_disk_gib": 40,
"system_disk_performance_level": "PL1",
"user_data_file": ".local/agent-call/asterisk-user-data.sh"
}
```
确认计划无误后才执行一次:
```bash
python3 deploy/aliyun_host.py \
--config .local/agent-call/aliyun.json \
--state .local/agent-call/host-state.json \
--apply
```
`SpotAsPriceGo` 不填写 `spot_price_limit`;创建后脚本会等待 ECS Running,再绑定既有 EIP。创建/绑定失败时保留 state 中的实例 ID,禁止自动再创建第二台。
## 4. 验证与单次外呼
UserData 完成后通过 Cloud Assistant 执行只读检查,确认容器健康、Asterisk 版本、PJSIP contact 和出站 dialplan:
```bash
# RunCommand 的实例数组使用 --InstanceId.1,不要传 JSON 数组给 --InstanceId
aliyun ecs RunCommand --RegionId cn-beijing \
--InstanceId.1 <instance-id> \
--Type RunShellScript \
--Name agent-call-readiness \
--CommandContent 'docker ps --format "{{.Names}} {{.Status}}"; docker exec agent-call-asterisk asterisk -rx "core show version"; docker exec agent-call-asterisk asterisk -rx "pjsip show endpoint provider-primary"; docker exec agent-call-asterisk asterisk -rx "dialplan show outbound"' \
--ContentEncoding PlainText \
--Timeout 60 \
--RepeatMode Once \
--KeepCommand false
```
确认供应商已经冻结目标号码、前缀、From 域、PAI 和编解码后,一次只测试一个号码;不自动重试:
```bash
docker exec agent-call-asterisk asterisk -rx \
'channel originate Local/<7089+原始被叫>@outbound application Wait 30'
```
记录完整 SIP 状态、`Reason`、RTP/录音结果和 `call_id`。`Avail`/OPTIONS 只证明探活,不证明外呼接通。
## 5. 更新与回收
- 配置变更:生成新的 UserData/配置目录,核对后再重启容器;不要覆盖正在使用的配置目录。
- 竞价回收:活动通话可能中断;EIP、实例状态、未上传录音和 outbox 必须单独对账。
- 测试环境若需回收,只删除明确标记为 `project=agent-call` 的本次实例;不要释放或解绑固定 EIP,不能删除无关实例。
- 当前供应商返回过 `488 / Q.850 cause=88 INCOMPATIBLE_DESTINATION`;未确认线路规则前不要重复拨号。
+3 -1
View File
@@ -33,6 +33,8 @@ services/asr-web/ ASR-only Go服务及Web页面
deploy/aliyun_host.py 阿里云CLI只读计划/显式apply
deploy/aliyun.example.json 实例/预算/网络参数占位
deploy/render_asterisk.py 受控配置生成
deploy/asterisk_bootstrap.sh ECS UserData安装Docker并启动Asterisk
deploy/build_asterisk_userdata.py 将配置安全嵌入UserData
deploy/asterisk.example.json SIP接入参数占位
deploy/asterisk.sh 默认检查;显式up才启动
tests/ 离线部署逻辑及PCM测试
@@ -167,7 +169,7 @@ python3 deploy/render_asterisk.py --config .local/asterisk.json
### 5.2 镜像与安全组
`.env`中的ASTERISK_IMAGE必须为批准镜像的`@sha256:`引用。本轮R1使用内部Registry中的固定digest镜像并实测Asterisk 22.10.1;该镜像验证仅覆盖单路底座,不等于生产镜像批准或容量验收。配置读取权限按镜像实际UID/GID修正为必要的组读权限,未使用chmod 777。
`.env`中的ASTERISK_IMAGE必须为批准镜像的`@sha256:`引用。本轮R1使用内部Registry中的固定digest镜像并实测Asterisk 22.10.1;该镜像验证仅覆盖单路底座,不等于生产镜像批准或容量验收。配置读取权限按镜像实际UID/GID修正为必要的组读权限,未使用chmod 777。ECS UserData快速部署步骤见`docs/真实SIP_Asterisk快速部署_v1.0.md`,生成文件包含ARI密钥,只能放在`.local/`并保持600权限。
确认:SIP服务端IP/协议/端口、RTP回程、EIP/NAT、实际VPC网段、管理来源、录音卷目录和权限。安全组只按来源和用途开放;不公开裸ARI,不清空既有防火墙。