外观
备份与恢复
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 ... 执行管理 |
| 对象存储 | 版本化 + 跨区 / 异机复制(部署方自备) | 持续同步 | 依赖底层 对象存储 系统自身配置 |
| OpenBao | Raft 快照或文件卷;打包在状态包中 | 每日 / 每次变更 | 通过 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 备份
- 在执行系统 安装 时,在安装命令中添加
--backup开关:bash该开关可以与其它环境参数任意组合。./install.sh --pack onprem --infra --backup - 安装脚本会自动完成以下 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 个)全量备份。
- 生成复制角色密码
- 检查备份存储与机房灾备:
- 默认配置下,备份文件写入套件自带的对象存储(在私有化 onprem 环境下为 SeaweedFS 的
kwork-backup存储桶,前缀为pg/)。 - 默认备份文件仍存储在同一台物理主机上。如果主机磁盘损坏,备份文件会一并丢失。
- 生产环境中,建议将
BACKUP_S3_*系列变量指向另一机房或第三方云对象存储。远端存储桶需要预先创建完成并设置BACKUP_CREATE_BUCKET=false,建议同时开启对象锁或多版本控制。
- 默认配置下,备份文件写入套件自带的对象存储(在私有化 onprem 环境下为 SeaweedFS 的
- 本地加密:
- 备份数据在上传前由 libsodium 在本机完成加密。更换加密密钥只对后续生成的新备份生效。
备份与还原系统状态包
OpenBao 卷、环境配置文件 .env 以及运行状态目录 state/ 需要通过状态包脚本单独备份。
- 执行状态包备份:
- 运行默认备份命令: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。
- 运行默认备份命令:
- 还原系统状态包:
- 执行还原命令:bash
deploy/scripts/backup-state.sh restore FILE --into DIR注意
还原命令不会直接覆盖当前主机的运行文件。它只会将解密后的文件提取到指定的
DIR目录中。 - 根据终端打印的
cp或docker run … tar命令提示,手动把文件复制放回原位。 - 启动 OpenBao 容器,并使用 3/5 份额的解封密钥完成解封。
- 执行还原命令:
执行恢复演练(不影响线上业务)
恢复演练用于在不触碰生产运行环境的前提下,验证备份的可用性与恢复时间。
- 在终端执行演练脚本:bash若要演练恢复至特定时间点,可以传入
deploy/scripts/restore-drill.sh--target-time参数:bashdeploy/scripts/restore-drill.sh --target-time '2026-10-06 11:40:00+00' - 脚本会在一次性临时容器与数据卷
kbk-drill-<时间>中拉取全量备份、回放 WAL 日志并执行提升。演练数据库仅开放 Unix domain socket,不监听外部网络端口。 - 脚本会自动执行以下检查:
- 校验
app、audit与zitadel数据库是否正常存在; - 校验
platform.tenant表是否可正常读取; - 校验数据库迁移版本记录;
- 校验
audit.event表; - 校验 Zitadel 数据表数量。
- 校验
- 终端会打印数据库各恢复阶段的耗时(数据库部分 RTO)与最后一个事务的时间戳。若当前可以连接在线源库,脚本还会比对 WAL 落后程度以及租户和审计日志记录数。
- 演练结束后,脚本会自动销毁临时容器与卷。如果需要保留环境排查问题,可以添加
--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 |
执行真实灾难恢复
当数据库发生故障或需要回滚至指定时刻时,必须先停止写入,再执行真实恢复。
- 停止业务写服务: 在终端停止核心应用容器,防止新数据写入:bash若开启了可选组件,还需要停止
docker compose stop kwork kwork-agent zitadel pg-backupcube、superset、langfuse、temporal与collab容器。提示
在停止服务与执行恢复前,建议先运行
restore-drill.sh演练确认全量备份与目标时间点。 - 转移现有数据卷: 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' - 执行数据库恢复: 使用备份容器执行恢复操作,可按需指定特定全量备份
--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 位置> | <新时间线>的确认信息。 - 验证数据库并重启业务: 先启动数据库容器:bash进入数据库检查租户数据表:
docker compose up -d postgresbash确认数据无误后,启动全部服务容器:psql -U kwork -d app -c 'select count(*) from platform.tenant'bashdocker compose up -d - 验收服务与审计日志:
- 通过
curl检查 工作台 访问入口与/api/capabilities接口状态。 - 对每一个租户调用
/api/audit/verify接口校验审计完整性。 - 告知用户所选恢复时间点之后的改动已经丢失。
- 通过
- 立即生成新全量备份: 恢复完成后数据库会进入新的时间线,复制槽也会重建。必须立刻手动触发一次全量备份:bash
docker compose exec pg-backup kwork-backup full - 清理临时故障数据卷: 在系统全面验收稳定后,再删除此前保存的临时卷
kwork_pg_broken。
整机灾备演练流程
在全新主机上进行完整灾难恢复时(runbook 建议每季度演练一次;公测文档建议上线前做一次,之后每月一次),遵循以下顺序执行:
- 在新主机上准备基础运行环境。
- 还原
.env配置文件与state/状态目录。 - 执行 PostgreSQL 时间点恢复。
- 还原 OpenBao 密钥卷并使用 3/5 份额解封。
- 执行
docker compose up -d启动全部容器。 - 对所有租户调用
/api/audit/verify完成审计完整性验证。
停止备份与故障排错
- 停用备份服务与清理复制槽: 如果停止了
pg-backup容器,必须手动在数据库中删除复制槽。否则数据库会持续为该槽保留 WAL 日志(最大占用 8 GB 空间):sqlselect 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),清空原目录后再重新运行恢复命令。