上篇文章介绍了如何使用 Patroni、HAProxy、Keepalived、watchdog 和 ETCD 搭建高可用的 PostgreSQL 集群。本文将对这套架构中的每个核心组件进行原理剖析和日常维护讲解。整个系统的流程架构如下图所示:

架构主要包含以下几层功能:
- 客户端应用层:业务系统通过统一的虚拟IP(VIP)进行访问。
- 负载均衡层:由 HAProxy 和 Keepalived 实现智能路由与读写分离,优化整体性能。
- 数据库集群层:PostgreSQL 结合 Patroni 实现数据的高可用,支持毫秒级故障切换,业务无感知。
- 分布式一致性层:ETCD 集群提供强一致性保障,有效防止集群脑裂。
HAProxy 组件
HAProxy(High Availability Proxy)是一款开源的、高性能的代理和负载均衡软件。它同时支持 TCP 和 HTTP/HTTPS 协议,在 PostgreSQL 高可用架构中扮演着关键角色,是实现数据库集群负载均衡与高可用性的核心。

其配置文件 /etc/haproxy/haproxy.cfg 中通常包含以下关键参数,用于定义服务器健康检查策略:
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
inter 2s # 健康检查间隔
fall 3 # 失败阈值(连续3次失败则标记为down)
rise 2 # 恢复阈值(连续2次成功则标记为up)
option pgsql-check # PostgreSQL专用健康检查
timeout connect 5s # 连接超时
timeout server 30s # 服务器响应超时
HAProxy 的核心功能由六大组件协同完成:
- Frontend:负责接收、解析客户端请求并进行路由。
- Backend:管理后端服务器池,执行负载均衡策略。
- Load Balancer:执行具体的负载均衡算法。
- Health Checker:对后端服务器进行健康状态检查。
- Connection Manager:负责连接的建立、管理和优化。
- Statistics Engine:提供性能统计和监控数据。
一个典型的请求在 HAProxy 中的处理流程如下:
- 接受连接
- 解析请求
- 路由请求
- 选择服务器
- 连接到服务器
- 代理流量
- 处理响应
- 关闭连接
HAProxy 支持多种负载均衡算法,默认使用 轮询算法(Round Robin):
- Round Robin:轮询算法,依次分配请求。
- Least Connections:最少连接算法,将新请求发送给当前连接数最少的服务器。
- Source IP Hashing:源 IP 哈希算法,保证同一来源的请求总是发往同一服务器。
- URI Hashing:URI 哈希算法。
- URL Parameter:URL 参数算法。
对于健康检查,HAProxy 也支持多种方式,默认基于 HTTP:
- TCP Check:简单的 TCP 端口连通性检查。
- HTTP Check:发送 HTTP 请求并校验响应。
- SSL Check:SSL 证书和连接检查。
- SMTP Check:SMTP 服务检查。
- PostgreSQL Check:专门用于检查 PostgreSQL 数据库服务的状态。
Keepalived 组件
Keepalived 的核心作用是通过 VRRP(虚拟路由器冗余协议)实现虚拟 IP(VIP)的高可用。它在主备 HAProxy 节点之间进行心跳检测与故障切换,确保负载均衡层的接入点始终可用,是避免单点故障的关键组件,也是实现高可用负载均衡的常用方案。

