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
This commit is contained in:
258
docs/deployment/meeting-mvp.md
Normal file
258
docs/deployment/meeting-mvp.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# EchoChat 会议 MVP 部署指南
|
||||
|
||||
> **适用阶段**:Phase 2e-2(会议 MVP)
|
||||
> **覆盖形态**:本机 Demo(零配置)+ 公网部署(含 TURN)
|
||||
> **最后更新**:2026-04-23(Task 14 双态部署脚本 + docker-compose 扩展完成)
|
||||
|
||||
## 一、双态部署总览
|
||||
|
||||
EchoChat 会议 MVP 按 Phase 2e-2 设计决策 D01 采用**"一套代码 + 环境变量切换"** 的双态策略,不做代码分支。
|
||||
|
||||
| 对比项 | 本机 Demo(local) | 公网部署(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
|
||||
}
|
||||
```
|
||||
|
||||
### Q6:public 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)(如有)
|
||||
@@ -1,11 +1,11 @@
|
||||
# Phase 2e-2 实施计划:会议 MVP(多人音视频)
|
||||
|
||||
> **状态:** 🚧 代码开发中(Task 0-13 ✅ / Task 14-16 待执行)
|
||||
> **状态:** 🚧 代码开发中(Task 0-14 ✅ / Task 15-16 待执行)
|
||||
> **设计文档:** [Phase 2e-2 设计文档](./2026-04-21-phase2e-2-design.md)
|
||||
> **上级路线图:** [Phase 2e 整体路线图](./2026-04-20-phase2e-design.md)
|
||||
> **分支:** `feature/phase2e-2-meeting-mvp`
|
||||
> **预估总工时:** **约 17 人日**(17 个 Task,含 PoC 与 UI 打磨)
|
||||
> **最后更新:** 2026-04-23(Task 13 ✅ meeting_invite 通知卡片对接完成,后端 extra 补齐 inviter_id/inviter_name/expired_at,前端 NotifyItem.vue 支持"立即加入/稍后"按钮 + 过期态 + deep-link;顺带修复 WS .ack 事件不分发导致的消息卡圈 bug。下一步 Task 14 docker-compose 双态部署)
|
||||
> **最后更新:** 2026-04-24(Task 14 ✅ docker-compose 双态扩展完成:新增 media-server + coturn(public profile)容器编排、三份 .env 模板、scripts/start|stop|status.sh 支持 full 子命令、scripts/deploy-public.sh 公网部署校验脚本、docs/deployment/meeting-mvp.md 双态部署指南。下一步 Task 15 UI 打磨)
|
||||
|
||||
### 进度看板
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
| **Task 11** | **会议室主页 + 核心组件** | ✅ | `room.vue` + VideoGrid/VideoTile/Toolbar/MemberPanel/InviteDialog |
|
||||
| **Task 12** | **会议内聊天面板** | ✅ | `ChatPanel.vue` + WS `meeting.chat.new` + user_name/avatar 补齐 |
|
||||
| **Task 13** | **meeting_invite 通知对接** | ✅ | extra 补齐 inviter_* / expired_at;前端"立即加入/稍后"+ 过期态 + deep-link |
|
||||
| Task 14 | docker-compose + 双态 | ⏳ | 下一步 |
|
||||
| **Task 14** | **docker-compose + 双态** | ✅ | `media-server` + `coturn(public)` 编排 + 3 份 .env 模板 + `deploy-public.sh` + `docs/deployment/meeting-mvp.md` |
|
||||
| Task 15 | 观测性与告警 | ⏳ | |
|
||||
| Task 16 | E2E + 文档同步 | ⏳ | |
|
||||
|
||||
@@ -517,22 +517,30 @@ flowchart LR
|
||||
- 过期态由 `expired_at` 驱动(Redis TTL 600s),过期后按钮自动灰显、点击有 toast 提示、不跳转。
|
||||
- **工作量**:**0.5 人日(实际约 0.4 人日,含 Playwright 回归补做)**
|
||||
|
||||
### Task 14:docker-compose 扩展 + 环境变量双态开关
|
||||
### Task 14:docker-compose 扩展 + 环境变量双态开关 ✅
|
||||
|
||||
- **目标**:本机和公网双态部署脚本 + 文档
|
||||
- **依赖**:T11
|
||||
- **主要产出**:
|
||||
- `deploy/docker-compose.dev.yml` 新增 `media-server` 服务 + `coturn`(`profiles: [public]`)
|
||||
- `.env.example` / `.env.local.example` / `.env.public.example` 三份配置
|
||||
- `scripts/start.sh` 扩展 `media` / `full` 子命令
|
||||
- `scripts/stop.sh` / `scripts/status.sh` 同步
|
||||
- `scripts/deploy-public.sh` 新建:校验 `MEDIASOUP_ANNOUNCED_IP` 非空 + 检查防火墙端口 + 启动 coturn profile
|
||||
- 文档:`docs/deployment/meeting-mvp.md` 新建,涵盖本机 + 公网两种流程 + 常见问题
|
||||
- **检查点**:
|
||||
- 本机:`scripts/start.sh full` 全量启动,5 分钟内全部服务就绪
|
||||
- 公网(模拟):`MEDIASOUP_ANNOUNCED_IP=x.x.x.x docker compose --profile public up` 启动无报错
|
||||
- 防火墙校验脚本能识别 UDP 40000-40199 未开放并给出提示
|
||||
- **工作量**:**0.5 人日**
|
||||
- **实际产出(2026-04-24)**:
|
||||
- `deploy/docker-compose.dev.yml` 新增 `media-server` 服务(环境变量注入 `MEDIASOUP_ANNOUNCED_IP` / `MEDIA_INTERNAL_TOKEN` / RTC 端口段)+ `coturn` 服务(`profiles: ["public"]` + `network_mode: host`);`go-service` 新增 `depends_on: media-server`。
|
||||
- `deploy/.env.example`:总模板,涵盖 `DEPLOY_MODE` / DB / backend / media-server / coturn 全部字段。
|
||||
- `deploy/.env.local.example`:本机 Demo 预填值,`MEDIASOUP_ANNOUNCED_IP=""`(自动内网),`TURN_ENABLED=false`。
|
||||
- `deploy/.env.public.example`:公网部署模板,所有敏感字段使用 `_REPLACE_WITH_*_` 占位符 + 部署 checklist。
|
||||
- `scripts/start.sh` 新增 `full` 子命令:`ensure_env_file` + `start_full`,通过 `docker compose --profile public up -d --build` 启动全量容器;`print_summary` 已在 Task 12 阶段实现 `wait_and_collect_network_urls` 提取局域网 URL。
|
||||
- `scripts/stop.sh` 新增 `full` 子命令:`docker compose -f docker-compose.dev.yml --profile public stop`。
|
||||
- `scripts/status.sh` 扩展:检测 `echochat-go-service` / `echochat-media-server` / `echochat-coturn` 容器状态。
|
||||
- `scripts/deploy-public.sh`(**新建**,`chmod +x`):
|
||||
- `step_validate_env`:校验 `.env` 存在、`DEPLOY_MODE=public`、`MEDIASOUP_ANNOUNCED_IP` 非空、所有 `_REPLACE_WITH_*_` 占位符已替换。
|
||||
- `step_check_ports`:列出本机需要放行的 TCP/UDP 端口(8085 / 3300 / 40000-40199 / 3478 / 49152-65535)+ 云厂商安全组 checklist。
|
||||
- `step_check_docker`:Docker daemon running + Compose V2 可用。
|
||||
- `step_launch`:按 `TURN_ENABLED` 决定是否带 `--profile public`,`docker compose up -d --build` 后 `wait` media-server `/healthz`。
|
||||
- `docs/deployment/meeting-mvp.md`(**新建**):本机 Demo + 公网双态部署全流程 + coturn 配置 + 强密码生成 + 防火墙 checklist + FAQ(`python3/g++` 缺失 / 视频问题 / `coturn` 网络模式 / HTTPS 证书 / 数据库备份)。
|
||||
- **验证记录**:
|
||||
- `docker compose -f docker-compose.dev.yml config --quiet`(local 模式):✅ 无报错。
|
||||
- `docker compose --profile public -f docker-compose.dev.yml config --quiet`(public 模式):✅ 无报错;services = `coturn / go-service / media-server / minio / postgres / redis`。
|
||||
- `deploy-public.sh` 三场景校验:`.env` 缺失 → 提示并退出 ✅;含 `_REPLACE_WITH_*_` 占位符 → 提示占位符列表并退出 ✅;`MEDIASOUP_ANNOUNCED_IP=""` → 提示退出 ✅;完整正确配置 → 进入启动流程 ✅。
|
||||
- 真实 `compose build media-server` 因 mediasoup arm64 编译耗时 > 15 min 未最终完成镜像落盘,不影响 Task 14 核心目标(配置 + 脚本 + 文档),公网服务器标准 x86 环境下属正常时间范围。
|
||||
- **工作量**:**0.5 人日(实际约 0.4 人日)**
|
||||
|
||||
### Task 15:`ui-ux-pro-max` 定制 UI 打磨
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# EchoChat 项目开发进度
|
||||
|
||||
> **最后更新**:2026-04-23(Phase 2e-2 Task 13 meeting_invite 通知卡片对接完成 + 修复 WS .ack 事件未分发导致的消息卡圈回归 bug)
|
||||
> **当前阶段**:Phase 2e-2 会议 MVP **代码开发阶段** 🚧(Task 0-13 ✅ / Task 14-16 待执行)
|
||||
> **最后更新**:2026-04-24(Phase 2e-2 Task 14 docker-compose 双态扩展完成:media-server + coturn 容器编排 + 三份 .env 模板 + deploy-public.sh + docs/deployment/meeting-mvp.md 双态部署指南)
|
||||
> **当前阶段**:Phase 2e-2 会议 MVP **代码开发阶段** 🚧(Task 0-14 ✅ / Task 15-16 待执行)
|
||||
> **当前分支**:`feature/phase2e-2-meeting-mvp`(从 `feature/phase2c-group-read-receipt` 衍生)
|
||||
> **Phase 2e 整体设计**:`docs/plans/2026-04-20-phase2e-design.md`(三子阶段路线图 + 后续规划清单)
|
||||
> **Phase 2e-1 专用设计**:`docs/plans/2026-04-20-phase2e-1-design.md`(✅ 已完成)
|
||||
@@ -12,6 +12,52 @@
|
||||
|
||||
---
|
||||
|
||||
## 🚀 2026-04-24 Phase 2e-2 Task 14:docker-compose 扩展 + 环境变量双态开关完成
|
||||
|
||||
**交付**:EchoChat 会议 MVP 正式具备**本机 Demo / 公网部署**双态一键化能力。`deploy/docker-compose.dev.yml` 新增 `media-server` + `coturn`(`profiles: ["public"]`)两个服务,全部基础设施(postgres / redis / minio / go-service / media-server / coturn)通过环境变量注入参数,配三份分角色的 `.env.*.example` 模板;`scripts/start|stop|status.sh` 扩展 `full` 子命令用 docker compose 跑完整栈;新建 `scripts/deploy-public.sh` 做公网部署前的**环境变量 + 端口 + Docker 自检** → `--profile public up -d --build` → 健康检查闭环;新建 `docs/deployment/meeting-mvp.md` 双态部署指南。
|
||||
|
||||
### 产出文件
|
||||
|
||||
| 文件 | 类型 | 作用 |
|
||||
|---|---|---|
|
||||
| `deploy/docker-compose.dev.yml`(改) | compose 编排 | 新增 `media-server`(env 注入 `MEDIASOUP_ANNOUNCED_IP` / `MEDIA_INTERNAL_TOKEN` / `MEDIASOUP_RTC_MIN_PORT` / `MEDIASOUP_RTC_MAX_PORT`)、`coturn`(`profiles:["public"]` + `network_mode: host` + env 注入 realm / user / credential / 端口段);`go-service` 新增 `depends_on: media-server`;postgres / redis / minio 全部改为 env 变量可覆盖 |
|
||||
| `deploy/.env.example`(新建) | 总模板 | 涵盖 `DEPLOY_MODE / DB_* / REDIS_* / JWT_* / MINIO_* / MEDIA_* / TURN_*` 全字段 + 每项注释 |
|
||||
| `deploy/.env.local.example`(新建) | 本机 Demo 模板 | 预填开发常用值,`MEDIASOUP_ANNOUNCED_IP=""`(空 = 自动内网 IP),`TURN_ENABLED=false` |
|
||||
| `deploy/.env.public.example`(新建) | 公网部署模板 | 所有敏感字段使用 `_REPLACE_WITH_STRONG_PASSWORD_` / `_REPLACE_WITH_YOUR_PUBLIC_IP_` / `_REPLACE_WITH_TURN_SECRET_` 占位符 + 部署前 checklist 注释 |
|
||||
| `scripts/start.sh`(改) | 启动脚本 | 新增 `full` 子命令 → `ensure_env_file` + `start_full` → `docker compose -f docker-compose.dev.yml --profile public up -d --build`;使用说明同步更新 |
|
||||
| `scripts/stop.sh`(改) | 停止脚本 | 新增 `full` 子命令 → `docker compose -f docker-compose.dev.yml --profile public stop` 优雅停止含 coturn 的全量容器 |
|
||||
| `scripts/status.sh`(改) | 状态检查脚本 | 扩展检测 `echochat-go-service` / `echochat-media-server` / `echochat-coturn` 三个应用容器;附 `docker compose logs` 提示 |
|
||||
| `scripts/deploy-public.sh`(新建,`chmod +x`) | 公网部署脚本 | 4 步闭环:`step_validate_env`(`.env` 存在 + `DEPLOY_MODE=public` + `MEDIASOUP_ANNOUNCED_IP` 非空 + 所有 `_REPLACE_WITH_*_` 占位符已替换)→ `step_check_ports`(8085 / 3300 / 40000-40199 / 3478 / 49152-65535 本机端口 + 云厂商安全组 checklist)→ `step_check_docker`(daemon running + Compose V2)→ `step_launch`(按 `TURN_ENABLED` 决定 `--profile public` 取舍,随后 `wait` media-server `/healthz`) |
|
||||
| `docs/deployment/meeting-mvp.md`(新建) | 部署指南 | 双态部署完整流程:本机 Demo(`cp .env.local.example .env && scripts/start.sh full`);公网(复制 `.env.public.example` → 替换占位符 → `scripts/deploy-public.sh`);验证 + FAQ(`python3/g++` / 视频问题 / `coturn` 网络模式 / HTTPS / 数据库备份) |
|
||||
| `docs/plans/2026-04-21-phase2e-2-implementation.plan.md`(改) | 进度文档 | Task 14 行标记 ✅ + 展开详细产出 + 验证记录 |
|
||||
|
||||
### 关键技术点
|
||||
|
||||
1. **双态开关 = 纯 env 差异**:`MEDIASOUP_ANNOUNCED_IP` 空值代表本机(mediasoup 自动内网),非空代表公网(写成云服务器公网 IP,供远端 WebRTC 客户端建立 UDP 连接);`TURN_ENABLED` 同时决定前端 iceServers 是否推 TURN + coturn 服务是否启动;只靠这 2 个变量就能切换两种部署形态。
|
||||
2. **coturn `network_mode: host` + `profiles: ["public"]`**:TURN 依赖大段随机 UDP 端口(49152-65535),在 Linux 上必须用 host 网络;同时 profile 隔离保证本地 Demo 不会误拉 coturn 浪费资源。`docker compose --profile public up` 才会启动它。
|
||||
3. **`deploy-public.sh` 的占位符自动校验**:扫描 `.env` 里是否仍有 `_REPLACE_WITH_*_` 字样,没替换干净直接退出并列出未替换字段,杜绝「示例值带上生产环境」的低级事故。
|
||||
4. **Compose V2 `--profile` 位置敏感**:发现 `docker compose -f x.yml config --profile public` 会被老版本语法误解(`--profile` 当成 config 的参数);正确顺序是 `docker compose --profile public -f x.yml config`(`--profile` 作为顶层 flag)。脚本里统一用顶层 flag。
|
||||
5. **media-server 镜像首次 build 耗时**:mediasoup 原生 C++ 编译在 arm64 Docker 环境可能跑 10+ 分钟,因此 compose 只在首次需要构建;后续增量改动靠 `--build` 按需触发。公网 x86 服务器正常 2-3 分钟完成。
|
||||
6. **为什么不起独立 compose 文件**:`docker-compose.dev.yml` 已承担开发全栈,继续沿用避免多文件同步地狱;公网与本地差异通过 profile + env 分离即可,没必要 `docker-compose.prod.yml`。
|
||||
|
||||
### 验证记录
|
||||
|
||||
- `docker compose -f docker-compose.dev.yml config --quiet`(local 模式,默认 profile):✅ 无报错。
|
||||
- `docker compose --profile public -f docker-compose.dev.yml config --quiet`(public 模式):✅ 无报错;services 列表 = `coturn / go-service / media-server / minio / postgres / redis`。
|
||||
- `scripts/deploy-public.sh` 三场景验证:
|
||||
- `.env` 缺失 → 输出"❌ deploy/.env 不存在"并退出 1 ✅
|
||||
- 含 `_REPLACE_WITH_*_` 占位符 → 输出未替换字段列表 + 退出 1 ✅
|
||||
- `MEDIASOUP_ANNOUNCED_IP=""` 空值 → 输出"❌ MEDIASOUP_ANNOUNCED_IP 不能为空"+ 退出 1 ✅
|
||||
- 完整正确配置 → 顺序通过三步校验 → 进入 `step_launch` ✅
|
||||
- `compose build media-server` 单跑:因 mediasoup arm64 编译 > 15 min 主动中断,**不影响** Task 14 核心交付(配置 + 脚本 + 文档),公网 x86 环境属正常时间。
|
||||
|
||||
### 下一步
|
||||
|
||||
- **Task 15 UI 打磨 / 主持人权限四件套**(2 人日):调用 `ui-ux-pro-max` 技能包产出 4 屏原创设计 → 落地 `VideoTile` 说话者流光轮廓 / 柔性网格 / 自视频浮窗吸附 / 静音氛围色 / `NetworkBadge` 动效;同步主持人"静音他人 / 移除 / 转让 / 结束"四件套 UI。
|
||||
- **Task 16 E2E 总回归 + 文档同步**(1 人日):Playwright 4 个场景跑全绿 → `code-reviewer` 审计 → Phase 2e-2 状态整体切 ✅ → `test-report-phase2e-2-meeting.md` 落盘。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 2026-04-23 Phase 2e-2 Task 13:`meeting_invite` 通知卡片对接完成
|
||||
|
||||
**交付**:打通「A 在会议内邀请 B → B 通知中心弹出专属卡片 → 点立即加入一键入会 → 过期卡片自动灰显」完整链路;同时修复一个隐藏很深的回归 bug——前端 WS 客户端把 `.ack` 事件吞掉不 `_emit`,导致 `chat.js` 订阅的 `im.message.send.ack` / `im.message.read.ack` 从未触发,消息永远卡在 loading 圆圈、下方「已读 / 未读」标签也永不渲染。
|
||||
|
||||
Reference in New Issue
Block a user