背景(Situation)
Sentry 是一款开源的前端监控组件,被广泛应用于各类 Web 应用、移动应用以及后端服务中,用于实时捕获和报告应用程序中的错误、性能问题。
Sentry开发团队后续更是推出了 Self-Hosted Sentry,一种基于docker和docker-compose部署的可开箱即用,适用于小规模入门应用的sentry。
我司的Self-Hosted Sentry服务移交到我手上后,先后踩了 版本升级 和 磁盘耗尽 的坑。
版本升级,可以参考此文章: develop.sentry.dev/self-hosted…
磁盘耗尽,由于sentry服务依赖postgres(后称pg),而pg存在逻辑删除及死元组问题,导致其真实数据大小和磁盘占用空间相去甚远。pg超过1T,实际约100G+。
而且此时执行vacuum full 或 vacuumdb -U postgres -d postgres -v -f --analyze,已然不可能。
目标(Target)
如果数据库交由DBA管控,专业的人做专业的事(甩锅),并且他们可以周期对数据库进行备份。大大降低数据遗失风险。
团队商定使用外接pg方案,其实如果从零搭建sentry,这本身没啥难度,但是如果考虑到数据迁移,问题就变很多了。拆解完,主要是两个问题:
- 数据库如何初始化
- 老库数据导出和新库导入
行动(Action)
数据库如何初始化
根据sentry镜像版本,安装pg,我的版本作为参考。
self-hosted sentry: 25.1.0
pg: 16
安装sentry必要的pg插件:
| Name | Version | Schema |
|---|---|---|
| citext | 1.6 | public |
| plpgsql | 1.0 | pg_catalog |
老库数据导出和新库导入
这个才是最坑的点,没搞过兄弟们,肯定觉得pg_dump就完了,但是由于表太大,会报错,如下图所示。
考虑分布导出
其中最大的表是:nodestore_node,实测超过900GB。psql+分时间段导出。
但是,兄弟们还有一个坑,把其他容器都停掉,再导出。
方案如下:
第一步:备份源数据库(远程)
# 备份全局对象(角色、权限等)
pg_dumpall --globals-only -h 10.1.1.1 -p 15432 -U postgres > /root/backup_mydb_global.sql
# 备份除 nodestore_node 外的全部数据,
# !!!注意一定要停掉除了pg之外的其他容器
pg_dump -h 10.1.1.1 -p 15432 -U postgres -d postgres -T nodestore_node -F c -f /root/backup_mydb_other.dump
# 导出 nodestore_node 的数据(分段)
pg_dump -h 10.1.1.1 -p 15432 -U postgres -d postgres -t nodestore_node --schema-only -f /root/nodestore_node_schema.sql
psql -h 10.1.1.1 -p 15432 -U postgres -d postgres -c \
"COPY (SELECT id, data, timestamp FROM public.nodestore_node WHERE timestamp >= '2025-07-08 00:00:00+08' AND timestamp < '2025-07-10 00:00:00+08') TO STDOUT" > /root/nodestore_node_part1.csv
psql -h 10.1.1.1 -p 15432 -U postgres -d postgres -c \
"COPY (SELECT id, data, timestamp FROM public.nodestore_node WHERE timestamp >= '2025-07-06 00:00:00+08' AND timestamp < '2025-07-08 00:00:00+08') TO STDOUT" > /root/nodestore_node_part2.csv
psql -h 10.1.1.1 -p 15432 -U postgres -d postgres -c \
"COPY (SELECT id, data, timestamp FROM public.nodestore_node WHERE timestamp >= '2025-07-04 00:00:00+08' AND timestamp < '2025-07-06 00:00:00+08') TO STDOUT" > /root/nodestore_node_part3.csv
第二步:本地恢复(清除 + 还原)
# 清空 public schema(避免冲突)
PGPASSWORD=... psql -U postgres -c "DROP SCHEMA public CASCADE;"
PGPASSWORD=... psql -U postgres -c "CREATE SCHEMA public;"
PGPASSWORD=... psql -U postgres -c "GRANT ALL ON SCHEMA public TO postgres;"
PGPASSWORD=... psql -U postgres -c "GRANT ALL ON SCHEMA public TO sentry;"
第三步:恢复数据库内容
# 1. 恢复全局对象(角色、权限等)
PGPASSWORD=... psql -U postgres -f /root/backup_mydb_global.sql
# 2. 恢复 main schema 数据(不含 nodestore_node)
PGPASSWORD=... pg_restore -U postgres -d postgres /root/backup_mydb_other.dump
# 3. 创建 nodestore_node 表结构
PGPASSWORD=... psql -U postgres -d postgres -f /root/nodestore_node_schema.sql
# 4. 分批导入 nodestore_node 数据
PGPASSWORD=... psql -U postgres -d postgres -c "COPY public.nodestore_node (id, data, timestamp) FROM STDIN" < /root/nodestore_node_part1.csv
PGPASSWORD=... psql -U postgres -d postgres -c "COPY public.nodestore_node (id, data, timestamp) FROM STDIN" < /root/nodestore_node_part2.csv
PGPASSWORD=... psql -U postgres -d postgres -c "COPY public.nodestore_node (id, data, timestamp) FROM STDIN" < /root/nodestore_node_part3.csv
总结
- 备份时注意 -T 排除 nodestore_node,由于单表空间过于大,导致失败;
恢复前用 DROP SCHEMA;
- 只恢复一次 global.sql;
- 创建 nodestore_node 表结构后再导入 CSV 数据;
- 保证顺序是:schema → dump → 手动 COPY 数据。
结果(Result)
Self-Hosted Sentry 的pg迁移后,验证一切功能正常,历史数据完好。