Files
EchoChat/docs/deployment/meeting-mvp.md
bujinyuan f41e63ba6b docs(phase2e-2): 补齐顶层文档进度同步,Phase 2e-2 会议 MVP 全面切
用户准备开始真实用户测试,此 commit 对"六份文档同步"之外遗漏的上层/入口文档
做二次补齐,保证所有出现阶段状态描述的文档都与 CURRENT_STATUS.md 一致。

补齐范围:
- README.md
  · "MVP 功能规划" 原把 Phase 2 / Phase 3 标记为"待开始",与实际进度严重偏离
  · 整段重写为"开发进度"小节,按 Phase 1 / 2a-d / 2e-1 / 2e-2 分别列出 
    已完成状态与关键交付;2e-3 / Phase 3 / P2 推迟项独立列为"待启动(规划中)"
  · "文档导航"扩展 5 条新入口:Phase 2e 路线图 / 2e-2 设计 / 2e-2 实施 /
    2e-2 代码审查 / 2e-2 验收报告 / 会议 MVP 部署指南
- docs/plans/2026-02-27-echochat-system-design.md(整体系统设计方案)
  · §Phase 2e 行:追加"2e-1  / 2e-2  / 2e-3 📋 待启动"状态徽标
  · §Phase 2e-2:从"📋 设计阶段完成,代码开发待启动"改为
    " 已完成(2026-04-24,Task 0-16 全量落地 + 代码审查 4 P0 + 8 P1 + 7 P2 + 7 Nit 闭环)"
- docs/plans/2026-04-21-phase2e-2-design.md(Phase 2e-2 设计文档)
  · 顶部"状态 / 最后更新"由"📋 设计阶段(待评审后进入代码开发)"切 
  · §一 文档定位的"本文档"行同步切 
- docs/plans/2026-04-24-phase2e-2-task15-ui-polish.plan.md(Task 15 计划)
  · 顶部"状态"由"📋 设计阶段(等待用户 Review)"切 
  · 追加"交付记录"行指向 CURRENT_STATUS.md Task 15/16 交付条目
- docs/deployment/meeting-mvp.md(会议 MVP 部署指南)
  · 顶部元信息追加阶段  标记 + Task 16 追加的 REDIS_PASSWORD × redis.conf
    requirepass 联动校验指引

