Files
EchoChat/docs/deployment/meeting-mvp.md
bujinyuan a01e973335 feat(deploy): Task 14 完成 docker-compose 扩展 + 环境变量双态部署
- deploy/docker-compose.dev.yml 新增 media-server 服务 + coturn(profiles:public),
  全部服务参数改为 env 变量注入,go-service 依赖 media-server
- deploy/.env.example / .env.local.example / .env.public.example 三份模板,
  公网模板用 _REPLACE_WITH_*_ 占位符防"示例值上生产"
- scripts/start.sh / stop.sh / status.sh 新增 full 子命令对接 docker compose --profile public
- scripts/deploy-public.sh 新建:env 校验 + 端口 checklist + Docker 自检 +
  --profile public up -d --build + 健康检查闭环
- docs/deployment/meeting-mvp.md 新建:双态部署全流程 + 强密码生成 + 防火墙 + FAQ
- 双模式 docker compose config 校验通过;deploy-public.sh 三种错误场景按预期退出

同步更新 CURRENT_STATUS / implementation.plan / project-context
Task 0-14  / 剩余 Task 15-16

Made-with: Cursor
2026-04-22 17:59:48 +08:00

9.3 KiB
Raw Blame History

EchoChat 会议 MVP 部署指南

适用阶段Phase 2e-2会议 MVP
覆盖形态:本机 Demo零配置+ 公网部署(含 TURN
最后更新2026-04-23Task 14 双态部署脚本 + docker-compose 扩展完成)

一、双态部署总览

EchoChat 会议 MVP 按 Phase 2e-2 设计决策 D01 采用**"一套代码 + 环境变量切换"** 的双态策略,不做代码分支。

