系统架构
1 - 实体关系图
先弄清楚“有哪些对象、它们如何互相引用”,再去讨论部署和运维。Pigsty 的 PGSQL 模块围绕几个核心实体构成一张稳定的 E-R 图,理解这张图能帮助你设计清晰的配置和自动化流程。
PGSQL模块在生产环境中以集群的形式组织,这些集群是由一组由主-备关联的数据库实例组成的逻辑实体。
每个集群都是一个自治的业务单元,由至少一个 主库实例 组成,并通过服务向外暴露能力。
在 Pigsty 的PGSQL模块中有四种核心实体:
- 集群(Cluster):自治的 PostgreSQL 业务单元,用作其他实体的顶级命名空间。
- 服务(Service):对外暴露能力的命名抽象,路由流量,并使用节点端口暴露服务。
- 实例(Instance):由在单个节点上的运行进程和数据库文件组成的单一 PostgreSQL 服务器。
- 节点(Node):运行 Linux + Systemd 环境的硬件资源抽象,可以是裸机、VM、容器或 Pod。
辅以“数据库”“角色”两个业务实体,共同组成完整的逻辑视图。如下图所示:

命名约定(沿用 Pigsty 早期约束)
- 集群名应为有效的 DNS 域名,不包含任何点号,正则表达式为:
[a-zA-Z0-9-]+ - 服务名应以集群名为前缀,并以特定单词作为后缀:
primary、replica、offline、delayed,中间用-连接。 - 实例名以集群名为前缀,以正整数实例号为后缀,用
-连接,例如${cluster}-${seq}。 - 节点由其首要内网IP地址标识,因为PGSQL模块中数据库与主机1:1部署,所以主机名通常与实例名相同。
2 - 身份标识符
Pigsty 使用 PG_ID 参数组为 PGSQL 模块的每个实体赋予确定的身份。
核心身份参数
三项必填参数构成 PGSQL 的最小 ID 集:
| 参数 | 层级 | 作用 | 约束 |
|---|---|---|---|
pg_cluster | 集群 | 业务命名空间 | [a-z][a-z0-9-]* |
pg_seq | 实例 | 集群内实例序号 | 递增分配的自然数,唯一且不可复用 |
pg_role | 实例 | 复制角色 | primary / replica / offline / delayed |
pg_cluster决定所有衍生名称:实例、服务、监控标签。pg_seq与节点 1:1 绑定,表达拓扑顺序和预期优先级。pg_role驱动 Patroni/HAProxy 的行为:primary唯一,replica承载在线只读,offline只接离线服务,delayed用于延迟集群。
Pigsty 不会为上述参数提供默认值,必须在 inventory 中显式写明。
实体身份标识
Pigsty 的 PostgreSQL 实体标识符将根据上面的核心身份参数自动生成
| 实体 | 生成规则 | 示例 |
|---|---|---|
| 实例 | {{ pg_cluster }}-{{ pg_seq }} | pg-test-1 |
| 服务 | {{ pg_cluster }}-{{ pg_role }} | pg-test-primary |
| 节点名 | 默认等同实例名,但可以显式覆盖 | pg-test-1 |
服务后缀采用内置约定:primary、replica、default、offline、delayed 等。HAProxy/pgbouncer 会读取这些标识自动构建路由。命名保持前缀一致,可直接通过 pg-test-* 查询或筛选。
监控标签体系
在 PGSQL 模块中,所有监控指标使用以下标签体系:
cls:集群名:{{ pg_cluster }}。ins:实例名:{{ pg_cluster }}-{{ pg_seq }}。ip:实例所在节点 IP。
对于 VictoriaMetrics 而言,采集 PostgreSQL 指标的 job 名固定为 pgsql;
用于监控远程 PG 实例的 job 名固定为 pgrds。
对于 VictoriaLogs 而言,采集 PostgreSQL CSV 日志的 job 名固定为 postgres;
采集 pgbackrest 日志的 job 名固定为 pgbackrest,其余组件通过 syslog 采集日志。
示例:pg-test 身份视图
pg-test:
hosts:
10.10.10.11: { pg_seq: 1, pg_role: primary }
10.10.10.12: { pg_seq: 2, pg_role: replica }
10.10.10.13: { pg_seq: 3, pg_role: replica, pg_offline_query: true }
vars:
pg_cluster: pg-test
| 集群 | 序号 | 角色 | 节点/IP | 实例 | 服务入口 |
|---|---|---|---|---|---|
| pg-test | 1 | primary | 10.10.10.11 | pg-test-1 | pg-test-primary |
| pg-test | 2 | replica | 10.10.10.12 | pg-test-2 | pg-test-replica |
| pg-test | 3 | replica+offline | 10.10.10.13 | pg-test-3 | pg-test-replica / pg-test-offline |
Prometheus 标签示例:
pg_up{cls="pg-test", ins="pg-test-1", ip="10.10.10.11", job="pgsql"}
pg_up{cls="pg-test", ins="pg-test-2", ip="10.10.10.12", job="pgsql"}
pg_up{cls="pg-test", ins="pg-test-3", ip="10.10.10.13", job="pgsql"}
3 - 组件与交互
总览
以下是 PostgreSQL 模块组件及其相互作用的详细描述,从上至下分别为:
- 集群 DNS 由 infra 节点上的 DNSMASQ 负责解析
- 集群 VIP 由
vip-manager组件管理,它负责将pg_vip_address绑定到集群主库节点上。vip-manager从etcd集群获取由patroni写入的集群领导者信息
- 集群服务由节点上的 Haproxy 对外暴露,不同服务通过节点的不同端口(543x)区分。
- Pgbouncer 是一个连接池中间件,默认监听6432端口,可以缓冲连接、暴露额外的指标,并提供额外的灵活性。
- Pgbouncer 是无状态的,并通过本地 Unix 套接字以 1:1 的方式与 Postgres 服务器部署。
- 生产流量(主/从)将默认通过 pgbouncer(可以通过
pg_default_service_dest指定跳过) - 默认/离线服务将始终绕过 pgbouncer ,并直接连接到目标 Postgres。
- PostgreSQL 监听5432端口,提供关系型数据库服务
- 在多个节点上安装 PGSQL 模块,并使用同一集群名,将自动基于流式复制组成高可用集群
- PostgreSQL 进程默认由
patroni管理。
- Patroni 默认监听端口 8008,监管着 PostgreSQL 服务器进程
- Patroni 将 Postgres 服务器作为子进程启动
- Patroni 使用
etcd作为 DCS:存储配置、故障检测和领导者选举。 - Patroni 通过健康检查提供 Postgres 信息(比如主/从),HAProxy 通过健康检查使用该信息分发服务流量
- Patroni 指标将被 infra 节点上的 Prometheus 抓取
- PG Exporter 在 9630 端口对外暴露 postgres 架空指标
- PostgreSQL 指标将被 infra 节点上的 Prometheus 抓取
- Pgbouncer Exporter 在端口 9631 暴露 pgbouncer 指标
- Pgbouncer 指标将被 infra 节点上的 Prometheus 抓取
- pgBackRest 默认在使用本地备份仓库 (
pgbackrest_method=local)- 如果使用
local(默认)作为备份仓库,pgBackRest 将在主库节点的pg_fs_bkup下创建本地仓库 - 如果使用
minio作为备份仓库,pgBackRest 将在专用的 MinIO 集群上创建备份仓库:pgbackrest_repo.minio
- 如果使用
- Postgres 相关日志(postgres, pgbouncer, patroni, pgbackrest)由 promtail 负责收集
- Promtail 监听 9080 端口,也对 infra 节点上的 Prometheus 暴露自身的监控指标
- Promtail 将日志发送至 infra 节点上的 Loki
集群 DNS
集群 DNS 服务由 infra 节点的 DNSMASQ 维护,为每个 pg_cluster 提供稳定的 FQDN(<cluster>.<pg_dns_suffix>)。DNS 记录会指向主库或 VIP,供业务侧、自动化流程与跨集群的数据服务访问,不需要直接关心节点的实时 IP。DNS 依赖部署期写入的库存信息,运行期只在 VIP 或主节点漂移时更新,其上游是 vip-manager 与 etcd 中的主节点状态。
DNS 的下游是客户端与第三方服务入口,它也为 HAProxy 等中间层提供统一的目标地址。组件可选;当集群运行在独立网络或业务端直接使用 IP 时可以跳过,但绝大多数生产环境建议启用,以避免硬编码节点地址。
关键参数
pg_dns_suffix:定义集群 DNS 记录的统一后缀。pg_dns_target:控制解析目标指向 VIP、主库或显式 IP。
主库虚拟 IP(vip-manager)
vip-manager 在每个 PG 节点上运行,通过监视 etcd 中由 Patroni 写入的领导者键,把 pg_vip_address 绑定到当前主节点上,实现透明的 L2 漂移。它依赖 DCS 的健康状态,并要求目标网络接口可被当前节点控制,从而在故障转移时立即释放并重绑 VIP,保障旧主机不会继续响应。
VIP 的下游包含 DNS、自建客户端、遗留系统等需要固定端点的访问者。该组件可选:只在 pg_vip_enabled 为 true 且业务需要静态地址时启用。启用后必须保证所有参与节点都具备相同的 VLAN 接入,否则 VIP 无法正确漂移。
关键参数
pg_vip_enabled:控制是否启用 L2 VIP。pg_vip_interface:指定监听和漂移 VIP 的网络接口。pg_vip_address:定义 VIP IPv4/掩码。pg_namespace:etcd 中的命名空间,Patroni 与 vip-manager 共享。
服务入口与流量调度(HAProxy)
HAProxy 安装在 PG 节点(或专用服务节点),统一对外暴露数据库服务端口组:5433/5434(读写/只读,通过 Pgbouncer),5436/5438(直连主库/离线库),以及 9101 管理接口。每个后端池依靠 patroni REST API 提供的角色与健康信息做路由判断,并把流量转发到对应实例或连接池。
该组件是整个集群的入口,下游直接面向应用、ETL 与管理工具。可将 pg_service_provider 指向专用 HA 节点以承载更高流量,也可在实例本地进行发布。HAProxy 对 VIP 无依赖,但通常与 DNS 和 VIP 联动,打造统一访问口。服务定义由 pg_default_services 与 pg_services 组合而成,可精细化配置端口、负载均衡策略与目标。
关键参数
pg_default_services:定义全局默认服务端口、目标与检查方式。pg_services:为特定集群追加或覆盖业务服务。pg_service_provider:指定发布服务的 HAProxy 节点组(留空代表本地)。pg_default_service_dest:决定默认服务是转发到 Pgbouncer 还是 Postgres。pg_weight:配置单实例在特定服务中的权重。
连接池(Pgbouncer)
Pgbouncer 在每个实例上以无状态方式运行,优先通过本地 Unix Socket 连接 PostgreSQL,用于吸收瞬时连接、稳定会话与提供额外指标。Pigsty 默认让生产流量(5433/5434)经由 Pgbouncer,只有默认/离线服务绕过它直连 PostgreSQL。Pgbouncer 不依赖 VIP,可与 HAProxy、Patroni 独立伸缩,在 Pgbouncer 停止时 PostgreSQL 仍可提供直连服务。
Pgbouncer 的下游是大量短连接客户端,以及统一入口 HAProxy。它允许基于 auth_query 的动态用户加载,并可按需配置 SSL。组件可选,通过 pgbouncer_enabled 关闭时,默认服务将直接指向 PostgreSQL,需要相应调整连接数与会话管理策略。
关键参数
pgbouncer_enabled:决定是否启用本地连接池。pgbouncer_port:监听端口(默认 6432)。pgbouncer_poolmode:连接池模式,控制事务或会话级复用。pgbouncer_auth_query:是否从 PostgreSQL 动态拉取凭据。pgbouncer_sslmode:客户端到连接池的 SSL 策略。pg_default_service_dest:影响默认服务是否经由 Pgbouncer。
数据库实例(PostgreSQL)
PostgreSQL 进程是整个模块的核心,默认监听 5432 并由 Patroni 托管。以同一 pg_cluster 在多节点安装 PGSQL 模块,将自动构建基于物理流复制的主从拓扑;primary/replica/offline 角色由 pg_role 控制,必要时可通过 pg_instances 在同一节点运行多实例。实例依赖本地数据盘、操作系统内核调优与 NODE 模块提供的系统服务。
该组件的下游是业务读写流量、pgBackRest、pg_exporter 等服务;上游是 Patroni、Ansible 引导脚本,以及 etcd 中的元数据。可根据 pg_conf 模板切换 OLTP/OLAP 配置,并通过 pg_upstream 定义级联复制。若使用 citus/gpsql,需进一步设置 pg_shard 与 pg_group。pg_hba_rules 与 pg_default_hba_rules 决定访问控制策略。
关键参数
pg_mode:实例运行模式(标准 PG、Citus、MSSQL 兼容等)。pg_seq:实例序号,用于锁定复制拓扑与服务权重。pg_role:定义实例角色(primary/replica/offline)。pg_instances:在单节点部署多实例的映射。pg_upstream:级联备库的复制源。pg_conf:加载的配置模板,决定资源与连接上限。pg_hba_rules/pg_default_hba_rules:访问控制列表。
高可用控制器(Patroni + etcd)
Patroni 监听 8008,接管 PostgreSQL 的启动、配置与健康状态,将领导者、成员信息写入 etcd(命名空间由 pg_namespace 定义)。它负责自动故障转移、保持复制因子、协调参数,以及提供 REST API 供 HAProxy、监控与管理员查询。Patroni 可启用看门狗强制隔离旧主机,以避免脑裂。
Patroni 的上游是 etcd 集群与系统服务(systemd、Keepalive),下游包括 vip-manager、HAProxy、Pgbackrest 与监控组件。可以通过 patroni_mode 切换为 pause/remove 模式,以便维护或删除集群。禁用 Patroni 仅在管理外部 PG 实例时使用。
关键参数
patroni_enabled:决定是否由 Patroni 管理 PostgreSQL。patroni_mode:设置运行模式(default/pause/remove)。patroni_port:REST API 端口。patroni_ssl_enabled:是否为 REST API 启用 SSL。patroni_watchdog_mode:看门狗策略。patroni_username/patroni_password:访问 REST API 的凭据。
备份子系统(pgBackRest)
pgBackRest 在主库上创建本地或远程仓库,用于全量/增量备份与 WAL 归档。它与 PostgreSQL 配合执行控制命令,支持本地磁盘(默认)、MinIO 等多种目标,能够覆盖 PITR、备份链条验证与远程拉起。上游是主库的数据与归档流,下游是对象存储或本地备份盘,以及 pgbackrest_exporter 提供的可观测性。
组件可择时运行,通常在初始化完成后立即发起一次全量备份;也支持关闭(实验环境或外部备份系统)。启用 minio 仓库时需要可达的对象存储服务与凭据。恢复过程与 Patroni 集成,可通过 pgbackrest 命令将副本引导为新的主库或备库。
关键参数
pgbackrest_enabled:控制是否安装与激活备份子系统。pgbackrest_method:仓库类型(local/minio/自定义)。pgbackrest_repo:仓库定义与访问凭据。pgbackrest_init_backup:初始化后是否自动执行全量备份。pgbackrest_clean:初始化时是否清理旧备份目录。pgbackrest_log_dir:日志输出路径。pg_fs_bkup:本地备份盘的挂载点。
PostgreSQL 指标(pg_exporter)
pg_exporter 运行在 PG 节点上,使用本地 socket 登录,导出覆盖会话、缓冲命中、复制延迟、事务率等指标供 infra 节点上的 Prometheus 抓取。它与 PostgreSQL 紧耦合,重启 PostgreSQL 时会自动重连,对外监听 9630(默认)。Exporter 不依赖 VIP,与 HA 拓扑解耦。
关键参数
pg_exporter_enabled:启用或关闭 exporter。pg_exporter_port:HTTP 监听端口。pg_exporter_config:采集配置模板。pg_exporter_cache_ttls:各采集器的缓存 TTL。
连接池指标(pgbouncer_exporter)
pgbouncer_exporter 启动在节点上,读取 Pgbouncer 的统计视图,提供连接池利用率、等待队列与命中率指标。它依赖 Pgbouncer 的 admin 用户,并通过独立端口暴露给 Prometheus。若禁用 Pgbouncer,本组件应同时关闭。
关键参数
pgbouncer_exporter_enabled:控制是否启用导出器。pgbouncer_exporter_port:监听端口(默认 9631)。pgbouncer_exporter_url:覆盖自动生成的 DSN。pgbouncer_exporter_options:可追加命令行参数。
备份指标(pgbackrest_exporter)
pgbackrest_exporter 解析该节点的 pgBackRest 状态,生成最近备份时间、大小、类型等指标。Prometheus 通过 9854(默认)采集这些指标,结合告警策略即可快速发现备份过期或失败。组件依赖 pgBackRest 元数据目录,关闭备份系统时也应禁用它。
关键参数
pgbackrest_exporter_enabled:是否收集备份指标。pgbackrest_exporter_port:HTTP 监听端口。pgbackrest_exporter_options:额外命令行参数。
日志采集(Promtail)
Promtail 常驻在节点上,跟踪 PostgreSQL、Pgbouncer、Patroni 与 pgBackRest 的日志目录,通过 Loki Pipeline 统一存档,并在 9080 提供自监控指标。它基于 NODE 模块提供的 systemd 服务,与 PG 模块解耦,但对排障与合规审计至关重要。Promtail 的上游是本地日志文件,下游是 infra 节点的 Loki/Prometheus 组合。
关键参数(位于 NODE 模块的 PROMTAIL 组件)
promtail_enabled:是否启用日志采集。promtail_port:HTTP/抓取端口。promtail_positions:游标状态文件路径。promtail_clean:部署时是否清理旧游标。
4 - 高可用集群
Pigsty 的 PostgreSQL 集群带有开箱即用的高可用方案,由 Patroni、Etcd 和 HAProxy 强力驱动。
当您的 PostgreSQL 集群含有两个或更多实例时,您无需任何配置即拥有了硬件故障自愈的数据库高可用能力 —— 只要集群中有任意实例存活,集群就可以对外提供完整的服务,而客户端只要连接至集群中的任意节点,即可获得完整的服务,而无需关心主从拓扑变化。
在默认配置下,主库故障恢复时间目标 RTO ≈ 30s,数据恢复点目标 RPO < 1MB;从库故障 RPO = 0,RTO ≈ 0 (闪断);在一致性优先模式下,可确保故障切换数据零损失:RPO = 0。以上指标均可通过参数,根据您的实际硬件条件与可靠性要求 按需配置。
Pigsty 内置了 HAProxy 负载均衡器用于自动流量切换,提供 DNS/VIP/LVS 等多种接入方式供客户端选用。故障切换与主动切换对业务侧除零星闪断外几乎无感知,应用不需要修改连接串重启。 极小的维护窗口需求带来了极大的灵活便利:您完全可以在无需应用配合的情况下滚动维护升级整个集群。硬件故障可以等到第二天再抽空善后处置的特性,让研发,运维与 DBA 都能在故障时安心睡个好觉。
许多大型组织与核心机构已经在生产环境中长时间使用 Pigsty ,最大的部署有 25K CPU 核心与 220+ PostgreSQL 超大规格实例(64c / 512g / 3TB NVMe SSD);在这一部署案例中,五年内经历了数十次硬件故障与各类事故,但依然可以保持高于 99.999% 的总体可用性战绩。
架构概览
Pigsty 高可用架构由四个核心组件构成,它们协同工作,实现故障自动检测、领导者选举与流量切换:
flowchart TB
subgraph Client["🖥️ 客户端接入层"]
C[("Client")]
ACCESS["DNS / VIP / HAProxy / L4 LVS"]
end
subgraph Node1["📦 Node 1"]
HAP1["HAProxy :9101<br/>Primary :5433 | Replica :5434"]
subgraph Stack1["Patroni :8008"]
PG1[("PostgreSQL<br/>[Primary] :5432")]
PGB1["PgBouncer :6432"]
end
end
subgraph Node2["📦 Node 2"]
HAP2["HAProxy :9101<br/>Primary :5433 | Replica :5434"]
subgraph Stack2["Patroni :8008"]
PG2[("PostgreSQL<br/>[Replica] :5432")]
PGB2["PgBouncer :6432"]
end
end
subgraph Node3["📦 Node 3"]
HAP3["HAProxy :9101<br/>Primary :5433 | Replica :5434"]
subgraph Stack3["Patroni :8008"]
PG3[("PostgreSQL<br/>[Replica] :5432")]
PGB3["PgBouncer :6432"]
end
end
subgraph ETCD["🔐 Etcd Cluster (Raft Consensus)"]
E1[("Etcd-1<br/>:2379")]
E2[("Etcd-2<br/>:2379")]
E3[("Etcd-3<br/>:2379")]
end
C --> ACCESS
ACCESS --> HAP1 & HAP2 & HAP3
HAP1 -.->|"HTTP Health Check"| Stack1
HAP2 -.->|"HTTP Health Check"| Stack2
HAP3 -.->|"HTTP Health Check"| Stack3
HAP1 --> PGB1
HAP2 --> PGB2
HAP3 --> PGB3
PG1 ==>|"Streaming Replication"| PG2
PG1 ==>|"Streaming Replication"| PG3
Stack1 <-->|"Leader Lease"| ETCD
Stack2 <-->|"Leader Lease"| ETCD
Stack3 <-->|"Leader Lease"| ETCD
E1 <--> E2 <--> E3
E1 <--> E3
style PG1 fill:#4CAF50,color:#fff
style PG2 fill:#2196F3,color:#fff
style PG3 fill:#2196F3,color:#fff
style ETCD fill:#FF9800,color:#fff组件详解
PostgreSQL
PostgreSQL 是核心数据库服务,使用标准流复制(Streaming Replication)搭建物理从库:
- 主库(Primary):接受读写请求,生成 WAL 日志
- 从库(Replica):通过流复制实时接收 WAL,提供只读查询
- 复制槽(Replication Slot):确保 WAL 不被过早清理
- 同步提交:可选的同步复制模式,确保 RPO = 0
关键配置(由 Patroni 动态管理):
wal_level: logical # 启用逻辑复制级别
max_wal_senders: 50 # 最大 WAL 发送进程数
max_replication_slots: 50 # 最大复制槽数量
hot_standby: on # 从库可读
wal_log_hints: on # 支持 pg_rewind
track_commit_timestamp: on # 追踪事务时间戳
synchronous_standby_names: '' # 同步从库列表(动态管理)
Patroni
Patroni 是高可用的核心引擎,负责管理 PostgreSQL 生命周期与集群状态:
核心职责:
- 管理 PostgreSQL 进程的启停与配置
- 维护领导者租约(Leader Lease)
- 执行自动故障切换(Failover)与主动切换(Switchover)
- 提供 REST API 用于健康检查与集群管理
- 处理从库的自动重建与
pg_rewind
关键时序参数(控制 RTO):
| 参数 | 默认值 | 说明 |
|---|---|---|
ttl | 30s | 领导者租约有效期,即故障检测时间窗口 |
loop_wait | 10s | Patroni 主循环间隔 |
retry_timeout | 10s | DCS 与 PostgreSQL 操作重试超时 |
primary_start_timeout | 10s | 主库启动超时时间 |
primary_stop_timeout | 30s | 主库优雅停止超时(同步模式下生效) |
这些参数由 pg_rto 统一计算派生,默认 30s 的 RTO 对应:
ttl: 30 # 领导者租约 TTL
loop_wait: 10 # 主循环间隔 = RTO/3
retry_timeout: 10 # 重试超时 = RTO/3
primary_start_timeout: 10 # 主库启动超时 = RTO/3
约束条件:ttl >= loop_wait + retry_timeout * 2
健康检查端点(供 HAProxy 使用):
| 端点 | 用途 | 返回 200 条件 |
|---|---|---|
/primary | 主库服务 | 当前节点是 Leader |
/replica | 从库服务 | 当前节点是 Replica |
/read-only | 只读服务 | 节点可读(主库或从库) |
/health | 健康检查 | PostgreSQL 运行正常 |
/leader | 领导者检查 | 持有领导者锁 |
/async | 异步从库 | 异步复制从库 |
/sync | 同步从库 | 同步复制从库 |
Etcd
Etcd 作为分布式配置存储(DCS),提供集群共识能力:
核心职责:
- 存储集群配置与状态信息
- 提供领导者选举的原子操作
- 通过租约机制实现故障检测
- 存储 PostgreSQL 动态配置
存储结构(以 /pg 命名空间为例):
/pg/
├── <cluster_name>/
│ ├── leader # 当前领导者标识
│ ├── config # 集群配置(DCS 配置)
│ ├── history # 故障切换历史
│ ├── initialize # 集群初始化标记
│ ├── members/ # 成员信息目录
│ │ ├── pg-test-1 # 实例 1 元数据
│ │ ├── pg-test-2 # 实例 2 元数据
│ │ └── pg-test-3 # 实例 3 元数据
│ └── sync # 同步从库状态
关键配置:
election_timeout: 1000ms # 选举超时(影响 Etcd 自身 HA)
heartbeat_interval: 100ms # 心跳间隔
quota_backend_bytes: 16GB # 存储配额
auto_compaction_mode: periodic # 自动压缩
auto_compaction_retention: 24h # 保留 24 小时历史
Etcd 集群要求:
- 必须奇数节点:3、5、7 个节点,确保多数派仲裁
- 推荐独立部署于管理节点,与 PostgreSQL 节点分离
- 网络延迟应保持在 10ms 以内
HAProxy
HAProxy 负责服务发现与流量分发:
核心职责:
- 通过 HTTP 健康检查发现主从角色
- 将流量路由到正确的后端节点
- 提供负载均衡与连接池功能
- 实现服务的自动故障转移
默认服务定义:
| 服务名 | 端口 | 目标 | 健康检查 | 用途 |
|---|---|---|---|---|
| primary | 5433 | pgbouncer | /primary | 读写服务,路由到主库 |
| replica | 5434 | pgbouncer | /read-only | 只读服务,优先路由到从库 |
| default | 5436 | postgres | /primary | 直连主库(绕过连接池) |
| offline | 5438 | postgres | /replica | 离线从库(ETL/备份) |
健康检查配置:
listen pg-test-primary
bind *:5433
mode tcp
option httpchk
http-check send meth OPTIONS uri /primary
http-check expect status 200
default-server inter 3s fastinter 1s downinter 5s rise 3 fall 3
on-marked-down shutdown-sessions slowstart 30s
maxconn 3000 maxqueue 128 weight 100
server pg-test-1 10.10.10.11:6432 check port 8008
server pg-test-2 10.10.10.12:6432 check port 8008 backup
server pg-test-3 10.10.10.13:6432 check port 8008 backup
健康检查时序参数(影响 RTO 敏感度):
| 参数 | 默认值 | 说明 |
|---|---|---|
inter | 3s | 正常检查间隔 |
fastinter | 1s | 状态变化后的快速检查间隔 |
downinter | 5s | 节点宕机后的检查间隔 |
rise | 3 | 节点恢复需要连续成功次数 |
fall | 3 | 节点宕机需要连续失败次数 |
流量切换时序(主库故障):
- 故障检测:
fall × inter= 3 × 3s = 9s - 快速探测:一旦发现异常,切换到
fastinter(1s) - 服务恢复:新主库提升后,
rise × fastinter= 3 × 1s = 3s
VIP Manager(可选)
vip-manager 提供可选的二层 VIP 支持:
工作原理:
- 监听 Etcd 中的领导者键(
/pg/<cluster>/leader) - 当本节点成为领导者时,绑定 VIP 到指定网卡
- 发送免费 ARP 通告网络中的设备更新 MAC 映射
- 当失去领导者地位时,解绑 VIP
配置示例:
interval: 1000 # 检查间隔(毫秒)
trigger-key: "/pg/pg-test/leader" # 监听的 Etcd 键
trigger-value: "pg-test-1" # 匹配的领导者值
ip: 10.10.10.100 # VIP 地址
netmask: 24 # 子网掩码
interface: eth0 # 绑定网卡
dcs-type: etcd # DCS 类型
retry-num: 2 # 重试次数
retry-after: 250 # 重试间隔(毫秒)
使用限制:
- 要求所有节点在同一二层网络
- 云环境通常不支持,需使用云厂商 VIP 或 DNS 方案
- 切换时间约 1-2 秒
控制流与数据流
正常运行状态
控制流:Patroni 与 Etcd 之间的心跳与租约管理
flowchart LR
subgraph Control["⚙️ 控制流 (Control Flow)"]
direction LR
P1["Patroni<br/>(Primary)"]
P2["Patroni<br/>(Replica)"]
ETCD[("Etcd<br/>Cluster")]
P1 -->|"续租/心跳"| ETCD
P2 -->|"续租/心跳"| ETCD
ETCD -->|"租约/配置"| P1
ETCD -->|"租约/配置"| P2
end
style ETCD fill:#FF9800,color:#fff数据流:客户端请求与 WAL 复制
flowchart LR
subgraph Data["📊 数据流 (Data Flow)"]
direction LR
CLIENT["Client"]
HAP["HAProxy"]
PGB["PgBouncer"]
PG_P[("PostgreSQL<br/>[Primary]")]
PG_R[("PostgreSQL<br/>[Replica]")]
PATRONI["Patroni :8008"]
CLIENT -->|"SQL Request"| HAP
HAP -->|"路由"| PGB
PGB --> PG_P
HAP -.->|"健康检查<br/>/primary /replica"| PATRONI
PG_P ==>|"WAL Stream"| PG_R
end
style PG_P fill:#4CAF50,color:#fff
style PG_R fill:#2196F3,color:#fff故障切换流程
当主库发生故障时,系统经历以下阶段:
sequenceDiagram
autonumber
participant Primary as 🟢 Primary
participant Patroni_P as Patroni (Primary)
participant Etcd as 🟠 Etcd Cluster
participant Patroni_R as Patroni (Replica)
participant Replica as 🔵 Replica
participant HAProxy as HAProxy
Note over Primary: T=0s 主库故障发生
rect rgb(255, 235, 235)
Note right of Primary: 故障检测阶段 (0-10s)
Primary-x Patroni_P: 进程崩溃
Patroni_P--x Etcd: 停止续租
HAProxy--x Patroni_P: 健康检查失败
Etcd->>Etcd: 租约倒计时开始
end
rect rgb(255, 248, 225)
Note right of Etcd: 选举阶段 (10-20s)
Etcd->>Etcd: 租约过期,释放领导者锁
Patroni_R->>Etcd: 检查资格 (LSN, 复制延迟)
Etcd->>Patroni_R: 授予领导者锁
end
rect rgb(232, 245, 233)
Note right of Replica: 提升阶段 (20-30s)
Patroni_R->>Replica: 执行 PROMOTE
Replica-->>Replica: 提升为新主库
Patroni_R->>Etcd: 更新状态
HAProxy->>Patroni_R: 健康检查 /primary
Patroni_R-->>HAProxy: 200 OK
end
Note over HAProxy: T≈30s 服务恢复
HAProxy->>Replica: 路由写入流量到新主库关键时序公式:
RTO ≈ TTL + Election_Time + Promote_Time + HAProxy_Detection
其中:
- TTL = pg_rto (默认 30s)
- Election_Time ≈ 1-2s
- Promote_Time ≈ 1-5s
- HAProxy_Detection = fall × inter + rise × fastinter ≈ 12s
实际 RTO 通常在 15-40s 之间,取决于:
- 网络延迟
- 从库 WAL 回放进度
- PostgreSQL 恢复速度
高可用部署模式
三节点标准模式
最推荐的生产部署模式,提供完整的自动故障转移能力:
flowchart TB
subgraph Cluster["🏢 三节点高可用架构"]
direction TB
subgraph Node1["Node 1"]
E1[("Etcd")]
H1["HAProxy"]
P1["Patroni + PostgreSQL<br/>🟢 Primary"]
end
subgraph Node2["Node 2"]
E2[("Etcd")]
H2["HAProxy"]
P2["Patroni + PostgreSQL<br/>🔵 Replica"]
end
subgraph Node3["Node 3"]
E3[("Etcd")]
H3["HAProxy"]
P3["Patroni + PostgreSQL<br/>🔵 Replica"]
end
end
E1 <-->|"Raft"| E2
E2 <-->|"Raft"| E3
E1 <-->|"Raft"| E3
P1 ==>|"Replication"| P2
P1 ==>|"Replication"| P3
style P1 fill:#4CAF50,color:#fff
style P2 fill:#2196F3,color:#fff
style P3 fill:#2196F3,color:#fff
style E1 fill:#FF9800,color:#fff
style E2 fill:#FF9800,color:#fff
style E3 fill:#FF9800,color:#fff故障容忍:
- ✅ 任意 1 个节点故障:自动切换,服务继续
- ⚠️ 2 个节点故障:需要人工介入
配置示例:
pg-test:
hosts:
10.10.10.11: { pg_seq: 1, pg_role: primary }
10.10.10.12: { pg_seq: 2, pg_role: replica }
10.10.10.13: { pg_seq: 3, pg_role: replica }
vars:
pg_cluster: pg-test
五节点增强模式
更高可用性要求的部署,可容忍 2 个节点故障:
flowchart LR
subgraph Cluster["🏛️ 五节点高可用架构"]
direction TB
subgraph Row1[""]
direction LR
N1["Node 1<br/>Etcd + 🟢 Primary"]
N2["Node 2<br/>Etcd + 🔵 Replica"]
N3["Node 3<br/>Etcd + 🔵 Replica"]
N4["Node 4<br/>Etcd + 🔵 Replica"]
N5["Node 5<br/>Etcd + 🔵 Replica"]
end
end
N1 ==> N2 & N3 & N4 & N5
N1 <-.->|"Etcd Raft"| N2
N2 <-.->|"Etcd Raft"| N3
N3 <-.->|"Etcd Raft"| N4
N4 <-.->|"Etcd Raft"| N5
style N1 fill:#4CAF50,color:#fff
style N2 fill:#2196F3,color:#fff
style N3 fill:#2196F3,color:#fff
style N4 fill:#2196F3,color:#fff
style N5 fill:#2196F3,color:#fffEtcd 仲裁:3/5 多数派 | PostgreSQL:1 主 4 从
故障容忍:
- ✅ 任意 2 个节点故障:自动切换
- ⚠️ 3 个节点故障:需要人工介入
适用场景:
- 金融核心系统
- 跨机房部署(2+2+1 分布)
- 需要专用离线从库的场景
两节点半高可用模式
资源受限时的折中方案,提供有限的自动切换能力:
flowchart TB
subgraph Cluster["⚠️ 两节点半高可用架构"]
direction LR
subgraph Node1["Node 1 (Infra)"]
E1[("Etcd")]
H1["HAProxy"]
P1["Patroni + PostgreSQL<br/>🟢 Primary"]
end
subgraph Node2["Node 2"]
E2[("Etcd")]
H2["HAProxy"]
P2["Patroni + PostgreSQL<br/>🔵 Replica"]
end
subgraph Arbiter["❓ 需要仲裁者"]
E3[("Etcd<br/>(外部)")]
end
end
E1 <-->|"无法形成多数派"| E2
E1 <-.-> E3
E2 <-.-> E3
P1 ==>|"Replication"| P2
style P1 fill:#4CAF50,color:#fff
style P2 fill:#2196F3,color:#fff
style E1 fill:#FF9800,color:#fff
style E2 fill:#FF9800,color:#fff
style E3 fill:#9E9E9E,color:#fff,stroke-dasharray: 5 5问题:Etcd 只有 2 节点,无法形成多数派
解决方案:
- 在外部添加第 3 个 Etcd 节点(纯仲裁)
- 使用 failsafe_mode 防止脑裂
- 接受非对称故障切换
非对称故障切换:
- 从库故障:✅ 自动处理,主库继续服务
- 主库故障:⚠️ 需要人工介入(无法自动选举)
配置建议:
# 启用 failsafe 模式防止误切换
patroni_watchdog_mode: off # 禁用 watchdog
pg_rto: 60 # 增大 RTO 减少误报
同城双中心模式
同城容灾部署,机房级故障容忍:
flowchart TB
subgraph DualDC["🌐 同城双中心架构"]
direction TB
subgraph DCA["📍 数据中心 A"]
direction LR
N1["Node 1<br/>Etcd + 🟢 Primary"]
N2["Node 2<br/>Etcd + 🔵 Replica"]
end
subgraph DCB["📍 数据中心 B"]
direction LR
N3["Node 3<br/>Etcd + 🔵 Replica"]
N4["Node 4<br/>Etcd + 🔵 Replica"]
end
subgraph Arbiter["🏠 第三方机房"]
N5["Node 5<br/>Etcd (仲裁)"]
end
end
N1 ==>|"Replication"| N2 & N3 & N4
N1 & N2 <-->|"< 5ms"| N3 & N4
N1 & N2 & N3 & N4 <-.->|"Etcd Raft"| N5
style N1 fill:#4CAF50,color:#fff
style N2 fill:#2196F3,color:#fff
style N3 fill:#2196F3,color:#fff
style N4 fill:#2196F3,color:#fff
style N5 fill:#FF9800,color:#fff网络要求:
- 机房间延迟 < 5ms(同步复制)或 < 20ms(异步复制)
- 带宽充足,确保 WAL 传输
- 仲裁节点可以是轻量级 VM
故障场景:
| 故障 | 影响 | 恢复方式 |
|---|---|---|
| DC-A 单节点故障 | 无影响 | 自动 |
| DC-B 单节点故障 | 无影响 | 自动 |
| DC-A 整体故障 | 切换到 DC-B | 自动(需仲裁节点) |
| DC-B 整体故障 | 无影响 | 自动 |
| 仲裁节点故障 | 降级为 4 节点 | 可容忍 1 节点故障 |
异地多活模式
跨地域部署,需要考虑延迟与带宽:
flowchart LR
subgraph GeoDR["🌍 异地容灾架构"]
direction LR
subgraph Beijing["🏙️ 主数据中心 (北京)"]
direction TB
BJ_E[("Etcd<br/>3节点")]
BJ1["🟢 Primary"]
BJ2["🔵 Replica"]
end
subgraph Shanghai["🏙️ 灾备数据中心 (上海)"]
direction TB
SH_E[("Etcd<br/>独立集群")]
SH1["🔵 Replica"]
SH2["🔵 Replica"]
end
end
BJ1 ==>|"Async Replication<br/>延迟: 20-50ms"| SH1
BJ1 --> BJ2
SH1 --> SH2
style BJ1 fill:#4CAF50,color:#fff
style BJ2 fill:#2196F3,color:#fff
style SH1 fill:#9C27B0,color:#fff
style SH2 fill:#9C27B0,color:#fff
style BJ_E fill:#FF9800,color:#fff
style SH_E fill:#607D8B,color:#fff部署策略:
- 主中心:完整 HA 集群(3+ 节点)
- 灾备中心:级联从库(Standby Cluster)
- 异步复制:容忍网络延迟
- 独立 Etcd:避免跨地域仲裁
级联从库配置:
# 灾备集群配置
pg-standby:
hosts:
10.20.10.11: { pg_seq: 1, pg_role: primary } # 级联领导者
10.20.10.12: { pg_seq: 2, pg_role: replica }
vars:
pg_cluster: pg-standby
pg_upstream: 10.10.10.11 # 指向主集群
pg_delay: 1h # 可选:延迟复制
故障场景分析
单节点故障
主库进程崩溃
场景:PostgreSQL 主库进程被 kill -9 或发生崩溃
flowchart LR
subgraph Detection["🔍 故障检测"]
D1["Patroni 检测进程消失"]
D2["尝试重启 PostgreSQL"]
D3["重启失败,停止续租"]
D1 --> D2 --> D3
end
subgraph Failover["🔄 故障切换"]
F1["Etcd 租约过期 (~10s)"]
F2["触发选举,最新从库胜出"]
F3["新主库提升"]
F4["HAProxy 检测到新主库"]
F1 --> F2 --> F3 --> F4
end
subgraph Impact["📊 影响"]
I1["写服务中断: 15-30s"]
I2["读服务: 短暂闪断"]
I3["数据丢失: < 1MB 或 0"]
end
Detection --> Failover --> Impact
style D1 fill:#ffcdd2
style F3 fill:#c8e6c9
style I1 fill:#fff9c4Patroni 进程故障
场景:Patroni 进程被杀或崩溃
flowchart TB
FAULT["Patroni 进程故障"]
subgraph Detection["故障检测"]
D1["Patroni 停止续租"]
D2["PostgreSQL 继续运行<br/>(orphan 状态)"]
D3["Etcd 租约倒计时"]
end
subgraph FailsafeOn["failsafe_mode: true"]
FS1["检查是否能访问其他 Patroni"]
FS2["✅ 可以 → 继续作为主库"]
FS3["❌ 不能 → 自我降级"]
end
subgraph FailsafeOff["failsafe_mode: false"]
FF1["租约过期后触发切换"]
FF2["原主库降级"]
end
FAULT --> Detection
Detection --> FailsafeOn
Detection --> FailsafeOff
style FAULT fill:#f44336,color:#fff
style FS2 fill:#4CAF50,color:#fff
style FS3 fill:#ff9800,color:#fff从库故障
场景:任意从库节点故障
影响:
- 只读流量重新分配到其他从库
- 如果无其他从库,主库承担只读流量
- ✅ 写服务完全不受影响
恢复:
- 节点恢复后 Patroni 自动启动
- 自动从主库重新同步
- 恢复为从库角色
多节点故障
三节点坏两个(2/3 故障)
场景:3 节点集群,2 个节点同时故障
flowchart TB
subgraph Analysis["情况分析"]
A1["Etcd 失去多数派 (1/3 < 2/3)"]
A2["无法进行领导者选举"]
A3["自动切换机制失效"]
end
subgraph Survivor["存活节点情况"]
S1{"存活节点是?"}
S2["🟢 主库<br/>failsafe_mode 下继续运行"]
S3["🔵 从库<br/>无法自动提升"]
end
A1 --> A2 --> A3 --> S1
S1 -->|"Primary"| S2
S1 -->|"Replica"| S3
style A1 fill:#ffcdd2
style S2 fill:#c8e6c9
style S3 fill:#fff9c4紧急恢复流程:
# 1. 确认存活节点状态
patronictl -c /etc/patroni/patroni.yml list
# 2. 如果存活节点是从库,手动提升
pg_ctl promote -D /pg/data
# 3. 或者使用 pg-promote 脚本
/pg/bin/pg-promote
# 4. 修改 HAProxy 配置,直接指向存活节点
# 注释掉健康检查,硬编码路由
# 5. 恢复 Etcd 集群后,重新初始化
两节点坏一个(1/2 故障)
场景:2 节点集群,主库故障
问题:
- Etcd 只有 2 节点,无多数派
- 无法完成选举
- 从库无法自动提升
解决方案:
- 方案 1:添加外部 Etcd 仲裁节点
- 方案 2:人工介入提升从库
- 方案 3:使用 Witness 节点
手动提升步骤:
- 确认主库确实不可恢复
- 停止从库 Patroni:
systemctl stop patroni - 手动提升:
pg_ctl promote -D /pg/data - 直接启动 PostgreSQL:
systemctl start postgres - 更新应用连接串或 HAProxy 配置
Etcd 集群故障
Etcd 单节点故障
场景:3 节点 Etcd 集群,1 节点故障
影响:
- ✅ Etcd 仍有多数派(2/3)
- ✅ 服务正常运行
- ✅ PostgreSQL HA 不受影响
恢复:
- 修复故障节点
- 使用 etcd-add 重新加入
- 或替换为新节点
Etcd 多数派丢失
场景:3 节点 Etcd 集群,2 节点故障
flowchart TB
subgraph Impact["❌ 影响"]
I1["Etcd 无法写入"]
I2["Patroni 无法续租"]
I3["failsafe_mode 激活"]
I4["无法进行故障切换"]
end
subgraph PG["PostgreSQL 表现"]
P1["🟢 主库: 继续运行"]
P2["🔵 从库: 继续复制"]
P3["✅ 新写入可以继续"]
end
subgraph Limit["⚠️ 限制"]
L1["无法 switchover"]
L2["无法 failover"]
L3["配置变更无法生效"]
end
Impact --> PG --> Limit
style I1 fill:#ffcdd2
style P1 fill:#c8e6c9
style L1 fill:#fff9c4恢复优先级:
- 恢复 Etcd 多数派
- 验证 PostgreSQL 状态
- 检查 Patroni 是否正常续租
网络分区
主库网络隔离
场景:主库与 Etcd/其他节点网络不通
flowchart LR
subgraph Isolated["🔒 隔离侧 (主库)"]
P1["Primary"]
CHECK{"failsafe_mode<br/>检查"}
CONT["继续运行"]
DEMOTE["自我降级"]
P1 --> CHECK
CHECK -->|"能访问其他 Patroni"| CONT
CHECK -->|"不能访问"| DEMOTE
end
subgraph Majority["✅ 多数派侧"]
E[("Etcd")]
P2["Replica"]
ELECT["触发选举"]
NEWPRI["新主库产生"]
E --> ELECT --> P2 --> NEWPRI
end
Isolated -.->|"网络分区"| Majority
style P1 fill:#ff9800,color:#fff
style DEMOTE fill:#f44336,color:#fff
style NEWPRI fill:#4CAF50,color:#fff脑裂防护:
- Patroni failsafe_mode
- 旧主库自我检测
- fencing(可选)
- Watchdog(可选)
Watchdog 机制
用于极端情况下的防护:
watchdog:
mode: automatic # off|automatic|required
device: /dev/watchdog
safety_margin: 5 # 安全边际(秒)
工作原理:
- Patroni 定期向 watchdog 设备写入
- 如果 Patroni 无响应,内核触发重启
- 确保旧主库不会继续服务
- 防止严重的脑裂场景
RTO / RPO 深度分析
RTO 时序分解
恢复时间目标(RTO) 由多个阶段组成:
gantt
title RTO 时间分解 (默认配置 pg_rto=30s)
dateFormat ss
axisFormat %S秒
section 故障检测
Patroni 检测/停止续租 :a1, 00, 10s
section 选举阶段
Etcd 租约过期 :a2, after a1, 2s
候选者竞选 (比较 LSN) :a3, after a2, 3s
section 提升阶段
执行 promote :a4, after a3, 3s
更新 Etcd 状态 :a5, after a4, 2s
section 流量切换
HAProxy 检测新主库 :a6, after a5, 5s
HAProxy 确认 (rise) :a7, after a6, 3s
服务恢复 :milestone, after a7, 0s影响 RTO 的关键参数
| 参数 | 影响 | 调优建议 |
|---|---|---|
pg_rto | TTL/loop_wait/retry_timeout 的基准 | 网络稳定可减小到 15-20s |
ttl | 故障检测时间窗口 | = pg_rto |
loop_wait | Patroni 检查间隔 | = pg_rto / 3 |
inter | HAProxy 健康检查间隔 | 可减小到 1-2s |
fall | 故障判定次数 | 可减小到 2 |
rise | 恢复判定次数 | 可减小到 2 |
激进配置(RTO ≈ 15s):
pg_rto: 15 # 更短的 TTL
# HAProxy 配置
default-server inter 1s fastinter 500ms fall 2 rise 2
警告:过短的 RTO 会增加误报切换的风险!
RPO 时序分解
恢复点目标(RPO) 取决于复制模式:
异步复制模式(默认)
sequenceDiagram
participant P as 🟢 Primary
participant W as WAL
participant R as 🔵 Replica
Note over P: T=0 Commit
P->>W: WAL 写入本地
P-->>P: 返回成功给客户端
Note over P,R: T+Δ (复制延迟)
P->>R: WAL 发送
R->>R: WAL 接收 & 回放
Note over P: T+X 故障发生
Note over P: ❌ 未发送的 WAL 丢失
Note over R: RPO = Δ ≈ 几十KB ~ 1MB复制延迟监控:
-- 查看复制延迟
SELECT client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes
FROM pg_stat_replication;
同步复制模式(RPO = 0)
sequenceDiagram
participant P as 🟢 Primary
participant W as WAL
participant R as 🔵 Sync Replica
Note over P: T=0 Commit
P->>W: WAL 写入本地
P->>R: WAL 发送
R->>R: WAL 接收
R-->>P: 确认接收 ✓
P-->>P: 返回成功给客户端
Note over P: 故障发生
Note over R: ✅ 所有已提交数据已在从库
Note over P,R: RPO = 0 (零数据丢失)启用同步复制:
# 使用 crit.yml 模板
pg_conf: crit.yml
# 或设置 RPO = 0
pg_rpo: 0
# Patroni 将自动配置:
# synchronous_mode: true
# synchronous_standby_names: '*'
RTO / RPO 权衡矩阵
| 配置模式 | pg_rto | pg_rpo | 实际 RTO | 实际 RPO | 适用场景 |
|---|---|---|---|---|---|
| 默认 (OLTP) | 30s | 1MB | 20-40s | < 1MB | 常规业务系统 |
| 快速切换 | 15s | 1MB | 10-20s | < 1MB | 低延迟要求 |
| 零丢失 (CRIT) | 30s | 0 | 20-40s | 0 | 金融核心系统 |
| 保守模式 | 60s | 1MB | 40-80s | < 1MB | 不稳定网络 |
配置示例:
# 快速切换模式
pg_rto: 15
pg_rpo: 1048576
pg_conf: oltp.yml
# 零丢失模式
pg_rto: 30
pg_rpo: 0
pg_conf: crit.yml
# 保守模式(不稳定网络)
pg_rto: 60
pg_rpo: 1048576
pg_conf: oltp.yml
利弊权衡
可用性优先 vs 一致性优先
| 维度 | 可用性优先 (默认) | 一致性优先 (crit) |
|---|---|---|
| 同步复制 | 关闭 | 开启 |
| 故障切换 | 快速,可能丢数据 | 谨慎,零数据丢失 |
| 写延迟 | 低 | 高(多一次网络往返) |
| 吞吐量 | 高 | 较低 |
| 从库故障影响 | 无 | 可能阻塞写入 |
| RPO | < 1MB | = 0 |
RTO 权衡
| 较小 RTO | 较大 RTO |
|---|---|
| ✅ 故障恢复快 | ✅ 误报风险低 |
| ✅ 业务中断短 | ✅ 网络抖动容忍度高 |
| ❌ 误报切换风险高 | ❌ 故障恢复慢 |
| ❌ 网络要求严格 | ❌ 业务中断长 |
RPO 权衡
| 较大 RPO | RPO = 0 |
|---|---|
| ✅ 高性能 | ✅ 零数据丢失 |
| ✅ 高可用(单从库故障无影响) | ✅ 金融合规 |
| ❌ 故障可能丢数据 | ❌ 写延迟增加 |
| ❌ 同步从库故障影响写入 |
最佳实践
生产环境检查清单
基础设施:
- 至少 3 个节点(PostgreSQL)
- 至少 3 个节点(Etcd,可与 PG 共用)
- 节点分布在不同故障域(机架/可用区)
- 网络延迟 < 10ms(同城)或 < 50ms(异地)
- 万兆网络(推荐)
参数配置:
-
pg_rto根据网络状况调整(15-60s) -
pg_rpo根据业务需求设置(0 或 1MB) -
pg_conf选择合适的模板(oltp/crit) -
patroni_watchdog_mode评估是否需要
监控告警:
- Patroni 状态监控(领导者/复制延迟)
- Etcd 集群健康监控
- 复制延迟告警(lag > 1MB)
- failsafe_mode 激活告警
灾备演练:
- 定期执行故障切换演练
- 验证 RTO/RPO 是否符合预期
- 测试备份恢复流程
- 验证监控告警有效性
常见问题排查
故障切换失败:
# 检查 Patroni 状态
patronictl -c /etc/patroni/patroni.yml list
# 检查 Etcd 集群健康
etcdctl endpoint health
# 检查复制延迟
psql -c "SELECT * FROM pg_stat_replication"
# 查看 Patroni 日志
journalctl -u patroni -f
脑裂场景处理:
# 1. 确认哪个是"真正"的主库
psql -c "SELECT pg_is_in_recovery()"
# 2. 停止"错误"的主库
systemctl stop patroni
# 3. 使用 pg_rewind 同步
pg_rewind --target-pgdata=/pg/data --source-server="host=<true_primary>"
# 4. 重启 Patroni
systemctl start patroni
相关参数
pg_rto
参数名称: pg_rto, 类型: int, 层次:C
以秒为单位的恢复时间目标(RTO)。默认为 30 秒。
此参数用于派生 Patroni 的关键时序参数:
ttl= pg_rtoloop_wait= pg_rto / 3retry_timeout= pg_rto / 3primary_start_timeout= pg_rto / 3
减小此值可以加快故障恢复,但会增加误报切换的风险。
pg_rpo
参数名称: pg_rpo, 类型: int, 层次:C
以字节为单位的恢复点目标(RPO),默认为 1048576(1MB)。
- 设为
0启用同步复制,确保零数据丢失 - 设为较大值允许更多复制延迟,提高可用性
- 此值也用于
maximum_lag_on_failover参数
pg_conf
参数名称: pg_conf, 类型: string, 层次:C
Patroni 配置模板,默认为 oltp.yml。可选值:
| 模板 | 用途 | 同步复制 | 适用场景 |
|---|---|---|---|
oltp.yml | OLTP 负载 | 否 | 常规业务系统 |
olap.yml | OLAP 负载 | 否 | 分析型应用 |
crit.yml | 关键系统 | 是 | 金融核心系统 |
tiny.yml | 微型实例 | 否 | 开发测试环境 |
patroni_watchdog_mode
参数名称: patroni_watchdog_mode, 类型: string, 层次:C
Watchdog 模式,默认为 off。可选值:
off:禁用 watchdogautomatic:如果可用则使用required:必须使用,否则拒绝启动
Watchdog 用于在极端情况下(如 Patroni 挂起)确保节点自我重启,防止脑裂。
pg_vip_enabled
参数名称: pg_vip_enabled, 类型: bool, 层次:C
是否启用 L2 VIP,默认为 false。
启用后需要配置:
pg_vip_address:VIP 地址(CIDR 格式)pg_vip_interface:绑定网卡
注意:云环境通常不支持 L2 VIP。
参考资料
5 - 时间点恢复
您可以将集群恢复回滚至过去任意时刻,避免软件缺陷与人为失误导致的数据损失。
Pigsty 的 PostgreSQL 集群带有自动配置的时间点恢复(PITR)方案,基于备份组件 pgBackRest 与可选的对象存储仓库 MinIO 提供。
高可用方案 可以解决硬件故障,但却对软件缺陷与人为失误导致的数据删除/覆盖写入/删库等问题却无能为力。 对于这种情况,Pigsty 提供了开箱即用的 时间点恢复(Point in Time Recovery, PITR)能力,无需额外配置即默认启用。
Pigsty 为您提供了基础备份与 WAL 归档的默认配置,您可以使用本地目录与磁盘,亦或专用的 MinIO 集群或 S3 对象存储服务来存储备份并实现异地容灾。 当您使用本地磁盘时,默认保留恢复至过去一天内的任意时间点的能力。当您使用 MinIO 或 S3 时,默认保留恢复至过去一周内的任意时间点的能力。 只要存储空间管够,您尽可保留任意长地可恢复时间段,丰俭由人。
时间点恢复(PITR)解决什么问题?
- 容灾能⼒增强:RPO 从 ∞ 降⾄ ⼗⼏MB, RTO 从 ∞ 降⾄ ⼏⼩时/⼏刻钟。
- 确保数据安全:C/I/A 中的 数据完整性:避免误删导致的数据⼀致性问题。
- 确保数据安全:C/I/A 中的 数据可⽤性:提供对“永久不可⽤”这种灾难情况的兜底
| 单实例配置策略 | 事件 | RTO | RPO |
|---|---|---|---|
| 什么也不做 | 宕机 | 永久丢失 | 全部丢失 |
| 基础备份 | 宕机 | 取决于备份大小与带宽(几小时) | 丢失上一次备份后的数据(几个小时到几天) |
| 基础备份 + WAL归档 | 宕机 | 取决于备份大小与带宽(几小时) | 丢失最后尚未归档的数据(几十MB) |
时间点恢复有什么代价?
- 降低数据安全中的 C:机密性,产生额外泄漏点,需要额外对备份进⾏保护。
- 额外的资源消耗:本地存储或⽹络流量 / 带宽开销,通常并不是⼀个问题。
- 复杂度代价升⾼:⽤户需要付出备份管理成本。
时间点恢复的局限性
如果只有 PITR 用于故障恢复,则 RTO 与 RPO 指标相比 高可用方案 更为逊色,通常应两者组合使用。
- RTO:如果只有单机 + PITR,恢复时长取决于备份大小与网络/磁盘带宽,从十几分钟到几小时,几天不等。
- RPO:如果只有单机 + PITR,宕机时可能丢失少量数据,一个或几个 WAL 日志段文件可能尚未归档,损失 16 MB 到⼏⼗ MB 不等的数据。
除了 PITR 之外,您还可以在 Pigsty 中使用 延迟集群 来解决人为失误或软件缺陷导致的数据误删误改问题。
原理
时间点恢复允许您将集群恢复回滚至过去的“任意时刻”,避免软件缺陷与人为失误导致的数据损失。要做到这一点,首先需要做好两样准备工作:基础备份 与 WAL归档。 拥有 基础备份,允许用户将数据库恢复至备份时的状态,而同时拥有从某个基础备份开始的 WAL归档,允许用户将数据库恢复至基础备份时刻之后的任意时间点。

