228 lines
12 KiB
Markdown
228 lines
12 KiB
Markdown
# 宠小它生产发布与回滚 Runbook
|
||
|
||
> 版本:2026-08-01
|
||
>
|
||
> 适用范围:3~5 家独立宠物洗护单店试点;backend、admin、H5、微信小程序。
|
||
>
|
||
> 当前结论:仓库交付到 `Ready for Release`,未取得真实生产域名、凭据、备份和线上验收证据前,不得标记 `Released`。
|
||
|
||
## 1. 发布原则
|
||
|
||
1. 发布单元必须冻结为四元组:`backend commit + admin commit + frontend commit + docs/RC manifest commit`,同时记录 jar/静态包校验和。
|
||
2. 生产数据库只通过已审核 SQL 迁移;应用固定 `ddl-auto=validate`,禁止启动期改表或修复数据。
|
||
3. 后端预检和自动 smoke 均为只读;创建预约、开始服务、提交报告等写路径只在明确指定的试点门店和人工验收窗口执行。
|
||
4. 应用、静态站点采用版本目录加 `current` 软链接切换;数据库迁移为向前兼容的追加式变更,不做自动向下迁移。
|
||
5. Petstore 使用独立域名、Nginx `server` 块、systemd unit 和发布目录。不得覆盖、重启或复用同机 Gitea/GitLab 的配置与服务。
|
||
6. 任何命令输出都不得包含数据库密码、微信 AppSecret、session secret、完整报告 token、手机号或私密媒体 URL。
|
||
|
||
## 2. 本次发布任务卡
|
||
|
||
| 字段 | 值 |
|
||
|---|---|
|
||
| Task Name | 建立可重复的生产发布与回滚闭环 |
|
||
| Role | Backend Ops / Frontend Release Ops |
|
||
| Owner Agent | Backend Ops(后端与 Nginx)/ Frontend Release Ops(admin、H5、小程序) |
|
||
| Paired QA | Core Flow QA / Report Share QA |
|
||
| Workstream | Release |
|
||
| Track | Ops |
|
||
| Status | Ready for Release(生产证据待补) |
|
||
| Priority | P0 |
|
||
| Repo/Path | `backend/deploy/**`、前端发布配置、`docs/**` |
|
||
| Service Lock | Petstore backend unit + Petstore 专用 Nginx server blocks + Petstore static roots |
|
||
| Depends On | 冻结 RC、真实域名/TLS、备份、六个迁移、微信合法域名 |
|
||
| Acceptance | 预检、构建、readiness、只读 smoke、三角色人工 smoke、回滚演练均有证据 |
|
||
| RC Included | Yes |
|
||
| Due Date | TBD |
|
||
| Output Link | 当前 Runbook + RC manifest |
|
||
| Blocker Owner | 生产环境负责人(域名/凭据/变更窗口) |
|
||
| Commit / RC | Not Frozen,提交后写入 RC manifest |
|
||
| Architecture Review Required | Yes |
|
||
| Data Model Review Required | Yes(迁移顺序与回滚边界) |
|
||
| Access / Privacy Review Required | Yes |
|
||
|
||
## 3. 立即停止条件
|
||
|
||
出现任一情况立即停止,不继续切流:
|
||
|
||
- 四仓 commit、构建产物或校验和与 RC manifest 不一致;
|
||
- 生产域名仍为 localhost、`.invalid`、`api.s-good.com` 或 `www.s-good.com` 等非本项目域名;
|
||
- 数据库备份未完成、校验和缺失或未在隔离库验证可恢复;
|
||
- 任一迁移末尾验证查询非 0,或预检的 24 项只读不变量非 0;
|
||
- `nginx -t` 失败,或 diff 显示将修改 Gitea/GitLab 的 server block;
|
||
- readiness 非 `UP`,上传目录/FFmpeg/数据库任一依赖失败;
|
||
- 发现凭据、完整 token、手机号或私密媒体地址进入日志/工单/截图;
|
||
- 无明确值班人、回滚执行人或生产变更授权。
|
||
|
||
## 4. 发布前冻结
|
||
|
||
RC Steward 在 `docs/qa-reports` 创建当次 manifest,至少记录:
|
||
|
||
- 四仓 commit、分支、`git status --short`(必须为空);
|
||
- Java/Node/npm/Maven 版本;
|
||
- jar、admin `dist`、H5 `dist`、小程序构建目录的 SHA-256;
|
||
- 目标环境、变更窗口、操作者、审核人;
|
||
- 本次包含/排除项、已知风险、旧版本回滚点;
|
||
- 域名只记 host,不记 query/token;配置只记变量名和“已配置”,不记值。
|
||
|
||
冻结后重新运行本地门禁:
|
||
|
||
```bash
|
||
cd /Users/apple/_src/petstore/backend
|
||
mvn test
|
||
mvn -DskipTests package
|
||
bash -n deploy/release-preflight.sh deploy/production-smoke.sh
|
||
|
||
cd /Users/apple/_src/petstore/admin
|
||
npm run build
|
||
|
||
cd /Users/apple/_src/petstore/frontend
|
||
npm run build:h5
|
||
npm run build:mp-weixin
|
||
|
||
cd /Users/apple/_src/petstore
|
||
python3 docs/graph/validate_ontology.py docs
|
||
python3 docs/graph/audit_drift.py
|
||
```
|
||
|
||
## 5. 数据库备份与迁移
|
||
|
||
### 5.1 备份证据
|
||
|
||
使用权限受控的 MySQL option file,避免把密码写在命令行。备份必须包含表结构、数据、触发器、事件和存储过程,并记录文件大小、SHA-256、开始/结束时间。示例:
|
||
|
||
```bash
|
||
mysqldump --defaults-extra-file=/secure/path/petstore-mysql.cnf \
|
||
--single-transaction --routines --triggers --events --hex-blob \
|
||
petstore > /secure/backup/petstore-before-<rc>-<timestamp>.sql
|
||
shasum -a 256 /secure/backup/petstore-before-<rc>-<timestamp>.sql
|
||
```
|
||
|
||
上线前必须把该备份恢复到隔离数据库并完成基础计数核对;“命令退出 0”不能替代恢复演练。备份保留周期由数据负责人决定,本流程不自动删除任何备份。
|
||
|
||
### 5.2 固定迁移顺序
|
||
|
||
在维护窗口中按以下顺序人工执行,每一步都保存执行时间和脚本 SHA-256,并检查脚本末尾验证查询:
|
||
|
||
1. `backend/db/migrations/20260801_split_service_identity.sql`
|
||
2. `backend/db/migrations/20260801_create_store_customer.sql`
|
||
3. `backend/db/migrations/20260801_create_business_event.sql`
|
||
4. `backend/db/migrations/20260801_create_booking_capacity.sql`
|
||
5. `backend/db/migrations/20260801_create_report_send_status.sql`
|
||
6. `backend/db/migrations/20260802_create_store_onboarding.sql`
|
||
|
||
禁止跳步、交换顺序或让应用自动补表。任一验证项非 0,停止发布并由 Data Model/业务 owner 判断修复方案;不得猜测性回填客户身份、报告作者、事件、历史发送事实或历史开通状态。
|
||
|
||
### 5.3 生产只读预检
|
||
|
||
把 `backend/deploy/petstore-backend.env.example` 复制到服务器权限受控的 `/etc/petstore/backend.env` 并填入真实值;文件权限建议 `0600`,所有者为服务账号。不要在交互 shell 中 `source` 该文件,也不要用 `env $(cat ...)`,避免特殊字符被 shell 解释或泄露。通过一次性 systemd unit 以与正式服务相同的 `EnvironmentFile` 和服务账号执行:
|
||
|
||
```bash
|
||
systemd-run --wait --pipe --collect \
|
||
--unit=petstore-preflight-<rc> \
|
||
--property=Type=oneshot \
|
||
--property=User=petstore \
|
||
--property=Group=petstore \
|
||
--property=WorkingDirectory=/opt/petstore/backend/releases/<rc> \
|
||
--property=EnvironmentFile=/etc/petstore/backend.env \
|
||
/usr/bin/env BUILD_FIRST=0 \
|
||
JAR_PATH=/opt/petstore/backend/releases/<rc>/petstore-backend.jar \
|
||
./deploy/release-preflight.sh
|
||
```
|
||
|
||
通过条件:静态配置检查通过、JPA `validate` 通过、24 项数据不变量全部为 0,并以退出码 0 结束。预检不会执行迁移或修复数据。
|
||
|
||
## 6. 构建发布产物
|
||
|
||
### 6.1 后端
|
||
|
||
将已冻结 jar 放入 `/opt/petstore/backend/releases/<rc>/petstore-backend.jar`,同时归档 commit、SHA-256 和 `deploy/` 模板。systemd unit 以 `backend/deploy/petstore-backend.service.example` 为基线,由服务器管理员安装为 Petstore 独立 unit。
|
||
|
||
### 6.2 Admin 与 H5
|
||
|
||
真实域名配置保存在权限受控文件,不提交仓库。门禁和构建必须使用同一份内容:
|
||
|
||
```bash
|
||
cd /opt/build/petstore/admin
|
||
PETSTORE_ADMIN_ENV_FILE=/secure/path/petstore-admin.env npm run preflight:release
|
||
cp /secure/path/petstore-admin.env .env.production.local
|
||
npm run build
|
||
|
||
cd /opt/build/petstore/frontend
|
||
PETSTORE_FRONTEND_ENV_FILE=/secure/path/petstore-h5.env npm run preflight:release
|
||
cp /secure/path/petstore-h5.env .env.production.local
|
||
npm run build:h5
|
||
```
|
||
|
||
产物进入版本目录后再切换 `current` 软链接。构建结束应清除工作区内临时 `.env.*.local`,但保留受控配置源和构建证据。
|
||
|
||
### 6.3 微信小程序
|
||
|
||
```bash
|
||
cd /opt/build/petstore/frontend
|
||
PETSTORE_FRONTEND_ENV_FILE=/secure/path/petstore-mp.env npm run preflight:release
|
||
cp /secure/path/petstore-mp.env .env.mp-weixin.local
|
||
npm run build:mp-weixin
|
||
```
|
||
|
||
微信公众平台的 `request` / `downloadFile` 合法域名必须与门禁通过的实际域名一致。先上传体验版并完成真机三角色验收,再决定提审/发布;后端或 H5 发布不会自动更新用户手机里的小程序。
|
||
|
||
## 7. 切换应用与 Nginx
|
||
|
||
1. 将后端 `current` 指向新版本目录,执行 `systemctl daemon-reload`,只重启 Petstore 独立 unit。
|
||
2. 确认进程稳定并通过本机 readiness,再切 admin/H5 静态目录 `current`。
|
||
3. 以 `backend/deploy/nginx-petstore.conf.example` 新建 Petstore 专用配置文件;替换域名、证书路径和静态根目录。
|
||
4. 保留模板中的 `petstore_safe` access log 格式,只记录 `$uri`,不得改回会包含 query 的 `$request` / `$request_uri`,避免报告 token 与邀请 token 进入访问日志。
|
||
5. 执行 `nginx -t`。检查配置 diff 仅增加/修改 Petstore 的日志格式和三个 server block,不改 Gitea/GitLab upstream、端口、证书和 location。
|
||
6. 只执行 Nginx reload,不停止 Nginx;reload 后同时检查 Petstore、Gitea、GitLab 的既有健康入口。
|
||
|
||
证书续签必须沿用服务器现有 ACME/证书管理方式,为 Petstore 新域名单独申请/续签;不要替换其他产品证书。续签后先 `nginx -t` 再 reload,并记录证书到期时间。
|
||
|
||
## 8. 上线验证
|
||
|
||
### 8.1 自动只读 smoke
|
||
|
||
```bash
|
||
cd /opt/petstore/backend/current
|
||
API_ORIGIN=https://<api-domain> ./deploy/production-smoke.sh
|
||
```
|
||
|
||
脚本验证 liveness、readiness、公开门店、服务时长和可约容量契约;提供受控的 `SMOKE_BEARER_TOKEN` 时才增加两个 protected GET。脚本不会创建或修改业务数据。
|
||
|
||
### 8.2 三角色人工 smoke
|
||
|
||
仅在预先标记的试点门店和测试宠主上执行,记录匿名化预约 ID/报告 ID,不记录完整 token/手机号:
|
||
|
||
1. **boss**:用微信核验手机号创建试点门店;在 Admin/小程序补齐资料、容量、服务并显式完成开通;创建一次性员工邀请、保存创建证据后用小程序分享卡片;查看工作台、客户、日程。
|
||
2. **staff**:从分享卡片进入,核对门店/姓名/脱敏手机号;用受邀微信手机号成功加入。用不匹配手机号验证被拒绝;加入后代客预约、开始服务、提交一约一报告并确认发送。
|
||
3. **customer**:小程序自助预约并验证长服务跨时段占号;打开匿名报告页;提交提醒留资;再次预约。
|
||
4. **权限反例**:customer 不可进入后台;A 店账号不可读取 B 店预约/报告/线索/邀请;legacy 永久邀请码与直接建员工均返回停用码;删除员工后旧 session 立即 401。
|
||
5. **异常反例**:邀请手机号不匹配/过期/撤销/重复使用、无容量、非法状态迁移、缺前/后照片、错误报告 token、FFmpeg 失败均有可理解提示。
|
||
|
||
## 9. 回滚
|
||
|
||
### 9.1 应用与静态资源回滚
|
||
|
||
- 保留前一版本目录和软链接目标。若新版本 readiness、错误率或主链路失败,先停止切流,把 backend/admin/H5 的 `current` 恢复到 RC manifest 中的前一版本,再只重启 Petstore unit、`nginx -t` 并 reload。
|
||
- 微信小程序不能靠服务器软链接回滚。应在公众平台保留上一稳定版本,按微信流程回退;同时确认其 API 契约仍受当前后端兼容。
|
||
- 回滚后重新执行只读 smoke,并记录故障窗口、影响门店、回滚时间和 owner。
|
||
|
||
### 9.2 数据库回滚边界
|
||
|
||
本批六个迁移以新增列/表/索引和兼容回填为主,旧应用应忽略新增结构。因此常规回滚只回应用,不删除列、表、索引,不执行自动 down migration,也不把已产生的新业务数据覆盖回旧备份。
|
||
|
||
只有确认发生灾难性数据破坏、已冻结所有写入、明确接受备份时间点之后的数据丢失,并取得用户/业务负责人、DBA 和隐私责任人的书面授权后,才可执行整库恢复。恢复必须先在隔离库验证,再制定专门变更单;本 Runbook 不授权该操作。
|
||
|
||
## 10. 发布完成定义
|
||
|
||
只有以下证据全部归档,RC 才能由 `Ready for Release` 改为 `Released`:
|
||
|
||
- 四仓冻结 commit 与产物校验和;
|
||
- 备份及隔离恢复验证;
|
||
- 六个迁移执行和末尾验证结果;
|
||
- 最终配置门禁与后端只读预检;
|
||
- `nginx -t`、readiness、自动只读 smoke;
|
||
- boss/staff/customer 三角色人工 smoke;
|
||
- Gitea/GitLab 回归未受影响;
|
||
- 监控、告警联系人、回滚点和值班窗口;
|
||
- 未解决问题清单及 Go/No-Go 决策人。
|