Skip to content

备份与恢复 ​

Raise 通过 --backup 开关使用 WAL-G 对整个 PostgreSQL 数据库集群执行持续的 WAL 日志归档与每日全量备份,并在加密后上传至对象存储;通过 backup-state.sh 脚本备份 OpenBao 密钥卷、环境配置 .env 与系统状态目录 state/。在恢复数据时,你可以先使用 restore-drill.sh 脚本在一次性临时容器中进行演练,验证通过后再对实际运行中的数据库执行恢复操作。

谁能用 ​

执行系统备份与恢复涉及以下权限与安全要求:

  • 部署方与主机运维(主机 root 用户):具备服务器系统操作权限,通过主机命令行执行备份开启、状态备份、演练与恢复脚本。脚本运行时需要读取 deploy/compose/.env 配置文件(文件中必须配置 BACKUP_ENCRYPTION_KEY 环境变量)。
  • 租户成员与管理员:不能查看、触发或管理平台级备份。

危险

丢失 BACKUP_ENCRYPTION_KEY 密钥将导致所有备份无法恢复。该密钥必须与 OpenBao 解封份额同等严格保管,切勿只存放在单台服务器的 .env 文件中。

怎么打开 ​

备份与恢复属于底层基础设施运维能力,系统没有提供独立的图形化产品界面。

你可以通过以下入口与命令管理不同对象的备份:

保护对象备份方式执行频率入口与管理方式
PostgreSQL(app、audit、zitadel 及工具库)--backup:WAL-G 流式复制 + 每日全量备份WAL 持续归档(约 ≤1 分钟);全量备份每日在 BACKUP_FULL_AT(默认 02:30 UTC)执行在安装时添加 --backup 参数;日常通过 docker compose exec pg-backup kwork-backup ... 执行管理
对象存储版本化 + 跨区 / 异机复制(部署方自备)持续同步依赖底层 对象存储 系统自身配置
OpenBaoRaft 快照或文件卷;打包在状态包中每日 / 每次变更通过 backup-state.sh 脚本备份
环境配置与系统状态(.env、compose/state/)backup-state.sh 状态备份脚本每次变更执行 deploy/scripts/backup-state.sh

常用运维命令速查:

bash
# 开启备份(可在执行安装脚本时与其它参数组合)
./install.sh --pack onprem --infra --backup

# 查看已有的全量备份列表
docker compose exec pg-backup kwork-backup list

# 手动触发一次全量备份
docker compose exec pg-backup kwork-backup full

# 查看 WAL 归档日志状态
docker compose exec pg-backup kwork-backup wal-g wal-show

# 校验 WAL 完整性与时间线
docker compose exec pg-backup kwork-backup wal-g wal-verify integrity timeline