详细原理,请参阅:基础备份与时间点恢复;具体操作,请参考 PGSQL管理:备份恢复。
基础备份
Pigsty 使用 pgbackrest 管理 PostgreSQL 备份。pgBackRest 将在所有集群实例上初始化空仓库,但只会在集群主库上实际使用仓库。
pgBackRest 支持三种备份模式:全量备份,增量备份,差异备份,其中前两者最为常用。 全量备份将对数据库集群取一个当前时刻的全量物理快照,增量备份会记录当前数据库集群与上一次全量备份之间的差异。
Pigsty 为备份提供了封装命令:/pg/bin/pg-backup [full|incr]。您可以通过 Crontab 或任何其他任务调度系统,按需定期制作基础备份。
WAL归档
Pigsty 默认在集群主库上启⽤了 WAL 归档,并使⽤ pgbackrest 命令行工具持续推送 WAL 段⽂件至备份仓库。
pgBackRest 会⾃动管理所需的 WAL ⽂件,并根据备份的保留策略及时清理过期的备份,与其对应的 WAL 归档⽂件。
如果您不需要 PITR 功能,可以通过 配置集群: archive_mode: off 来关闭 WAL 归档,移除 node_crontab 来停止定期备份任务。
实现
默认情况下,Pigsty提供了两种预置备份策略:默认使用本地文件系统备份仓库,在这种情况下每天进行一次全量备份,确保用户任何时候都能回滚至一天内的任意时间点。备选策略使用专用的 MinIO 集群或S3存储备份,每周一全备,每天一增备,默认保留两周的备份与WAL归档。
Pigsty 使用 pgBackRest 管理备份,接收 WAL 归档,执行 PITR。备份仓库可以进行灵活配置(pgbackrest_repo):默认使用主库本地文件系统(local),但也可以使用其他磁盘路径,或使用自带的可选 MinIO 服务(minio)与云上 S3 服务。
pgbackrest_enabled: true # 在 pgsql 主机上启用 pgBackRest 吗?
pgbackrest_clean: true # 初始化时删除 pg 备份数据?
pgbackrest_log_dir: /pg/log/pgbackrest # pgbackrest 日志目录,默认为 `/pg/log/pgbackrest`
pgbackrest_method: local # pgbackrest 仓库方法:local, minio, [用户定义...]
pgbackrest_repo: # pgbackrest 仓库:https://pgbackrest.org/configuration.html#section-repository
local: # 默认使用本地 posix 文件系统的 pgbackrest 仓库
path: /pg/backup # 本地备份目录,默认为 `/pg/backup`
retention_full_type: count # 按计数保留完整备份
retention_full: 2 # 使用本地文件系统仓库时,最多保留 3 个完整备份,至少保留 2 个
minio: # pgbackrest 的可选 minio 仓库
type: s3 # minio 是与 s3 兼容的,所以使用 s3
s3_endpoint: sss.pigsty # minio 端点域名,默认为 `sss.pigsty`
s3_region: us-east-1 # minio 区域,默认为 us-east-1,对 minio 无效
s3_bucket: pgsql # minio 桶名称,默认为 `pgsql`
s3_key: pgbackrest # pgbackrest 的 minio 用户访问密钥
s3_key_secret: S3User.Backup # pgbackrest 的 minio 用户秘密密钥
s3_uri_style: path # 对 minio 使用路径风格的 uri,而不是主机风格
path: /pgbackrest # minio 备份路径,默认为 `/pgbackrest`
storage_port: 9000 # minio 端口,默认为 9000
storage_ca_file: /etc/pki/ca.crt # minio ca 文件路径,默认为 `/etc/pki/ca.crt`
bundle: y # 将小文件打包成一个文件
cipher_type: aes-256-cbc # 为远程备份仓库启用 AES 加密
cipher_pass: pgBackRest # AES 加密密码,默认为 'pgBackRest'
retention_full_type: time # 在 minio 仓库上按时间保留完整备份
retention_full: 14 # 保留过去 14 天的完整备份
# 您还可以添加其他的可选备份仓库,例如 S3,用于异地容灾
Pigsty 参数 pgbackrest_repo 中的目标仓库会被转换为 /etc/pgbackrest/pgbackrest.conf 配置文件中的仓库定义。
例如,如果您定义了一个美西区的 S3 仓库用于存储冷备份,可以使用下面的参考配置。
s3: # ------> /etc/pgbackrest/pgbackrest.conf
repo1-type: s3 # ----> repo1-type=s3
repo1-s3-region: us-west-1 # ----> repo1-s3-region=us-west-1
repo1-s3-endpoint: s3-us-west-1.amazonaws.com # ----> repo1-s3-endpoint=s3-us-west-1.amazonaws.com
repo1-s3-key: '<your_access_key>' # ----> repo1-s3-key=<your_access_key>
repo1-s3-key-secret: '<your_secret_key>' # ----> repo1-s3-key-secret=<your_secret_key>
repo1-s3-bucket: pgsql # ----> repo1-s3-bucket=pgsql
repo1-s3-uri-style: host # ----> repo1-s3-uri-style=host
repo1-path: /pgbackrest # ----> repo1-path=/pgbackrest
repo1-bundle: y # ----> repo1-bundle=y
repo1-cipher-type: aes-256-cbc # ----> repo1-cipher-type=aes-256-cbc
repo1-cipher-pass: pgBackRest # ----> repo1-cipher-pass=pgBackRest
repo1-retention-full-type: time # ----> repo1-retention-full-type=time
repo1-retention-full: 90 # ----> repo1-retention-full=90
恢复
您可以直接使用以下封装命令可以用于 PostgreSQL 数据库集群的 时间点恢复。
Pigsty 默认使用增量差分并行恢复,允许您以最快速度恢复到指定时间点。
pg-pitr # 恢复到WAL存档流的结束位置(例如在整个数据中心故障的情况下使用)
pg-pitr -i # 恢复到最近备份完成的时间(不常用)
pg-pitr --time="2022-12-30 14:44:44+08" # 恢复到指定的时间点(在删除数据库或表的情况下使用)
pg-pitr --name="my-restore-point" # 恢复到使用 pg_create_restore_point 创建的命名恢复点
pg-pitr --lsn="0/7C82CB8" -X # 在LSN之前立即恢复
pg-pitr --xid="1234567" -X -P # 在指定的事务ID之前立即恢复,然后将集群直接提升为主库
pg-pitr --backup=latest # 恢复到最新的备份集
pg-pitr --backup=20221108-105325 # 恢复到特定备份集,备份集可以使用 pgbackrest info 列出
pg-pitr # pgbackrest --stanza=pg-meta restore
pg-pitr -i # pgbackrest --stanza=pg-meta --type=immediate restore
pg-pitr -t "2022-12-30 14:44:44+08" # pgbackrest --stanza=pg-meta --type=time --target="2022-12-30 14:44:44+08" restore
pg-pitr -n "my-restore-point" # pgbackrest --stanza=pg-meta --type=name --target=my-restore-point restore
pg-pitr -b 20221108-105325F # pgbackrest --stanza=pg-meta --type=name --set=20221230-120101F restore
pg-pitr -l "0/7C82CB8" -X # pgbackrest --stanza=pg-meta --type=lsn --target="0/7C82CB8" --target-exclusive restore
pg-pitr -x 1234567 -X -P # pgbackrest --stanza=pg-meta --type=xid --target="0/7C82CB8" --target-exclusive --target-action=promote restore
在执行 PITR 时,您可以使用 Pigsty 监控系统观察集群 LSN 位点状态,判断是否成功恢复到指定的时间点,事务点,LSN位点,或其他点位。

