Files
photo-live/docs/plans/2026-09-24-正式环境双地域部署.md
2026-09-24 13:41:47 +08:00

174 lines
12 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.
# 2026-09-24 正式环境双地域部署(P5 生产上线)
> 状态:已执行完毕(2026-09-24,验收记录见 progress/正式环境双地域部署-验收记录.md)。
> 输入:用户 2026-09-24 指令;记忆双地域方案(跨境 HMAC 同步 + 安全组互白名单 +
> 全新空库 + 域名证书);G4 验收记录 §4.6/§4.8 全部坑位经验。
> 用户已拍板(2026-09-24):**hnxinyouhe.com 已备案**(cn 直接域名+HTTPS);
> **不接 CDN**(A25「图片经全球 CDN」暂不满足,如实标注);**OSS 复用现有桶**
> (G1 凭证,从测试服务器 env 取)。
## 0. 现状侦察结论(2026-09-24)
### SSH 通道
| 机 | IP | 通道 | 状态 |
| --- | --- | --- | --- |
| ov 境外 | 47.254.176.10 | 本机 → ProxyJump `root@8.152.213.143`(跳板在 ov 安全组白名单)→ root,钥匙 `~/.ssh/id_ed25519`(即 cms-cj-deploy,ov 机已装过) | ✅ 已通 |
| cn 国内 | 101.37.116.184 | 本机直连 root + 同一把钥匙 | ⏳ 等公钥装入 authorized_keys(用户进行中) |
| 跳板(老国内机) | 8.152.213.143 | 本机直连 root + 同钥匙 | ✅ 已通 |
写入 `~/.ssh/config`:`photo-ov`(带 ProxyJump)/ `photo-cn` 两个 Host 条目。
### ov 机探测(已完成)
- Debian 13 / 2C / 7.4G / 40G 盘 **83% 用**(剩 6.5G + `docker builder prune` 可回收 ~2G → 部署前清一次,够用)
- Docker 29.4 + Compose v5.1.3;**Docker Hub 直连正常**(registry-1.docker.io 401=通)
- **NTP 已同步**(阿里云内部 NTP,Stratum 3)——HMAC ±300s 防重放硬前提 ✓
- 出网:aliyun intl / docker hub / **gitea:25311(0.58s)** / oss-hangzhou 全通
- **跨境:ov→cn 22/443 TCP 可达** ✓
- DNS:inside→cn、outside→ov 解析正确 ✓
- 机上已有:1Panel 全家桶(OpenResty 占 **80/443**、MySQL **5.7.44**@127.0.0.1:3306、Redis 8.6.2、**phpMyAdmin 占 8089**、php-fpm)+ tracker / cc-ai-kefu / cms-cj 等项目
- 本机命令样例:`ssh photo-ov '…'` / `scp … photo-ov:/opt/…`
### 坑位(G4 §4.6/§4.8 经验,全部纳入流程)
1. **web 容器随 api 容器重建后必须一并 restart**(nginx 缓存旧上游容器 IP → 502)。
2. **`nginx:alpine` 最新 digest 的 10-listen-on-ipv6 entrypoint 卡死**——本次 T0 把补丁行正式进仓库 Dockerfile。
3. 本机 Docker Hub 抽风预案:**构建挪服务器**(ov 直连 Hub;cn 若不通走 1Panel 加速器,探测时确认 `/etc/docker/daemon.json`)。
4. MySQL 8.4 `trigger`/`cursor` 为保留字,手工 SQL 全部反引号。
5. `.env.production-*` chmod 600、不入库不入镜像。
## 1. 目标形态
```
cn 101.37.116.184 (live-album.inside.hnxinyouhe.com, 已备案)
└ photo-live-cn 栈:api-cn / worker / sync / web + 自管 mysql:8.4 + redis:7
ov 47.254.176.10 (live-album.outside.hnxinyouhe.com)
└ photo-live-ov 栈:api-ov / web-ov + 自管 mysql:8.4 + redis:7
跨境链路:
sync 推送/对账:cn sync → https://live-album.outside.hnxinyouhe.com(HMAC)
统计回传读库:cn sync → 47.254.176.10:13306(ov mysql,安全组仅放行 cn IP)
入口:两机各自 1Panel OpenResty 建站点 → 反代 127.0.0.1 回环端口 → Let's Encrypt 证书
```
## 2. 任务序
### T0 仓库准备(本机,先行)
- `docker/web.Dockerfile`:`FROM nginx:alpine` 后加
`RUN rm -f /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh`(§4.8 事件 B 补丁正式化,钉版本列为后续待办)。
- 服务器 compose 适配(两台各自 `/opt/photo-live/deploy/`,不进仓库):基于
`infra/deploy/docker-compose-ov.yml` / `-cn.yml` 改三点——
① web 主机端口:ov `127.0.0.1:18089`(8089 被 phpMyAdmin 占),cn `127.0.0.1:18088`(避冲突统一 18xxx);
② 增自管服务 `mysql`(mysql:8.4 + utf8mb4_0900_bin + named volume)与 `redis`(redis:7-alpine);
③ 网络改独立桥 `photo-net`(不挂 1panel-network——不复用 1Panel 的 MySQL5.7/Redis)。
ov mysql 额外映射 `0.0.0.0:13306:3306`(跨境 sync 读库,安全组仅放行 cn IP)。
- commit + push gitea(两台服务器 `git pull` 用)。
- 验收:flutter/后端零改动,`docker build` 本机两镜像可构建(顺带验证 Hub 状态)。
### T1 cn 环境探测(公钥装入后立即)
- 跑与 ov 同一份探测脚本(.tmp-probe.sh):规格/系统/Docker/Compose/加速器/MySQL/NTP/时区/端口占用/出网(Hub、gitea、uniservice、oss)/跨境 cn→ov TCP。
- 决定 cn 构建路径(本机构建传输 vs 服务器构建)与磁盘是否需清理。
- 验收:探测记录回填本文档 §0;cn 18088/13306 无端口冲突或改用其它高位端口。
### T2 ov 栈部署(先 ov——cn 的 sync 依赖 ov API 可达)
1. `mkdir -p /opt/photo-live/{src,deploy}`;clone gitea(凭证用令牌,clone 后 remote 去掉令牌或用 deploy 方式)。
2. `docker builder prune -f`(回收 ~2G)。
3. 写 `.env.production-ov`(600,全新值):
- `DATABASE_URL_OV=mysql://photo_ov:<新强密码>@mysql:3306/photo_ov?connection_limit=10`
- `REDIS_URL_OV=redis://redis:6379`
- S3 四元组:从测试服务器 `/opt/photo-live/deploy/.env.production-cn` 抄 G1 真实值(同桶同凭证,ov 只读现签)
- `CORS_ORIGINS=https://live-album.outside.hnxinyouhe.com`
- `SYNC_INTERNAL_KEY=<openssl rand -hex 32 新生成,与 cn 同值>`
- 互动/限速参数按模板缺省
4. mysql 容器起后建库建账号:
`CREATE DATABASE photo_ov CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin;`
`CREATE USER 'photo_ov'@'%' …GRANT ON photo_ov.*`(安全组收口;sync 专用账号
`photo_ov_sync`@`101.37.116.184` 后续可拆,首期单账号+% 靠安全组白名单)。
5. 构建/导入镜像(按 T0 验证结果选路径):
- 路径 A:本机 `docker build` → save/gzip → `scp -J root@8.152.213.143` 直达 ov → `docker load`
- 路径 B(本机 Hub 抽风):源码已在 `/opt/photo-live/src`(git pull)→ ov 服务器 `docker build`(Hub 直连已验证 OK)
- tag 约定 `<git短sha>-20260924`(当前 d331d55,若 T0 有新提交以新 sha 为准);web ov 版带 `VITE_VIEWER_BASE=https://live-album.outside.hnxinyouhe.com`
6. `docker compose … run --rm <server image> migrate-deploy ov`(迁移先于 API)。
7. `SERVER_IMAGE=… WEB_IMAGE=… docker compose -f docker-compose-ov.yml up -d`;health `{"status":"ok"}`。
8. 1Panel 建网站 `live-album.outside.hnxinyouhe.com` → 反代 `127.0.0.1:18089` →
Let's Encrypt 证书(HTTP-01,80 已开)→ 强制 HTTPS;**反代配置确认 WebSocket
透传**(/v1/realtime Upgrade 头;1Panel 反代开 websocket 选项或手补
`proxy_set_header Upgrade/Connection`)。
9. 外网验证:`https://live-album.outside.hnxinyouhe.com/v1/health` + 观众页 404 兜底正常。
- 验收:HTTPS 入口通、api-ov healthy、(无数据期)公开端点空态正常。
- 回滚:`docker compose down`;全新空库无数据风险;1Panel 站点删除。
### T3 cn 栈部署
1. 同 T2 建目录/clone/探测磁盘。
2. `.env.production-cn`(600,全新值):
- `DATABASE_URL_CN`/`REDIS_URL_CN` → 自管容器
- `DATABASE_URL_OV=mysql://photo_ov:<ov应用账号密码>@47.254.176.10:13306/photo_ov?connection_limit=10`(**跨公网直连 ov mysql**)
- S3 同桶同凭证;`CORS_ORIGINS=https://live-album.inside.hnxinyouhe.com`
- `SYNC_INTERNAL_KEY` 同 ov;`CN_API_URL=http://api-cn:8080`;`OV_API_URL=https://live-album.outside.hnxinyouhe.com`
- `USER_SYNC_SOURCE=http` + uniservice URL/KEY(从测试服务器 env 抄真实值)
- `SYNC_USERS_INTERVAL_MS=43200000`(12h,用户既定指令)
- `ADMIN_ACCOUNT=admin` + `ADMIN_PASSWORD=<新强密码>`(seed 后交付用户,建议首次登录即改)
3. mysql 建库 photo_cn(同 collate)+ 账号。
4. 镜像同 T2(web cn 版 `VITE_VIEWER_BASE=https://live-album.inside.hnxinyouhe.com`)。
5. `migrate-deploy cn` → `seed-admin` → `up -d`(api-cn/worker/sync/web)。
6. sync 日志核对:推送循环启动、连 ov inbox 通(若 ov API 443 未就绪会重试——T2 已先行则应直通)。
7. 1Panel 建站点 `live-album.inside.hnxinyouhe.com` → 反代 `127.0.0.1:18088` → 证书(已备案,HTTP-01 可过)→ 强制 HTTPS + WS 透传。
8. **重建 api 后 restart web 容器**(坑位 ①)。
- 验收:后台可登录(新密码)、health OK、sync 无连接错误。
- 回滚:同 T2;RUNBOOK 换旧 tag。
### T4 跨境链路验证(依赖用户安全组操作)
**用户在阿里云控制台操作(唯一外部依赖)**:
- ov 安全组:入方向放行 `TCP/13306 ← 101.37.116.184`(sync 读 ov 库);
建议加 `TCP/22 ← 101.37.116.184`(cn→ov 备用 SSH 通道)。
- cn 安全组:确认 80/443 公网放行(站点要通)。
验证序:
1. cn 容器内 `mysql -h 47.254.176.10 -P 13306` 连通(或 nc 探测)。
2. **全链路**:后台建相册→上传测试照片→worker 出变体→outbox→sync 推送(~2s)→
ov 公开页 `https://live-album.outside…/a/<token>` 看到照片、图片 presigned OSS URL 可加载。
3. 反向:ov 页点赞→统计回传 60s→cn 库计数增长。
4. 对账循环日志(5min 周期无 diff)。
5. `scripts/prod-smoke.sh` 四点冒烟 + CORS 收窄 curl 验证(第三方 Origin 拒)。
6. uniservice 定时同步:首批手动触发(12h 间隔下重启后首批要等——用后台「手动同步」按钮)。
### T5 收尾
- 验收记录 `docs/plans/progress/` 落档(含坑位新增、A25 CDN 标注、跨境外网实测数据)。
- 密钥交付:`.env.production-*` 真实文件留存两台服务器(600)+ 本机工作区(.gitignore 已排除);ADMIN 密码/DB 密码清单单独交用户保管。
- 监控:`infra/deploy/monitoring.md` 告警项按现状裁剪接入(可后置)。
## 3. 密钥与凭据清单(全部新生成,唯一复用 = OSS/uniservice)
| 项 | 来源 | 存放 |
| --- | --- | --- |
| SYNC_INTERNAL_KEY | `openssl rand -hex 32` 新生成 | 两端 .env(600) |
| mysql root / photo_cn / photo_ov 应用账号密码 | 新生成 | 各机 .env + mysql |
| ADMIN_PASSWORD | 新强密码 | cn .env + 交付用户 |
| S3 四元组(同桶) | 测试服务器 env 抄 | 两端 .env |
| OPEN_API_USERS_URL/KEY | 测试服务器 env 抄 | cn .env |
## 4. 风险与预案
| 风险 | 预案 |
| --- | --- |
| 本机 Docker Hub 抽风(§4.8 事件 A 复发) | 路径 B 服务器构建(ov 已验证 Hub 直连;cn 走加速器) |
| ov 磁盘 6.5G 紧张 | 部署前 builder prune(~2G);mysql/redis/server/web 镜像+库合计估 ~3G,余量足够;超预期则报告用户扩容 |
| 1Panel 反代丢 WS 头 | 手补 Upgrade/Connection 两行;实测 /v1/realtime 握手 |
| 跨境 13306 被扫 | 高位端口 + 安全组单 IP 白名单 + 账号强密码;后续可加 REQUIRE SSL |
| cn 公钥迟迟未装 | T1-T3 的 cn 部分阻塞,但 T2 ov 全部可先行(本计划顺序已兼容) |
| 测试服务器不受影响 | 全新机器全新库,零接触;gitea 仅读 |
## 5. 与准备件的偏差登记(部署后回填 RUNBOOK/README)
1. compose 自管 mysql:8.4/redis(1Panel MySQL 是 5.7.44 不满足 utf8mb4_0900_bin/8.4 基线),独立 photo-net 网络。
2. web 回环端口 18089/18088(8089 与 phpMyAdmin 冲突;18xxx 统一)。
3. ov mysql 公网映射 13306(模板未含,跨境 sync 读库需要——白名单+SSL 待实测定案项落地)。
4. `docker/web.Dockerfile` entrypoint 补丁正式化(钉 nginx 版本仍为待办)。
5. CDN 不接(用户拍板);OSS 复用 G1 桶(用户拍板)。