# 宠小它生产发布与回滚 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,或预检的 15 项只读不变量非 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--.sql shasum -a 256 /secure/backup/petstore-before--.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` 禁止跳步、交换顺序或让应用自动补表。任一验证项非 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- \ --property=Type=oneshot \ --property=User=petstore \ --property=Group=petstore \ --property=WorkingDirectory=/opt/petstore/backend/releases/ \ --property=EnvironmentFile=/etc/petstore/backend.env \ /usr/bin/env BUILD_FIRST=0 \ JAR_PATH=/opt/petstore/backend/releases//petstore-backend.jar \ ./deploy/release-preflight.sh ``` 通过条件:静态配置检查通过、JPA `validate` 通过、15 项数据不变量全部为 0,并以退出码 0 结束。预检不会执行迁移或修复数据。 ## 6. 构建发布产物 ### 6.1 后端 将已冻结 jar 放入 `/opt/petstore/backend/releases//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. 执行 `nginx -t`。检查配置 diff 仅增加/修改 Petstore 的三个 server block,不改 Gitea/GitLab upstream、端口、证书和 location。 5. 只执行 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:// ./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 店预约/报告/线索;公开报告响应不含内部身份字段。 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 决策人。