Moved the backup out of the NAS home dir to /volume1/NetBackup/kaotings per request. Synology restricts rsync-over-SSH to registered shared folders and scp/sftp yields root-owned files that the backup account cannot rotate, so the backup now uses tar over SSH (kts_bak-owned, rotatable, encrypted, key auth). Document the transport rationale and the DSM shared-folder upgrade path for incremental rsync.
87 lines
6.6 KiB
Markdown
87 lines
6.6 KiB
Markdown
# 运维方案(OPERATIONS)
|
||
|
||
日期:2026-09-10 · 版本 `8657f44`
|
||
范围:音频容量/保留/清理/备份/恢复、数据库备份/恢复、Worker 与额度对账、告警。**本轮不擅自删除任何既有音频。**
|
||
|
||
## 1. 音频存储现状
|
||
|
||
- 位置:`/home/flym/kaotings-audio`,布局 `{user_id}/{task_id}.wav`(私有本地目录,非对象存储)。
|
||
- 元数据:`audio_files`(`storage_key`、`mime_type`、`size_bytes`、`checksum`、`status`、`expires_at`)。
|
||
- 写入:成功结算时先写文件、再登记 `audio_files`(含 SHA256)。
|
||
- 读取:回放/下载按 `storage_key` 取文件;文件缺失返回 404(优雅降级)。
|
||
|
||
## 2. 备份(已配置异地备份)
|
||
|
||
- 本机暂存:`/home/flym/backups/`(仅存放 `.dump`)。
|
||
- 异地备份目标:**Synology NAS** `103.40.14.100`,账号 `kts_bak`,落盘 **`/volume1/NetBackup/kaotings/`**(`db/` + `audio/`,**不在 home 下**),可用容量约 92 GiB。
|
||
- 传输方式:**tar over SSH**(端口 `52200`,**加密**),密钥认证(`/root/.ssh/id_ed25519_ktsbak`,公钥已加入 NAS `kts_bak` 的 `authorized_keys`)。**不使用** 公网明文 rsync 守护端口 `50873`。
|
||
- 为何不用 rsync:Synology 限制 rsync-over-SSH 仅能写入**已注册的共享文件夹**;`/volume1/NetBackup` 是普通目录(非 DSM 共享文件夹),rsync 会报 `0 bytes received`。而 `scp`/sftp 虽能写入,但会生成 **root 属主**文件,`kts_bak` 无法轮转删除。**tar over SSH(ssh exec)** 生成的文件属主为 `kts_bak`,可正常轮转。
|
||
- 升级路径:若在 DSM 中将 `NetBackup` 建为**共享文件夹**(控制面板→共享文件夹,授予 `kts_bak` 读写),即可改回 `rsync -a --delete` 获得增量传输。
|
||
- 脚本:`/usr/local/sbin/kaotings-backup.sh`(root 运行):
|
||
1. `pg_dump --format=custom` 业务库(`/etc/kaotings/api.env` → `DATABASE_URL`)与 CMS 库(`/etc/kaotings/cms.env` → `DATABASE_URI`)。
|
||
2. 每库本机保留最近 **7** 份(按序列独立轮转)。
|
||
3. `tar` over SSH 传 `db/` 与 `audio/`(`AUDIO_STORAGE_DIR`)到 NAS;再在远端按序列保留 7 份(ssh exec,可删除 `kts_bak` 属主文件)。
|
||
- 定时:systemd `kaotings-backup.timer`,每日 **03:00**(`Persistent=true`,`RandomizedDelaySec=30min`)触发 `kaotings-backup.service`。
|
||
- 验证:备份已落盘 `/volume1/NetBackup/kaotings`(db 5+6 份、音频 22 文件,共 5.4M,属主 `kts_bak`);`pg_restore --list` 确认归档可恢复。
|
||
|
||
| 对象 | 工具 | 本机 | 异地(NAS `/volume1/NetBackup/kaotings`) |
|
||
| --- | --- | --- | --- |
|
||
| 业务库 `kaotings` | `pg_dump --format=custom` + tar | `/home/flym/backups/kaotings_<ts>.dump` | `…/kaotings/db/` |
|
||
| CMS 库 `kaotings_cms` | `pg_dump --format=custom` + tar | `/home/flym/backups/kaotings_cms_<ts>.dump` | `…/kaotings/db/` |
|
||
| 音频目录 | tar over SSH | `/home/flym/kaotings-audio/` | `…/kaotings/audio/` |
|
||
|
||
> 仍待用户确认:音频保留期、磁盘告警阈值、是否启用按 `expires_at` 的定时清理(见 §5);备份保留份数当前=7(可调脚本 `KEEP`)。如希望增量传输,请在 DSM 将 `NetBackup` 建为共享文件夹(见上“升级路径”)。
|
||
|
||
## 3. 容量 / 保留 / 清理(需用户决定的参数集中列出)
|
||
|
||
| 参数 | 当前值 | 需用户决定 |
|
||
| --- | --- | --- |
|
||
| 音频保留期 `AUDIO_RETENTION_SECONDS` | 7 天(默认) | 是(产品/隐私/存储预算) |
|
||
| 磁盘容量上限 / 告警阈值 | 根盘约 48 GiB 可用,无上限告警 | 是(阈值 + 通知方式) |
|
||
| 保留期清理策略 | 未启用定时清理(仅启动时清孤儿) | 是(是否按 `expires_at` 定时删除) |
|
||
| 原始文本保留期 | 存于 `tts_tasks.text` | 是(产品/隐私) |
|
||
| 备份保留份数 / 周期 | 已配置:每库 7 份 · 每日 03:00 · 异地 NAS | 否(可调脚本 `KEEP` 与 timer) |
|
||
|
||
- 清理安全边界:
|
||
- 启动时 `cleanup_orphan_audio` 仅删除“**无** `available` 记录且 mtime>1h”的孤儿文件。
|
||
- 保留期清理在参数确认并实现后启用;**在此之前不删除任何既有音频**。
|
||
- 告警:磁盘使用率超阈值、`audio_files` 与磁盘文件数量长期不一致、Worker 长期无 `queued` 消化。
|
||
|
||
## 4. 恢复(含隔离演练结果)
|
||
|
||
### 4.1 流程
|
||
|
||
1. 停止写入(可选,视 RPO):`systemctl stop kaotings-api`。
|
||
2. 建空库 → `pg_restore --clean --if-exists` 业务库 / CMS 库。
|
||
3. 音频:从 NAS 用 `tar` over SSH 拉回 `/home/flym/kaotings-audio`(源:`/volume1/NetBackup/kaotings/audio/`)。
|
||
4. 校验:行数、音频文件↔`audio_files` 关联、`checksum`、额度↔流水一致(见 §4.2 校验 SQL)。
|
||
5. `systemctl start kaotings-api`,`/healthz` 复核。
|
||
|
||
### 4.2 校验(隔离演练已执行)
|
||
|
||
- 业务库行数源=恢复:users 35 / quota 34 / tasks 28 / audio 26 / usage 61 / grants 10。
|
||
- 额度↔流水:`used` 与 `consume`、`reserved` 与 `reserve-consume-release` 均 0 不符。
|
||
- 音频:磁盘 22 文件全部被 `available` 引用(无孤儿)。
|
||
- **发现**:4 条 `status=available` 且任务 `succeeded` 的 `audio_files` 行在磁盘无文件(既有不一致,回放 404 降级)。待调查根因(见 §6)。
|
||
|
||
### 4.3 Worker 重启 / 额度对账
|
||
|
||
- 单 Worker(systemd 单进程),原子领取(`FOR UPDATE SKIP LOCKED`)+ 5 分钟租约 + 过期 `running` 回收(`WORKER_LEASE_EXPIRED`,释放冻结)。
|
||
- 对账:`GET /admin/usage/summary` 汇总 `quota_accounts` 与 `usage_records` 口径;`GET /admin/tts/tasks` 查结算状态(consumed/released/pending)。
|
||
- 不变式(每账户恒成立):`limit_snapshot + adjustment - used - reserved >= 0`(由 Phase 5 异常矩阵 S3/S4 保证)。
|
||
|
||
## 5. 待用户确认清单(集中)
|
||
|
||
1. 音频保留期(`AUDIO_RETENTION_SECONDS`)。
|
||
2. 磁盘容量上限与告警阈值、通知方式。
|
||
3. 是否启用按 `expires_at` 的定时保留期清理。
|
||
4. 原始文本保留期。
|
||
5. 备份:异地 NAS 已配置(`103.40.14.100`,每日 03:00,每库 7 份);**剩余待确认**:保留份数/周期是否调整、备份责任人。
|
||
6. 上游 HTTPS/加密端点、允许来源网段、凭据轮换(见 `DEPLOYMENT.md` §4)。
|
||
|
||
## 6. 待调查 / 未闭环项(不标为完成)
|
||
|
||
- **4 条 `available` 音频行无对应文件**:既有 DB/文件系统不一致,待定位根因(文件被外部删除 or 写入路径异常);恢复流程本身通过。
|
||
- **上游公网 HTTP**:上线阻断,待加密 + 来源限制 + 外部复测(`DEPLOYMENT.md` §4)。
|
||
- **音频容量/保留/清理参数**:待用户确认(§5)。
|