验证:workspace grep "📋 设计阶段|🚧 代码开发|待开始" 在顶层 README/docs 下
已无遗留进度描述与实际状态冲突(仅 README 新增的"Phase 2e-3 / Phase 3 — 待启动
(规划中)"章节为规划项,符合预期)。

Made-with: Cursor
2026-04-23 17:59:32 +08:00

259 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# EchoChat 会议 MVP 部署指南
> **适用阶段**Phase 2e-2会议 MVP ✅ 已完成)
> **覆盖形态**:本机 Demo零配置+ 公网部署(含 TURN
> **最后更新**2026-04-24Phase 2e-2 Task 16 收官E2E 回归 + 代码审查修复 + 资源生命周期审计;本指南 Task 14 双态部署脚本仍为最新有效版本Task 16 追加 `REDIS_PASSWORD` 与 `redis.conf requirepass` 联动校验,详见 [Task 16 收官说明](../progress/CURRENT_STATUS.md)
## 一、双态部署总览
EchoChat 会议 MVP 按 Phase 2e-2 设计决策 D01 采用**"一套代码 + 环境变量切换"** 的双态策略,不做代码分支。
| 对比项 | 本机 Demolocal | 公网部署public |
|---|---|---|
| 适用场景 | 开发调试、同局域网演示 | 上线、跨公网用户 |
| `MEDIASOUP_ANNOUNCED_IP` | 留空(自动探测) | **必填**服务器公网 IP 或域名解析 A 记录 IP |
| `TURN_ENABLED` | `false`(不起 coturn | `true`coturn 走 `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 双态部署拓扑](../plans/2026-04-21-phase2e-2-design.md)。
---
## 二、本机 Demo 部署
**前置要求**Docker Desktop含 Compose V2、Node 20+、Go 1.22+
### Step 1复制环境变量文件
```bash
cp deploy/.env.local.example deploy/.env
```
本机模式下无需修改任何字段即可跑通(`MEDIASOUP_ANNOUNCED_IP` 留空,`TURN_ENABLED=false`)。
### Step 2一键启动全栈
```bash
./scripts/start.sh full
```
这会做以下事情:
1. `docker compose up -d --build` 拉起 5 个容器:`postgres / redis / minio / go-service / media-server`
2. 等待 `media-server` healthcheck 通过(最多 180 秒)
3. 用本地进程启动 `frontend`5173+ `admin`3100—— 热更体验更好
启动完成后会打印访问地址:
```
前台用户端 (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验证
```bash
./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 已就绪
```
### 停止
```bash
./scripts/stop.sh full # 停止 docker compose 全栈(保留数据卷)
./scripts/stop.sh # 仅停应用层(保留所有容器)
./scripts/stop.sh --all # 含数据库中间件
```
---
## 三、公网部署
**前置要求**
- 服务器有**公网 IP** 或已解析 A 记录的域名
- Docker 已安装
- 云安全组/iptables 可由你控制(不能仅有 80/443 的 PaaS
### Step 1复制模板并替换所有占位符
```bash
cp deploy/.env.public.example deploy/.env
vim deploy/.env
```
**必须替换**:所有 `_REPLACE_WITH_*_` 占位符(否则 `deploy-public.sh` 会拒绝启动)。
生成强密码的示例:
```bash
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 调整):
```bash
# 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运行部署脚本
```bash
./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`(或你放通的端口)即可。
### 验证
```bash
./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-internals``Remote candidate type` —— 对称 NAT 场景能看到 `relay` 类型,说明 TURN 生效
- `curl http://<ANNOUNCED_IP>:8085/healthz` 返回 200
### 停止
```bash
./scripts/stop.sh full
# 等价于docker compose -f deploy/docker-compose.dev.yml --profile public stop
```
---
## 四、常见问题 FAQ
### Q1`docker compose up` 卡在 media-server 构建,报 `python3 not found` 或 `g++ not found`
**原因**mediasoup 的 C++ worker 需要在构建阶段编译原生代码。`media-server/Dockerfile` 的 builder 阶段已装 python3 + build-essential若你绕过 Dockerfile 本地构建,需要手动装这些。
```bash
# 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但视频流对端一片黑
**检查 1**`MEDIASOUP_ANNOUNCED_IP` 一定不要写 `127.0.0.1`,留空让 mediasoup 自动探测到局域网 IP 即可。
**检查 2**浏览器摄像头权限是否授予Chrome 左上角锁图标)。
**检查 3**`docker logs echochat-media-server | grep announcedIp`,确认探测到的 IP 在你本机 `ifconfig` 能看到。
### Q3公网部署后用手机4G 网络)加入会议,能看到自己但看不到别人?
**典型症状**:单向 ICE 候选协商不成功。通常是以下之一:
1. **TURN 没打开** —— 确认 `TURN_ENABLED=true``docker 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=49200`Docker 默认 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 stop`profile 参数确保 coturn 这种默认不暴露的服务也被覆盖。
### Q7数据库要备份怎么操作
```bash
# 快速备份当前数据库
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 管理:
```bash
docker volume inspect deploy_pgdata
```
---
## 五、相关文档
- 设计文档:[docs/plans/2026-04-21-phase2e-2-design.md](../plans/2026-04-21-phase2e-2-design.md)
- 实施计划:[docs/plans/2026-04-21-phase2e-2-implementation.plan.md](../plans/2026-04-21-phase2e-2-implementation.plan.md)
- 会议 API[docs/api/frontend/meeting.md](../api/frontend/meeting.md)
- WebSocket 信令:[docs/api/frontend/meeting.md#websocket-信令协议](../api/frontend/meeting.md)
- media-server 子项目:[media-server/README.md](../../media-server/README.md)(如有)