建立每日自动异机备份基线(07-27-automatic-offhost-backup)
这个提交包含在:
@@ -912,3 +912,102 @@ SQL
|
||||
test "$(printf '%s\n' "$actual" | LC_ALL=C sort)" = \
|
||||
"$(printf '%s\n' "$expected" | LC_ALL=C sort)"
|
||||
```
|
||||
|
||||
## Scenario: Daily Restricted Off-Host Backup
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: installing or changing automatic paired backups, off-host transfer,
|
||||
retention, freshness alerts or quarterly restore reminders.
|
||||
- Purpose: keep a 24-hour recovery point without giving the production host
|
||||
ordinary shell or delete access to the ARM archive.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Production runner: `scripts/run-automatic-backup.sh` with `run`,
|
||||
`check-stale` and `remind-drill` modes.
|
||||
- ARM forced receiver: `scripts/receive-offhost-backup.sh`.
|
||||
- Installers: `install-automatic-backup-timer.sh` and
|
||||
`install-offhost-backup-receiver.sh`.
|
||||
- Local contract: `bash scripts/qa/run-automatic-backup-contract.sh`.
|
||||
- Production required environment: `REMOTE_DIR`, `REMOTE_ENV_FILE`,
|
||||
`COMPOSE_PROJECT_NAME`, `POSTGRES_DATA_DIR`, `STORAGE_HOST_DIR`, `BUILD_SHA`,
|
||||
`VITE_BUILD_SHA`, `AUTOMATIC_BACKUP_DIR`, `AUTOMATIC_BACKUP_STATE_DIR`,
|
||||
`OFFHOST_TARGET`, `OFFHOST_SSH_KEY` and `OFFHOST_KNOWN_HOSTS`.
|
||||
- Receiver config: independent absolute `BACKUP_INBOX_DIR`,
|
||||
`BACKUP_ARCHIVE_DIR`, `BACKUP_WEEKLY_DIR`, `RRSYNC_BIN`, `PYTHON_BIN`,
|
||||
`DAILY_KEEP` and `WEEKLY_KEEP`.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Resolve one active Compose server/database pair and verify its build, PGDATA,
|
||||
storage mount and database identity before backup. Do not use fixed container
|
||||
names or repository-relative runtime data.
|
||||
- Transfer only a complete `auto-<UTC timestamp>` three-file batch to ARM
|
||||
inbox. The forced key allows write-only `rrsync` or an exact finalize command;
|
||||
it rejects shell, sender/read and destructive rsync options.
|
||||
- ARM revalidates manifest identity, exact regular-file set, sizes and SHA-256,
|
||||
then atomically moves the batch to archive. Archive and weekly directories are
|
||||
outside the rsync root; an archived name is never overwritten.
|
||||
- Write production `last-success.json` and prune source copies only after ARM
|
||||
finalize succeeds. Keep seven automatic source/ARM daily points and four ARM
|
||||
weekly checkpoints; unrelated manual batches are outside automatic retention.
|
||||
- Daily, freshness and quarterly reminder timers are separate. Alert summaries
|
||||
contain categories only. AUTO_BACKUP sends the first failure, worsening/new
|
||||
findings and the first quarterly reminder, while suppressing an identical
|
||||
repeated failure or reminder.
|
||||
- Installer `install` is disabled-by-default; `enable`, real transfer, real
|
||||
WeCom alert and all server/account/key changes remain `EXTERNAL_GATED`.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Source mount/build/project drift, lock conflict or incomplete backup -> fail
|
||||
before success state and source retention.
|
||||
- Transfer/finalize failure, wrong/extra/link artifact, bad hash or second batch
|
||||
for one UTC day -> leave no off-host success state and preserve source batch.
|
||||
- Missing, stale or project/build-mismatched success record -> watchdog finding.
|
||||
- Ordinary shell, rsync sender/read/delete or archive overwrite -> receiver
|
||||
rejection without changing archive.
|
||||
- Host/network/WeCom all unavailable at once -> self-hosted alert cannot prove
|
||||
its own outage; this is a documented physical boundary, not a green state.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: one verified active source creates a complete batch, write-only rsync
|
||||
resumes inside inbox, ARM finalize verifies and archives it, then production
|
||||
writes success and prunes old automatic source points.
|
||||
- Base: local implementation and synthetic contracts pass while both server
|
||||
installers remain disabled and explicitly externally gated.
|
||||
- Bad: give production a normal ARM login key, put archive below inbox, accept
|
||||
a new host fingerprint automatically, or prune local recovery points after
|
||||
rsync but before target-side finalize.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Shell syntax plus `run-automatic-backup-contract.sh` using temporary files and
|
||||
mocked Docker/SSH boundaries; it must never contact real hosts.
|
||||
- Focused WeCom alert tests for stdin path redaction, preserved AUTO_BACKUP
|
||||
issue identity, first/repeated failure and quarterly reminder behavior.
|
||||
- Environment contract assertions for all four scripts, three timers, explicit
|
||||
project/data/build/SSH binding and disabled-by-default installation.
|
||||
- Real ARM setup, first paired batch, failure alert and quarterly shadow restore
|
||||
are separately approved operational evidence.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```bash
|
||||
rsync -a <batch>/ backup-host:<archive>/
|
||||
rm -rf <old-local-batches>
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```bash
|
||||
# Install only with explicit active identity; default remains disabled.
|
||||
<explicit-project-data-build-and-ssh-env> \
|
||||
bash deploy/trialrun/install-automatic-backup-timer.sh install
|
||||
# The runner writes success and prunes only after restricted target finalize.
|
||||
bash scripts/run-automatic-backup.sh run
|
||||
```
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,76 @@
|
||||
# 每日自动异机备份设计
|
||||
|
||||
## 总体边界
|
||||
|
||||
本任务不建设备份平台。生产主机负责生成和推送一个不可变成对批次,ARM 用受限 SSH 接收身份把传输区与归档区隔离;systemd 负责每日执行、缺失监测和季度提醒。
|
||||
|
||||
```text
|
||||
production 02:30
|
||||
resolve active Compose identity
|
||||
-> flock
|
||||
-> backup-all.sh (DB + storage + manifest)
|
||||
-> local verify
|
||||
-> rsync to ARM inbox/<run-id>.partial
|
||||
-> restricted finalize <run-id> <manifest-sha>
|
||||
-> ARM verify exact files/manifest/hash
|
||||
-> atomic move to archive/<run-id>
|
||||
-> daily + weekly retention
|
||||
-> write safe success state
|
||||
-> prune local daily copies
|
||||
|
||||
production 07:10 watchdog
|
||||
-> last off-host success <= 24h ? pass : alert
|
||||
|
||||
quarterly reminder
|
||||
-> one redacted WeCom reminder per calendar quarter
|
||||
-> human runs ARM shadow restore separately
|
||||
```
|
||||
|
||||
## 生产备份编排
|
||||
|
||||
- 新增一个宿主 wrapper,所有正式输入由安装器写入 systemd:仓库根、Compose project/file/env、活动 PGDATA/附件目录、完整/短构建 SHA、备份根、状态根、SSH alias/key/known-hosts 和告警开关。
|
||||
- wrapper 通过 Compose label 解析唯一 server/db 容器,并把实际 DB/附件 mount、server build SHA 与安装时冻结值逐项比较。任一漂移在调用 `backup-all.sh` 前停止。
|
||||
- DB 名称/用户和密码只从活动容器环境在内存中取得,传给现有 Docker 备份路径;不得 source 漂移的旧部署 env,不得输出取值。
|
||||
- 每个自动批次使用 `auto-<UTC timestamp>`,源端根和状态根均为显式安全绝对路径、权限 `0700`。`flock` 拒绝同一任务重入。
|
||||
- 只有 ARM finalize 成功后才写 `last-success.json` 并清理源端旧日批次。失败保留本次完整源批次供重试,不产生异机成功状态。
|
||||
|
||||
## ARM 受限接收边界
|
||||
|
||||
- ARM 一次性安装 rsync,创建密码锁定的专用账号、独立 inbox/archive/weekly 目录和一把专用公钥。该公钥的 `authorized_keys` 强制执行仓库提供的 receiver,并禁用 PTY、端口转发、agent 转发和 X11 转发。
|
||||
- receiver 只接受两类 `SSH_ORIGINAL_COMMAND`:rsync server 接收模式,或 `hyb-backup-finalize <run-id> <manifest-sha256>`。其他命令全部拒绝。
|
||||
- rsync 通过 `rrsync -wo` 只能进入 inbox;archive 和 weekly 位于 rrsync 根之外。即使生产端密钥泄露,也不能用 rsync 读取或删除已归档批次。
|
||||
- finalize 只接受 `auto-` 安全批次号和完整 manifest SHA。它要求 inbox 中恰有三个普通文件、无链接,manifest 的 runId/status/schema、相对文件名、大小和 SHA-256 全部匹配;已存在 archive 目标时拒绝覆盖。
|
||||
- 校验通过后在同一文件系统内原子移动到 archive。每个自然日最多接受一个正式自动批次进入保留计算,避免失陷源端通过短时间伪造大量批次提前挤掉历史。
|
||||
- archive 保留最近 7 个日批次;每个 ISO 周首次成功批次用只读硬链接快照形成周检查点,保留最近 4 周。清理不得删除最后一个 archive 或当前 finalize 批次。
|
||||
- 旧手工/审计批次不进入自动前缀和自动轮转范围。
|
||||
|
||||
## 传输与主机身份
|
||||
|
||||
- 生产 root 使用一把仅用于本接收器的私钥,权限 `0600`;SSH config 使用专用 alias,known_hosts 由人工核对的固定主机指纹生成,timer 固定 `StrictHostKeyChecking=yes`,不得使用 `accept-new`。
|
||||
- rsync 只发送完整源批次到同名 `.partial` inbox。网络中断可续传同一 partial;远端 archive 同名存在时 finalize 失败,不覆盖。
|
||||
- 仓库、任务和报告只记录 alias 类别、布尔值和批次数量,不记录地址、账号、密钥、公钥原文或完整运行目录。
|
||||
|
||||
## 告警与静默失败检测
|
||||
|
||||
- wrapper 无论成功或失败都生成字段白名单 summary;失败 trap 尝试通过活动 server 容器调用现有企微运维告警 CLI,并使用备份专属 state file,避免与 WARCH3 降噪状态互相覆盖。
|
||||
- 现有告警 CLI 小范围泛化为接受 stdin summary、保留 summary issueId,并为备份失败/过期提供正确建议;发送仍受既有 `WARCH3_OPS_ALERT_SEND=1` 双门控制。
|
||||
- 独立 watchdog timer 在每日巡检前读取宿主 `last-success.json`。文件缺失、格式错误、构建/项目不匹配或成功时间超过 24 小时均产生失败 summary 并尝试告警。
|
||||
- 若 Docker、整机网络或企微本身同时不可用,主机无法自证并外发,这是自托管告警的物理边界;watchdog 能覆盖 timer 未运行和短暂失败,但不替代外部主机存活监控。
|
||||
- 季度提醒以年份+季度作为 finding key,确保每季度一次且同季度不重复;只提醒人工影子恢复,不执行恢复。
|
||||
|
||||
## 安装与回滚
|
||||
|
||||
1. 本地先实现合成测试、受限 receiver、告警适配和两个安装器,不接触生产/ARM。
|
||||
2. 获批后先在 ARM 安装 rsync、受限账号/receiver/公钥和目标目录;使用无业务数据的合成批次验证拒绝 shell、拒绝 archive 覆盖和 finalize。
|
||||
3. 再在生产安装专用私钥/known_hosts、每日备份 service/timer、stale watchdog 和季度提醒;首次安装先保持 timer disabled。
|
||||
4. 手工启动一次获批自动备份,核对源/ARM 哈希、状态和无敏感输出;再 enable timers。
|
||||
5. 用不包含业务正文的可控失败验证企微告警后恢复链路,最终确认下次执行时间。
|
||||
|
||||
回滚只 disable/stop 新 timer、移除 unit 和专用密钥授权;保留已生成的完整备份、状态报告和 ARM archive,不删除历史数据。旧手工 `backup-all.sh` 入口保持兼容。
|
||||
|
||||
## 取舍
|
||||
|
||||
- 选择生产推送而非 ARM 拉取,是为了让一次任务同时掌握生成、传输、目标校验和即时告警结果。
|
||||
- 不给生产普通 ARM shell,而用 inbox + 强制 receiver + archive 隔离,抵消推送方案最主要的跨机删除风险。
|
||||
- 使用 rsync 而不是新备份服务;当前压缩批次规模较小,收益主要是断点续传和成熟 SSH 传输,不追求块级去重。
|
||||
- 不自动恢复。季度人工影子恢复才是可恢复性证据,日 timer 只负责生成恢复点。
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,57 @@
|
||||
# 每日自动异机备份实施计划
|
||||
|
||||
## 实施步骤
|
||||
|
||||
- [x] 实现宿主自动备份 wrapper:安全绝对路径、唯一 Compose 来源、活动 mount/build 校验、`flock`、成对备份、ARM 推送/finalize、成功状态和源端 7 日保留。
|
||||
- [x] 实现 ARM 受限 receiver:只允许 rrsync 写 inbox 和 finalize,严格校验三文件 manifest/大小/哈希、原子归档、7 日 + 4 周保留和最后成功保护。
|
||||
- [x] 实现生产与 ARM 安装器:专用账号/密钥/known_hosts 边界、systemd daily/watchdog/quarterly units、安装/卸载幂等和默认不触发真实任务。
|
||||
- [x] 泛化现有企微运维告警的 stdin summary、issueId 和备份建议,使用独立状态文件并补单元测试。
|
||||
- [x] 增加合成契约测试:来源漂移、锁冲突、不完整/损坏/重复批次、续传 partial、拒绝普通 shell、保留轮转、stale 和季度降噪。
|
||||
- [x] 更新部署环境契约、runbook、checklist、管理员手册的最小操作入口和 `.trellis/spec/hyb-oa/deployment-trialrun.md`;不新增平行长文档。
|
||||
- [x] 生成一页脱敏恢复卡内容,优先放在现有 runbook 的首部紧急段;由用户复制到公司知识库并安排第二人走查。
|
||||
- [ ] 完成本地全量门禁并提交;生产、ARM 安装另行请求批准。
|
||||
- [ ] 获批后先安装 ARM 接收端并用合成批次验证,再安装生产 units,手工运行首次真实自动批次,最后 enable timers。
|
||||
- [ ] 获批执行一次可控传输失败告警并恢复正常链路;不读取附件正文、不执行业务或企微其他写入。
|
||||
|
||||
## 预计文件边界
|
||||
|
||||
- 新增 `scripts/run-automatic-backup.sh`:生产 host 编排与 safe summary。
|
||||
- 新增 `scripts/receive-offhost-backup.sh`:ARM forced-command receiver、finalize 和 retention。
|
||||
- 新增 `deploy/trialrun/install-automatic-backup-timer.sh`:生产 units 安装/卸载。
|
||||
- 新增 `deploy/trialrun/install-offhost-backup-receiver.sh`:ARM rsync/账号/目录/公钥安装检查;软件安装和账号创建只在获批远端阶段执行。
|
||||
- 新增 `scripts/qa/run-automatic-backup-contract.sh`:纯合成正负向测试。
|
||||
- 修改 `server/src/scripts/send-wecom-ops-alert.js` 及其单测,仅做通用脱敏 summary 兼容。
|
||||
- 修改 `deploy/trialrun/validate-env-contract.js`、现有 runbook/checklist/管理员手册和部署规约。
|
||||
|
||||
## 验证命令
|
||||
|
||||
- `bash -n scripts/run-automatic-backup.sh scripts/receive-offhost-backup.sh deploy/trialrun/install-automatic-backup-timer.sh deploy/trialrun/install-offhost-backup-receiver.sh scripts/qa/run-automatic-backup-contract.sh`
|
||||
- `bash scripts/qa/run-automatic-backup-contract.sh`
|
||||
- `bash scripts/qa/run-backup-restore-drill.sh`
|
||||
- `cd server && node --check src/scripts/send-wecom-ops-alert.js`
|
||||
- `cd server && npm test -- --run tests/unit/scripts/send-wecom-ops-alert.test.js`
|
||||
- `node deploy/trialrun/validate-env-contract.js`
|
||||
- `python3 ./.trellis/scripts/task.py validate .trellis/tasks/07-27-automatic-offhost-backup`
|
||||
- `node scripts/qa/check-trellis-governance.mjs`
|
||||
- `node scripts/qa/check-sensitive-content.mjs`
|
||||
- `node scripts/qa/audit-docs.mjs --check-registry`
|
||||
- `node scripts/qa/check-process-contract.mjs --baseline --json`
|
||||
- `git diff --check`
|
||||
|
||||
## 外部门禁与回滚点
|
||||
|
||||
- 安装 ARM rsync/账号/authorized_keys/目录属于远端配置写入,须单独批准;不得复用普通登录账号或本地工作站私钥。
|
||||
- 安装生产 SSH key/config/systemd units、首次真实备份、真实异机传输和企微失败告警均须单独批准。
|
||||
- 首次真实执行前记录现有手工批次数量、磁盘余量、活动容器/mount/build 和现有 timer;只输出脱敏计数/布尔值。
|
||||
- 任何目标身份、主机指纹、活动 mount、manifest 或哈希不匹配立即停止,不猜路径、不接受新指纹、不清理源端批次。
|
||||
- 回滚新 timer/receiver 时只撤销调度和专用授权;ARM archive、源端完整批次和原 WARCH3 timer 保留。
|
||||
|
||||
## 本地实施与验证证据
|
||||
|
||||
- 已实现生产 wrapper、ARM forced receiver、两端安装器和 3 组 systemd timer;安装器默认只安装 disabled units。
|
||||
- `run-automatic-backup-contract.sh` 使用临时目录与 mock Docker/SSH/rsync 完成 28 项正负向合同,不连接真实主机。
|
||||
- 企微告警聚焦单测 15 项通过;stdin 路径被替换为安全类别,AUTO_BACKUP 首次失败/季度提醒发送且相同重复状态降噪。
|
||||
- `run-backup-restore-drill.sh` 本地合成 DB/附件往返通过,本次合成 RTO 为 8 秒;该结果不是生产 RTO 或 ARM 恢复证据。
|
||||
- `scripts/check-minimal.sh` 通过:server 162 个测试文件、1004 项测试全部通过,client typecheck/build 通过。
|
||||
- 环境合同、Trellis task/governance、敏感扫描、文档注册、流程合同和 `git diff --check` 均通过;文档注册保留既有 22 个未注册文件 warning,无本任务新增文档。
|
||||
- ARM 安装、生产 units、真实首批、timer enable、真实企微失败告警和第二人季度走查仍为外部门禁,未执行、不得写成已完成。
|
||||
@@ -0,0 +1,63 @@
|
||||
# 每日自动异机备份
|
||||
|
||||
## Goal
|
||||
|
||||
为 5–10 人内部 OA 建立低维护的每日自动恢复点:生产每天生成同批数据库、附件和 manifest,完整校验后复制到 ARM 备份机,失败可被当天发现并告警;同时用简单保留策略和季度人工影子恢复降低主机故障与静默备份失效风险。
|
||||
|
||||
## Background
|
||||
|
||||
- 现有 `scripts/backup-all.sh` 已能生成 DB、附件和脱敏 manifest 的完整批次,并在发布完整目录前验证 dump、tar 和 SHA-256。
|
||||
- 当前仓库没有任何生产调度器调用 `backup-all.sh`;唯一活动 timer 是 07:30 运维巡检,不执行备份。
|
||||
- 生产主机和 ARM 均有 systemd、SSH、`flock` 和 SHA-256 工具;生产有 rsync,ARM 当前没有 rsync。
|
||||
- 两台主机之间当前没有可直接复用的免密 SSH 别名或密钥链路,不能把本地工作站的 SSH 配置误当作服务器间自动化能力。
|
||||
- 当前手工正确批次和 ARM 影子恢复已证明格式可用,但不能满足 RPO 24 小时或自动失败发现。
|
||||
- 已确认采用“生产主动推送到 ARM 的专用受限接收身份”:生产保留即时传输结果,ARM 的已归档批次不暴露给 rsync 写入根。
|
||||
|
||||
## Scope
|
||||
|
||||
- 生产每日成对备份、ARM 受限接收与目标端校验。
|
||||
- 源端 7 个日批次、ARM 7 个日批次加 4 个周检查点的安全轮转。
|
||||
- 每日 systemd 调度、独立 stale watchdog、失败企微告警和季度人工恢复提醒。
|
||||
- 两端安装器、合成契约测试、现有运维文档/规约更新和一页脱敏恢复卡。
|
||||
|
||||
## Requirements
|
||||
|
||||
- 每日自动从唯一活动 Compose project 解析 DB 容器、活动附件挂载和实际构建标识,不读取旧项目、固定容器名或仓库默认附件目录。
|
||||
- 复用 `backup-all.sh` 生成完整批次;任何来源核验、dump、tar、manifest 或哈希检查失败时不得进入异机完成态。
|
||||
- 使用独立 systemd service/timer 和 `flock`,避免与部署、人工备份或自身上次运行重叠。
|
||||
- 完整批次通过固定主机指纹和专用受限密钥自动推送到 ARM;该身份无普通 shell,只能把文件写入 inbox 或请求校验/归档,不能通过 rsync 读取或删除 archive。
|
||||
- 传输端和目标端都要校验 manifest、文件大小和 SHA-256;同名已归档目标不得覆盖。中断传输可以在 inbox 内续传,但只有完整校验后才能原子进入 archive。
|
||||
- 生产保留最近 7 个成功日批次;ARM 至少保留最近 7 个日批次和 4 个周检查点。清理只能发生在本次异机成功确认之后,且不得删除最后一个已知成功批次。
|
||||
- 备份或异机校验失败必须生成脱敏状态报告,并复用现有企微运维告警能力;独立 stale watchdog 应在现有 07:30 巡检前检查最近一次异机成功时间,防止备份 timer 未运行或即时告警自身失败造成静默缺口。
|
||||
- 告警只包含阶段、状态、finding 数和脱敏建议,不包含数据库凭证、完整目录、SSH 信息、真实业务数据、附件名或原始命令输出。
|
||||
- 季度恢复保持人工触发,在 ARM 隔离目标执行;季度 timer 只发送一次脱敏提醒,不得自动运行恢复,更不得触碰生产库。
|
||||
- 形成一页脱敏恢复卡,说明主机故障后的负责人、备份位置类别、恢复顺序和验收项,供另一名人员存入公司知识库。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] 本地合成测试证明每日 wrapper 只接受唯一活动来源、互斥锁有效,并只发布完整备份批次。
|
||||
- [x] 传输测试证明不完整批次、哈希不一致、重复批次和不可达 ARM 均失败,且不产生异机成功状态。
|
||||
- [x] 保留测试覆盖 7 个日批次、4 个周检查点、最后成功批次保护和仅在异机确认后清理。
|
||||
- [x] 告警测试证明首次失败和状态恶化会告警,重复同一失败降噪;07:30 stale 检查能发现超过 24 小时没有异机成功记录。
|
||||
- [ ] systemd 安装器显式绑定 Compose project、活动数据路径、构建标识、备份根和受限远端身份;安装后 timer enabled/active,但部署验证不手工执行真实告警。
|
||||
- [ ] 经单独生产授权后完成一次自动日备份:源端完整、ARM 目标端哈希一致、状态报告脱敏,且未读取附件正文或修改业务数据。
|
||||
- [ ] 模拟传输失败能在当天产生一条脱敏企微告警;测试后恢复正常链路并完成成功批次。
|
||||
- [ ] 恢复卡经第二名内部人员只读走查,能够指出恢复入口、影子目标和 `/ready`/关键计数验收项。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不引入备份平台、对象存储、MinIO、云厂商 SDK、Kubernetes 或持续复制。
|
||||
- 不自动执行生产恢复、数据库 reset、迁移改写或附件回写。
|
||||
- 不把真实业务正文、附件清单、凭证、主机地址、完整目录或 SSH 私钥写入仓库、报告或聊天。
|
||||
- 不在本任务继续企微本人授权、员工角色、付款、报销或其他产品功能。
|
||||
- 不因自动备份完成而取消季度人工影子恢复。
|
||||
|
||||
## Test Plan
|
||||
|
||||
- Shell 语法与合成文件系统测试,覆盖锁、来源解析、传输失败、目标重复、哈希和保留轮转。
|
||||
- 复用 `scripts/qa/run-backup-restore-drill.sh` 验证成对备份合同未回退。
|
||||
- 聚焦测试现有企微运维告警的备份状态与 stale finding 适配。
|
||||
- `node deploy/trialrun/validate-env-contract.js`。
|
||||
- `python3 ./.trellis/scripts/task.py validate .trellis/tasks/07-27-automatic-offhost-backup`。
|
||||
- Trellis governance、敏感扫描、文档注册/流程合同和 `git diff --check`。
|
||||
- 生产安装、首次自动批次、真实失败告警和 ARM 目标验证分别保持外部门禁。
|
||||
@@ -0,0 +1,32 @@
|
||||
# 自动异机备份运行拓扑调查
|
||||
|
||||
## 边界
|
||||
|
||||
- 调查日期:2026-07-27。
|
||||
- 仅执行只读命令;未创建账号、密钥、目录、timer 或备份,未安装软件、传输数据或发送告警。
|
||||
- 只记录工具可用性、批次数量和连接布尔值,不记录主机地址、账号、完整路径、凭证或业务数据。
|
||||
|
||||
## 仓库事实
|
||||
|
||||
- `scripts/backup-all.sh` 只被手工文档和合成恢复演练调用,没有生产调度入口。
|
||||
- 完整批次由 `database.dump`、`storage.tar.gz` 和 `manifest.json` 组成;最终目录只在 dump/tar 可读取且 SHA-256 生成后发布。
|
||||
- DB Docker 路径要求显式容器和数据库参数;附件路径要求显式 `STORAGE_DIR_HOST`,不存在历史默认回退。
|
||||
- 07:30 WARCH3 timer 只运行每日巡检。现有告警 CLI 可读取通用 summary 的 `issueId`、`status`、`findingCount` 和报告目录,但帮助、状态文件和部分建议文案仍绑定 WARCH3,备份复用前需要小范围泛化或独立状态文件。
|
||||
|
||||
## 主机能力
|
||||
|
||||
| 能力 | 生产主机 | ARM 备份机 |
|
||||
| --- | --- | --- |
|
||||
| systemd / SSH / flock / SHA-256 | 可用 | 可用 |
|
||||
| rsync | 可用 | 当前不可用 |
|
||||
| 现成跨主机免密别名/链路 | 无 | 无 |
|
||||
|
||||
- 生产当前存在少量历史/手工批次,但没有定时策略。
|
||||
- 正确异机批次此前已复制并完成影子恢复;本轮默认目录探测没有把它当作新自动化根,说明自动化必须显式配置目标根,不能猜历史位置。
|
||||
|
||||
## 设计影响
|
||||
|
||||
- 无论推送或拉取,都需要一次性安装/配置 ARM rsync 并建立专用受限身份,不能复用本地工作站密钥作为生产事实。
|
||||
- 直接给生产保存 ARM 普通登录密钥虽然最省事,但会让生产失陷扩展为异机备份可删除,不应采用。
|
||||
- 生产推送更容易在同一任务内获得传输退出码并触发即时告警;要用无交互 shell、仅允许新批次写入的受限接收身份,并让已归档批次脱离该身份的删除权限。
|
||||
- ARM 拉取让生产主机没有 ARM 凭证,隔离更强;代价是调度和告警分散在两台机器,需要额外心跳或状态回传才能保证当天发现失败。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "automatic-offhost-backup",
|
||||
"name": "automatic-offhost-backup",
|
||||
"title": "每日自动异机备份",
|
||||
"description": "为内部 OA 建立每日成对备份、ARM 异机复制、保留轮转、失败告警与季度恢复提醒的低维护自动化基线。",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": "scripts/backup automation; deploy/trialrun backup receiver and systemd installers; server ops-alert adapter/tests; deployment runbook/checklist/spec evidence",
|
||||
"package": null,
|
||||
"priority": "P0",
|
||||
"creator": "shunli",
|
||||
"assignee": "shunli",
|
||||
"createdAt": "2026-07-27",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户