使用 Tenax 恢复¶
每一次恢复都是一个 Restore 资源。控制台、CLI 和 API 创建的都是它。除非你要求原地恢复并输入目标的名称,
否则恢复绝不会写入已有的数据库。选择一种模式:
| 需求 | 模式 | 角色 |
|---|---|---|
| 查看数据、验证修复、生成报表 | 沙箱(默认) | operator |
| 找回一张表或几行数据 | 沙箱,然后复制所需内容 | operator |
| 在生产环境旁边建一份副本(新的数据库名) | 新数据库 | restorer |
| 给集群外的人一份转储文件 | 下载(预签名 URL,1 小时) | restorer |
| 将整个目标回滚 | 原地(先做安全备份) | restorer-admin |
1. 找到时间点¶
tenax targets # PITR windows and last backups
tenax backups team/app # versions (ids, times, sizes, verified, pinned)
tenax changes team/app --since 6h # what changed when: per-table counts and DDL per segment
tenax diff <backup-a> <backup-b> # schema and row changes between two versions
在控制台中,打开目标,然后进入变更。每一行变更段都有 “sandbox before” 和 “sandbox after” 链接, 会自动填好时间。
启用 PITR 时,窗口内的任意一秒都可以(UTC,RFC3339):--at 2026-01-02T03:04:05Z。未启用 PITR 时,选择一个
备份 ID:--at 20260102T020000Z-1a2b3c。如果该时间无法恢复,Tenax 会说明原因并打印出各个窗口。
2. 恢复到沙箱(安全,默认方式)¶
tenax restore team/app --at 2026-01-02T03:04:05Z --ttl 6h --wait
tenax sandbox show <sandbox> # connection info, expiry
tenax query <sandbox> -e "select count(*) from orders where created_at > now() - interval '1 day'"
tenax query <sandbox> -e "select * from orders where id = 42" -o csv > order42.csv
- 沙箱拥有独立的命名空间
tenax-sbx-<name>、随机凭据和 TTL(默认 24 小时,可用tenax sandbox extend <sandbox> --by 24h延长,最长不超过策略上限)。 - 查询控制台默认只读,超时 30 秒,最多返回 1,000 行。与目标
mask模式匹配的列会被脱敏。每次查询都会被审计。 - 从其他 Pod 访问:为它加上标签
tenax.run/sandbox-client: <sandbox>,然后连接sandbox show打印出的集群内地址。 - 从笔记本访问(沙箱创建时使用了
--expose):tenax sandbox connect <sandbox> --port 15432。这会通过 Cloudflare Access 打开隧道并打印连接字符串。 - 用完后执行
tenax sandbox delete <sandbox>。过期的沙箱会被自动删除。
3. 恢复到新数据库¶
tenax restore team/app --at 2026-01-02T03:04:05Z --to new \
--host postgres.team.svc --database app_restored --credentials-secret tenax-admin --wait
Tenax 先恢复到一个暂存沙箱并运行检查,然后通过逻辑转储和加载把结果复制到新数据库(ADR 0007)。
它绝不会覆盖已有的数据库。管理员 Secret(tenax- 前缀)位于目标的命名空间中,需要具备 CREATE DATABASE 权限。
4. 下载¶
--wait 会打印一个预签名 URL,指向一份已解密并重新压缩的转储,有效期 1 小时。这个 URL 本身就是机密:
请像对待机密一样分享它。
5. 原地恢复(最后手段)¶
tenax restore team/app --at 2026-01-02T03:04:05Z --to in-place --confirm app --credentials-secret tenax-admin --wait
- 先停止写入方。 Tenax 不会停止应用。请把它们缩容到零,或切换到维护模式。
- Tenax 会对当前状态做一次安全备份(原因为
safety)。若要跳过,需要使用--skip-safety-backup。 - 它先恢复到一个暂存沙箱并检查结果,然后替换目标的数据库。
- 重新启动应用并检查它们。
如果出现任何问题,安全备份就是该目标的一个普通版本:用同样的方式恢复它即可。
不使用 CLI¶
Restore 资源本身就是 API:
apiVersion: tenax.run/v1alpha1
kind: Restore
metadata: { name: app-incident-42, namespace: team }
spec:
target: app
source: { time: "2026-01-02T03:04:05Z" } # or backupID: ..., or latest: true
destination:
mode: sandbox
sandbox: { ttl: 6h }
kubectl -n team get restore app-incident-42 -w 会显示进度:计划、获取基础备份、重放、检查、Succeeded。
当 Tenax 自身宕机时¶
存储是唯一可信来源,而且备份格式很朴素:
- manifest.json 列出了每个对象及其大小和 SHA-256。
- 对象经过 zstd 压缩和 age 加密:age -d -i key.txt obj.zst.age | zstd -d。
- Postgres 物理备份是 pg_basebackup tar 流加上 WAL 段。
- Postgres 逻辑备份是 pg_dump -Fc 文件,每个数据库一个。
- MySQL 备份是 mysqldump 输出加上 binlog 文件。
集群丢失或重建之后¶
恢复是自动的。Tenax 自身不维护任何数据库:存储中保存着每一个备份(也是唯一重要的副本),Git 中保存着 BackupStore、策略和目标(GitOps),而 age 身份是 Secret,像其他 Secret 一样恢复即可。在新集群上使用相同的存储和 相同的身份安装 Tenax,并应用相同的目标。然后:
- 控制器在启动时检查每个存储(
BackupStore.status.lastImportTime)。对于每个目标,它会为最新的keepBackupObjects个备份(默认 20 个,包括已固定的)重新创建 Backup 对象。不会向存储写入任何内容。 更早的备份仍保留在存储中,受保留策略约束,也仍在控制台的目录中。 - 恢复、PITR 窗口和控制台照常工作。流式传输程序会从一次新的全量备份开始一条新链。
当目标发生变化时,以及每隔 controller.autoImport.interval(1 小时),导入也会运行。用
controller.autoImport.enabled: false 可以关闭它。tenax catalog import [--dry-run] 使用你的 kubeconfig
手动完成同样的操作。