# 备份系统状态包
deploy/scripts/backup-state.sh [--out DIR] [--keep 14] [--upload s3://...]

# 还原系统状态包至指定目录
deploy/scripts/backup-state.sh restore FILE --into DIR

# 执行无损恢复演练
deploy/scripts/restore-drill.sh

# 演练恢复到指定时间点
deploy/scripts/restore-drill.sh --target-time '2026-10-06 11:40:00+00'

界面一览 ​

系统没有提供图形化备份页面。你可以通过日志输出与管理命令查看备份状态与运行健康度:

  • 查看备份运行日志:
    bash
    docker compose logs pg-backup
  • 查看全量备份列表:
    bash
    docker compose exec pg-backup kwork-backup list
  • 健康检查判定标准:
    • 容器内的 WAL 归档流进程处于正常运行状态;
    • 最近一次全量备份的完成时间距当前不超过 26 小时。

提示

Grafana 与 Prometheus 的监控指标不能替代这份健康检查。部署运维人员仍需定期检查备份日志与全量备份时间。

常用操作 ​

开启 PostgreSQL 备份 ​

  1. 在执行系统 安装 时,在安装命令中添加 --backup 开关:
    bash
    ./install.sh --pack onprem --infra --backup
    该开关可以与其它环境参数任意组合。
  2. 安装脚本会自动完成以下 4 项配置(已有密钥不会被覆盖):
    • 生成复制角色密码 BACKUP_DB_PASSWORD 与备份加密密钥 BACKUP_ENCRYPTION_KEY(64 位十六进制)。
    • 初始化复制规则:在数据库中创建专用的复制角色 kwork_backup(具备 REPLICATION 权限,对 app 与 audit 业务库无 CONNECT 权限);在 pg_hba 中增加一行复制规则;将 max_slot_wal_keep_size 设为 8GB;执行配置重载 pg_reload_conf()。此过程不修改 PostgreSQL 镜像,也不需要重启数据库。
    • 基于当前的 POSTGRES_IMAGE 镜像构建备份容器镜像 deploy/compose/pg-backup(内置 WAL-G v3.0.9)。
    • 启动 pg-backup 容器:启动 wal-g wal-receive 进程持续接收 WAL 流(复制槽名为 kwork_backup,日志段大小为 16 MB);每隔 BACKUP_WAL_SWITCH_SECONDS(默认 60 秒)调用一次 pg_switch_wal() 触发切段;并在每日指定时刻生成全量备份,仅保留最近 BACKUP_RETAIN_FULL(默认 7 个)全量备份。
  3. 检查备份存储与机房灾备:
    • 默认配置下,备份文件写入套件自带的对象存储(在私有化 onprem 环境下为 SeaweedFS 的 kwork-backup 存储桶,前缀为 pg/)。
    • 默认备份文件仍存储在同一台物理主机上。如果主机磁盘损坏,备份文件会一并丢失。
    • 生产环境中,建议将 BACKUP_S3_* 系列变量指向另一机房或第三方云对象存储。远端存储桶需要预先创建完成并设置 BACKUP_CREATE_BUCKET=false,建议同时开启对象锁或多版本控制。
  4. 本地加密:
    • 备份数据在上传前由 libsodium 在本机完成加密。更换加密密钥只对后续生成的新备份生效。

备份与还原系统状态包 ​

OpenBao 卷、环境配置文件 .env 以及运行状态目录 state/ 需要通过状态包脚本单独备份。

  1. 执行状态包备份:
    • 运行默认备份命令:
      bash
      deploy/scripts/backup-state.sh
      命令将在 ./state-backups/ 目录下生成加密压缩包 kwork-state-<utc>.tar.gz.enc。
    • 指定输出目录并保留最近 14 份备份:
      bash
      deploy/scripts/backup-state.sh --out /srv/bk --keep 14
    • 备份完成后直接上传至 S3 对象存储:
      bash
      deploy/scripts/backup-state.sh --upload s3://bucket/kwork-state/ --endpoint https://s3.example.com
    • 状态包使用 OpenSSL AES-256-CBC 配合 PBKDF2(迭代 200000 次)加密,密钥直接使用 .env 中的 BACKUP_ENCRYPTION_KEY。
  2. 还原系统状态包:
    • 执行还原命令:
      bash
      deploy/scripts/backup-state.sh restore FILE --into DIR

      注意

      还原命令不会直接覆盖当前主机的运行文件。它只会将解密后的文件提取到指定的 DIR 目录中。

    • 根据终端打印的 cp 或 docker run … tar 命令提示,手动把文件复制放回原位。
    • 启动 OpenBao 容器,并使用 3/5 份额的解封密钥完成解封。

执行恢复演练(不影响线上业务) ​

恢复演练用于在不触碰生产运行环境的前提下,验证备份的可用性与恢复时间。

  1. 在终端执行演练脚本:
    bash
    deploy/scripts/restore-drill.sh
    若要演练恢复至特定时间点,可以传入 --target-time 参数:
    bash
    deploy/scripts/restore-drill.sh --target-time '2026-10-06 11:40:00+00'
  2. 脚本会在一次性临时容器与数据卷 kbk-drill-<时间> 中拉取全量备份、回放 WAL 日志并执行提升。演练数据库仅开放 Unix domain socket,不监听外部网络端口。
  3. 脚本会自动执行以下检查:
    • 校验 app、audit 与 zitadel 数据库是否正常存在;
    • 校验 platform.tenant 表是否可正常读取;
    • 校验数据库迁移版本记录;
    • 校验 audit.event 表;
    • 校验 Zitadel 数据表数量。
  4. 终端会打印数据库各恢复阶段的耗时(数据库部分 RTO)与最后一个事务的时间戳。若当前可以连接在线源库,脚本还会比对 WAL 落后程度以及租户和审计日志记录数。
  5. 演练结束后,脚本会自动销毁临时容器与卷。如果需要保留环境排查问题,可以添加 --keep 参数。

演练脚本支持的常用参数:

参数选项说明默认值
--env-file指定使用的环境配置文件deploy/compose/.env
--pack指定环境包名称当前环境配置
--network指定容器网络kwork_default
--backup NAME指定恢复的特定全量备份名称最新全量备份
--target-time指定时间点恢复的目标时间戳(UTC)最新 WAL 终点
--source-url / --no-source比对所连接的源数据库地址,或跳过源库比对连接源库
--probe SQL附加自定义 SQL 探测验证语句无
--superuser演练数据库的超级用户名kwork

执行真实灾难恢复 ​

当数据库发生故障或需要回滚至指定时刻时,必须先停止写入,再执行真实恢复。

  1. 停止业务写服务: 在终端停止核心应用容器,防止新数据写入:
    bash
    docker compose stop kwork kwork-agent zitadel pg-backup
    若开启了可选组件,还需要停止 cube、superset、langfuse、temporal 与 collab 容器。

    提示

    在停止服务与执行恢复前,建议先运行 restore-drill.sh 演练确认全量备份与目标时间点。

  2. 转移现有数据卷: PostgreSQL 恢复命令要求目标数据目录必须为空。停止数据库容器并将现有数据移动到临时数据卷中,不要直接删除:
    bash
    docker compose stop postgres
    docker volume create kwork_pg_broken
    docker run --rm -v kwork_pg:/from -v kwork_pg_broken:/to alpine sh -c 'cp -a /from/. /to/ && find /from -mindepth 1 -delete'
  3. 执行数据库恢复: 使用备份容器执行恢复操作,可按需指定特定全量备份 --backup 或恢复时间点 --target-time:
    bash
    docker compose --profile backup run --rm --no-deps -v pg:/var/lib/postgresql/data pg-backup restore --stop [--backup NAME] [--target-time '2026-10-06 11:40:00+00']
    恢复完成后,终端会输出格式为 KBK-RESTORED <最后事务时间> | <WAL 位置> | <新时间线> 的确认信息。
  4. 验证数据库并重启业务: 先启动数据库容器:
    bash
    docker compose up -d postgres
    进入数据库检查租户数据表:
    bash
    psql -U kwork -d app -c 'select count(*) from platform.tenant'
    确认数据无误后,启动全部服务容器:
    bash
    docker compose up -d
  5. 验收服务与审计日志:
    • 通过 curl 检查 工作台 访问入口与 /api/capabilities 接口状态。
    • 对每一个租户调用 /api/audit/verify 接口校验审计完整性。
    • 告知用户所选恢复时间点之后的改动已经丢失。
  6. 立即生成新全量备份: 恢复完成后数据库会进入新的时间线,复制槽也会重建。必须立刻手动触发一次全量备份:
    bash
    docker compose exec pg-backup kwork-backup full
  7. 清理临时故障数据卷: 在系统全面验收稳定后,再删除此前保存的临时卷 kwork_pg_broken。

整机灾备演练流程 ​

在全新主机上进行完整灾难恢复时(runbook 建议每季度演练一次;公测文档建议上线前做一次,之后每月一次),遵循以下顺序执行:

  1. 在新主机上准备基础运行环境。
  2. 还原 .env 配置文件与 state/ 状态目录。
  3. 执行 PostgreSQL 时间点恢复。
  4. 还原 OpenBao 密钥卷并使用 3/5 份额解封。
  5. 执行 docker compose up -d 启动全部容器。
  6. 对所有租户调用 /api/audit/verify 完成审计完整性验证。

停止备份与故障排错 ​

  • 停用备份服务与清理复制槽: 如果停止了 pg-backup 容器,必须手动在数据库中删除复制槽。否则数据库会持续为该槽保留 WAL 日志(最大占用 8 GB 空间):
    sql
    select pg_drop_replication_slot('kwork_backup');
  • 复制槽失效(wal_status=lost): 若复制槽状态变为 wal_status=lost,说明日志段已丢失。你需要删除当前槽、重启 pg-backup 容器,并立即执行一次全量备份。在缺口发生前的时间范围仍可依赖旧全量备份恢复,缺口之后的恢复则从新全量备份开始。
  • 容器持续重启排查: 检查对象存储网络连通性与认证配置,或确认 .env 中的 BACKUP_ENCRYPTION_KEY 是否为标准的 64 位十六进制格式。若日志中偶发出现 wal-receive exited 报错,进程会在 5 秒后自动重新建立连接。
  • 数据库镜像升级兼容性: 如果更改了 POSTGRES_IMAGE(例如升级至包含 pgvector 的新版本),必须重新构建 pg-backup 镜像。用于数据恢复的 PostgreSQL 容器必须与备份时保持相同的主版本号以及相同的 C 语言运行库。从 PostgreSQL 17 升级至 18 后,旧版本的备份文件无法直接用于新版本数据库:升级完成后必须立刻创建一次新的全量备份,并更换新的 BACKUP_S3_PREFIX 存储路径。

实测性能指标 ​

以下数据基于开发机一次性环境(329 MB 数据库集群)实测:

  • 冷启动恢复演练全程合计耗时约 35 秒;
  • 时间点恢复能够精确还原数据;
  • 强行停止源数据库并删除数据卷后,实际丢失数据约 33 秒(由于闲时切 WAL 间隔为 60 秒,远低于 5 分钟的 RPO 目标);
  • 冷拉取全量备份耗时约 19 秒(传输速率约 17 MB/s);
  • 几十 GB 以上的大规模数据场景尚未进行实测。

让助手帮你做 ​

系统没有向助手(Agent)开放任何备份与恢复相关的操作指令。备份属于主机底层的基础设施管理命令,不暴露给租户或组织的助手,助手不能查询、触发或配置备份任务。

与其他应用的关系 ​

  • 身份中心(Zitadel):用户账号与身份鉴权数据存储在 zitadel 数据库中,随 PostgreSQL 集群的 WAL 日志与全量备份一同备份。
  • 密钥管理(OpenBao):OpenBao 中保存了租户自带存储(BYO 存储)凭证与自备模型 API 密钥。在灾难恢复时仅恢复 PostgreSQL 数据库是不够的,必须同时恢复 OpenBao 的状态卷。
  • 对象存储:用户上传的文件保存在对象存储中,不包含在 WAL-G 的备份范围内,需要依赖对象存储自身的多版本控制或跨区复制策略。
  • 审计日志归档:审计日志归档存储桶可以通过配置 KWORK_AUDIT_ARCHIVE_BUCKET 环境变量单独开启对象锁,防止归档日志被意外删除或篡改。

设置 ​

PostgreSQL 备份相关的 配置项 位于 deploy/compose/.env 文件中:

环境变量作用说明默认值
BACKUP_ENCRYPTION_KEY加密全量备份、WAL 日志与 state 状态包(64 位十六进制)运行 --backup 时自动生成
BACKUP_DB_PASSWORD数据库复制角色 kwork_backup 的连接密码自动生成
BACKUP_S3_ENDPOINT备份对象存储的 API 端点地址(未配置时可回退读取 KWORK_S3_* 或 S3_*)onprem 环境使用内置套件存储
BACKUP_S3_BUCKET备份对象存储桶名称kwork-backup(onprem)
BACKUP_S3_PREFIX备份在存储桶中的对象前缀路径pg/
BACKUP_S3_ACCESS_KEY备份对象存储的访问密钥 Access Key同上
BACKUP_S3_SECRET_KEY备份对象存储的私有密钥 Secret Key同上
BACKUP_WAL_SWITCH_SECONDS闲时强制切换 WAL 日志的间隔秒数60
BACKUP_FULL_AT每日触发全量备份的时刻02:30
BACKUP_TZ全量备份时刻对应的时区(对应 compose 中的 TZ)UTC
BACKUP_RETAIN_FULL保留的全量备份份数7
BACKUP_CREATE_BUCKET是否在启动时自动创建备份存储桶套件环境下通常为 true
BACKUP_MAX_SLOT_WAL_KEEP复制槽保留 WAL 日志的最大磁盘空间8GB

注意事项 ​

  • 高权限复制角色:复制角色 kwork_backup 能够通过复制协议读取整个集群的数据,其密码必须按照超级用户密码级别进行保密。
  • 单机同盘风险:默认配置下备份存储在同一台物理主机上,磁盘损坏会导致数据与备份一并丢失,不属于真正的异地灾备。
  • 恢复目标目录限制:执行 restore 命令时,目标数据目录必须为空,否则恢复命令将拒绝执行。
  • 演练与线上环境隔离:恢复演练在独立容器内进行,不触碰线上运行中的服务栈;真实恢复时必须先停止所有业务写入。
  • 时间线切换与全量备份:恢复到新库后系统会切换时间线,必须立刻执行一次全量备份以重建复制槽。

目前不支持 ​

  • 运营台没有一键备份或恢复按钮,不支持在网页界面上直接触发或还原备份。
  • 对象存储的跨区或异机自动复制功能未内置,需部署方自备。
  • 未提供把演练数据库容器直接切换为生产运行栈的自动化流程。
  • 真实环境未提供从头再跑一遍 install.sh --backup 的自动化操作。

常见问题 ​

备份加密密钥丢失后能否恢复数据? ​

不能。丢失 BACKUP_ENCRYPTION_KEY 会导致所有已加密的历史全量备份、WAL 日志与 state 状态包彻底无法解密恢复。更换加密密钥只对后续新产生的备份生效。因此必须将该密钥与 OpenBao 解封份额一起进行脱机异地保存。

为什么停止备份容器后数据库磁盘空间持续下降? ​

停止 pg-backup 容器后,若没有在 PostgreSQL 中删除复制槽 kwork_backup,数据库会持续保留 WAL 日志,直到达到 BACKUP_MAX_SLOT_WAL_KEEP 设定的 8 GB 上限。停用备份后必须执行 select pg_drop_replication_slot('kwork_backup') 显式清理复制槽。

恢复演练会不会影响正在运行的业务系统? ​

不会。restore-drill.sh 脚本在一次性临时容器与临时卷(kbk-drill-<时间>)中拉取备份与回放 WAL 日志,恢复后的测试库仅绑定 Unix domain socket,不监听对外网络端口。演练完成并输出检查结果后,脚本会自动销毁临时容器与卷。

真实恢复时执行 restore 命令报错提示目录非空该如何处理? ​

WAL-G 恢复工具出于安全保护机制,只允许写入空的数据目录。你需要先停止 PostgreSQL 容器,将原有数据目录中的文件移动至临时卷(例如 kwork_pg_broken),清空原目录后再重新运行恢复命令。