docs: 正式环境双地域部署计划与验收记录
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
# 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 桶(用户拍板)。
|
||||
@@ -0,0 +1,62 @@
|
||||
# 正式环境双地域部署 验收记录(2026-09-24)
|
||||
|
||||
> 对应计划:docs/plans/2026-09-24-正式环境双地域部署.md(用户确认后执行)。
|
||||
> 版本:`photo-live-server:30a3520-20260924`(commit 30a3520,含 nginx entrypoint 补丁正式化);
|
||||
> web 双版本 `photo-live-web:30a3520-20260924-ov` / `-cn`(VITE_VIEWER_BASE 分域构建)。
|
||||
|
||||
## 1. 部署形态(结果)
|
||||
|
||||
| 机 | 域名 | 栈 | 外网验证 |
|
||||
| --- | --- | --- | --- |
|
||||
| cn 101.37.116.184(Rocky 9.6, 2C/3.5G) | live-album.inside.hnxinyouhe.com | photo-live-cn:api-cn/worker/sync/web + 自管 mysql:8.4(调优)/redis:7,web@127.0.0.1:18088 | `/v1/health` ok;`/admin/` 200 |
|
||||
| ov 47.254.176.10(Debian 13, 2C/7.4G) | live-album.outside.hnxinyouhe.com | photo-live-ov:api-ov/web-ov + 自管 mysql:8.4/redis:7,web@127.0.0.1:18089,mysql 公网映射 13306(等安全组) | `/v1/health` ok;根 302 |
|
||||
|
||||
入口:两机 1Panel OpenResty 站点(用户自建)+ Let's Encrypt 证书 + 强制 HTTPS;
|
||||
反代配置确认含 Upgrade/Connection 头。SSH 通道:`photo-ov` 走 ProxyJump 跳板
|
||||
8.152.213.143(ov 安全组白名单了它;cn→ov 22/443 亦可达);`photo-cn` 直连。
|
||||
均用 `~/.ssh/id_ed25519`(cms-cj-deploy)。
|
||||
|
||||
## 2. 验证结果(全部通过)
|
||||
|
||||
- **prod-smoke 四点冒烟 9/9 PASS**:双端 health、真实密码登录、建时间戳相册
|
||||
→ OSS presign PUT(3118B 真实上传,G1 桶凭证复用)→ complete → worker 处理 →
|
||||
**cn 公开 0s 可见 → ov 公开 0s 可见(跨境 HMAC 推送端到端)**;清理软删 ✓。
|
||||
- WS:`/v1/realtime` 经 OpenResty→web→api-ov 升级 101 ✓。
|
||||
- CORS:第三方 Origin 无 Access-Control-Allow-Origin(收窄生效)✓。
|
||||
- sync 日志:outbox batch pushed(acceptedEventIds 落 ov inbox)✓。
|
||||
- 迁移:cn/ov 两库 migrate deploy 全量应用;seed-admin created admin (active) ✓。
|
||||
- NTP:两机均同步(阿里云内部 NTP,HMAC ±300s 前提)✓。
|
||||
|
||||
## 3. 与准备件的偏差(实际落地)
|
||||
|
||||
1. compose 自管 mysql:8.4 + redis:7(两机 1Panel MySQL 均为 5.7.44 不满足基线),
|
||||
独立桥网络 photo-net,与机上既有项目(tracker/cc-ai-kefu/cms-cj/loan-reminder 等)零接触。
|
||||
2. web 回环端口 18089(ov)/18088(cn)——8089 两机都被 1Panel phpMyAdmin 占用。
|
||||
3. cn mysql 调优:performance-schema=OFF + innodb-buffer-pool-size=128M
|
||||
(cn 仅 3.5G 内存,栈占用实测 ~495M,available ~1.0G)。
|
||||
4. ov mysql 公网映射 0.0.0.0:13306(sync 直连读库:对账+统计回传;**安全组未放行前
|
||||
这两循环持续幂等重试**,内容推送走 API 443 不受影响)。
|
||||
5. `docker/web.Dockerfile` 正式化 entrypoint ipv6 脚本移除(commit 30a3520;钉版本仍待办)。
|
||||
|
||||
## 4. 遗留与待办
|
||||
|
||||
- **[用户操作] ov 安全组放行 TCP/13306 ← 101.37.116.184**——放行后 sync 的
|
||||
reconcile(5min 对账)与 stat return(60s 统计回传)即自愈(幂等重试中)。
|
||||
- **[用户决策] cn 机 2G swap**:机器级改动经用户确认后再做(当前 1.0G available
|
||||
无 swap,worker sharp 峰值有 OOM 风险;建议做)。
|
||||
- **[用户操作] uniservice 首批用户同步**:12h 间隔下重启后首批要等——后台「手动同步」
|
||||
按钮触发(或等定时)。
|
||||
- gitea push 待令牌(本会话推送认证失败;commit 30a3520 未推送,镜像已直传服务器)。
|
||||
- 服务器无源码 clone(部署走镜像传输);后续更新流程待 gitea 令牌后补 `git pull` 工作流。
|
||||
- 磁盘两机均 ~85%(cn 5.6G/ov 5.7G 余)——长期扩容建议已登记。
|
||||
- 既有风险提醒(未动):cn 机 1Panel MySQL 5.7 绑 0.0.0.0:3306 公网暴露,建议收口。
|
||||
- 密钥管理:全部新生成(SYNC_INTERNAL_KEY/mysql×4/ADMIN_PASSWORD),本地
|
||||
`.env.prod-secrets/`(gitignore)+ 两机 `.env*`(600)。ADMIN 密码建议首次登录后
|
||||
经后台修改(env 值仅 seed 用)。
|
||||
|
||||
## 5. 运维备忘(本次新增坑位)
|
||||
|
||||
- cn/ov 入口均为 1Panel OpenResty 站点(用户面板管理,续期由 1Panel 负责);
|
||||
容器操作走 `docker compose -f /opt/photo-live/deploy/docker-compose-{cn,ov}.yml`。
|
||||
- 本机→ov 文件传输走 `scp photo-ov:`(自动过跳板);226MB 镜像包约 4 分钟。
|
||||
- 重建 api 容器后 web 需一并 restart(既有坑位,本次未触发——首装 up 一次成型)。
|
||||
Reference in New Issue
Block a user