docs: update production mobile deployment runbook
Some checks failed
CI / verify (push) Has been cancelled

Sync the CDN origin, five-node cache policy, audio compatibility routing, validation, rollback, and current branch baseline.

Made-with: Proma
This commit is contained in:
cxk
2026-07-28 01:00:40 +08:00
parent fc93b8d7d3
commit 0b3a31f556

View File

@@ -1,20 +1,23 @@
# frontend-miniapp 正式环境部署手册
最后更新2026-07-24(同步五节点 `17083` 缓存策略修复及逐节点验收
最后更新2026-07-28(同步 CDN 源站域名、五节点缓存策略、讲解音频兼容路由及 CDN 验收/回滚流程
## 适用范围
本文档适用于将本地仓库 `F:\深圳自然博物馆\frontend-miniapp` 的 H5 产物部署到深圳自然博物馆正式环境。
本文档适用于将本地仓库 `E:\深圳自然博物馆\workplace\frontend-miniapp` 的 H5 产物部署到深圳自然博物馆正式环境。
- 正式域名:`https://guide.sznhmuseum.org.cn`
- CDN 源站域名:`https://guide-real.sznhmuseum.org.cn`(只供 CDN 回源和源站排障,不作为游客入口)
- 源站公网 IP`218.17.240.244`
- 主节点:`172.20.14.21`
- 部署目录:`/data/nginx/html/mobile`
- Nginx 容器:`sgs-nginx``nginx/1.30.3`Docker host 网络)
- Nginx 生效配置目录:`/data/nginx/conf.d`(容器内 `/etc/nginx/conf.d`,只读挂载)
- 同步脚本:`/data/scripts/sync-frontend-nodes.sh`
- Nginx 配置变更记录2026-07-24五台 `17083`统一 assets/字体缓存静态资源 404 防 SPA 回退规则
- Nginx 配置变更记录2026-07-28`.21` 公网入口及五台 `17083`完成 CDN 源站、分层缓存静态资源 404 防 SPA 回退和音频兼容路由适配
- 同步节点:`172.20.14.23``172.20.14.26``172.20.14.27``172.20.14.33`
- 远程仓库:`http://192.168.0.93:3333/lyf/frontend-miniapp.git`
- 远程仓库:`https://git.whaoyue.com/lyf/frontend-miniapp.git`
- 本次文档同步基线:分支 `codex/import-sgs-frontend-mobile-20260727`,提交 `fc93b8d7d32a208a037be576ce3b9da2db3c1fe3`(仅表示仓库同步基线,不表示该提交已部署到生产)
不要把服务器密码、证书私钥、token、真实后台账号、腾讯地图正式 key 写入仓库文档或提交记录。
@@ -23,7 +26,7 @@
进入项目目录:
```powershell
cd 'F:\深圳自然博物馆\frontend-miniapp'
cd 'E:\深圳自然博物馆\workplace\frontend-miniapp'
```
检查分支、远程仓库和本地改动:
@@ -80,11 +83,13 @@ Saved working directory and index state On master: pre-deploy-prod-local-fixes-Y
STASHED=pre-deploy-prod-local-fixes-YYYYMMDDHHmmss
```
拉取远程最新代码
拉取目标发布分支。发布前必须由变更单或版本记录明确分支名,不得默认把 `master` 当作本次目标
```powershell
$branch = 'codex/import-sgs-frontend-mobile-20260727'
git fetch origin
git pull --ff-only origin master
git switch $branch
git pull --ff-only origin $branch
git log -1 --pretty=format:'%h %s'
```
@@ -103,15 +108,26 @@ git status --short --branch --untracked-files=all
恢复后重点检查 `.env.production``static/guide-data/manifest.json``src/utils/publicUrl.ts`,确认正式环境仍指向 `172.20.14.21``https://guide.sznhmuseum.org.cn`
## Nginx 当前生产配置2026-07-24
## Nginx 与 CDN 当前生产配置2026-07-28
本节以 `172.20.14.21``docker exec sgs-nginx nginx -T` 的实际生效结果为准。主 Nginx 使用 Docker host 网络,公共域名不直接读取宿主静态目录,而是通过 upstream `sgs_mobile_pool` 转发到五台 `17083` 工作节点:
```text
guide.sznhmuseum.org.cn:80 -> sgs_mobile_pool -> 172.20.14.21/.23/.26/.27/.33:17083
guide.sznhmuseum.org.cn:443 -> sgs_mobile_pool -> 172.20.14.21/.23/.26/.27/.33:17083
游客 HTTPS -> guide.sznhmuseum.org.cn CDN -> guide-real.sznhmuseum.org.cn:443
-> 218.17.240.244 / 172.20.14.21 sgs-nginx
-> sgs_mobile_pool -> 172.20.14.21/.23/.26/.27/.33:17083
```
`.21:/data/nginx/conf.d/guide-public.conf` 的 HTTP/HTTPS `server_name` 均包含 `guide.sznhmuseum.org.cn``guide-real.sznhmuseum.org.cn`。通配符证书 `*.sznhmuseum.org.cn` 覆盖两者,有效期至 2027-01-14。80 端口只保留 ACME challenge 直出和 `/_analytics/` 统一 404其余请求 301 到正式 HTTPS 域名。游客 HTTP 到 HTTPS 的跳转仍应在 CDN 边缘启用CDN 使用 HTTPS 回源时,不能依赖源站 80 端口完成游客侧跳转。
CDN 控制台应使用以下回源基线:
- 回源地址、Host 和 SNI`guide-real.sznhmuseum.org.cn`
- 回源协议/端口HTTPS `443`
- 启用源站证书校验;
- 游客域名始终为 `guide.sznhmuseum.org.cn`
- 不把 `guide-real` 写入前端构建变量或对外链接。
当前移动端工作节点 `17083` 的关键规则如下:
```nginx
@@ -130,48 +146,30 @@ server {
try_files $uri =404;
}
# 模型、manifest、GLB/GLTF 等:明确 MIME缺失时返回 404
location ^~ /static/nav-assets/ {
types {
application/json json;
model/gltf-binary glb;
model/gltf+json gltf;
text/csv csv;
text/markdown md;
}
default_type application/octet-stream;
expires 30d;
add_header Cache-Control "public";
try_files $uri =404;
}
# 字体30 天缓存;其他 static 资源不得回退 index.html
location ^~ /static/Fonts/ {
add_header Cache-Control "public, max-age=2592000";
try_files $uri =404;
}
location ^~ /static/ {
try_files $uri =404;
}
# 页面路由才允许 SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# 实际配置还按可变性拆分以下资源;变更时以 nginx -T 为准:
# - index.html、SPA fallback、/engine/index.html、guide-data、固定名 SDK 入口no-store
# - engine chunks、字体、模型及普通静态文件30 天
# - 缺失静态资源404且不附加长期缓存头
# - 页面路由:仅页面请求允许回退 /index.html回退响应为 no-store
}
```
注意:
- `/assets/``/static/nav-assets/``/static/Fonts/` 和其他 `/static/` 路径必须优先 `try_files $uri =404`,不能把缺失的 JSON、GLB、字体返回为 `index.html`
- `index.html` 仍应保持不缓存或短缓存;带 hash 的 `/assets/` 才使用长期不可变缓存。
- `guide-public.conf` 的 80/443 入口包含 ACME challenge 的直接静态规则,其余请求代理到 `sgs_mobile_pool`;不要把公共域名改回单节点静态 root
- `index.html`、SPA fallback、`/engine/index.html``/static/guide-data/` 和固定文件名 SDK 入口必须为 `Cache-Control: no-store`;带 hash 的 `/assets/` 才使用一年不可变缓存。
- engine chunk、字体、模型和普通静态文件使用 30 天缓存;缺失资源返回 404不能带长期缓存头
- `guide-public.conf` 的 80/443 入口包含 ACME challenge 和 `/_analytics/` 404 规则;不要把公共域名改回单节点静态 root。
- 当前 upstream 使用 `least_conn`,单节点由 `max_fails=3 fail_timeout=10s` 被动摘除。
- 所有 `/_analytics/` 路径必须保持 404不得恢复 Tracker、Matomo 注入、匿名访问通知、`visitor.js``privacy.html`
2026-07-24 逐节点直连 `17083` 复核时发现配置漂移:`.21` 缓存策略正确,`.23/.26/.27/.33` 缺少 `/assets/` 长期 immutable 缓存头和 `/static/Fonts/` 专用规则。四台异常节点已分别备份配置、补齐对应 `17083` location并在 `nginx -t` 通过后平滑 reload。修复后五台均满足:
2026-07-24 逐节点直连 `17083` 复核时发现配置漂移:`.21` 缓存策略正确,`.23/.26/.27/.33` 缺少 `/assets/` 长期 immutable 缓存头和 `/static/Fonts/` 专用规则。四台异常节点已分别备份配置、补齐对应 `17083` location并在 `nginx -t` 通过后平滑 reload。2026-07-27 至 28 日又完成 CDN 分层缓存适配,并同步更新 `.21:/data/scripts/sync-frontend-nodes.sh``write_worker_conf()` 模板。当前五台均满足:
- 字体返回 `Cache-Control: public, max-age=2592000`
- 带 hash 的 assets 返回 `Cache-Control: public, max-age=31536000, immutable`
- HTML、SPA fallback、guide-data 和固定名 SDK 入口返回 `Cache-Control: no-store`
- engine chunk、字体、模型及普通静态文件返回 30 天缓存;
- 缺失静态资源返回 HTTP `404` 且不附加长期缓存头;
- 字体 ETag 条件请求返回 HTTP `304`,且 `304` 响应仍保留 30 天缓存头;
- 20 个 `17080-17083 /healthz` 端点全部正常,五台 `sgs-nginx` 容器均未重启。
@@ -342,15 +340,15 @@ $script | ssh -o StrictHostKeyChecking=no root@172.20.14.21 'bash -s'
ssh -o StrictHostKeyChecking=no root@172.20.14.21 'bash /data/scripts/sync-frontend-nodes.sh'
```
执行前必须注意:该脚本不仅同步 `/data/nginx/html`,还会通过 `write_worker_conf()` 重写五台工作节点的 `/data/nginx/conf.d/sgs-worker.conf`。2026-07-24 已修复 `.23/.26/.27/.33``17083` 缓存配置漂移,当前五台生效配置均包含 `/assets/` 一年 immutable 缓存、`/static/Fonts/` 30 天缓存以及静态资源 404 规则;但同步脚本文件本身仍需要单独复核,不能用脚本内容推断当前生产配置。**普通移动端静态发布不得在未复核脚本内嵌配置前直接运行该脚本。**
执行前必须注意:该脚本不仅同步 `/data/nginx/html`,还会通过 `write_worker_conf()` 重写五台工作节点的 `/data/nginx/conf.d/sgs-worker.conf`脚本模板已于 2026-07-28 同步当前 `17083` CDN 分层缓存策略,但每次使用前仍必须复核脚本与现网配置。**普通移动端静态发布不得在未复核脚本内嵌配置前直接运行该脚本。**
继续使用同步脚本前先检查它是否已经包含本次规则:
```powershell
ssh -o StrictHostKeyChecking=no root@172.20.14.21 'grep -n -E "static/Fonts|31536000|try_files \\$uri =404" /data/scripts/sync-frontend-nodes.sh'
ssh -o StrictHostKeyChecking=no root@172.20.14.21 'grep -n -E "no-store|static/guide-data|engine/index.html|31536000|2592000|try_files \\$uri =404" /data/scripts/sync-frontend-nodes.sh'
```
如果检查没有同时看到 `static/Fonts``31536000`,应先在维护窗口审核并更新 `write_worker_conf()`,再执行 `bash -n /data/scripts/sync-frontend-nodes.sh`、逐节点 `nginx -t``/healthz` 验证。不要用 `.21` 的整份 `sgs-worker.conf` 覆盖其他节点,因为 `.21``17080` 还包含本节点专用 CSP 配置;只同步审核后的 `17083` 移动端配置块。
如果检查未同时覆盖 no-store、30 天缓存、一年 immutable 和静态 404 规则,应先在维护窗口审核并更新 `write_worker_conf()`,再执行 `bash -n /data/scripts/sync-frontend-nodes.sh`、逐节点 `nginx -t``/healthz` 验证。不要用 `.21` 的整份 `sgs-worker.conf` 覆盖其他节点,因为 `.21``17080` 还包含本节点专用 CSP 配置;只同步审核后的 `17083` 移动端配置块。
如果本次仅发布 H5 静态文件、没有修改 Nginx 配置,推荐采用“仅同步 `/data/nginx/html/mobile`”的流程,不要触发同步脚本中的 `write_worker_conf()``/data/apps/sgs-map` 同步和 PM2 重启。静态文件替换完成后不需要 Nginx reload只有配置变更才执行 `nginx -t` 后 reload。
@@ -413,6 +411,37 @@ foreach ($node in $nodes) {
此流程不修改 `/data/nginx/conf.d`,不执行 `nginx -s reload`,也不操作 `/data/apps/sgs-map` 或 PM2。只有 Nginx 配置变更才需要先 `nginx -t` 再 reload。
## 讲解音频与 CDN 兼容
### 当前链路
讲解音频的正确生产链路为:
```text
/app-api/gis/guide/stop/info 或 /app-api/gis/guide/audio/play-info
-> 返回 https://guide.sznhmuseum.org.cn/minio/museum-assets/...mp3
-> CDN
-> .21 /minio/ 代理
-> sgs_minio / museum-assets 对象
```
`.21:/data/nginx/conf.d/guide-public.conf` 当前还保留以下兼容措施:
- `/app-api/gis/sdk/minio/museum-assets/` 直接代理到 `sgs_minio/museum-assets/`,绕过会返回拒绝访问 HTML 的 Java SDK 媒体代理;
-`/app-api/gis/guide/stop/info``/app-api/gis/guide/audio/play-info` 精确启用 no-store 响应改写,将相对 `/minio/museum-assets/...` 地址改为正式域名绝对 URL
- `/minio/museum-assets/tts-audio/audio/` 保留 Range 请求能力,正常响应应为 `audio/mpeg` 和 HTTP `206`
上述 Nginx 兼容层属于生产止血措施。长期方案应在以下两项中择一实施并完成回归测试:
1. 前端修正 `src/utils/publicUrl.ts`,不再把 `/minio/` 强制转换为 `/app-api/gis/sdk/minio/`
2. Java SDK 媒体代理修复权限、状态码、Content-Type 和 Range 转发。
在长期方案上线、CDN 污染对象完成清理且原 SDK 媒体 URL 验证正常前,不得删除当前兼容路由或 API URL 改写。
### 故障识别
已确认的错误特征为:请求 MP3 时收到 HTTP `206`,但 `Content-Type``text/html;charset=utf-8`、响应约 726 字节,正文提示页面被管理员禁止。该响应来自 Java SDK 媒体代理的拒绝访问页CDN 曾缓存该错误对象。只看 HTTP 状态码会误判成功,音频验收必须同时检查 MIME、Range、长度和文件头。
## 线上验证
公网验证:
@@ -437,9 +466,10 @@ curl.exe -sI "$base/static/__not_exists__.json" | Select-String 'HTTP/|Content-T
预期:
- `/assets/<hash>.js`HTTP `200``Cache-Control``max-age=31536000, immutable`
- `/``/index.html``/engine/index.html``/static/guide-data/*.json`HTTP `200``Cache-Control` 为 no-store
- manifestHTTP `200``Content-Type: application/json`
- 真实 GLBHTTP `200``Content-Type: model/gltf-binary`
- `/static/__not_exists__.json`HTTP `404`,不得返回 `200 text/html`
- `/static/__not_exists__.json`HTTP `404`,不得返回 `200 text/html`,也不得附加长期缓存头
入口 JS 验证:
@@ -513,6 +543,62 @@ foreach ($node in $nodes) {
注意:`.21` 的整份 `sgs-worker.conf` 哈希可以与其余节点不同,因为它还承载额外的 `17080` CSP 配置;验收目标是五台 `17083` 移动端配置块和响应行为一致。不要用 `.21` 的整份配置覆盖其他节点。
### 源站、音频与安全负向验证
使用 `--resolve` 绕过 CDN 直连源站,确认 Host/SNI、入口缓存和音频 Range 行为:
```powershell
$origin = '218.17.240.244'
$originHost = 'guide-real.sznhmuseum.org.cn'
$public = 'https://guide.sznhmuseum.org.cn'
$audio = '/minio/museum-assets/tts-audio/audio/2026/07/17/cdb263222e7749009db1768b49477283.mp3'
curl.exe --resolve "${originHost}:443:${origin}" -sI "https://${originHost}/"
curl.exe --resolve "${originHost}:443:${origin}" -sI "https://${originHost}/engine/index.html"
curl.exe --resolve "${originHost}:443:${origin}" -sI "https://${originHost}/static/guide-data/manifest.json"
curl.exe --resolve "${originHost}:443:${origin}" -sS -D - -o NUL -H 'Range: bytes=0-31' "https://${originHost}${audio}"
curl.exe -sS -D - -o NUL -H 'Range: bytes=0-31' "${public}${audio}"
```
音频源站和 CDN 响应均应满足HTTP `206``Content-Type: audio/mpeg`、有效 `Content-Range`,且不得出现约 726 字节的 `text/html`。随后在 390 x 844 移动浏览器中打开英文展项 `865546638960193536`,确认播放器时间从 `0:00` 推进、可完整播放并正常结束。
安全负向检查:
```powershell
@('/_analytics/visitor.js','/_analytics/privacy.html','/_analytics/matomo.js','/_analytics/matomo.php') |
ForEach-Object { curl.exe -s -o NUL -w "%{http_code} $_`n" "$public$_" }
```
四项必须全部返回 HTTP `404`
### CDN 控制台发布后检查
CDN 控制台操作不能由源站部署脚本代替。每次部署涉及 HTML、guide-data、engine 固定入口或音频路由时,至少刷新:
```text
https://guide.sznhmuseum.org.cn/
https://guide.sznhmuseum.org.cn/index.html
https://guide.sznhmuseum.org.cn/engine/index.html
https://guide.sznhmuseum.org.cn/static/guide-data/
```
若处理过讲解音频代理,还需刷新:
```text
https://guide.sznhmuseum.org.cn/app-api/gis/sdk/minio/museum-assets/
```
CDN 规则应保持:
- `/assets/*`:遵循源站一年 immutable
- `/minio/museum-assets/tts-audio/audio/*`30 天;仅在确认文件名内容不可变时才可提升为一年 immutable
- `/app-api/gis/guide/audio/*``/app-api/gis/guide/stop/*`TTL 0
- `/app-api/gis/sdk/minio/museum-assets/*`:污染对象清理和直源验证完成前 TTL 0
- HTML、SPA fallback、`/engine/index.html`、guide-data 和固定名 SDK 入口TTL 0 或严格遵循源站 no-store
- 媒体缓存必须校验成功状态和正确 Content-Type禁止把 `text/html` 当作音频缓存。
同时在 CDN 边缘启用游客 HTTP 到 HTTPS 的 301。控制台变更后重新执行公网与 `--resolve` 对照检查,以区分 CDN 和源站响应。
## 回滚
主节点部署脚本会在 `/data/nginx/html/_backups` 下生成备份,例如:
@@ -540,6 +626,14 @@ $script | ssh -o StrictHostKeyChecking=no root@172.20.14.21 'bash -s'
回滚后应使用前述“仅同步 mobile 静态目录”流程把回滚版本同步到四个工作节点,并重新验证五节点入口 JS 的 SHA256。只有已确认 `/data/scripts/sync-frontend-nodes.sh` 内嵌 Nginx 配置与生产一致时,才允许用全量脚本同步。
如需回滚 CDN/Nginx 适配,使用对应变更备份,不能用旧整目录覆盖现网:
- CDN 源站集群适配:`/data/nginx/_backups/cdn-origin-cluster-20260727223921`
- SDK MinIO 音频绕过:`/data/nginx/_backups/audio-sdk-minio-bypass-20260727232518`
- 音频 API URL 改写:`/data/nginx/_backups/audio-api-url-rewrite-20260727233006`
回滚顺序固定为:确认影响范围,备份当前配置,只恢复对应文件或 location逐节点执行 `docker exec sgs-nginx nginx -t`,平滑 reload再验证五节点 `/healthz`、正式域名、源站域名、音频 Range 和 `/_analytics/` 404。若 CDN 已缓存回滚前对象,还需在控制台刷新对应 URL仅回滚源站不会自动清除 CDN 对象。
## 已遇到的问题与处理
### 1. terser 压缩阶段内存不足
@@ -643,7 +737,30 @@ current: { node: 'v20.19.5', npm: '10.8.2' }
- 验收时关注最终 PM2 状态是否 `online`
- 如未来只需同步 mobile可拆分或新增专用同步脚本避免不必要地重启 map 服务。
### 8. 页面返回 200 但浏览器空白
### 8. CDN 后讲解音频返回 HTML
现象:
```text
HTTP 206
Content-Type: text/html;charset=utf-8
Content-Length: 726
```
根因:
- 前端把 `/minio/...mp3` 转换成 `/app-api/gis/sdk/minio/...mp3`
- Java SDK 媒体代理返回拒绝访问 HTML但状态仍为 200
- CDN 缓存后以 206 响应 Range 请求,造成播放器无法解码。
处理:
- `.21` 兼容路由将 `/app-api/gis/sdk/minio/museum-assets/` 直接代理到 MinIO
- 两个讲解 API 的响应 URL 改写为正式域名 `/minio/museum-assets/...`
- CDN 清除污染前SDK media 路径 TTL 设为 0
- 验收同时检查状态、`audio/mpeg``Content-Range`、文件头和浏览器播放进度。
### 9. 页面返回 200 但浏览器空白
现象:
@@ -679,3 +796,6 @@ https://guide.sznhmuseum.org.cn/ HTTP 200
- 2026-07-20拉取远程最新代码前已执行 `git stash push -u -m pre-deploy-prod-local-fixes-20260720232941`,确认“本地补丁已 stash”随后快进到 `735c5cd`,执行 `git stash pop` 恢复正式环境补丁,无冲突。构建入口为 `assets/index-CsFOrYfU.js`,主节点备份为 `/data/nginx/html/_backups/mobile-before-deploy-20260720233343.tar.gz`,同步脚本最终输出 `All frontend worker nodes synced.`
- 2026-07-23移动端 `17083` 目标配置新增 `/assets/` 一年 immutable 缓存、`/static/Fonts/` 30 天缓存、`/static/nav-assets/` MIME/30 天缓存及全部 `/static/` 缺失资源返回 404。普通 H5 发布改为优先仅同步 mobile 静态目录,不再无条件重写 `sgs-worker.conf`、同步地图应用或重启 PM2。
- 2026-07-24逐节点直连复核发现 `.23/.26/.27/.33` 存在 `17083` 缓存配置漂移,仅 `.21` 正确;四台异常节点已分别备份并补齐字体和 hash assets 缓存规则,`nginx -t` 后平滑 reload。修复后字体缓存头、hash asset immutable 缓存头及字体 ETag/304 均为 5/5 一致20 个工作端点和六类对外入口正常,五台 Nginx 容器均未重启。部署验收新增五节点直连缓存头与 ETag/304 检查,避免公网 `least_conn` 抽样漏检单节点漂移。
- 2026-07-27从分支 `codex/import-sgs-frontend-mobile-20260727` 部署提交 `f1047414bbf00a254e1c0cf99755556e45a18e6f`,生产入口 `assets/index-DvpqYP-p.js`SHA256 `b9f099e2d30d71ca691cc64f20ca6808af834b12dce1c9af8838a6aef1eb893d`;五节点一致,`/engine/` 运行时和移动端导览/讲解流程验证通过。
- 2026-07-27 至 28 日:新增 `guide-real.sznhmuseum.org.cn` CDN 源站域名并完成五节点 `17083` 分层缓存适配;同步脚本模板已更新。随后修复 CDN 后英文讲解音频失败:绕过错误 Java SDK 媒体代理,并把讲解 API 的 `/minio/` URL 改写为正式域名绝对路径。五组语言/声线音频均验证为 HTTP 206、`audio/mpeg` 和正确 MPEG 文件头,展项 `865546638960193536` 英文音频可完整播放至 48.96 秒。CDN 控制台刷新、边缘 HTTP 到 HTTPS 和 Host/SNI 规则仍需按本文清单人工维护。
- 2026-07-28目标分支已快进同步到 `fc93b8d7d32a208a037be576ce3b9da2db3c1fe3`,包含讲解列表、宿主环境判断、展厅卡片图片及相关测试更新。本条只记录文档编制时的代码基线;生产仍以最近一次明确部署记录 `f1047414bbf00a254e1c0cf99755556e45a18e6f` 为准,后续上线 `fc93b8d` 时必须另行补充构建产物、五节点哈希和验收记录。