其配置文件示例如下,定义了VRRP实例和健康检查脚本:
vrrp_instance VI_1 {
state MASTER/BACKUP
interface eth0
virtual_router_id 51
priority 100/90 # 主/备节点优先级
advert_int 1 # VRRP心跳广播间隔
authentication {
auth_type PASS
auth_pass 62f7f8e5
}
virtual_ipaddress {
192.168.231.140/24
}
}
vrrp_script chk_haproxy {
script "/check/haproxy.sh"
interval 3
timeout 2
rise 2
fall 3
weight -30
}
Keepalived 的五大核心组件包括:
- VRRP 模块:实现虚拟路由冗余协议的核心逻辑。
- 健康检查模块:监控后端服务(如HAProxy)的状态。
- IPVS 集成:提供负载均衡功能(在此架构中主要使用其VIP管理能力)。
- 脚本执行器:执行用户自定义的健康检查脚本。
- 通知系统:在状态发生变化时发送告警。
Keepalived 节点的 VRRP 状态转换通常为:INIT → BACKUP → MASTER → FAULT。
其健康检查流程为:
- 开始检查:按配置的间隔启动检查。
- 执行检查:运行指定的检查脚本或命令。
- 检查结果:分析脚本或命令的返回状态码。
- 执行动作:根据结果调整本节点的VRRP优先级或状态。
故障处理机制如下:
- 主节点故障:备份节点通过选举成为新的主节点,VIP自动、无缝地转移到新主节点。
- 健康检查失败:如果主节点上的服务(如HAProxy)异常,其VRRP优先级会被降低,可能触发主备切换。
- 服务恢复:故障节点恢复后,可以自动或手动回归正常状态。
ETCD 组件
etcd 是一个基于 Raft 共识算法的分布式键值存储系统。在本架构中,它为 Patroni 集群提供强一致性的状态存储与共识服务,记录所有数据库节点的角色、选举信息和集群配置数据。它保障了 Leader 选举的唯一性,是防止集群脑裂、实现 PostgreSQL 高可用自动故障转移的基石,是构建分布式系统共识层的可靠选择。

etcd 在运行和选举过程中,主要通过以下参数进行控制:
--heartbeat-interval=100ms # Leader发送心跳的间隔
--election-timeout=1000ms # Follower等待Leader心跳的超时时间,超时则发起选举
--min-election-timeout=1000ms # 最小选举超时
--max-election-timeout=2000ms # 最大选举超时
--initial-election-tick-advance=true # 初始选举优化
Raft 选举的基本流程如下:
- Follower状态:节点启动后处于Follower状态,等待Leader的心跳。
- 选举超时:如果Follower在
election-timeout 时间内未收到Leader心跳,则触发选举。
- Candidate状态:该节点转变为Candidate,增加自己的任期(term),并向集群其他节点发起投票请求。
- 收集选票:Candidate收集其他节点的投票。
- 选举Leader:如果Candidate获得了集群中多数节点的投票,则成为新的Leader。
- Leader状态:新Leader开始向所有Follower定期发送心跳,以维持自己的领导地位。
watchdog 组件
Watchdog 是一个监控和恢复机制。它接收来自 Patroni 的定时心跳,以监控 Patroni 进程的运行状态。一旦检测到进程假死、无响应或心跳超时,Watchdog 会立即触发服务重启甚至系统级恢复,强制终止异常节点,从而有效防止因软件僵死导致的脑裂问题,保障数据库集群的一致性和可用性。

