给内测补上每日加密备份、宕机告警和日志上限。
主机安装和一次恢复演练留给产品。页面没有变化。 Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
71f7fac3a1
commit
53d8df257d
@@ -36182,3 +36182,48 @@
|
||||
- 复发自:无
|
||||
- 修复版本:分支 `codex/chart-ascendant-row-20261015`。
|
||||
|
||||
## BUG-1354 | 生产没有每日自动备份
|
||||
|
||||
- 状态:代码已交,主机安装待产品
|
||||
- 首次发现 / 最近更新:2026-10-14 / 2026-10-15
|
||||
- 来源:产品 10-14 内测体检。任务书 `docs/tasks/TASK-beta-safety-net-20261014.md`。
|
||||
- 现象:生产备份只在有人手动点恢复点时才做。仓库里没有按天跑的备份,也没有定时器文件。
|
||||
- 触发条件:内测开始写入真实用户数据之后,若两次手动备份之间出错或磁盘故障,中间的数据没有恢复点。
|
||||
- 根因:备份是按「发布前做一次」设计的,没有持续运行的每日任务。
|
||||
- 修复:新增只从 `main` 运行的每日备份工作流,复用现有加密与还原校验脚本。每日文件用单独前缀,过期清理不会碰到手动恢复点。工作流要等进入 `main` 后才会在北京时间 04:00 跑。本机没有生产主机,没有手动触发。
|
||||
- 验证:合同测试锁定有每日时刻、只在 `main` 上跑、调用同一个恢复脚本、清理只删过期的每日前缀。虚构目录里,手动恢复点和其他文件都还在。
|
||||
- 防复发:上述合同测试。清理函数不接受外部传入的前缀。
|
||||
- 相关记录:BUG-1350、BUG-1355、BUG-1356。
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/beta-safety-net-20261014`。开工时本文件最大号是 BUG-1353。
|
||||
|
||||
## BUG-1355 | 站点打不开或磁盘过满时没有告警
|
||||
|
||||
- 状态:代码已交,主机安装待产品
|
||||
- 首次发现 / 最近更新:2026-10-14 / 2026-10-15
|
||||
- 来源:同 BUG-1354。
|
||||
- 现象:容器自己的健康检查只会尝试重启,不会通知人。磁盘变满也没有通知。
|
||||
- 触发条件:公网健康检查连续失败,或生产主机磁盘使用率达到 80%。
|
||||
- 根因:上线时没有持续探活,也没有把告警发到人用的渠道。
|
||||
- 修复:探活脚本连续 3 次失败才发一条告警,恢复只发一次。磁盘检查单独在生产主机上跑。Webhook 从环境文件读取,没有配置就只写本机日志。外发正文不含用户内容。脚本还没装到 runner 或生产主机上。
|
||||
- 验证:本地桩服务覆盖正常、500、超时和缺少字段。连续 3 次才告警,恢复只出现一次。签名测试里的密钥没有出现在发出的正文里。
|
||||
- 防复发:上述脚本测试。告警行若含不允许的字段,脚本拒绝发送。
|
||||
- 相关记录:BUG-1354、BUG-1356。
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/beta-safety-net-20261014`。编号接在 BUG-1354 之后。
|
||||
|
||||
## BUG-1356 | 容器日志不轮转,也不能按一轮请求查找
|
||||
|
||||
- 状态:代码已交,主机安装待产品
|
||||
- 首次发现 / 最近更新:2026-10-14 / 2026-10-15
|
||||
- 来源:同 BUG-1354。BUG-1306 取证时日志只在容器控制台,重建后查不到。
|
||||
- 现象:编排没有日志大小上限。知道一轮请求的编号时,没有只读的查找命令。
|
||||
- 触发条件:日志持续写入,或需要按 requestId 回看 web、api、caddy。
|
||||
- 根因:日志沿用守护进程的未知默认值,也没有按编号检索的入口。
|
||||
- 修复:每个服务的日志改为单文件 20 MB、最多 10 个文件。新增只读查找脚本,只接受 UUID。错误汇总只计数和最多 5 个编号,丢掉含邮箱、额外日期或非 ASCII 文本的行。要等生产容器按新编排重建后,轮转才生效。
|
||||
- 验证:两个编排文件里每个服务的日志段一致。虚构日志能按时间排序找到编号;命令注入写法被拒绝,旁边的标记文件还在。含邮箱和汉字的样本没有进入告警文本。
|
||||
- 防复发:上述合同测试和查找脚本的格式检查。
|
||||
- 相关记录:BUG-1306、BUG-1354、BUG-1355。
|
||||
- 复发自:无。
|
||||
- 修复版本:分支 `codex/beta-safety-net-20261014`。编号接在 BUG-1355 之后。
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# 生产备份恢复演练(2026-10-14)
|
||||
|
||||
给产品负责人照着做。这一次演练还原到一次性库,不碰正在服务的主库,网站不用停。真正整库还原会停写,放在文末,只有确认要覆盖现网数据时才做。
|
||||
|
||||
不要把备份文件、密钥、数据库口令、出生资料、姓名、邮箱或会话内容写进仓库、聊天或这份记录。
|
||||
|
||||
## 备份在哪里
|
||||
|
||||
- 每天北京时间 04:00,从 `main` 自动跑「Backup Production Daily」。也可以在 Gitea 里手动点一次。它只在 `main` 上跑。
|
||||
- 加密文件在生产主机 `/opt/jyotisha-production/backups/`,文件名是 `production-daily-日期T时间Z.dump.enc`。旁边有同名的 `-restore-verify.json`,里面只有行数和权限是否一致,没有用户正文。
|
||||
- 手动恢复点仍叫 `production-pre-migration-...`。每日清理只删过期的 `production-daily-` 文件,不会删手动恢复点。
|
||||
- Gitea 上的加密产物默认留 14 天。主机上的每日文件默认留 7 天。
|
||||
- 加密方式与现有恢复点相同:AES-256-CBC、PBKDF2。密钥在主机的数据库环境文件里,不要打印。
|
||||
|
||||
## 一次性库演练(不停机)
|
||||
|
||||
在生产主机上用 `deploy` 用户做。下面的库名换成当天的日期,不要用正在服务的那个库名。
|
||||
|
||||
1. 选定一个 `production-daily-*.dump.enc`。确认旁边的校验文件写着 `"ok": true` 和 `"restore_database_removed": true`。
|
||||
2. 进入 `/opt/jyotisha-production`。数据库口令和加密密钥已经在 `.env.production.database` 里。只在这台机器上读取,不要回显。
|
||||
3. 确认生产 Postgres 容器只有一个。容器名用 `docker ps` 看,下面记成 `<postgres>`。
|
||||
4. 建一次性库,名字必须不是主库名,例如 `restore_drill_20261015`:
|
||||
|
||||
```bash
|
||||
sudo -n docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" <postgres> \
|
||||
psql -U "$POSTGRES_USER" -d postgres -v ON_ERROR_STOP=1 \
|
||||
-c 'CREATE DATABASE "restore_drill_20261015";'
|
||||
```
|
||||
|
||||
5. 解密并还原进这个一次性库。不要把输出重定向到主库。
|
||||
|
||||
```bash
|
||||
openssl enc -d -aes-256-cbc -pbkdf2 \
|
||||
-pass env:PRODUCTION_BACKUP_ENCRYPTION_KEY \
|
||||
-in /opt/jyotisha-production/backups/production-daily-日期T时间Z.dump.enc |
|
||||
sudo -n docker exec -i -e PGPASSWORD="$POSTGRES_PASSWORD" <postgres> \
|
||||
pg_restore -U "$POSTGRES_USER" -d restore_drill_20261015 --exit-on-error
|
||||
```
|
||||
|
||||
6. 只记四个数字:`identity.users`、`public.profiles`、`public.credit_transactions`、`public` 里的表张数。不要把表里的内容抄出来。
|
||||
7. 删掉一次性库,并确认它已经不在:
|
||||
|
||||
```bash
|
||||
sudo -n docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" <postgres> \
|
||||
psql -U "$POSTGRES_USER" -d postgres -v ON_ERROR_STOP=1 \
|
||||
-c 'DROP DATABASE "restore_drill_20261015" WITH (FORCE);'
|
||||
```
|
||||
|
||||
8. 把四个数字、文件名(不要附文件)、一次性库是否已删除,填进 `docs/testing/production-restore-drill-20261014.md`。
|
||||
|
||||
通过:还原命令退出码是 0,四个数字都是非负整数,一次性库已删除,主库没有被这条命令点名。
|
||||
|
||||
失败:停在失败的那一步。不要为了演练去改主库。把退出码交给验收,不要附带环境文件。
|
||||
|
||||
## 整库还原(会停机,平时不要做)
|
||||
|
||||
只有在确认现网库要被这份备份覆盖时才做。预留 30 分钟停写。这不是实测时间,数据变大就再加。
|
||||
|
||||
1. 停掉 `web` 和 `api`,让没有新的写入。Postgres 先留着。
|
||||
2. 用上面同一条解密和 `pg_restore`,目标改成主库名。这一步会覆盖主库里的现有数据。
|
||||
3. 核对四个行数,并对照校验文件里的权限三项是否为真。格式版本低于 2 的旧备份不要拿来做这件事。
|
||||
4. 再启动 `web` 和 `api`。打开 `https://jyotisha.chat/api/health`,`status` 应为 `ok`。
|
||||
5. 把实际停了多久填进演练模板。不要写用户内容。
|
||||
@@ -0,0 +1,34 @@
|
||||
# 进度 · 内测安全网(2026-10-15)
|
||||
|
||||
- 分支:`codex/beta-safety-net-20261014`
|
||||
- 基线:开工时 `origin/staging` 为 `71f7fac3a13f817f91755ff01b25b8706380ae19`
|
||||
- 前置:`09906cdc`(发布通路)和 `4c3485bc`、`3893783e`(#58)都已是这个基线的祖先。`deploy/docker-compose.server.yml` 在加日志段之前没有 #58 留下的未合入冲突。
|
||||
- 编号:本文件最大号是 BUG-1353。本单用 BUG-1354(没有每日备份)、BUG-1355(没有探活和磁盘告警)、BUG-1356(日志不轮转、不能按请求编号查)。
|
||||
|
||||
## 做了什么
|
||||
|
||||
- 新增每日备份工作流。北京时间 04:00,只在 `main` 上跑,调用现有的 `deploy/run-production-recovery.sh`。产物默认留 14 天。主机上只清理超过 7 天、且文件名严格是 `production-daily-` 的加密备份和对应校验文件。
|
||||
- 恢复脚本增加可选前缀。不设置时文件名仍是 `production-pre-migration-`。每日任务把它设为 `production-daily`。加密、还原校验、锁和磁盘 70% 拒绝没有改。
|
||||
- 探活、磁盘、错误汇总、飞书文本消息(可选签名)。没有 Webhook 时只写本机日志。
|
||||
- 两个编排文件的每个服务加上相同的日志上限:json-file,20 MB,10 个文件。镜像、环境、端口、健康检查没改。
|
||||
- 按 UUID 查 web / api / caddy 日志的只读脚本。拒绝其它写法。
|
||||
- 恢复演练步骤和空白结果模板。安装步骤写在 `deploy/README.md` 和 `BLOCKED.md`。
|
||||
|
||||
## 跑过的检查
|
||||
|
||||
| 检查 | 命令 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 安全网测试 | `.venv\Scripts\python.exe -m pytest tests/test_beta_safety_net.py -q` | 13 通过,退出码 0 |
|
||||
| 隐私标记 | `.venv\Scripts\python.exe -m pytest tests/test_repo_privacy_markers.py -q` | 退出码 0 |
|
||||
| 编排能否解析 | `docker compose -f deploy/docker-compose.server.yml -f deploy/docker-compose.postgres.yml config --quiet` | 退出码 0。本机没有真实环境文件,检查时用了临时空文件,没有提交 |
|
||||
| shellcheck | 无 | 本机没有这条命令 |
|
||||
| 前端 tsc / lint / 全量测试 | 未跑 | 没有改网页 |
|
||||
| Python 快速门 | 未跑 | 按本轮约定不跑 |
|
||||
| 数据库测试 | 未跑 | 没有改数据库 |
|
||||
|
||||
## 偏离
|
||||
|
||||
- 没有改现有的四个生产工作流,也没有把它们的 SSH 步骤抽成共用脚本。新工作流自己复制了同一套 SSH,并调用同一个恢复脚本。
|
||||
- 没有往网页或接口日志里加新字段。现有观测记录已经有 requestId 和 errorCode。错误汇总会丢掉含有汉字、邮箱或第二个日期的整行,避免把对话带出去。因此中文报错不会进入计数。
|
||||
- 探活外发不带提交号,只带错误码 `commit_changed`。提交号留在 runner 本机状态里。
|
||||
- 本机没有生产主机,没有登录 runner,没有手动触发生产工作流。告警和定时备份要等产品安装,见 `BLOCKED.md`。
|
||||
@@ -338,7 +338,7 @@
|
||||
| `TASK-release-path-production-20261014.md` | `PROGRESS-release-path-production-20261014.md` | **发布通路 + 生产首发**:产品 10-14 授权改生产工作流(发布 SHA = 最近门禁成功提交 + 其后纯文档);63 个迁移清单与演练脚本、首发 runbook;生产拒绝测试验证码。Grok 不 dispatch 生产工作流 | 第 2 次打回后续修 | `codex/release-path-production-20261014`(BUG-1348、BUG-1349) |
|
||||
| `TASK-release-path-production-fix-20261014.md` | `PROGRESS-release-path-production-fix-20261014.md` | **#74 第 1 次打回**:发布规则成立;测试写死 Windows 路径(Linux 全红)、主机演练依赖主机 node 且会留下生产数据副本、docs-only 头时回滚被拒;另补 CHECK_RELEASE_GATE 断言与 OTP 守卫规范化 | 已修,第 2 次仍未过 | `codex/release-path-production-20261014` |
|
||||
| `TASK-release-path-production-fix2-20261014.md` | `PROGRESS-release-path-production-fix2-20261014.md` | **#74 修复单 2(产品选 A)**:恢复点要带属主与权限,旧格式不得回退;演练按备份里的授权比对,并在真实 PostgreSQL 17 上跑通一次 | 待验收 | `codex/release-path-production-20261014` |
|
||||
| `TASK-beta-safety-net-20261014.md` | — | **内测安全网**:每日备份 + 恢复演练、探活告警、本地错误汇总、日志轮转、按 requestId 查日志。**等发布通路单合入、#58 合入后开工** | 待前置 | — |
|
||||
| `TASK-beta-safety-net-20261014.md` | `PROGRESS-beta-safety-net-20261014.md` | **内测安全网**:每日备份 + 恢复演练、探活告警、本地错误汇总、日志轮转、按 requestId 查日志。发布通路与 #58 已在开工时的 `origin/staging` 上。主机安装和一次演练留给产品 | 待验收 | `codex/beta-safety-net-20261014`(BUG-1354~1356) |
|
||||
| `TASK-beta-capacity-20261014.md` | — | **内测容量**:按用户限流、重计算短排队与对话重试、兑换码当邀请码、staging 10/20 并发压测。**等 #58 合入后开工,压测等 #57 合入** | 待前置 | — |
|
||||
| `TASK-special-lagnas-native-20261008.md` | `PROGRESS-special-lagnas-native-20261008.md` | **特殊上升点原生计算(线上缺陷)**:sync6 U6 运行时调 PyJHora,生产镜像没有 PyJHora → staging 上 Hora/Ghati/Sree/Indu/Pranapada 全 blocked;原生实现、PyJHora 只作冻结对照、快速门禁止运行时 import jhora;最先做 | 已验收(已部署 staging `795b8845`,BUG-1273) | `codex/special-lagnas-native-20261008`(未上线;部署以 `/api/health` 为准) |
|
||||
| `TASK-ephemeris-nutation-20261008.md` | `PROGRESS-ephemeris-nutation-20261008.md` | **恒星黄道少扣章动(BUG-1275)**:行星 = 含章动热带视位置 − 平岁差,全体偏 Δψ(≤17″),大运日期差 2–3 天;改标准算法、PyJHora 冻结对照、scoring-12、v5 重跑 | 已验收(已部署 staging `db0134f9`,BUG-1275) | `codex/ephemeris-nutation-20261008`(未推送,未部署) |
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# 生产恢复演练结果(2026-10-14)
|
||||
|
||||
内测开放前由产品做一次。步骤见 `docs/operations/production-restore-drill-20261014.md`。不要填写姓名、出生资料、邮箱、会话或密钥。
|
||||
|
||||
| 项 | 结果 |
|
||||
| --- | --- |
|
||||
| 日期 | |
|
||||
| 用的备份文件名(只要 `production-daily-` 开头的文件名) | |
|
||||
| 还原目标 | 一次性库 / 整库(圈一个) |
|
||||
| 一次性库是否已删除 | |
|
||||
| identity.users 行数 | |
|
||||
| public.profiles 行数 | |
|
||||
| public.credit_transactions 行数 | |
|
||||
| public 表张数 | |
|
||||
| 网站是否停过 | 一次性库应为否 |
|
||||
| 停写大约多久 | 一次性库填「未停」 |
|
||||
| 是否通过 | |
|
||||
| 备注(不要写用户内容) | |
|
||||
Reference in New Issue
Block a user