From 52d8a577516f81dbaa419bf92a089ca7d0cf5ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Mon, 21 Sep 2026 10:27:02 +0800 Subject: [PATCH] docs(deploy): single-server dual-stack test deployment plan Co-Authored-By: Claude Code --- docs/plans/2026-09-21-测试服务器部署方案.md | 156 ++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/plans/2026-09-21-测试服务器部署方案.md diff --git a/docs/plans/2026-09-21-测试服务器部署方案.md b/docs/plans/2026-09-21-测试服务器部署方案.md new file mode 100644 index 0000000..ab05267 --- /dev/null +++ b/docs/plans/2026-09-21-测试服务器部署方案.md @@ -0,0 +1,156 @@ +# 测试服务器部署方案(国内 ECS · 单机双栈) + +日期:2026-09-21 · 目标:把完整系统(cn 管理栈 + ov 观众栈)部署到国内测试 ECS,真实 OSS、可从外网访问、App/浏览器可联调。 + +服务器画像(已确认):国内阿里云 ECS,已装 1Panel + Docker + MySQL 容器。OSS 凭证已验证(G1 通过)。 + +**单机双栈说明**:cn 与 ov 两套栈同机部署——ov 的 MySQL 库(photo_ov)也建在同一 1Panel MySQL 实例里,sync 走本机回环连双库,**无需跨境链路**。功能与生产架构完全同构(观众走 ov、管理走 cn、跨境同步本机化),仅地域拆分这一维度被测试环境折叠。 + +--- + +## 0. 前置检查(2 分钟) + +在 1Panel 终端执行: + +```bash +# MySQL 版本必须 ≥ 8.0.30(utf8mb4_0900_bin 校对门槛,需求 §5/D12) +docker exec $(docker ps --format '{{.Names}}' | grep -i mysql | head -1) mysql -uroot -p'你的MySQL root密码' -e "SELECT VERSION();" +# Redis 是否已装(应用商店装一个即可,记下容器名) +docker ps --format '{{.Names}}' | grep -i redis || echo "需要装 Redis(1Panel 应用商店 → Redis,任意版本)" +``` + +版本 < 8.0.30 → 先升级 MySQL 容器或改用 utf8mb4_bin(改 infra 迁移脚本四处 CREATE TABLE 的 collation + 重建本地验证,通知我处理)。 + +## 1. 本地导出镜像(开发机执行) + +```bash +cd /d/project/photo +TAG=test-20260921 +docker build -f docker/server.Dockerfile -t photo-live-server:$TAG . +docker build -f docker/web.Dockerfile -t photo-live-web:$TAG . +docker save photo-live-server:$TAG photo-live-web:$TAG | gzip > /d/photo-live-$TAG.tar.gz +ls -lh /d/photo-live-$TAG.tar.gz # 约 700-900MB(gzip 后) +``` + +## 2. 传输与导入 + +```bash +# 开发机 → 服务器(1Panel 终端也能看 scp 命令;或用宝塔式上传到 /opt) +scp /d/photo-live-test-20260921.tar.gz root@<服务器IP>:/opt/ + +# 服务器上导入 +docker load < /opt/photo-live-test-20260921.tar.gz +docker images | grep photo-live # 确认两个镜像 +``` + +## 3. MySQL 建库授权(1Panel 终端) + +```sql +-- 1Panel → 数据库 → 终端,或 docker exec 进 MySQL 容器 +CREATE DATABASE photo_cn CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin; +CREATE DATABASE photo_ov CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin; +CREATE USER 'photo'@'%' IDENTIFIED BY '<给 photo 用户设一个强密码>'; +GRANT ALL PRIVILEGES ON photo_cn.* TO 'photo'@'%'; +GRANT ALL PRIVILEGES ON photo_ov.* TO 'photo'@'%'; +FLUSH PRIVILEGES; +``` + +Redis:1Panel 应用商店装 Redis(无密码即可起步;容器在 1panel-network 网络里,服务经容器名访问)。 + +## 4. 目录与 env(服务器执行) + +```bash +mkdir -p /opt/photo-live && cd /opt/photo-live +``` + +**`.env.cn`**(关键项;完整清单见仓库 `.env.production.example-cn`——复制后改以下值): + +```env +# 数据库:容器名取决于 1Panel 实例名(docker ps 查,替换 mysql 的实际容器名) +DATABASE_URL_CN=mysql://photo:<上面设的密码>@:3306/photo_cn?connection_limit=10 +DATABASE_URL_OV=mysql://photo:<密码>@:3306/photo_ov?connection_limit=10 +REDIS_URL_CN=redis://:6379 +# OSS(G1 已验证的定案形态) +S3_ENDPOINT=https://oss-cn-beijing.aliyuncs.com +S3_REGION=oss-cn-beijing +S3_ACCESS_KEY=<你的AK> +S3_SECRET_KEY=<你的SK> +S3_BUCKET=live-album +S3_FORCE_PATH_STYLE=false +# 同步密钥(生成:openssl rand -hex 32) +SYNC_INTERNAL_KEY= +OV_API_URL=http://api-cn:8081 # 同机:sync 直连 api-ov 容器 +CN_API_URL=http://api-cn:8080 +PORT=8080 +PORT_OV=8081 +# 管理员种子 +ADMIN_ACCOUNT=admin +ADMIN_PASSWORD=<设一个初始管理员密码> +# 测试环境:允许跨源(有正式域名后收窄) +CORS_ORIGINS=* +``` + +**`.env.ov`**:同文件的 OSS 段 + `DATABASE_URL_OV` + `REDIS_URL_OV=redis://:6379` + `PORT_OV=8081`(完整清单见 `.env.production.example-ov`)。 + +## 5. 编排启动 + +把仓库的 `infra/deploy/docker-compose-cn.yml`、`docker-compose-ov.yml` 上传到 `/opt/photo-live/`,然后: + +```bash +cd /opt/photo-live +# 1Panel → 容器 → 编排 → 创建 → 粘贴 compose 内容(界面化路径) +# 或命令行(compose 文件里 SERVER_IMAGE/WEB_IMAGE 环境变量指定 tag): +SERVER_IMAGE=photo-live-server:test-20260921 WEB_IMAGE=photo-live-web:test-20260921 \ + docker compose -f docker-compose-cn.yml up -d +SERVER_IMAGE=photo-live-server:test-20260921 WEB_IMAGE=photo-live-web:test-20260921 \ + docker compose -f docker-compose-ov.yml up -d +``` + +compose 默认连接 `1panel-network` 外部网络;若你的网络名不同(`docker network ls` 查),改 compose 尾部 networks 段。 + +## 6. 数据库迁移 + 管理员种子(一次性) + +```bash +docker run --rm --network 1panel-network --env-file .env.cn \ + photo-live-server:test-20260921 migrate-deploy all +docker run --rm --network 1panel-network --env-file .env.cn \ + photo-live-server:test-20260921 seed-admin +``` + +## 7. 验证(冒烟四点) + +```bash +# 服务器上: +bash scripts/prod-smoke.sh http://127.0.0.1:8080 http://127.0.0.1:8081 admin '' +# 开发机上(外网可达性;先在 ECS 安全组放行 8088/8089): +curl http://<服务器IP>:8088/v1/health +curl http://<服务器IP>:8089/v1/health +``` + +通过标志:四点全 PASS + 两个 health 返回 `{"status":"ok"}`。 + +## 8. 访问入口与联调 + +| 入口 | 地址 | 用途 | +|---|---|---| +| 观众页(走 ov 栈,真实观众链路) | `http://:8089/a/` | 浏览/点赞/气泡/热门 | +| 管理后台(cn 栈) | `http://:8088/admin/` | 全功能后台 | +| 观众页(cn 只读形态) | `http://:8088/a/` | 备用 | +| App | 构建时 `--dart-define API_BASE=http://:8080` 或装 debug 包后改配置 | 真机联调 | +| 嵌入组件 | 宿主页引 `http://:8089/embed/v1/loader.js` | 官网接入形态 | + +后续上正式域名:1Panel → 网站 → OpenResty 反代 8088/8089 + 证书,env 的 CORS_ORIGINS 收窄为域名,无需改代码。 + +## 9. 安全与运维注意(测试环境基线) + +- 安全组只放行 8088/8089(web);8080/8081/3306/6379 不对公网开放(web 容器同机反代 /v1)。 +- `.env.cn/.env.ov` 权限 `chmod 600`;AK 建议按此前提醒在稳定后轮换。 +- 日志:compose 已配 json-file 10m×3;看实时 `docker logs -f photo-api-cn`。 +- 回滚:`SERVER_IMAGE` 换回旧 tag `docker compose up -d` 即可(迁移向前兼容原则,详见 RUNBOOK.md)。 + +--- + +## 执行分工(二选一) + +- **你执行**:按 0→8 逐步粘贴(约 30-40 分钟,大头是镜像传输);卡在任何一步叫我。 +- **我执行**:提供 SSH 访问方式(密钥/密码经安全渠道),我在服务器上跑完全部步骤并回报冒烟结果;镜像 tar 我先在本机备好。