20 KiB
Shared PostgreSQL:Pigsty 裁剪研究
日期:2026-09-25。状态:源码研究与方案建议,尚未部署、压测或决定迁移。维护者已确定 etcd 按全 homelab 共享基础设施设计,其余实现细节仍为建议。
研究基线:Pigsty v4.5.0,commit dab5dba333a070d96fde1f9feb41761148f2be8c,浅克隆位于 /tmp/pigsty-source-review。下文相对源码路径均指此版本。保留上游 Apache-2.0 许可及适用的版权声明。
结论
可以裁剪,优先保留 PostgreSQL + Patroni + etcd + pgBackRest,复用 pg_exporter 和经过筛选的规则。无需带入仓库服务器、PgBouncer、Supabase、Grafana/VictoriaMetrics 全栈、门户和 MinIO。
但这不是一份关闭功能开关的 inventory 就能完成的工作。主要改造点是:实例与宿主机解耦、移除对宿主机的全局接管、重做初始化权限、补齐可验证的备份和切换流程。建议维护独立的精简 Ansible 实现,明确记录取自上游的文件和基线;不保留无用组件及其配置兼容负担。
现阶段没有实测 RSS、HDD fsync 延迟和故障恢复时间,不能宣称任何 HA 方案已经满足资源预算。
已知需求与现状
- 数据库独立于 k3s;生产自动故障转移;开发不要求 HA。
- laptop ZFS SSD 为正常生产 primary 所在地;PVE HDD LXC 承担 standby。
- 开发与生产共用物理机器,但使用独立 PGDATA、端口、操作系统账号和资源约束。
- IaC 管宿主资源、实例、HA、备份、监控;Ayatori 管业务数据库、账号和凭据;Backstage 管门户。
- 异地备份暂缓,首期仍包含本地备份与恢复验证。
- 本会话先前只读检查:现有 CNPG 为 PostgreSQL 18.3,数据库合计约 185 MiB,PGDATA 约 748 MiB,其中 WAL 约 561 MiB;活跃 10 GiB PVC 的文件系统使用量约 795 MiB。这不是备份压缩后的体积,也不是新的持续监控结果。
- Gitea 数据库约 43 MiB、NetBox 约 29 MiB;NetBox 可能退役。Git 仓库、LFS、附件等不包含在数据库体积里。
源码发现与处理
| 范围 | 源码依据 | 结论与处理 |
|---|---|---|
| 最小入口 | slim.yml |
已跳过 INFRA、监控栈和仓库服务,但仍执行 NODE、HAProxy、ETCD、PGSQL;不能直接当共享宿主部署入口。 |
| 生命周期 | pgsql.yml、roles/pgsql/tasks/main.yml |
初始化与日常收敛不是同一件事。拆成 provision/configure/upgrade/recover,禁止普通配置更新重跑 bootstrap。 |
| 同机多实例 | roles/pgsql/tasks/install.yml、config.yml;全仓检索 pg_instances |
pg_instances 仅见注释/元数据,没有实例循环实现。硬编码 /pg、Patroni/PG service、pgBackRest/exporter 配置与全局环境文件;改端口或 inventory alias 不够。 |
| 宿主机接管 | roles/pgsql/tasks/install.yml:58 起、roles/node/tasks/main.yml |
Debian 清理逻辑会停止默认 PG、删除发行版 systemd unit,包含立即停库与 kill 回退;NODE 管 DNS、软件源、调优等。删除这些逻辑,仅管理自己声明的资源。 |
| 包依赖 | roles/node_id/vars/d12.aarch64.yml 等发行版映射 |
pgsql-common 仍包含 PgBouncer、vip-manager、backup exporter;pgsql-main 也含多种扩展/语言包。关闭服务不等于减少安装,必须改为明确的包清单并固定版本策略。 |
| 调优 | roles/node_id/tasks/main.yml、roles/pgsql/templates/tiny.yml:14 起 |
tiny 默认仍为 250 connections,内存按宿主计算,work_mem 下限 16 MiB。共享 laptop/LXC 应按实例预算计算,不能把整机资源分配给每个实例。 |
| HA 替换 | roles/pgsql/tasks/main.yml、Patroni 模板与恢复脚本 |
禁用 Patroni 并不会得到另一套完整的初始化/HA 实现;换 pg_auto_failover 是重新接生命周期和恢复流程,不能按少一个组件直接判定更便宜。 |
| 路由 | roles/pgsql/templates/service.cfg |
已有 Patroni HTTP 角色检查、PostgreSQL 直连目标与摘除旧会话的逻辑,可抽取接既有 HAProxy。自动选主必须同时解决稳定入口与客户端重连。 |
| 初始化权限 | roles/pgsql/tasks/patroni.yml:103 起、pg-init-roles.sql、pg-init-template.sql |
pg_provision: false 只跳过业务 provisioning,不跳过 bootstrap SQL。默认角色和 template1 修改仍会发生;替换为最小初始化,不能直接给 Ayatori 默认 superuser DBA。 |
| 备份 | roles/pgsql/tasks/pgbackrest.yml、roles/pgsql/templates/pgbackrest.conf |
stanza/首备错误被忽略;全局 initial.done;模板仅渲染 repo1;按整机 CPU 计算并行度。需要实例化路径、明确失败门禁、保守并行度。 |
| 调度 | roles/pgsql/defaults/main.yml:25、files/postgres/pg-backup |
默认 pg_crontab 为空;备份脚本虽然检查 primary,但硬编码 /pg 与默认配置。部署成功不代表周期备份存在。 |
| 监控接入 | roles/pg_monitor/tasks/register_victoria.yml 等 |
上游注册到自己的 infra 目录。只取 exporter 部分,改为本仓现有 VMStaticScrape/VMRule 接入方式,不部署新的监控服务。 |
上游固定版本入口:slim.yml、pgsql tasks、监控规则。
建议拓扑
| 故障域 | 首期部署 |
|---|---|
| laptop | prod primary + Patroni;dev 单实例;etcd 成员 A;exporter。prod/dev 分别使用 ZFS dataset。 |
| PVE 主机 A 的 LXC | prod standby + Patroni;etcd 成员 B;exporter。 |
| PVE 主机 B 的 LXC | etcd 成员 C;本地 pgBackRest 仓库可与此共置,不额外引入对象存储服务。 |
| 既有入口 | HAProxy 按 Patroni /primary 健康检查转发生产写连接,开发使用独立入口。 |
| 既有监控 | vmagent 抓取;现有 vmalert/Alertmanager 评估与发送告警。 |
首期只配一个 PG standby;第二个 PVE LXC 可以只承担投票成员和备份仓库。两个 PVE LXC 必须跨实际物理主机,否则不能算两个故障域。etcd 不复用 k3s 的 DCS。
Shared etcd 的管理边界
维护者于本轮明确:etcd 做成全 homelab 共享服务。原方案将 etcd 列入数据库部署角色;调整后 etcd 拥有独立的 inventory、部署入口、升级、快照和恢复流程,PostgreSQL 是第一个消费者。建议代码归属 infrastructure/etcd/,数据库只声明端点、客户端凭据和自身的 DCS prefix。物理共置方式可沿用上表,不因此增加节点。
- 为每个消费者分配独立账号与 key prefix,并用 etcd RBAC 约束读写范围。Patroni 示例:
namespace: /homelab/patroni/、scope: pg-prod,只授权/homelab/patroni/pg-prod/。路径命名本身不是权限隔离。参考 etcd RBAC。 - 客户端配置三个直接可达的 TLS endpoints;etcd 维护不依赖生产 PostgreSQL、Ayatori 或 k3s 的可用性。etcd root 仅用于基础设施管理,不交给消费者。
- 维护者明确 mTLS 证书使用 OpenBao 中央 CA 签发,删除 Pigsty 自建 CA 的部署依赖。建议分别定义 etcd server、peer、consumer 签发角色,限制名称、SAN、用途和签发权限;peer 需要 serverAuth/clientAuth,消费者凭据按服务独立。中央 CA 签名本身不等于获准加入 peer 网络,必须限制 peer 身份及网络入口,必要时使用中央 CA 下的专用中间 CA。证书私钥在目标机生成并以 CSR 签发;IaC 管角色和续签流程,秘密不进入 Git/state。
- 已签发证书与信任链保存在本地,etcd 正常启动不要求即时连接 Bao;提前续签、校验证书并按成员轮换,告警覆盖过期风险。Bao 不迁入此 etcd,也不依赖 PostgreSQL;灾难恢复沿用先恢复信任根的边界。Bao 暂时不可用时,现存有效证书仍可完成 TLS 验证。
- Patroni etcd3 使用 gRPC gateway,不能依赖客户端证书 CN 映射来完成 RBAC 身份认证;隔离实测还要求 gateway 客户端证书省略 CN;配置独立 username/password,并保留 TLS 校验。凭据由现有秘密管理提供。Pigsty tiny 模板已包含 username/password,但默认密码可退化为集群名,必须替换。参考 Patroni etcd 配置。
- 维护者确定 Patroni etcd 密码随机生成并存入 Bao。实施时首次创建使用 KV v2 CAS 防止覆盖已有值,之后收敛复用同一 secret;读取失败不能当成不存在而重置密码。显式轮换需同步 etcd 账号与 Patroni 配置。Ansible 在执行时读取、以 no_log 和受限文件权限下发,不新增 Ansible Vault 副本。共享 etcd 实施阶段已生成并写入该线上 secret,gateway 登录验证通过;Patroni 尚未部署。
- 仓库配置检查显示 Ansible Vault 尚未整体退出:OpenBao、Samba AD、Proxmox 的 ansible.cfg 仍引用根目录
.vault_pass;OpenBao/Samba AD 仍有 vault.yml 路径,OpenBao README 仍将 OIDC client secret 归入该文件。未解密这些文件,也未查询 Bao 中是否存在对应副本。既有秘密迁移另行盘点;Bao 自身 bootstrap/恢复材料须保留独立恢复途径,不能只存在需要它们才能恢复的 Bao 中。 - 共享的是小规模配置、服务协调和选主能力。key 权限不隔离磁盘、写入负载、compaction 或集群故障;新增消费者须控制写入量、value 大小与历史保留,监控全局容量和延迟。暂不迁移 k3s 的内部 datastore。
- 快照、compaction、defrag 和成员维护统一由共享 etcd IaC 管理。卸载 PostgreSQL只清理其授权范围内的资源,不删除 etcd 成员或恢复整个集群。
- etcd 全集群恢复会回退所有消费者状态,不能拿整集群 snapshot 当单个应用的回滚。恢复 runbook 要协调 Patroni 的实际数据库角色、lease 与 DCS 状态,并为其他消费者保留恢复步骤。
这项决策进一步支持保留 Patroni:etcd 的运行与维护成本由多个使用者共享,无需为每个数据库集群再建一套 DCS。
PVE 改为 bare-metal 目前只是维护者表达的后续倾向,不构成本轮迁移决定。etcd/PG 角色面向普通 Linux systemd 主机,LXC 创建及宿主资源配置单独管理,以便未来更换承载方式时复用。
三个 etcd 成员不是三个额外的大 VM,可与上述角色共置。etcd 数据很小,但对磁盘延迟敏感;必须观察 PVE HDD 在备份/数据库负载下的 fsync 和选举稳定性,低数据量不自动代表低延迟。
开发默认不使用 Patroni/etcd,不做 standby,进一步减少常驻资源。若另建 laptop 容器,能保留更多上游单机单实例代码,但会引入容器生命周期、网络和挂载管理;因此首选直接实例化 systemd service,容器为已有成熟运行时可复用时的备选。
生产 PG、Patroni 和 exporter 数量分别为 2、2、2;开发再加一个 PG 和 exporter;etcd 共 3 个成员。pgBackRest 可通过 timer/SSH 工作,不必常驻对象存储服务器。这是角色数量,不是 RSS 估算。
自动切换的边界
建议先采用异步复制,使常态 SSD 写延迟不被 HDD 提交路径限制。故障切换可能丢失尚未复制的已提交事务。maximum_lag_on_failover 是候选资格阈值,不能宣传成精确 RPO;官方还计入最近 TTL 窗口的 WAL。见 Patroni replication modes。
自动化包含:故障检测、候选提升、入口切换、旧主恢复后 rewind/rejoin。不自动抢回 laptop 主角色,避免反复抖动;正常时优先让 laptop 承担 primary,故障后先稳定运行于 HDD。是否需要 laptop 恢复后经过稳定窗口自动回切,须另定义策略,不能用循环抢主代替。
必须区分“进程死亡”和“进程暂停/内核卡死”。上游 watchdog 默认 off,DCS 多数派本身不构成对卡死旧主的物理隔离。LXC 不应随意透传宿主 /dev/watchdog,否则可能重启整台 PVE。首期可评估软件降级与入口隔离的 best-effort 边界,但不能宣称所有故障下零双主;暂停 Patroni、网络单向中断和旧连接的行为必须进入演练。必要时再增加受控的宿主级 fencing,而不是先部署一套重型编排。见 Patroni watchdog。
上游 tiny 开启 DCS failsafe:现有 primary 在满足与所有已知 PG 成员通信等条件时可继续服务,但没有 DCS quorum 不能把它当作通用选主替代。见 DCS failsafe。
既有 VyOS/HAProxy 仍是端到端可用性的依赖;复用它不等于数据库入口已经消除单点。要验证现有 VyOS 故障恢复及角色感知检查,不能沿用只检查 TCP 可连接的逻辑。
备份首期设计
- pgBackRest 备份仓库放在 laptop 之外的 PVE HDD,使用普通文件系统仓库与 SSH/TLS 访问,不需要 MinIO。两台生产 PG 都能归档到同一 stanza/repository。
- 以每日 full、保留最近 3 个成功 full 及恢复所需 WAL 为初始本地策略;这不等于严格覆盖任意时刻之前 72 小时。若要求完整 72 小时窗口,应增加锚点备份及相应 WAL。异地策略另行决定。
- 周期任务跟随实际 primary 或由仓库端发现 primary;切换之后必须继续备份,不能把 inventory 中初始 primary 当作永久主库。
- 备份并行度先从 1 开始,设置任务超时、磁盘配额与成功时间告警;初始备份失败即判部署验收失败。
- 连续 WAL 归档也应明确
archive_timeout,低写入量下不能把“配置 archive_command”当作 WAL 已及时落到仓库。初始候选为 5 分钟,结合生成量与恢复目标实测调整。 - 上游固定
archive-push-queue-max=4GiB。超过限制时 pgBackRest 可丢弃归档并破坏该区间 PITR,不能照抄后声称持续可恢复;先明确可用性与保留 WAL 的取舍,并补积压、归档失败和磁盘告警。见 pgBackRest 配置。 - 备份文件存在不算验收:隔离恢复到指定时间点并执行一致性检查;避免恢复出来的开发实例向生产仓库归档。
exporter 与规则:可以裁得很小
对固定版本 files/victoria/rules/pgsql.yml 做 YAML 解析:共 402 条 recording rules、16 条 alerts。排除 4 条 PgBouncer alerts 和依赖整机综合压力的 PostgresPressureHigh,剩余 11 条 alerts 仅依赖 8 条 recording rules(按表达式中的 recording metric 递归追踪)。没有必要搬完整 402 条。
需要保留的 recording metrics:pg:cls:partition、pg:db:age、pg:db:conn_limit、pg:db:conn_usage、pg:db:ixact_backends、pg:db:num_backends、pg:ins:lag_bytes、pg:ins:lag_seconds。这只是依赖分析,不代表这些告警已通过运行验证。
仍需修正:
- 使用 Pigsty 的
pg_exporter指标体系,不能直接替换为另一款postgres_exporter而照搬规则。 - 保持或显式转换
cls/ins/ip/job;同 IP 多实例按实例端口和标签识别,禁止把宿主 CPU/内存用量重复算成每个数据库的用量。 pg:cls:partition = count(... == 0)在没有 primary 时可能返回空向量,后续!= 1不足以保证无主告警;增加预期集群基线/缺失检测。- exporter 完全不可抓取时不能指望 exporter 自己的
*_up指标告警;增加 scrapeup == 0与 target 消失检查。 PostgresReplicationBreak用 streaming 数量的变化检测,不能覆盖所有持续缺副本情况;补预期 replica 数与持续复制失败。- severity 从上游
CRIT/WARN/INFO映射为既有路由使用的critical/warning/info,去掉指向未部署 Pigsty UI 的链接。 - 从 collector 白名单开始,避免默认启用所有逐库/逐查询采集。一些 collector 依赖
monitorschema/辅助函数;删 bootstrap SQL 时要一起裁 collector。监控辅助 SECURITY DEFINER 与 Ayatori 管理 API 是不同权限问题,不为兼容 exporter 保留整套 DBA 权限。 - 补本地备份年龄、WAL 归档失败/积压、磁盘余量、etcd quorum/延迟,以及数据库端到端读写探测。
Ayatori 与 IaC 的边界
IaC 仅建立实例运维必需的账号、复制账号、最小监控权限和 Ayatori 管理账号;业务数据库及 owner 不再同时放入 Pigsty pg_users/pg_databases。
Ayatori 使用既定的非 superuser CREATEDB/CREATEROLE 管理账号,按现有契约验证 PostgreSQL 18 的角色成员关系与所有权操作。应用凭据走 OpenBao/ESO,不进入 inventory 明文、日志或 CR。凭据发放、轮换和删除能力是否已可用要以 Ayatori 当前实现验收,不能把 Instance 注册成功当作 provisioning 已完成。
Backstage 不属于本裁剪仓库。PG/etcd/systemd 的创建与恢复也不塞进 Ayatori。
推荐实现切分与验收
- 建立最小 inventory 模型,区分 host 与 instance;声明 prod/dev 的 uid、dataset、端口、socket、配置路径、systemd slice 和预算。所有控制文件、锁文件、日志及 pgBackRest spool 都实例化。
- 独立实现 shared etcd 的部署与消费者账号管理,再提取数据库 package、instance、Patroni、backup、exporter 角色;移除 upstream NODE 全局操作。初始化有前置条件,空目录才能 init,变更部署与恢复入口分开。
- 先在隔离环境验证同宿主 prod/dev:重复执行不重新初始化、不重启无关实例、不改变全局 PG unit、DNS 或调优;资源统计使用 cgroup/RSS/PSS,不能把 PostgreSQL 各进程共享内存重复求和。
- 加入两数据节点与三投票成员,验证 primary 故障、任一 etcd 故障、网络分区、进程暂停、旧主回归、入口旧连接回收和客户端重试;记录实际 RTO 与丢失事务。
- 触发主库切换后再次备份,并从备份恢复;模拟仓库不可达、任务失败、WAL 积压,验证报警及恢复链边界。
- 连接现有监控与 Ayatori,验证 create/rotate/retain/delete 契约,再安排 CNPG 分应用迁移。Gitea 迁移同时协调应用停写与其非数据库存储,但不将其文件迁移混入本次 DB 基础设施研究。
pg_auto_failover 保留为候选,其官方主分支安装文档声明支持 PG 13–18,但依赖带 PG 的 monitor;是否更省资源需要在同预算、同故障场景下测量。当前不建议为了组件名字少一点而舍弃已经能提取的 Patroni 配置、路由与恢复经验。见 官方安装文档。
本研究阶段未修改现有数据库;后续 shared etcd 已以独立 IaC 部署,详见下方实现状态。设计边界已同步到 homelab-wiki 的 architecture/constraints.md 与 services/shared-postgresql.md,文档直接发布到 main,IaC 通过独立 PR 发布;现有 CNPG 服务状态未发生变化。
实现入口
共享 etcd 的首轮实现与验证见 etcd README,包括独立 Bao PKI/policy Terraform、
LXC 与成员 Ansible、消费者秘密与 RBAC、快照和隔离三成员测试。三成员已部署,PVE 两个 LXC 使用 pve-rg SSD DRBD;数据库 IaC 与隔离切换/恢复测试已具备,生产主从、开发实例、稳定入口和本地备份已上线,真实切换与隔离恢复通过;现有 CNPG 应用迁移未开始。具体状态见 部署 README。
旧 Ansible Vault 迁移按维护者要求后置,不阻塞新增消费者直接使用 Bao。
2026-09-25 后续只读前置核查:laptop 为 Ubuntu 24.04.4,ZFS data 池当时约 526 GiB 可用,
5432/5433/8008 未监听;CNPG 声明镜像为 18.3-system-trixie。新实例继续按 PG 18 主版本准备。
此核查没有创建 PG dataset、安装数据库或迁移应用;容量与端口使用可能随后改变,执行前须再核对。