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-cbc 和 repo1-cipher-pass。但 pg_tde 已经加密了数据文件和 WAL,这层加密更多是合规需求而非安全必要。
采纳建议
- 别被之前的说法吓住——异步归档在 pg_tde + pgBackRest 组合下实测可用,不需要自定义解密/重加密脚本包装 WAL。
- 关掉压缩和块级增量——加密数据对这两者天然不友好,开着只浪费 CPU 和时间。
- 关掉 WAL 头部检查和数据页校验——这是让 pgBackRest 不报错的关键,加密后这些检查要么失败要么无意义。
- 密钥管理是最大复杂度——OpenBao/Vault 的 Token、策略、轮换都需要运维投入,生产环境要考虑高可用和灾备。
- 关注 PostgreSQL 核心 TDE 进展——PGConf.dev 上已有讨论把 TDE 合入核心,一旦落地就不需要 Percona 的补丁分支了,扩展兼容性也会更好。
一句话总结:pgBackRest 和 pg_tde 可以一起用,加密数据在备份链路中全程保持加密状态。代价是压缩和增量备份的收益缩水,以及几个必须关闭的校验选项。配置对了,备份恢复流程和普通 PostgreSQL 没有本质区别。