pgBackRest + pg_tde:加密数据库的备份还原实测

2026-06-04 21 预计阅读时间:1 分钟
来源:postgr.es AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

Percona 之前发过一篇博客,说 pgBackRest 配合 pg_tde(透明数据加密扩展)时,异步归档不可用——原因是对加密的 WAL 段做归档会出问题。Stefan Fercot 在会议走廊里听到这个说法后觉得不对劲:pgBackRest 应该能透明处理加密 WAL。于是他亲手搭了一套环境跑了一遍,结论是:异步归档完全可用,之前说的限制并不成立

不过要让 pgBackRest 正常工作,有几个配置必须调整。加密数据对压缩和增量备份也有实际影响。下面是完整实测路径和最终配置。

环境搭建:Percona Server + pg_tde + OpenBao

pg_tde 目前需要打补丁的 PostgreSQL,所以直接装 Percona Server for PostgreSQL 18:

# Debian 12 上安装 Percona Server 18 和 pg_tde
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo apt-get update
sudo percona-release setup ppg-18
sudo apt install percona-postgresql-18
sudo apt-get install -y percona-pg-tde18

加载扩展并重启:

ALTER SYSTEM SET shared_preload_libraries = 'pg_tde';
sudo systemctl restart postgresql@18-main.service

创建测试数据库并启用 pg_tde:

CREATE DATABASE bench;
\c bench
CREATE EXTENSION pg_tde;

密钥管理:用 OpenBao 替代 Vault

pg_tde 需要外部密钥管理。这里用 OpenBao(HashiCorp Vault 的开源分支)跑在 Docker 里:

docker run \
  --name openbao \
  --detach \
  --volume /openbao/config:/openbao/config \
  --publish 8200:8200 \
  openbao/openbao

写策略文件,只允许 secret/ 路径下的读写:

cat > pg-tde-policy.hcl << 'EOF'
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
EOF

注册策略、创建受限 Token:

# 把策略文件拷进容器
docker cp pg-tde-policy.hcl openbao:/tmp/pg-tde-policy.hcl

# 写入策略(XXX 替换为 root token)
docker exec -e VAULT_ADDR='http://127.0.0.1:8200' -e VAULT_TOKEN='XXX' \
  openbao bao policy write pg-tde-policy /tmp/pg-tde-policy.hcl

# 创建最小权限 token(YYY 是生成的 token,记下来)
docker exec -e VAULT_ADDR='http://127.0.0.1:8200' -e VAULT_TOKEN='XXX' \
  openbao bao token create \
  -policy=pg-tde-policy \
  -display-name=pg-tde \
  -no-default-policy \
  -period=0

把 Token 存到 PostgreSQL 可读的位置:

echo -n 'YYY' > /etc/postgresql/18/main/token_file
chmod 600 /etc/postgresql/18/main/token_file

在 PostgreSQL 里注册密钥提供者

-- 注册 OpenBao 为全局密钥提供者
SELECT pg_tde_add_global_key_provider_vault_v2(
  'my-openbao-provider',
  'http://192.168.121.1:8200',
  'secret',
  '/etc/postgresql/18/main/token_file',
  NULL
);

-- 创建并设置默认主密钥
SELECT pg_tde_create_key_using_global_key_provider('keytest1', 'my-openbao-provider');
SELECT pg_tde_set_default_key_using_global_key_provider('keytest1', 'my-openbao-provider');

开启 WAL 加密并重启:

ALTER SYSTEM SET pg_tde.wal_encrypt = on;
sudo systemctl restart postgresql@18-main.service

加密测试表

用 pgbench 生成约 1.5 GB 数据,然后把所有表切换到 tde_heap 访问方法:

pgbench -i -s 100 bench
ALTER TABLE pgbench_accounts  SET ACCESS METHOD tde_heap;
ALTER TABLE pgbench_branches  SET ACCESS METHOD tde_heap;
ALTER TABLE pgbench_history   SET ACCESS METHOD tde_heap;
ALTER TABLE pgbench_tellers   SET ACCESS METHOD tde_heap;

验证加密状态:

SHOW pg_tde.wal_encrypt;
SELECT pg_tde_is_encrypted('pgbench_accounts');
SELECT pg_tde_is_encrypted('pgbench_tellers');

pgBackRest 配置:三个必须关掉的选项

安装 pgBackRest(PGDG 仓库):

sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y pgbackrest

核心配置文件 /etc/pgbackrest/pgbackrest.conf

[global]
repo1-type=s3
repo1-s3-endpoint=192.168.122.1
repo1-s3-region=eu-central-1
repo1-s3-bucket=pgbackrest-tde
repo1-s3-key=XXX
repo1-s3-key-secret=YYY
repo1-s3-uri-style=path
repo1-storage-verify-tls=n
repo1-path=/repo1
repo1-retention-full=1
repo1-bundle=y
repo1-block=n
start-fast=y
delta=y
process-max=2
log-level-console=info
log-level-file=detail
compress-type=none
archive-header-check=n
checksum-page=n
archive-async=y

[tdedemo]
pg1-path=/var/lib/postgresql/18/main
pg-version-force=18

为什么这几个选项必须调整:

选项 设为 原因
archive-header-check n pg_tde 加密了 WAL 内容,pgBackRest 无法解析加密后的 WAL 头部
checksum-page n 数据页被加密后校验和验证无意义,甚至可能报错
compress-type none 加密数据看起来像随机字节,压缩算法几乎无效,白耗 CPU
repo1-block n 加密后块级增量备份的节省效果大幅降低,实测增量备份体积仍然接近全量
pg-version-force 18 Percona Server 的版本检测可能不走标准路径,强制指定避免误判

启用 WAL 归档:

ALTER SYSTEM SET archive_mode = 'on';
ALTER SYSTEM SET archive_command = 'pgbackrest --stanza=tdedemo archive-push %p';
sudo systemctl restart postgresql@18-main.service

初始化 stanza 并做第一次全量备份:

pgbackrest --stanza=tdedemo stanza-create
pgbackrest --stanza=tdedemo --type=full backup

恢复实测:删数据、建还原点、完整恢复

这是最关键的一步——证明加密 WAL 的异步归档和恢复都能正常工作。

先建还原点,再删数据:

SELECT pg_create_restore_point('RP1');

BEGIN;
SELECT pg_current_wal_lsn(), current_timestamp;
DELETE FROM pgbench_tellers;
COMMIT;
-- DELETE 1000

停库、恢复到还原点 RP1

sudo systemctl stop postgresql@18-main.service

pgbackrest --stanza=tdedemo restore --type=name --target='RP1'

检查 pgBackRest 生成的恢复配置:

cat /var/lib/postgresql/18/main/postgresql.auto.conf
# 应包含:
# restore_command = 'pgbackrest --stanza=tdedemo archive-get %f "%p"'
# recovery_target_name = 'RP1'

启动 PostgreSQL,完成恢复:

sudo systemctl start postgresql@18-main.service

日志确认恢复到达目标点后,推进恢复:

SELECT pg_wal_replay_resume();

验证数据回来了:

SELECT count(*) FROM pgbench_tellers;
-- 1000

异步归档在日志里也正常工作——archive-push 命令以 async 模式成功推送了 WAL 文件,archive-get 也以 async 模式成功取回了归档 WAL。之前 Percona 博客说的"异步归档不可用"在这个测试中并不成立。

增量备份的现实:加密后节省有限

实测中跑了几次增量备份,结果不太乐观:

full backup:  database size 1.5GB, repo1 backup size 1.5GB
incr backup:  database size 1.5GB, repo1 backup size 1.5GB  (第一次)
incr backup:  database size 1.5GB, repo1 backup size 560MB  (第二次)
incr backup:  database size 1.5GB, repo1 backup size 1.1GB  (第三次)

pgbench 的变更量本不该产生这么大的增量。加密让数据页看起来像随机字节,块级增量备份依赖的"相似块检测"几乎失效。所以最终配置里 repo1-block=n 是合理选择——开着反而增加处理开销,节省不了多少空间。

最终配置清单

把上面的经验浓缩成一份可直接改造的配置:

[global]
# --- 存储层 ---
repo1-type=s3
repo1-s3-endpoint=你的S3端点
repo1-s3-region=你的区域
repo1-s3-bucket=你的桶名
repo1-s3-key=你的AccessKey
repo1-s3-key-secret=你的SecretKey
repo1-s3-uri-style=path
repo1-storage-verify-tls=n
repo1-path=/repo1
repo1-retention-full=1

# --- 加密场景必调 ---
repo1-bundle=y            # 合并小文件,对象存储友好
repo1-block=n             # 加密后块级增量效果差,关掉
compress-type=none        # 加密数据压缩无效,省CPU
archive-header-check=n    # 加密WAL头部无法解析
checksum-page=n           # 加密数据页校验无意义
archive-async=y           # 异步归档实测可用

# --- 通用 ---
start-fast=y
delta=y
process-max=2
log-level-console=info
log-level-file=detail

[tdedemo]
pg1-path=/var/lib/postgresql/18/main
pg-version-force=18

如果备份策略要求独立加密层,可以加 repo1-cipher-type=aes-256-cbcrepo1-cipher-pass。但 pg_tde 已经加密了数据文件和 WAL,这层加密更多是合规需求而非安全必要。

采纳建议

  1. 别被之前的说法吓住——异步归档在 pg_tde + pgBackRest 组合下实测可用,不需要自定义解密/重加密脚本包装 WAL。
  2. 关掉压缩和块级增量——加密数据对这两者天然不友好,开着只浪费 CPU 和时间。
  3. 关掉 WAL 头部检查和数据页校验——这是让 pgBackRest 不报错的关键,加密后这些检查要么失败要么无意义。
  4. 密钥管理是最大复杂度——OpenBao/Vault 的 Token、策略、轮换都需要运维投入,生产环境要考虑高可用和灾备。
  5. 关注 PostgreSQL 核心 TDE 进展——PGConf.dev 上已有讨论把 TDE 合入核心,一旦落地就不需要 Percona 的补丁分支了,扩展兼容性也会更好。

一句话总结:pgBackRest 和 pg_tde 可以一起用,加密数据在备份链路中全程保持加密状态。代价是压缩和增量备份的收益缩水,以及几个必须关闭的校验选项。配置对了,备份恢复流程和普通 PostgreSQL 没有本质区别。


相关推荐