6 - 安全与合规
Pigsty v4.0 提供了 企业级 的 PostgreSQL 安全配置,涵盖身份鉴别、访问控制、通信加密、审计日志、数据完整性、备份恢复等多个维度。
本文档以 中国等保三级(GB/T 22239-2019)和 SOC 2 Type II 安全合规要求为参照,逐项对比验证 Pigsty 的安全能力。
每个安全维度包含两部分说明:
- 默认配置:使用
conf/meta.yml及默认参数时的安全合规状态 (个人使用) - 可用配置:通过调整 Pigsty 参数可达到的增强安全状态 (企业级配置可达)
合规对照总结
等保三级核心要求对照
| 要求项 | 默认满足 | 配置可达 | 说明 |
|---|---|---|---|
| 身份唯一性 | ✅ | ✅ | 角色系统保证用户唯一标识 |
| 口令复杂度 | ⚠️ | ✅ | 可启用 passwordcheck / credcheck 强制执行密码复杂度 |
| 口令定期更换 | ⚠️ | ✅ | 通过 expire_in/expire_at 设置用户有效期并定时刷新 |
| 登录失败处理 | ⚠️ | ✅ | 日志中记录失败的登陆请求,可配合 fail2ban 自动封禁 |
| 双因素认证 | ⚠️ | ✅ | 密码 + 客户端 SSL 证书认证 |
| 访问控制 | ✅ | ✅ | HBA规则 + RBAC + SELinux |
| 最小权限 | ✅ | ✅ | 分层角色体系 |
| 权限分离 | ✅ | ✅ | DBA / Monitor / 应用读/写/ETL/个人用户分离 |
| 通信加密 | ✅ | ✅ | 默认启用并使用 SSL,可强制 SSL |
| 数据完整性 | ✅ | ✅ | 数据校验和默认启用 |
| 存储加密 | ⚠️ | ✅ | 备份加密 + Percona TDE 内核支持 |
| 审计日志 | ✅ | ✅ | 日志记录 DDL 与敏感操作,可记录所有操作。 |
| 日志保护 | ✅ | ✅ | 文件权限隔离,VictoriaLogs 集中收集防篡改 |
| 备份恢复 | ✅ | ✅ | pgBackRest 自动备份 |
| 网络隔离 | ✅ | ✅ | 防火墙 + HBA |
SOC 2 Type II 控制点对照
| 控制点 | 默认满足 | 配置可达 | 说明 |
|---|---|---|---|
| CC6.1 逻辑访问控制 | ✅ | ✅ | HBA + RBAC + SELinux |
| CC6.2 用户注册授权 | ✅ | ✅ | Ansible声明式管理 |
| CC6.3 最小权限 | ✅ | ✅ | 分层角色 |
| CC6.6 传输加密 | ✅ | ✅ | SSL/TLS 全局启用 |
| CC6.7 静态加密 | ⚠️ | ✅ | 可使用 Percona PGTDE 内核,以及 pgsodium/valut 等扩展 |
| CC6.8 恶意软件防护 | ⚠️ | ✅ | 最小安装 + 审计 |
| CC7.1 入侵检测 | ⚠️ | ✅ | 设置日志 Auth Fail 监控告警规则 |
| CC7.2 系统监控 | ✅ | ✅ | VictoriaMetrics + Grafana |
| CC7.3 事件响应 | ✅ | ✅ | Alertmanager |
| CC9.1 业务连续性 | ✅ | ✅ | HA + 自动故障转移 |
| A1.2 数据恢复 | ✅ | ✅ | PITR备份恢复 |
图例:✅ 默认满足 ⚠️ 需要额外配置
身份鉴别
等保要求:应对登录的用户进行身份标识和鉴别,身份标识具有唯一性;应采用口令、密码技术、生物技术等两种或两种以上组合的鉴别技术。
SOC 2:CC6.1 - 逻辑和物理访问控制;用户身份验证机制。
用户身份标识
PostgreSQL 通过角色(Role)系统实现用户身份标识,每个用户具有唯一的角色名。
| 配置项 | 默认值 | 说明 |
|---|---|---|
pg_default_roles | 4个默认角色 + 4个系统用户 | 预定义角色体系 |
pg_users | [] | 业务用户定义列表 |
默认配置:Pigsty 预置了分层的角色体系:
pg_default_roles:
- { name: dbrole_readonly ,login: false ,comment: '全局只读角色' }
- { name: dbrole_offline ,login: false ,comment: '受限只读角色(离线查询)' }
- { name: dbrole_readwrite ,login: false ,roles: [dbrole_readonly] ,comment: '全局读写角色' }
- { name: dbrole_admin ,login: false ,roles: [pg_monitor,dbrole_readwrite] ,comment: '对象管理角色' }
- { name: postgres ,superuser: true ,comment: '系统超级用户' }
- { name: replicator ,replication: true,roles: [pg_monitor,dbrole_readonly] ,comment: '复制用户' }
- { name: dbuser_dba ,superuser: true ,roles: [dbrole_admin] ,pgbouncer: true ,comment: '管理员用户' }
- { name: dbuser_monitor ,roles: [pg_monitor,dbrole_readonly] ,pgbouncer: true ,comment: '监控用户' }
可用配置:用户可通过 pg_users 定义业务用户,支持设置账户有效期、连接限制等:
pg_users:
- name: dbuser_app
password: 'SecurePass123!'
roles: [dbrole_readwrite]
expire_in: 365 # 365天后过期
connlimit: 100 # 最大100个连接
comment: '应用程序用户'
密码策略
| 配置项 | 默认值 | 说明 |
|---|---|---|
pg_pwd_enc | scram-sha-256 | 密码加密算法 |
pg_dbsu_password | ''(空) | 数据库超级用户密码 |
默认配置:
- 密码加密采用 SCRAM-SHA-256 算法,这是目前 PostgreSQL 支持的最安全的密码哈希算法
- 密码在设置时自动使用
SET log_statement TO 'none'防止明文泄露到日志 - 数据库超级用户
postgres默认无密码,仅允许通过本地 Unix Socket 使用ident认证
可用配置:
启用
passwordcheck扩展强制密码复杂度:pg_libs: 'passwordcheck, pg_stat_statements, auto_explain'使用
credcheck扩展实现更丰富的密码策略(长度、复杂度、历史记录等)设置用户账户有效期:
pg_users: - { name: temp_user, password: 'xxx', expire_in: 30 } # 30天后过期 - { name: temp_user, password: 'xxx', expire_at: '2025-12-31' } # 指定日期过期
认证机制
| 配置项 | 默认值 | 说明 |
|---|---|---|
pg_default_hba_rules | 12条规则 | 默认HBA认证规则 |
pg_hba_rules | [] | 业务HBA规则 |
默认配置:Pigsty 实现了基于来源地址的分层认证策略:
pg_default_hba_rules:
- {user: '${dbsu}' ,db: all ,addr: local ,auth: ident ,title: 'dbsu本地ident认证'}
- {user: '${dbsu}' ,db: replication ,addr: local ,auth: ident ,title: 'dbsu本地复制'}
- {user: '${repl}' ,db: replication ,addr: localhost ,auth: pwd ,title: '复制用户本地密码认证'}
- {user: '${repl}' ,db: replication ,addr: intra ,auth: pwd ,title: '复制用户内网密码认证'}
- {user: '${repl}' ,db: postgres ,addr: intra ,auth: pwd ,title: '复制用户内网访问postgres'}
- {user: '${monitor}' ,db: all ,addr: localhost ,auth: pwd ,title: '监控用户本地密码认证'}
- {user: '${monitor}' ,db: all ,addr: infra ,auth: pwd ,title: '监控用户从基础设施节点访问'}
- {user: '${admin}' ,db: all ,addr: infra ,auth: ssl ,title: '管理员SSL+密码认证'}
- {user: '${admin}' ,db: all ,addr: world ,auth: ssl ,title: '管理员全局SSL+密码认证'}
- {user: '+dbrole_readonly',db: all ,addr: localhost ,auth: pwd ,title: '只读角色本地密码认证'}
- {user: '+dbrole_readonly',db: all ,addr: intra ,auth: pwd ,title: '只读角色内网密码认证'}
- {user: '+dbrole_offline' ,db: all ,addr: intra ,auth: pwd ,title: '离线角色内网密码认证'}
支持的认证方法别名:
| 别名 | 实际方法 | 说明 |
|---|---|---|
deny | reject | 拒绝连接 |
pwd | scram-sha-256 | 密码认证(默认加密) |
ssl | scram-sha-256 + hostssl | SSL + 密码认证 |
cert | cert | 客户端证书认证 |
os/ident/peer | ident/peer | 操作系统用户映射 |
trust | trust | 无条件信任(不推荐) |
可用配置:
启用客户端证书认证实现双因素认证:
pg_hba_rules: - {user: 'secure_user', db: all, addr: world, auth: cert, title: '证书认证用户'}限制特定用户只能从指定 IP 访问:
pg_hba_rules: - {user: 'app_user', db: 'appdb', addr: '192.168.1.100/32', auth: ssl}
访问控制
等保要求:应授予管理用户所需的最小权限,实现管理用户的权限分离;应由授权主体配置访问控制策略。
SOC 2:CC6.3 - 基于角色的访问控制和最小权限原则。
权限分离
默认配置:Pigsty 实现了清晰的职责分离模型:
| 角色 | 权限 | 用途 |
|---|---|---|
postgres | SUPERUSER | 系统超级用户,仅限本地OS认证 |
dbuser_dba | SUPERUSER + dbrole_admin | 数据库管理员 |
replicator | REPLICATION + pg_monitor | 复制和监控 |
dbuser_monitor | pg_monitor + dbrole_readonly | 只读监控 |
dbrole_admin | CREATE + dbrole_readwrite | 对象管理(DDL) |
dbrole_readwrite | INSERT/UPDATE/DELETE + dbrole_readonly | 数据读写 |
dbrole_readonly | SELECT | 只读访问 |
dbrole_offline | SELECT(受限) | 离线/ETL查询 |
可用配置:
细粒度权限控制通过
pg_default_privileges实现:pg_default_privileges: - GRANT USAGE ON SCHEMAS TO dbrole_readonly - GRANT SELECT ON TABLES TO dbrole_readonly - GRANT SELECT ON SEQUENCES TO dbrole_readonly - GRANT EXECUTE ON FUNCTIONS TO dbrole_readonly - GRANT INSERT ON TABLES TO dbrole_readwrite - GRANT UPDATE ON TABLES TO dbrole_readwrite - GRANT DELETE ON TABLES TO dbrole_readwrite - GRANT TRUNCATE ON TABLES TO dbrole_admin - GRANT CREATE ON SCHEMAS TO dbrole_admin
操作系统层面权限
| 配置项 | 默认值 | 说明 |
|---|---|---|
pg_dbsu | postgres | 数据库超级用户OS账号 |
pg_dbsu_sudo | limit | sudo权限级别 |
node_admin_sudo | nopass | 管理员sudo权限 |
默认配置:
- 数据库超级用户
postgres的 sudo 权限为limit,仅允许执行特定的服务管理命令:- 启动/停止/重启 PostgreSQL 相关服务
- 加载 softdog 内核模块(用于 watchdog)
%postgres ALL=NOPASSWD: /bin/systemctl stop postgres
%postgres ALL=NOPASSWD: /bin/systemctl start postgres
%postgres ALL=NOPASSWD: /bin/systemctl reload patroni
# ... 等受限命令
可用配置:
pg_dbsu_sudo: none- 完全禁用 sudo 权限(最严格)pg_dbsu_sudo: all- 需要密码的完整 sudo(平衡方案)pg_dbsu_sudo: nopass- 无密码完整 sudo(不推荐)
行级安全策略(RLS)
PostgreSQL 原生支持行级安全策略(Row Level Security),可通过 pg_users 设置用户属性:
pg_users:
- name: secure_user
bypassrls: false # 不允许绕过RLS
roles: [dbrole_readwrite]
配合数据库内的 RLS 策略,可实现细粒度的数据访问控制。
通信安全
等保要求:应采用密码技术保证通信过程中数据的完整性和保密性。
SOC 2:CC6.6 - 数据传输安全;CC6.7 - 加密控制。
SSL/TLS 加密
| 配置项 | 默认值 | 说明 |
|---|---|---|
ssl (postgresql.conf) | on | 服务端 SSL 开关 |
patroni_ssl_enabled | false | Patroni API SSL |
pgbouncer_sslmode | disable | PgBouncer 客户端 SSL |
nginx_sslmode | enable | Nginx HTTPS |
默认配置:
- PostgreSQL 服务端 默认启用 SSL,支持加密连接
- 管理员用户(
${admin})强制使用hostssl连接 - 自动生成并分发 SSL 证书到所有数据库节点
# patroni.yml 中的 SSL 配置
ssl: 'on'
ssl_cert_file: '/pg/cert/server.crt'
ssl_key_file: '/pg/cert/server.key'
ssl_ca_file: '/pg/cert/ca.crt'
可用配置:
启用 Patroni REST API SSL 加密:
patroni_ssl_enabled: true启用 PgBouncer 客户端 SSL:
pgbouncer_sslmode: require # 或 verify-ca, verify-full强制所有连接使用 SSL:
pg_hba_rules: - {user: all, db: all, addr: world, auth: ssl, title: '强制SSL'}
PKI 证书管理
| 配置项 | 默认值 | 说明 |
|---|---|---|
cert_validity | 7300d | 证书有效期(20年) |
| CA 证书有效期 | 100年 | 自签名 CA 有效期 |
默认配置:
Pigsty 使用自建 PKI 体系,自动管理证书生命周期:
files/pki/
├── ca/ # CA 根证书
│ ├── ca.crt # CA 公钥证书
│ └── ca.key # CA 私钥
├── csr/ # 证书签名请求
├── pgsql/ # PostgreSQL 集群证书
├── etcd/ # ETCD 集群证书
├── infra/ # 基础设施节点证书
└── minio/ # MinIO 证书
- 每个 PostgreSQL 集群共享一个私钥,每个实例有独立的证书
- 证书包含正确的 SAN(Subject Alternative Name)配置
- CA 证书自动分发到
/etc/pki/ca.crt和/pg/cert/ca.crt
可用配置:
- 使用外部 CA 签发的证书:将证书放入
files/pki/目录,设置ca_create: false - 调整证书有效期:
cert_validity: 365d(1年)
ETCD 通信安全
ETCD 作为 Patroni 的 DCS(分布式配置存储),默认使用 mTLS(双向 TLS)认证:
etcd3:
hosts: '10.10.10.10:2379'
protocol: https
cacert: /pg/cert/ca.crt
cert: /pg/cert/server.crt
key: /pg/cert/server.key
username: 'pg-meta' # 集群专用账号
password: 'pg-meta' # 默认与集群名相同
数据加密
等保要求:应采用密码技术保证重要数据在存储过程中的保密性。
SOC 2:CC6.1 - 数据加密存储。
备份加密
| 配置项 | 默认值 | 说明 |
|---|---|---|
cipher_type | aes-256-cbc | 备份加密算法(MinIO仓库) |
cipher_pass | pgBackRest | 加密密码(需修改) |
默认配置:
- 本地备份(
pgbackrest_method: local)默认不加密 - 远程对象存储备份支持 AES-256-CBC 加密
可用配置:
启用备份加密(推荐用于远程存储):
pgbackrest_method: minio
pgbackrest_repo:
minio:
type: s3
s3_endpoint: sss.pigsty
s3_bucket: pgsql
s3_key: pgbackrest
s3_key_secret: S3User.Backup
cipher_type: aes-256-cbc
cipher_pass: 'YourSecureBackupPassword!' # 务必修改!
retention_full_type: time
retention_full: 14
透明数据加密(TDE)
PostgreSQL 社区版本不支持原生 TDE,但可通过以下方式实现存储加密:
- 文件系统级加密:使用 LUKS/dm-crypt 加密存储卷
- pgsodium 扩展:支持列级加密
# 启用 pgsodium 列级加密
pg_libs: 'pgsodium, pg_stat_statements, auto_explain'
# 自定义加密密钥(64位十六进制)
pgsodium_key: 'a1b2c3d4e5f6...' # 或使用外部密钥管理脚本
数据完整性校验
| 配置项 | 默认值 | 说明 |
|---|---|---|
pg_checksum | true | 数据校验和 |
默认配置:
- 数据校验和默认启用,可检测存储层数据损坏
crit.yml模板强制启用数据校验和- 支持
pg_rewind进行故障恢复
pg_checksum: true # 强烈建议保持启用
安全审计
等保要求:应启用安全审计功能,审计覆盖到每个用户,对重要的用户行为和重要安全事件进行审计。
SOC 2:CC7.2 - 系统监控和日志记录;CC7.3 - 安全事件检测。
数据库审计日志
| 配置项 | 默认值 | 说明 |
|---|---|---|
logging_collector | on | 启用日志收集器 |
log_destination | csvlog | CSV格式日志 |
log_statement | ddl | 记录DDL语句 |
log_min_duration_statement | 100ms | 慢查询阈值 |
log_connections | authorization (PG18) / on | 连接审计 |
log_disconnections | on (crit模板) | 断开连接审计 |
log_checkpoints | on | 检查点日志 |
log_lock_waits | on | 锁等待日志 |
log_replication_commands | on | 复制命令日志 |
默认配置:
# oltp.yml 模板的审计配置
log_destination: csvlog
logging_collector: 'on'
log_directory: /pg/log/postgres
log_filename: 'postgresql-%a.log' # 按星期轮转
log_file_mode: '0640' # 限制日志文件权限
log_rotation_age: '1d'
log_truncate_on_rotation: 'on'
log_checkpoints: 'on'
log_lock_waits: 'on'
log_replication_commands: 'on'
log_statement: ddl # 记录所有DDL
log_min_duration_statement: 100 # 记录慢查询 >100ms
可用配置(crit.yml 关键业务模板):
# crit.yml 提供更全面的审计
log_connections: 'receipt,authentication,authorization' # PG18完整连接审计
log_disconnections: 'on' # 记录断开连接
log_lock_failures: 'on' # 记录锁失败(PG18)
track_activity_query_size: 32768 # 完整查询记录
启用 pgaudit 扩展实现细粒度审计:
pg_libs: 'pgaudit, pg_stat_statements, auto_explain'
pg_parameters:
pgaudit.log: 'all'
pgaudit.log_catalog: 'on'
pgaudit.log_relation: 'on'
性能与执行审计
| 扩展 | 默认启用 | 说明 |
|---|---|---|
pg_stat_statements | 是 | SQL统计信息 |
auto_explain | 是 | 慢查询执行计划 |
pg_wait_sampling | 配置可用 | 等待事件采样 |
默认配置:
pg_libs: 'pg_stat_statements, auto_explain'
# auto_explain 配置
auto_explain.log_min_duration: 1s # 记录>1s的查询计划
auto_explain.log_analyze: 'on'
auto_explain.log_verbose: 'on'
auto_explain.log_timing: 'on'
# pg_stat_statements 配置
pg_stat_statements.max: 10000
pg_stat_statements.track: all
日志集中管理
默认配置:
- PostgreSQL 日志:
/pg/log/postgres/ - Patroni 日志:
/pg/log/patroni/ - PgBouncer 日志:
/pg/log/pgbouncer/ - pgBackRest 日志:
/pg/log/pgbackrest/
可用配置:
通过 Vector 将日志发送到 VictoriaLogs 集中存储:
# 日志自动收集到 VictoriaLogs
vlogs_enabled: true
vlogs_port: 9428
vlogs_options: >-
-retentionPeriod=15d
-retention.maxDiskSpaceUsageBytes=50GiB
网络安全
等保要求:应在网络边界部署访问控制设备,对进出网络的数据流实现访问控制。
SOC 2:CC6.1 - 边界保护和网络安全。
防火墙配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
node_firewall_mode | zone | 防火墙模式 |
node_firewall_intranet | RFC1918网段 | 内网CIDR |
node_firewall_public_port | [22,80,443,5432] | 公网开放端口 |
默认配置:
node_firewall_mode: zone # 启用区域防火墙
node_firewall_intranet: # 定义内网地址
- 10.0.0.0/8
- 192.168.0.0/16
- 172.16.0.0/12
node_firewall_public_port: # 公网开放端口
- 22 # SSH
- 80 # HTTP
- 443 # HTTPS
- 5432 # PostgreSQL(谨慎开放)
防火墙规则:
- 内网地址自动加入
trusted区域 - 仅指定端口对外开放
- 支持 firewalld(RHEL系)和 ufw(Debian系)
可用配置:
node_firewall_mode: off- 禁用防火墙(不推荐)node_firewall_mode: none- 不修改现有配置- 移除5432端口,仅允许内网访问数据库
服务访问控制
| 配置项 | 默认值 | 说明 |
|---|---|---|
pg_listen | 0.0.0.0 | PostgreSQL监听地址 |
patroni_allowlist | infra + cluster | Patroni API白名单 |
默认配置:
Patroni REST API 仅允许来自以下地址的访问:
# 自动计算的白名单
pg_allow_list = [admin_ip] + pg_cluster_members + groups["infra"]
可用配置:
限制 PostgreSQL 只监听特定网卡:
pg_listen: '${ip}' # 仅监听主机IP,不监听0.0.0.0
SELinux
| 配置项 | 默认值 | 说明 |
|---|---|---|
node_selinux_mode | permissive | SELinux模式 |
默认配置:SELinux 设为 permissive 模式(记录但不阻止)
可用配置:
node_selinux_mode: enforcing # 强制模式(需要额外配置策略)
可用性与恢复
等保要求:应提供数据备份与恢复功能;应提供自动故障恢复功能。
SOC 2:CC9.1 - 业务连续性;A1.2 - 数据备份和恢复。
高可用架构
| 配置项 | 默认值 | 说明 |
|---|---|---|
patroni_enabled | true | 启用Patroni HA |
pg_rto | 30 | 恢复时间目标(秒) |
pg_rpo | 1048576 | 恢复点目标(1MB) |
默认配置:
- Patroni 自动故障检测与切换(RTO < 30秒)
- 异步复制,最大数据丢失 1MB(RPO)
failsafe_mode: true防止脑裂
可用配置:
启用同步复制实现 RPO = 0:
pg_rpo: 0 # 零数据丢失
pg_conf: crit.yml # 使用关键业务模板
# crit.yml 自动启用 synchronous_mode: true
启用硬件看门狗:
patroni_watchdog_mode: automatic # 或 required
备份恢复
| 配置项 | 默认值 | 说明 |
|---|---|---|
pgbackrest_enabled | true | 启用pgBackRest |
pgbackrest_method | local | 备份存储方式 |
retention_full | 2 | 保留完整备份数量 |
默认配置:
pgbackrest_enabled: true
pgbackrest_method: local
pgbackrest_repo:
local:
path: /pg/backup
retention_full_type: count
retention_full: 2 # 保留2个完整备份
可用配置:
异地备份到对象存储:
pgbackrest_method: minio
pgbackrest_repo:
minio:
type: s3
s3_endpoint: sss.pigsty
s3_bucket: pgsql
cipher_type: aes-256-cbc # 加密备份
retention_full_type: time
retention_full: 14 # 保留14天
block: y # 块级增量备份
bundle: y # 小文件合并
定时备份策略:
node_crontab:
- '00 01 * * * postgres /pg/bin/pg-backup full' # 每日1点全量备份
- '00 */4 * * * postgres /pg/bin/pg-backup diff' # 每4小时差异备份
入侵防范
等保要求:应遵循最小安装的原则,仅安装需要的组件和应用程序;应能够检测到对重要节点进行入侵的行为,并在发生严重入侵事件时提供报警。
SOC 2:CC6.8 - 恶意软件防护;CC7.1 - 入侵检测。
最小化安装
默认配置:
- 仅安装必要的 PostgreSQL 组件和扩展
- 通过
pg_packages和pg_extensions精确控制安装内容 - 生产系统不安装开发工具和调试符号
pg_packages: [ pgsql-main, pgsql-common ] # 最小化安装
pg_extensions: [] # 按需添加扩展
安全扩展
Pigsty 提供以下 安全相关扩展,可按需安装启用:
| 扩展/包 | 版本 | 描述 |
|---|---|---|
| passwordcheck_cracklib | 3.1.0 | 使用 cracklib 加固 PG 用户密码 |
| supautils | 3.0.2 | 用于在云环境中确保数据库集群的安全 |
| pgsodium | 3.1.9 | 表数据加密存储 TDE |
| supabase_vault / pg_vault | 0.3.1 | 在 Vault 中存储加密凭证的扩展 (supabase) |
| pg_session_jwt | 0.4.0 | 使用JWT进行会话认证 |
| anon | 2.5.1 | 数据匿名化处理工具 |
| pgsmcrypto | 0.1.1 | 为PostgreSQL提供商密算法支持:SM2,SM3,SM4 |
| pg_enigma | 0.5.0 | PostgreSQL 加密数据类型 |
| pgaudit | 18.0 | 提供审计功能 |
| pgauditlogtofile | 1.7.6 | pgAudit 子扩展,将审计日志写入单独的文件中 |
| pg_auditor | 0.2 | 审计数据变更并提供闪回能力 |
| logerrors | 2.1.5 | 用于收集日志文件中消息统计信息的函数 |
| pg_auth_mon | 3.0 | 监控每个用户的连接尝试 |
| pg_jobmon | 1.4.1 | 记录和监控函数 |
| credcheck | 4.2 | 明文凭证检查器 |
| pgcryptokey | 0.85 | PG密钥管理 |
| login_hook | 1.7 | 在用户登陆时执行login_hook.login()函数 |
| set_user | 4.2.0 | 增加了日志记录的 SET ROLE |
| pg_snakeoil | 1.4 | PostgreSQL动态链接库反病毒功能 |
| pgextwlist | 1.19 | PostgreSQL扩展白名单功能 |
| sslutils | 1.4 | 使用SQL管理SSL证书 |
| noset | 0.3.0 | 阻止非超级用户使用SET/RESET设置变量 |
| pg_tde | 1.0 | Percona加密存储引擎 |
| sepgsql | - | 基于SELinux标签的强制访问控制 |
| auth_delay | - | 在返回认证失败前暂停一会,避免爆破 |
| pgcrypto | 1.3 | 实用加解密函数 |
| passwordcheck | - | 用于强制拒绝修改弱密码的扩展 |
安装所有安全扩展包:
pg_extensions: [ pg18-sec ] # 安装安全类扩展组
告警与监控
默认配置:
- VictoriaMetrics + Alertmanager 提供监控告警
- 预置 PostgreSQL 告警规则
- Grafana 可视化仪表板
关键安全相关告警:
- 认证失败次数过多
- 复制延迟过大
- 备份失败
- 磁盘空间不足
- 连接数耗尽
