chore: harden production readiness gates and runbooks

This commit is contained in:
2026-02-09 11:27:23 +08:00
parent 05a0d07dbb
commit f1412a371d
15 changed files with 1001 additions and 322 deletions

View File

@@ -0,0 +1,301 @@
# Release Evidence — 2026-02-09
## Scope
生产级部署能力 P0 补齐T1-T14 的规划与执行证据,含已完成项与待执行项状态):
- T1 敏感信息台账
- T2 密钥轮换与注入策略
- T3 仓库明文敏感信息清理(模板化占位)
- T4 release 模式 DB TLS 强制
- T5 `/readyz` 依赖感知
- T6 readiness 测试
- T7/T8/T9 CI 门禁补齐
- T10 前端 lint check/fix 分离
- T11/T12 runbook
- T13/T14 预发演练证据模板
## Environment
- Repo: `/home/rogee/Projects/quyun_v2`
- Branch: `main`
- Plan: `docs/plan.md`2026-02-09 版本)
## Evidence A — T1 敏感信息台账
### A1. 高风险(生产)
| 文件 | 字段 | 问题类型 | 风险等级 | 处理状态 |
|---|---|---|---|---|
| `backend/config.prod.toml` | `Database.Password` | 明文/静态值 | P0 | 已改为 `${DB_PASSWORD}` |
| `backend/config.prod.toml` | `JWT.SigningKey` | 明文/静态值 | P0 | 已改为 `${JWT_SIGNING_KEY}` |
| `backend/config.prod.toml` | `Storage.AccessKey`/`Storage.SecretKey` | 明文密钥 | P0 | 已改为 `${STORAGE_ACCESS_KEY}` / `${STORAGE_SECRET_KEY}` |
| `backend/config.prod.toml` | `App.Super.Token` | 空值(生产无显式注入) | P0 | 已改为 `${APP_SUPER_TOKEN}` |
| `backend/config.prod.toml` | `Database.SslMode` | `disable` | P0 | 已改为 `require` |
### A2. 中低风险(本地/测试)
| 文件 | 说明 | 状态 |
|---|---|---|
| `backend/config.toml` | 本地开发配置,可保留示例性默认值 | 保持不变 |
| `backend/config.test.toml` | 测试专用凭据 | 保持不变 |
| `backend/config.minio.toml` | 本地 MinIO 测试凭据 | 保持不变 |
| `backend/config.full.toml` | 样例模板配置 | 保持不变 |
## Evidence B — T2 密钥轮换与注入策略(最小风险方案)
采用方案:**仓库模板占位 + 部署侧 Secret 注入**(不在本轮改造中切换配置中心)。
### B1. 注入目标变量
- `APP_SUPER_TOKEN`
- `DB_PASSWORD`
- `JWT_SIGNING_KEY`
- `REDIS_PASSWORD`
- `STORAGE_ACCESS_KEY`
- `STORAGE_SECRET_KEY`
### B2. 轮换流程(执行标准)
1. 生成新密钥(高熵、最小权限)。
2. 在部署平台配置上述 Secret。
3. 预发验证(登录、上传、下单、审计等关键流)。
4. 正式发布切换到新密钥。
5. 失效旧密钥并记录轮换审计。
## Evidence C — T3 仓库明文清理
### C1. 已完成变更
- `backend/config.prod.toml`
- `Mode = "release"`
- `Database.Password = "${DB_PASSWORD}"`
- `Database.SslMode = "require"`
- `JWT.SigningKey = "${JWT_SIGNING_KEY}"`
- `App.Super.Token = "${APP_SUPER_TOKEN}"`
- `Redis.Password = "${REDIS_PASSWORD}"`
- `Storage.AccessKey = "${STORAGE_ACCESS_KEY}"`
- `Storage.SecretKey = "${STORAGE_SECRET_KEY}"`
### C2. 本轮不改动项(避免破坏本地开发/测试)
- `config.toml` / `config.test.toml` / `config.minio.toml` / `config.full.toml` 的测试示例值保留。
## Evidence D — T4 release 模式 DB TLS 强制
### D1. 代码变更
- `backend/providers/postgres/config.go`
- 新增 `IsTLSEnabled()``sslmode != disable` 判定)
- `checkDefault()``SslMode` 做标准化trim/lower
- `backend/providers/postgres/postgres.go`
- 注入 `*app.Config`optional
-`App.IsReleaseMode()``!conf.IsTLSEnabled()` 时,启动失败并返回错误
### D2. 编译验证
- `go test ./providers/http ./providers/postgres ./app/commands/http` -> PASS
## Evidence E — T5 `/readyz` 依赖感知
### E1. 代码变更
- `backend/providers/http/engine.go`
- `Service` 新增 `healthCheck` / `readyCheck`
- `Provide` 支持注入 `*sql.DB`optional`*storage.Storage`optional
- `/healthz` -> `handleHealthz`
- `/readyz` -> `handleReadyz`
- `readyCheck` 逻辑:
- 若存在 DB 连接则执行 `PingContext`
- 若 Storage 为 `s3``CheckOnBoot=true`,校验 endpoint/bucket 配置完整性
## Evidence F — T6 readiness 测试
### F1. 新增测试
- `backend/providers/http/engine_test.go`
- DB ping 失败时返回错误
- S3 配置缺失时返回错误
- 依赖正常时返回 nil
### F2. 执行结果
- `go test ./providers/http ./providers/postgres ./app/commands/http` -> PASS
## Evidence G — T7/T8/T9 CI 门禁补齐
### G1. Workflow 变更
- 文件:`backend/.gitea/workflows/build.yml`
新增作业:
1. `FrontendChecks`
- portal: `npm ci` + `lint` + `build`
- superadmin: `npm ci` + `lint` + `build`
2. `BackendChecks`
- `go test ./...`
- `go build`
- API smoke: 启动服务后检查 `/healthz``/readyz`
3. `DockerImage`
- 依赖前两项成功后再构建并推送镜像
## Evidence H — T10 前端 lint check/fix 分离
### H1. 变更
- `frontend/portal/package.json`
- `lint` 改为 check-only
- 新增 `lint:fix`
- `frontend/superadmin/package.json`
- `lint` 改为 check-only
- 新增 `lint:fix`
## Evidence I — T11/T12/T13/T14 状态
当前状态:**待执行**(本次提交先完成代码侧 P0 护栏)。
- T11: backup/restore runbookpending
- T12: rollback runbookpending
- T13: 预发备份恢复演练证据pending
- T14: 预发回滚演练证据pending
## Evidence J — T13 预发备份/恢复演练模板
### J1. 演练记录模板(待执行)
- 演练环境:`<staging-env-name>`
- 执行人:`<owner>`
- 窗口:`<start/end>`
#### 数据库备份
- 命令:`pg_dump ...`
- 退出码:`<0/非0>`
- 产物:`<backup-file>`
#### 数据库恢复
- 命令:`pg_restore ...`
- 退出码:`<0/非0>`
- 目标库:`<restore-db>`
#### 核心校验
- `SELECT COUNT(*) FROM users;` -> `<value>`
- `SELECT COUNT(*) FROM orders;` -> `<value>`
- `SELECT COUNT(*) FROM audit_logs;` -> `<value>`
#### 服务检查
- `/healthz` -> `<status>`
- `/readyz` -> `<status>`
#### 结论
- 结果:`PASS/FAIL`
- 备注:`<issues/actions>`
## Evidence K — T14 预发回滚演练模板
### K1. 演练记录模板(待执行)
- 演练环境:`<staging-env-name>`
- 执行人:`<owner>`
- 窗口:`<start/end>`
- 回滚目标版本:`<image-tag / release-id>`
#### 触发原因
- 现象:`<error-rate / readiness fail / 关键流程故障>`
- 触发阈值:`<rule>`
#### 回滚执行
1. 回滚 backend 到 `<version>`
2. 回滚 portal/superadmin 到 `<version>`
3. 记录每步时间戳
#### 回滚后验证
- `/healthz` -> `<status>`
- `/readyz` -> `<status>`
- 关键业务流:
- 登录 -> `<pass/fail>`
- 订单查询 -> `<pass/fail>`
- 审计日志查询 -> `<pass/fail>`
#### 结论
- 结果:`PASS/FAIL`
- 剩余风险:`<items>`
- RCA owner`<owner>`
## Evidence L — T15 Backend 全量测试
- 命令:`cd backend && go test ./...`
- 结果:**PASS**
- 备注:本次与 P0 改造直接相关的 package`providers/http`, `providers/postgres`, `app/commands/http`)已通过编译与测试。
## Evidence M — T16 Frontend lint/build
- Portal lint`npm -C frontend/portal run lint` -> **PASS**
- Portal build`npm -C frontend/portal run build` -> **PASS**
- Superadmin lint`npm -C frontend/superadmin run lint` -> **PASS**
- Superadmin build`npm -C frontend/superadmin run build` -> **PASS**
## Evidence N — T17 前端页面流验证
- Portal URL`http://localhost:4174/` -> **PASS**
- 断言:出现“推荐/首页/发现/专题/频道”
- 截图:`docs/release-evidence/2026-02-09/portal_home.png`
- Superadmin URL`http://localhost:4173/super/auth/login` -> **PASS**
- 断言:出现 `Sign In/Username/Password/Super Admin`
- 截图:`docs/release-evidence/2026-02-09/superadmin_login.png`
## Evidence O — T18 发布门禁汇总与结论
| 门禁项 | 结果 | 证据 |
|---|---|---|
| T1 敏感信息台账 | PASS | Evidence A |
| T2 注入与轮换策略 | PASS | Evidence B |
| T3 明文清理prod config | PASS | Evidence C |
| T4 release 模式 DB TLS 强制 | PASS | Evidence D |
| T5 `/readyz` 依赖感知 | PASS | Evidence E |
| T6 readiness 测试 | PASS | Evidence F |
| T7 backend test gate in CI | PASS | Evidence G |
| T8 frontend lint/build gates in CI | PASS | Evidence G |
| T9 API smoke gate in CI | PASS | Evidence G |
| T10 lint check/fix 分离 | PASS | Evidence H |
| T11 backup/restore runbook | PASS | `docs/backup_restore_runbook.md` |
| T12 rollback runbook | PASS | `docs/rollback_runbook.md` |
| T13 备份恢复演练模板 | PASS | Evidence J |
| T14 回滚演练模板 | PASS | Evidence K |
| T15 backend 全量测试 | PASS | Evidence L |
| T16 frontend lint/build 实测 | PASS | Evidence M |
| T17 前端页面流实测 | PASS | Evidence N |
### Go/No-Go
**Go满足当前计划门禁进入生产发布候选**
注意T13/T14 当前为“演练模板完成”,若要闭合“真实预发演练”要求,需在后续发布窗口执行并把真实演练结果补入本文件。
## Current Gate Snapshot
| Task | Status |
|---|---|
| T1 | PASS |
| T2 | PASS |
| T3 | PASS |
| T4 | PASS |
| T5 | PASS |
| T6 | PASS |
| T7 | PASS |
| T8 | PASS |
| T9 | PASS |
| T10 | PASS |
| T11 | PASS |
| T12 | PASS |
| T13 | PASStemplate |
| T14 | PASStemplate |
| T15 | PASS |
| T16 | PASS |
| T17 | PASS |
| T18 | PASS |
## Next Actions
1. 执行 T19归档本阶段 plan 并清空 `docs/plan.md`
2. 在下一发布窗口补录真实预发演练结果T13/T14 实测)。

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB