PostgreSQL:Patroni 高可用架构,基于etcd的自动故障转移集群

文章目录

    • 一、核心概念与架构
      • [1.1 Patroni 是什么?](#1.1 Patroni 是什么?)
      • [1.2 架构组成](#1.2 架构组成)
    • 二、环境规划
      • [2.1 节点角色与 IP](#2.1 节点角色与 IP)
    • [三、部署 etcd 集群](#三、部署 etcd 集群)
      • [3.1 安装 etcd(以 CentOS 为例)](#3.1 安装 etcd(以 CentOS 为例))
      • [3.2 创建配置文件](#3.2 创建配置文件)
      • [3.3 启动 etcd](#3.3 启动 etcd)
      • [3.4 验证 etcd 集群](#3.4 验证 etcd 集群)
    • [四、部署 PostgreSQL 与 Patroni](#四、部署 PostgreSQL 与 Patroni)
      • [4.1 安装 PostgreSQL(所有 PG 节点)](#4.1 安装 PostgreSQL(所有 PG 节点))
      • [4.2 安装 Patroni](#4.2 安装 Patroni)
      • [4.3 配置 Patroni(以 pg1 为例)](#4.3 配置 Patroni(以 pg1 为例))
      • [4.4 创建数据目录并授权](#4.4 创建数据目录并授权)
      • [4.5 启动 Patroni](#4.5 启动 Patroni)
    • 五、集群状态验证
      • [5.1 使用 patronictl 查看集群](#5.1 使用 patronictl 查看集群)
      • [5.2 访问 REST API](#5.2 访问 REST API)
      • [5.3 验证流复制](#5.3 验证流复制)
    • 六、自动故障转移演练
      • [6.1 模拟主库宕机](#6.1 模拟主库宕机)
      • [6.2 观察自动切换](#6.2 观察自动切换)
      • [6.3 验证新主库](#6.3 验证新主库)
      • [6.4 恢复原主库](#6.4 恢复原主库)
    • 七、应用连接与负载均衡(HAProxy)
      • [7.1 安装 HAProxy](#7.1 安装 HAProxy)
      • [7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg)](#7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg))
      • [7.3 启动 HAProxy](#7.3 启动 HAProxy)
    • 八、高级特性与运维
      • [8.1 手动切换(Switchover)](#8.1 手动切换(Switchover))
      • [8.2 配置动态重载](#8.2 配置动态重载)
      • [8.3 备份集成](#8.3 备份集成)
      • [8.4 监控与告警](#8.4 监控与告警)
    • 九、常见问题与实践建议
      • [9.1 脑裂防护](#9.1 脑裂防护)
      • [9.2 网络分区(Split-Brain)](#9.2 网络分区(Split-Brain))
      • [9.3 性能建议](#9.3 性能建议)
      • [9.4 安全加固](#9.4 安全加固)

PostgreSQL 本身具备强大的流复制能力,但缺乏内置的自动故障转移(Automatic Failover)机制。为实现生产级高可用(High Availability, HA),社区广泛采用 Patroni 配合分布式协调服务(如 etcd、Consul、ZooKeeper)构建自动主备切换集群。本文将从原理、架构、部署、配置、演练到运维,详解基于 etcd 的 Patroni 高可用集群。


一、核心概念与架构

1.1 Patroni 是什么?

Patroni 是一个用 Python 编写的 PostgreSQL 高可用模板(Template),由 Zalando 开发并开源。它不修改 PostgreSQL 源码,而是通过外部守护进程(Daemon)管理 PostgreSQL 实例的生命周期,结合分布式协调器(DCS)实现:

  • 自动主节点选举(Leader Election)
  • 故障检测与自动 Failover
  • 在线配置重载
  • RESTful API 管理接口
  • 支持多副本、同步/异步复制、级联复制等

1.2 架构组成

一个典型的 Patroni + etcd 高可用集群包含以下组件:

组件 作用
PostgreSQL 实例 数据库服务,运行在每个节点
Patroni 进程 每个节点运行一个,监控本地 PG 状态,与 DCS 通信
etcd 集群 分布式键值存储,用于存储集群元数据、锁、Leader 信息(至少 3 节点以容忍 1 节点故障)
HAProxy / PgBouncer(可选) 应用连接代理,自动路由读写请求
VIP / DNS / Service Discovery(可选) 提供统一访问入口

典型拓扑(3 节点 PG + 3 节点 etcd):

复制代码
+----------------+     +----------------+     +----------------+
|   PG Node 1    |     |   PG Node 2    |     |   PG Node 3    |
| - PostgreSQL   |     | - PostgreSQL   |     | - PostgreSQL   |
| - Patroni      |<--->| - Patroni      |<--->| - Patroni      |
+----------------+     +----------------+     +----------------+
        ^                      ^                      ^
        |                      |                      |
        +----------------------+----------------------+
                               |
                         +-------------+
                         |  etcd Cluster (3 nodes) |
                         +-------------+

注意:etcd 可独立部署,也可与 PG 节点共存(不推荐生产环境混部)。


二、环境规划

2.1 节点角色与 IP

主机名 IP 地址 角色 说明
etcd1 192.168.10.10 etcd etcd 集群成员
etcd2 192.168.10.11 etcd etcd 集群成员
etcd3 192.168.10.12 etcd etcd 集群成员
pg1 192.168.10.20 PostgreSQL + Patroni 数据库节点
pg2 192.168.10.21 PostgreSQL + Patroni 数据库节点
pg3 192.168.10.22 PostgreSQL + Patroni 数据库节点
haproxy 192.168.10.30 HAProxy 应用连接代理(可选)

操作系统:CentOS 7 或 Ubuntu 20.04+

PostgreSQL 版本:14(所有 PG 节点版本必须一致)

Python 版本:3.6+


三、部署 etcd 集群

etcd 是 Patroni 的"大脑",用于存储集群状态。需部署奇数节点(3、5...)以支持容错。

3.1 安装 etcd(以 CentOS 为例)

在 etcd1、etcd2、etcd3 上执行:

bash 复制代码
# 下载 etcd
ETCD_VER=v3.5.10
wget https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf etcd-${ETCD_VER}-linux-amd64.tar.gz
sudo cp etcd-${ETCD_VER}-linux-amd64/etcd* /usr/local/bin/

3.2 创建配置文件

以 etcd1 为例(/etc/etcd/etcd.conf.yml):

yaml 复制代码
name: etcd1
data-dir: /var/lib/etcd
listen-peer-urls: http://192.168.10.10:2380
listen-client-urls: http://192.168.10.10:2379,http://127.0.0.1:2379
initial-advertise-peer-urls: http://192.168.10.10:2380
advertise-client-urls: http://192.168.10.10:2379
initial-cluster: etcd1=http://192.168.10.10:2380,etcd2=http://192.168.10.11:2380,etcd3=http://192.168.10.12:2380
initial-cluster-token: pg-etcd-token
initial-cluster-state: new

etcd2 和 etcd3 修改 name、IP 即可。

3.3 启动 etcd

创建 systemd 服务(/etc/systemd/system/etcd.service):

ini 复制代码
[Unit]
Description=etcd
After=network.target

[Service]
Type=exec
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.conf.yml
Restart=always
User=root

[Install]
WantedBy=multi-user.target

启动:

bash 复制代码
sudo systemctl daemon-reload
sudo systemctl enable --now etcd

3.4 验证 etcd 集群

在任意 etcd 节点执行:

bash 复制代码
etcdctl --endpoints=http://192.168.10.10:2379,http://192.168.10.11:2379,http://192.168.10.12:2379 endpoint health

应全部返回 healthy


四、部署 PostgreSQL 与 Patroni

4.1 安装 PostgreSQL(所有 PG 节点)

bash 复制代码
# CentOS
sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo yum install -y postgresql14-server postgresql14-contrib

# 不要初始化!Patroni 会自动初始化

4.2 安装 Patroni

bash 复制代码
sudo yum install -y python3-pip
pip3 install patroni[etcd] psycopg2-binary

验证安装:

bash 复制代码
patroni --version

4.3 配置 Patroni(以 pg1 为例)

创建 /etc/patroni/patroni.yml

yaml 复制代码
scope: pg-ha-cluster                 # 集群名称,所有节点必须一致
namespace: /service/                 # etcd 中的命名空间
name: pg1                            # 本节点名称,必须唯一

restapi:
  listen: 192.168.10.20:8008         # Patroni API 监听地址
  connect_address: 192.168.10.20:8008

etcd:
  hosts:                             # etcd 集群地址
    - 192.168.10.10:2379
    - 192.168.10.11:2379
    - 192.168.10.12:2379

bootstrap:
  dcs:
    ttl: 30                          # Leader 锁 TTL(秒)
    loop_wait: 10                    # 主循环间隔
    retry_timeout: 10                # 操作超时
    maximum_lag_on_failover: 1048576 # 最大允许延迟(字节),约 1MB
    synchronous_mode: false          # 是否启用同步复制
    postgresql:
      use_pg_rewind: true            # 故障恢复时使用 pg_rewind
      use_slots: true                # 使用复制槽防止 WAL 过早清理
      parameters:
        wal_level: replica
        hot_standby: "on"
        max_connections: 200
        max_wal_senders: 10
        wal_keep_size: 1GB
        max_replication_slots: 10
        checkpoint_completion_target: 0.9
        archive_mode: "off"

  initdb:
    - encoding: UTF8
    - data-checksums                # 启用数据页校验,pg_rewind 必需

  users:
    admin:                          # 初始化超级用户
      password: admin123
      options:
        - createrole
        - createdb

postgresql:
  listen: 192.168.10.20:5432        # PostgreSQL 监听地址
  connect_address: 192.168.10.20:5432
  data_dir: /var/lib/pgsql/14/data
  bin_dir: /usr/pgsql-14/bin
  authentication:
    replication:
      username: replicator
      password: replpass123
    superuser:
      username: postgres
      password: postgres123
  parameters:
    unix_socket_directories: '/tmp'

tags:
  nofailover: false                  # 允许参与 Failover
  noloadbalance: false               # 允许负载均衡(读)
  clonefrom: true                    # 允许其他节点从此节点克隆

关键说明

  • scope 必须全局唯一,用于隔离多个 Patroni 集群;
  • use_pg_rewind: true 要求 data-checksumswal_log_hints = on
  • 所有 PG 节点的 patroni.yml 仅需修改 namelistenconnect_address

4.4 创建数据目录并授权

bash 复制代码
sudo mkdir -p /var/lib/pgsql/14/data
sudo chown postgres:postgres /var/lib/pgsql/14/data

4.5 启动 Patroni

切换到 postgres 用户启动(避免权限问题):

bash 复制代码
sudo -u postgres patroni /etc/patroni/patroni.yml

首次启动时,Patroni 会:

  1. 在 etcd 中注册集群;
  2. 执行 initdb 初始化数据库;
  3. 设置复制用户和参数;
  4. 自动选举第一个启动的节点为主库(Leader)。

建议使用 systemd 管理 Patroni 进程(见附录)。


五、集群状态验证

5.1 使用 patronictl 查看集群

在任意 PG 节点执行:

bash 复制代码
patronictl -c /etc/patroni/patroni.yml list

输出示例:

复制代码
+ Cluster: pg-ha-cluster (7073733813877921290) -------+----+-----------+
| Member | Host           | Role    | State   | TL | Lag in MB |
+--------+----------------+---------+---------+----+-----------+
| pg1    | 192.168.10.20  | Leader  | running |  1 |         0 |
| pg2    | 192.168.10.21  | Replica | running |  1 |         0 |
| pg3    | 192.168.10.22  | Replica | running |  1 |         0 |
+--------+----------------+---------+---------+----+-----------+

5.2 访问 REST API

bash 复制代码
curl http://192.168.10.20:8008/cluster
curl http://192.168.10.20:8008/primary  # 返回 200 表示是主库

5.3 验证流复制

在主库执行:

sql 复制代码
SELECT * FROM pg_stat_replication;

应看到两个备库连接。


六、自动故障转移演练

6.1 模拟主库宕机

在 pg1 上 kill Patroni 进程:

bash 复制代码
sudo pkill -f "patroni /etc/patroni/patroni.yml"

6.2 观察自动切换

  • etcd 在 30 秒(ttl)内未收到 pg1 的心跳;
  • Patroni 在 pg2/pg3 上检测到 Leader 失联;
  • 触发选举,其中一个备库(如 pg2)自动 promote 为主库;
  • 原主库(pg1)重启后自动 rejoin 为备库。

查看日志(/var/log/patroni.log 或终端输出)可看到:

复制代码
2026-02-10 19:30:00,000 INFO: Lock owner: pg1; I am pg2
2026-02-10 19:30:05,000 INFO: does not have lock
2026-02-10 19:30:10,000 INFO: demoting self because leader is not available
2026-02-10 19:30:15,000 INFO: promoting to leader

6.3 验证新主库

bash 复制代码
patronictl -c /etc/patroni/patroni.yml list
# pg2 应变为 Leader

# 在 pg2 上写入数据
psql -h 192.168.10.21 -U admin -d postgres -c "CREATE TABLE test(id int);"

6.4 恢复原主库

重启 pg1 上的 Patroni:

bash 复制代码
sudo -u postgres patroni /etc/patroni/patroni.yml

Patroni 会自动:

  1. 检测到自己不是 Leader;
  2. 使用 pg_rewind 同步差异(因 use_pg_rewind: true);
  3. 启动为备库。

若未启用 checksums,Patroni 会使用 pg_basebackup 重建,耗时较长。


七、应用连接与负载均衡(HAProxy)

为避免应用硬编码主库 IP,使用 HAProxy 提供统一入口。

7.1 安装 HAProxy

在 haproxy 节点:

bash 复制代码
sudo yum install -y haproxy

7.2 配置 HAProxy(/etc/haproxy/haproxy.cfg)

cfg 复制代码
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

defaults
    mode                    tcp
    log                     global
    option                  tcplog
    timeout connect         10s
    timeout client          1h
    timeout server          1h

# 写请求:只路由到主库
frontend pg_write
    bind *:5000
    default_backend pg_primary

# 读请求:可路由到主库或备库
frontend pg_read
    bind *:5001
    default_backend pg_replicas

backend pg_primary
    option httpchk GET /primary
    http-check expect status 200
    server pg1 192.168.10.20:5432 maxconn 100 check port 8008
    server pg2 192.168.10.21:5432 maxconn 100 check port 8008
    server pg3 192.168.10.22:5432 maxconn 100 check port 8008

backend pg_replicas
    option httpchk GET /replica
    http-check expect status 200
    server pg1 192.168.10.20:5432 maxconn 100 check port 8008
    server pg2 192.168.10.21:5432 maxconn 100 check port 8008
    server pg3 192.168.10.22:5432 maxconn 100 check port 8008

7.3 启动 HAProxy

bash 复制代码
sudo systemctl enable --now haproxy

应用连接:

  • 写:host=192.168.10.30 port=5000
  • 读:host=192.168.10.30 port=5001

八、高级特性与运维

8.1 手动切换(Switchover)

计划内维护时,安全切换主库:

bash 复制代码
patronictl -c /etc/patroni/patroni.yml switchover
# 交互式选择新主库

或指定目标:

bash 复制代码
patronictl -c /etc/patroni/patroni.yml switchover --candidate pg2

8.2 配置动态重载

修改 patroni.yml 后无需重启:

bash 复制代码
patronictl -c /etc/patroni/patroni.yml reload pg1

8.3 备份集成

Patroni 支持与 WAL-G、pgBackRest 集成,实现 PITR。

8.4 监控与告警

  • 使用 patronictl list 或 API 获取状态;
  • Prometheus exporter:patroni --config-file ... --prometheus-port 9369
  • 告警指标:复制延迟、主库宕机、etcd 不可用。

九、常见问题与实践建议

9.1 脑裂防护

  • etcd 的 TTL 机制确保同一时间只有一个 Leader;
  • 不要手动干预 PostgreSQL 角色(如直接执行 pg_ctl promote)。

9.2 网络分区(Split-Brain)

  • etcd 需多数派存活(3 节点最多容忍 1 节点故障);
  • 避免将 etcd 与 PG 混部在同一物理机。

9.3 性能建议

  • etcd 使用 SSD 存储;
  • Patroni 节点时间同步(NTP);
  • 合理设置 ttlloop_wait(通常 ttl > 2 * loop_wait)。

9.4 安全加固

  • etcd 启用 TLS;
  • Patroni API 启用认证;
  • PostgreSQL 使用 scram-sha-256 密码加密。

总结:基于 Patroni + etcd 的 PostgreSQL 高可用架构具备以下优势:

  • 自动化:故障检测、Failover、Rejoin 全自动;
  • 安全:通过 DCS 避免脑裂;
  • 灵活:支持同步/异步、多副本、级联;
  • 可观测:提供 REST API 和 CLI 工具;
  • 生态友好:与 Kubernetes(Spilo)、Prometheus、HAProxy 无缝集成。
相关推荐
失忆爆表症10 小时前
03_数据库配置指南:PostgreSQL 17 + pgvector 向量存储
数据库·postgresql
牛奶11 小时前
《前端架构设计》:除了写代码,我们还得管点啥
前端·架构·设计
苏渡苇13 小时前
Java + Redis + MySQL:工业时序数据缓存与持久化实战(适配高频采集场景)
java·spring boot·redis·后端·spring·缓存·架构
麦聪聊数据13 小时前
如何用 B/S 架构解决混合云环境下的数据库连接碎片化难题?
运维·数据库·sql·安全·架构
2的n次方_14 小时前
CANN HCOMM 底层架构深度解析:异构集群通信域管理、硬件链路使能与算力重叠优化机制
架构
技术传感器14 小时前
大模型从0到精通:对齐之心 —— 人类如何教会AI“好“与“坏“ | RLHF深度解析
人工智能·深度学习·神经网络·架构
数据知道15 小时前
PostgreSQL:如何实现数据恢复?
数据库·postgresql
小北的AI科技分享15 小时前
万亿参数时代:大语言模型的技术架构与演进趋势
架构·模型·推理
IvorySQL17 小时前
无需修改内核即可为 PostgreSQL 数据库对象添加自定义属性
数据库·postgresql·开源
一条咸鱼_SaltyFish17 小时前
从零构建个人AI Agent:Node.js + LangChain + 上下文压缩全流程
网络·人工智能·架构·langchain·node.js·个人开发·ai编程