对比项 本机 Demolocal 公网部署public
适用场景 开发调试、同局域网演示 上线、跨公网用户
MEDIASOUP_ANNOUNCED_IP 留空(自动探测) 必填服务器公网 IP 或域名解析 A 记录 IP
TURN_ENABLED false(不起 coturn truecoturn 走 profiles: [public]
防火墙/安全组 无特殊要求 需放通 UDP:40000-40199 + TURN 端口
启动方式 ./scripts/start.sh full ./scripts/deploy-public.sh
关键文件 deploy/.env.local.example deploy/.env.public.example

服务拓扑对比见设计文档 docs/plans/2026-04-21-phase2e-2-design.md §4.3 双态部署拓扑


二、本机 Demo 部署

前置要求Docker Desktop含 Compose V2、Node 20+、Go 1.22+

Step 1复制环境变量文件

cp deploy/.env.local.example deploy/.env

本机模式下无需修改任何字段即可跑通(MEDIASOUP_ANNOUNCED_IP 留空,TURN_ENABLED=false)。

Step 2一键启动全栈

./scripts/start.sh full

这会做以下事情:

  1. docker compose up -d --build 拉起 5 个容器:postgres / redis / minio / go-service / media-server
  2. 等待 media-server healthcheck 通过(最多 180 秒)
  3. 用本地进程启动 frontend5173+ admin3100—— 热更体验更好

启动完成后会打印访问地址:

前台用户端 (H5):   http://localhost:5173
后台管理端:        http://localhost:3100
Go 后端 API:       http://localhost:8085
媒体服务器 (SFU):  http://localhost:3300 (healthz: /healthz)
MinIO 控制台:      http://localhost:9001 (echochat / echochat123456)

Step 3验证

./scripts/status.sh
# 预期全部 ● RUNNING

curl http://localhost:8085/healthz         # Go backend
curl http://localhost:3300/healthz         # media-server
curl http://localhost:3300/readyz          # mediasoup worker 已就绪

停止

./scripts/stop.sh full              # 停止 docker compose 全栈(保留数据卷)
./scripts/stop.sh                   # 仅停应用层(保留所有容器)
./scripts/stop.sh --all             # 含数据库中间件

三、公网部署

前置要求

  • 服务器有公网 IP 或已解析 A 记录的域名
  • Docker 已安装
  • 云安全组/iptables 可由你控制(不能仅有 80/443 的 PaaS

Step 1复制模板并替换所有占位符

cp deploy/.env.public.example deploy/.env
vim deploy/.env

必须替换:所有 _REPLACE_WITH_*_ 占位符(否则 deploy-public.sh 会拒绝启动)。

生成强密码的示例:

openssl rand -hex 32    # JWT_SECRET / MEDIA_INTERNAL_TOKEN
openssl rand -base64 24 # 数据库密码 / TURN 密码

核心字段

字段 示例 说明
MEDIASOUP_ANNOUNCED_IP 203.0.113.42 必填服务器公网 IP不能写 127.0.0.1
TURN_ENABLED true 对称 NAT fallback 必备
TURN_USERNAME / TURN_PASSWORD 自设 避免 TURN 账号被互联网滥用
JWT_SECRET 64 hex 生产必改
MEDIA_INTERNAL_TOKEN 32 hex Go ↔ Node 内部鉴权,必须与 media-server/.env 一致

Step 2放通云安全组 / iptables

本脚本不会自动改防火墙(不同云厂商命令不同),请你提前在云控制台/iptables 放通:

协议 端口 用途
TCP 8085 Go backend API
TCP 5173 前台用户端 H5如单独暴露
UDP 40000-40199 mediasoup RTC 流量
TCP 40000-40199 mediasoup ICE-TCP fallback
UDP 3478 coturn STUN/TURN
TCP 3478 coturn
TCP 5349 coturn TLS
UDP 49160-49200 coturn 中继流量

iptables 示例(仅参考,请结合实际 zone 调整):

# UDP 范围
sudo iptables -I INPUT -p udp --dport 40000:40199 -j ACCEPT
sudo iptables -I INPUT -p udp --dport 49160:49200 -j ACCEPT
# STUN/TURN
sudo iptables -I INPUT -p udp --dport 3478 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 3478 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 5349 -j ACCEPT
# API
sudo iptables -I INPUT -p tcp --dport 8085 -j ACCEPT

Step 3运行部署脚本

./scripts/deploy-public.sh

脚本会按 4 步依次执行:

  1. 校验 .env:占位符、长度、ANNOUNCED_IP 必填、DEPLOY_MODE=public
  2. 本机端口检查TCP:8085 / TCP:3300 / 潜在冲突
  3. Docker 环境docker daemon + compose v2 可访问
  4. 启动TURN_ENABLED=true 时用 --profile public(含 coturn否则跳过

启动完成后会打印访问地址。此时用浏览器访问 http://<ANNOUNCED_IP>:5173(或你放通的端口)即可。

验证

./scripts/status.sh

# 容器级检查
docker compose -f deploy/docker-compose.dev.yml ps
docker logs echochat-media-server | tail -40
docker logs echochat-coturn | tail -20

公网验证要点:

  • 两台不同网络的设备(比如手机 4G + 家里 WiFi分别入会双向能看到对端画面
  • Chrome 调 chrome://webrtc-internalsRemote candidate type —— 对称 NAT 场景能看到 relay 类型,说明 TURN 生效
  • curl http://<ANNOUNCED_IP>:8085/healthz 返回 200

停止

./scripts/stop.sh full
# 等价于docker compose -f deploy/docker-compose.dev.yml --profile public stop

四、常见问题 FAQ

Q1docker compose up 卡在 media-server 构建,报 python3 not foundg++ not found

原因mediasoup 的 C++ worker 需要在构建阶段编译原生代码。media-server/Dockerfile 的 builder 阶段已装 python3 + build-essential若你绕过 Dockerfile 本地构建,需要手动装这些。

# Debian/Ubuntu
sudo apt install python3 build-essential pkg-config
# macOS
brew install python3
xcode-select --install

Q2本机 Demo 下两个浏览器 Tab 都在 127.0.0.1:5173但视频流对端一片黑

检查 1MEDIASOUP_ANNOUNCED_IP 一定不要写 127.0.0.1,留空让 mediasoup 自动探测到局域网 IP 即可。

检查 2浏览器摄像头权限是否授予Chrome 左上角锁图标)。

检查 3docker logs echochat-media-server | grep announcedIp,确认探测到的 IP 在你本机 ifconfig 能看到。

Q3公网部署后用手机4G 网络)加入会议,能看到自己但看不到别人?

典型症状:单向 ICE 候选协商不成功。通常是以下之一:

  1. TURN 没打开 —— 确认 TURN_ENABLED=truedocker ps 里有 echochat-coturn。对称 NAT 用户必走 TURN。
  2. UDP:40000-40199 没放通 —— mediasoup 要在这个范围分配动态 RTC 端口。很多云默认只放 80/443/22。
  3. MEDIASOUP_ANNOUNCED_IP 写错 —— 如果是 NAT 机器且写了内网 IP公网用户拿到的候选地址不可达。

Q4为什么 coturn 用 network_mode: host

TURN 服务器为每个会话动态分配中继端口(--min-port=49160 --max-port=49200Docker 默认 bridge 网络的端口映射会把回包的源端口改写导致客户端收不到流量。host 网络能绕过这个问题,是 coturn 官方推荐做法。

代价coturn 会占用宿主机端口命名空间,请确保 3478 / 5349 / 49160-49200 宿主机无占用。

Q5我有 HTTPS 证书,想把前端端口改成 443

MVP 阶段脚本没封装 HTTPS 反代,建议在 compose 外层加 Nginx或 Caddy作为 TLS 终结。mediasoup 的 RTC 流量本身就走 DTLS 加密,不需要你额外处理。

Caddy 最小示例(独立容器或宿主进程):

yourdomain.com {
  reverse_proxy /api/* localhost:8085
  reverse_proxy /ws localhost:8085
  reverse_proxy * localhost:5173
}

Q6public profile 下 stop.sh full 能停掉 coturn 吗?

能。scripts/stop.sh full 里写了 docker compose --profile public stopprofile 参数确保 coturn 这种默认不暴露的服务也被覆盖。

Q7数据库要备份怎么操作

# 快速备份当前数据库
docker exec echochat-postgres pg_dump -U echochat echochat | gzip > backup-$(date +%F).sql.gz

# 从备份恢复(容器停机时)
gunzip -c backup-2026-04-23.sql.gz | docker exec -i echochat-postgres psql -U echochat -d echochat

pgdata / redisdata / minio_data 三个 volume 的物理位置由 Docker 管理:

docker volume inspect deploy_pgdata

五、相关文档