跳转至

使用 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. 下载

tenax restore team/app --at <backup-id> --to download --wait

--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
  1. 先停止写入方。 Tenax 不会停止应用。请把它们缩容到零,或切换到维护模式。
  2. Tenax 会对当前状态做一次安全备份(原因为 safety)。若要跳过,需要使用 --skip-safety-backup。
  3. 它先恢复到一个暂存沙箱并检查结果,然后替换目标的数据库。
  4. 重新启动应用并检查它们。

如果出现任何问题,安全备份就是该目标的一个普通版本:用同样的方式恢复它即可。

不使用 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,并应用相同的目标。然后:

  1. 控制器在启动时检查每个存储(BackupStore.status.lastImportTime)。对于每个目标,它会为最新的 keepBackupObjects 个备份(默认 20 个,包括已固定的)重新创建 Backup 对象。不会向存储写入任何内容。 更早的备份仍保留在存储中,受保留策略约束,也仍在控制台的目录中。
  2. 恢复、PITR 窗口和控制台照常工作。流式传输程序会从一次新的全量备份开始一条新链。

当目标发生变化时,以及每隔 controller.autoImport.interval(1 小时),导入也会运行。用 controller.autoImport.enabled: false 可以关闭它。tenax catalog import [--dry-run] 使用你的 kubeconfig 手动完成同样的操作。