其配置通常集成在 Patroni 配置文件和 systemd 服务单元中:
# Patroni配置中的Watchdog部分
watchdog:
mode: automatic
safety_margin: 5
interval: 10
timeout: 30
# Systemd服务配置
[Service]
WatchdogSec=30s
Restart=on-failure
RestartSec=1s
StartLimitBurst=3
StartLimitInterval=60s
Watchdog 机制涉及四个核心部分:
- Watchdog:进程监控器,负责接收和验证 Patroni 的心跳。
- Patroni:PostgreSQL 高可用管理器,定期向 Watchdog 发送心跳信号。
- Systemd:Linux 系统的服务管理器,根据 Watchdog 信号处理服务重启。
- Linux Kernel Watchdog:内核级的看门狗,提供硬件级别的最终保障。
心跳检测机制如下:
- 心跳发送:Patroni 进程每隔
interval(如10秒)发送一次心跳。
- 心跳接收:Watchdog 监控并记录最后一次收到心跳的时间。
- 超时检测:如果在
timeout(如30秒)内未收到心跳,则判定为故障。
- 安全裕度:
safety_margin(如5秒)提供了一个缓冲时间,避免因微小延迟误触发。
Watchdog 主要处理以下故障场景:
- 脑裂风险:防止出现多个主节点同时写入。
- 进程假死:Patroni 进程存在但已失去响应,无法执行故障转移。
- 网络分区:节点与 etcd 集群隔离,但本地进程仍在运行。
- 资源耗尽:因 CPU、内存或磁盘资源不足导致进程卡死。
恢复处理流程:
- 故障检测:Watchdog 检测到心跳超时。
- 告警通知:触发告警,通知管理员和监控系统。
- 服务重启:Systemd 尝试重启 Patroni 服务。
- 系统重启:如果服务重启无效,内核 Watchdog 作为最后手段可能触发系统重启。
- 恢复验证:节点恢复后,检查其在集群中的状态和数据一致性。
防止脑裂的工作原理:
- 心跳超时检测:确保在设定时间(如30秒)内无响应的节点被强制停止。
- 自动重启机制:防止故障节点继续以“主节点”身份接受写入,造成数据分歧。
- 集群状态验证:节点恢复后,会从 etcd 获取最新的集群状态,确保与共识层一致。
- 数据一致性检查:通过对比 WAL 日志位置,确保不会引入数据不一致。
Patroni 组件
Patroni 是 PostgreSQL 高可用架构中的“大脑”或“管家”。它基于 etcd 提供的分布式共识,实现了自动化的主备选举、故障检测与故障转移。Patroni 实时监控数据库实例的状态,维护集群配置的一致性,协调主备节点间的流复制,并有效防止脑裂。它还能与 HAProxy 等负载均衡器无缝集成,实现流量的智能路由,使得 PostgreSQL 集群的高可用运维实现高度自动化。

其关键配置参数决定了集群的行为:
dcs:
ttl: 30 # 存储在etcd中的Leader租约超时时间
loop_wait: 10 # Patroni主循环周期,即状态检查/心跳间隔
retry_timeout: 10 # 操作重试的超时时间
maximum_lag_on_failover: 1048576 # 故障转移时允许的从库最大复制延迟(字节)
election:
retry_timeout: 10 # 选举失败后的重试超时
maximum_retry_timeout: 30 # 选举重试的最大超时
retry_interval: 2 # 选举重试间隔
priority: 100 # 节点的优先级(用于选举排序)
Patroni 的五大核心组件:
- 选举管理器:处理集群节点的状态转换和领导者选举投票。
- 健康监控器:持续检测本地 PostgreSQL 服务及节点的健康状态。
- etcd 客户端:与 etcd 集群交互,实现分布式锁、租约和状态存储。
- PostgreSQL 管理器:根据节点角色(Leader/Follower)控制 PostgreSQL 的启动、停止、提升和降级。
- REST API:提供 HTTP 接口,用于查询集群状态和进行手动干预(如故障切换)。
Patroni 管理的节点状态转换流程通常为:
- 启动或恢复:
INIT → FOLLOWER → CANDIDATE → LEADER
- 降级:
LEADER → DEMOTED → FOLLOWER
故障转移与选举流程:
- 故障检测:Follower 节点检测到 Leader 心跳超时(通过etcd)或服务不可用。
- 触发选举:符合条件的 Follower 转换为 CANDIDATE 状态。
- 竞选投票:Candidate 向 etcd 发起竞选请求(获取分布式锁)。
- 收集选票:本质上是通过 etcd 的原子操作和租约机制模拟“投票”,获得多数认可。
- 选举领导者:成功获得锁(即“当选”)的节点成为新 Leader。
- 提升 Postgres:新 Leader 节点上的 Patroni 将本地 PostgreSQL 实例提升为主库(如果之前是从库)。
- 通知集群:更新 etcd 中的 Leader 信息,并通知其他组件(如HAProxy)和节点。
Patroni 防止脑裂的机制:
- etcd 共识保障:所有状态变更都通过 etcd 的原子操作和租约完成,确保同一时刻只有一个节点能成为 Leader。
- 多数票原则:Leader 必须能与 etcd 集群多数节点通信并维持租约,网络分区中的旧 Leader 会因租约过期自动降级。
- 自动降级机制:故障节点或分区中的节点恢复后,会从 etcd 读取最新状态,如果已有新 Leader,则自动降级为 Follower,从而保证数据一致性。