基于 pg_auto_failover的高可用PostgreSQL

基于 pg_auto_failover

参考:

pg_auto_failover 的需求和能力列表

1. 需求(安装和运行所需条件)

基础要求

条件 详细信息
PostgreSQL 版本 支持 PostgreSQL 13-17
操作系统 Linux(推荐)或 macOS(可测试)
依赖软件 pg_autoctl(pg_auto_failover CLI 工具),PostgreSQL 服务器
存储和网络 主库与备库网络稳定,支持同步或异步复制
监控节点 至少 3 台服务器 才能完全避免脑裂

架构要求

条件 详细信息
最少服务器数量 2 台(但可能会有脑裂风险)
推荐服务器数量 3 台(主库、备库、监控)
数据复制方式 默认异步(提高性能),支持同步(提高一致性)
PostgreSQL 流复制 pg_auto_failover 自动管理

2. 能力(提供的功能)

高可用性(HA)

功能 详细信息
自动主备切换 主节点故障时,自动提升备节点
自动重新加入集群 原主节点恢复后自动降级为备节点
数据复制 默认异步(高性能),支持同步复制(一致性更高)
防止脑裂 需要监控节点2 台服务器时可能有脑裂风险

集群管理

功能 详细信息
自动 PostgreSQL 配置 无需手动配置流复制
集群状态管理 监控节点会管理 主/备/备用节点
拓扑结构 仅支持 主 - 备架构(1 主 1 备)
自动故障检测 检测主库状态,自动触发故障转移

管理与监控

功能 详细信息
管理工具 pg_autoctl CLI 提供简单的命令行管理
集群健康检查 定期检查节点状态
支持日志与指标监控 可与 Prometheus/Grafana 结合

额外特性

功能 详细信息
支持 Load Balancer 允许配置读流量均衡
多数据中心部署 支持跨数据中心 HA(需要额外配置)

3. 适用场景

适用 详细信息
适用于 高可用 PostgreSQL、自动主备切换、低运维成本
不适用于 仅 2 台服务器(可能有脑裂风险),多主架构

4. 典型架构

组件 作用
主库(Primary) 处理所有写请求,并同步数据到备库
备库(Secondary) 复制主库数据,在主库故障时提升为新主库
监控节点(Monitor) 负责监控集群状态,决定故障转移

5. 结论

  • 如果有 3 台服务器(主、备、监控)→ pg_auto_failover 是合适的,自动化程度高,管理简单
  • 如果只有 2 台服务器可能会有脑裂风险,建议改用 Patroni + watchdog

安装部署

定义基本信息

本次安装的环境:

环境
操作系统 Ubuntu 22.04.5 LTS
数据库 PostgreSQL 14.15 (Ubuntu 14.15-0ubuntu0.22.04.1)
pg-auto-failover-cli 1.6.3-1
仲裁机 192.192.192.9
主机 192.192.192.204
备机 192.192.192.205
VIP 192.192.192.260

在每个主机执行:

# 可能需要修改的
monitor_ip="192.192.192.9" # 仲裁
primary_ip="192.192.192.204" # 主
secondary_ip="192.192.192.205" # 备

# 几个路径,可以在后边安装好数据库后再确认
pgdata="/var/lib/postgresql/14/main/"
pgbin="/usr/lib/postgresql/14/bin"
pgconfig="/etc/postgresql/14/main/"

# 密码含有@则考虑后续命令中手动填充
autoctl_node_pwd="autoctl_ceni_2025"
replication_pwd="replication_ceni_2025"
export autoctl_node_pwd=$autoctl_node_pwd
export replication_pwd=$replication_pwd

# 无需修改
cat >> /etc/profile << EOF
export monitor_ip=$monitor_ip
export primary_ip=$primary_ip
export secondary_ip=$secondary_ip
export pgdata=$pgdata
export pgbin=$pgbin
export pgconfig=$pgconfig
export PGDATA=$pgdata
export PGHOME=$pgdata
export PATH=\$PATH:$pgbin
EOF

source /etc/profile

echo "$monitor_ip monitor" >> /etc/hosts
echo "$primary_ip primary" >> /etc/hosts
echo "$secondary_ip secondary" >> /etc/hosts

![[Pasted image 20250207132357.png]]

安装数据库

分别在主备和仲裁机上安装:

apt update
apt install -y postgresql-common
# 调整配置文件以阻止初始化cluster
echo 'create_main_cluster = false' | sudo tee -a /etc/postgresql-common/createcluster.conf

apt install -y --no-install-recommends postgresql-14
apt install -y pg-auto-failover-cli 

执行 pg_autoctl 测试:

![[Pasted image 20250206150642.png]]

仲裁节点 执行

创建监控节点:

hostnamectl set-hostname monitor

su - postgres -c "pg_autoctl create monitor --auth scram-sha-256 \
	--pgdata $pgdata \
	--pgport 5432 \
	--hostname monitor \
	--ssl-self-signed"

创建服务启动服务 (root 用户):

pg_autoctl -q show systemd --pgdata $pgdata > /usr/lib/systemd/system/pgautofailover.service

systemctl daemon-reload
systemctl enable pgautofailover.service
systemctl start pgautofailover.service
systemctl status pgautofailover.service

创建密码:

su - postgres -c "psql -d pg_auto_failover -c \"alter user autoctl_node password '$autoctl_node_pwd';\""

主节点 执行

hostnamectl set-hostname primary

su - postgres -c "pg_autoctl create postgres --auth scram-sha-256 \
	--hostname primary \
	--ssl-self-signed \
	--monitor 'postgres://autoctl_node:$autoctl_node_pwd@monitor:5432/pg_auto_failover?sslmode=require'"

su - postgres -c "pg_autoctl config set replication.password $replication_pwd"

创建服务 (root 用户):

pg_autoctl -q show systemd --pgdata $pgdata > /usr/lib/systemd/system/pgautofailover.service

systemctl daemon-reload
systemctl enable pgautofailover.service
systemctl start pgautofailover.service
systemctl status pgautofailover.service

创建流复制用户:

su - postgres -c "psql -d postgres -c \"ALTER USER pgautofailover_replicator PASSWORD '$replication_pwd';\""

备节点 执行

# 备节点要执行
hostnamectl set-hostname secondary

su - postgres -c "export PGPASSWORD=$replication_pwd; export PGSSLMODE=require; pg_autoctl create postgres --auth scram-sha-256 \
	--hostname secondary \
	--ssl-self-signed \
	--monitor 'postgres://autoctl_node:$autoctl_node_pwd@monitor:5432/pg_auto_failover?sslmode=require'"

su - postgres -c "pg_autoctl config set replication.password $replication_pwd"

创建服务启动服务 (root 用户):

pg_autoctl -q show systemd --pgdata $pgdata > /usr/lib/systemd/system/pgautofailover.service

systemctl daemon-reload
systemctl enable pgautofailover.service
systemctl start pgautofailover.service
systemctl status pgautofailover.service

查看状态

su - postgres -c "pg_autoctl show state"

![[Pasted image 20250206180129.png]]

在一主多从情况下手动提升某个从节点为主库(在要提升的从节点 node_2 上执行):

适用场景

  • 你需要强制进行主从切换,比如主节点已经失效,但故障转移没有自动发生。
  • 你希望人为干预集群的主从角色变化。
su - postgres -c "pg_autoctl perform promotion -name node_2"

如果需要手动触发主备切换:

  • 该命令用于 手动触发主备切换(switchover)。
  • 作用是 让当前 primary(主节点)降级为 secondary,同时 让当前最合适的 secondary(备节点)提升为 primary
su - postgres -c "pg_autoctl perform switchover"

![[Pasted image 20250207161040.png]]

如果有异常需要关闭 pg_autoctl :

# 1. 首先停止节点服务
su - postgres -c "pg_autoctl stop"

# 2. 删除节点
su - postgres -c "pg_autoctl drop node --pgdata $PGDATA"

# 3. 清理数据目录和配置文件
su - postgres -c "rm -rf $PGDATA/* && rm -rf /var/lib/postgresql/.local/pg_autoctl/*"

测试

当前状态 node1 为主,node2 为备

当主机关机,会检测到 timeout

![[Pasted image 20250207155841.png]]

然后备机会变为 read-write 模式

![[Pasted image 20250207155914.png]]

主机重启后,备机 node2 自动升级为 primary,原主机 node1 降级为 secondary.

![[Pasted image 20250207160051.png]]

在当前的主服务器 node2 上创建表:

su - postgres -c "psql -c \"CREATE TABLE test_table (id SERIAL PRIMARY KEY, name TEXT);INSERT INTO test_table (name) VALUES ('test');\""

在备服务器 node1 查看:

su - postgres -c "psql -c \"SELECT * FROM test_table;\""

![[Pasted image 20250207160446.png]]

如果需要在仲裁机上删除某节点:

su - postgres -c "pg_autoctl drop node --pgdata $pgdata --name node_2 --force"

创建用户

su - postgres -c "psql -c \"CREATE USER dbuser WITH PASSWORD 'gg@Ceni@2025#gg';\""

# 允许用户远程连接
echo "host    all       dbuser  192.192.192.0/24  md5" >> $pgconfig/pg_hba.conf

查看用户,应该能够在主备机上同步:

su - postgres -c "psql -c '\du'"

删除用户:

su - postgres -c "psql -c 'DROP USER dbuser;'"

注意

状态机 Monitor 挂了对集群使用完全没有影响。但是此时如果集群里面的机器在挂一台,是没有办法自动进行故障转移的,因为我们失去了状态机。

配置 Keepalived

在主机和备机安装 Keepalived

apt install keepalived

主节点配置文件:

cat > /etc/keepalived/keepalived.conf << EOF
global_defs {
   router_id LVS_DEVEL
}

vrrp_script chk_pgdb {
    script "/etc/keepalived/check.sh"
    interval 5
    weight 2
}

vrrp_instance VI_1 {
    state MASTER
    interface ens192 # 网卡编号
    virtual_router_id 99 # 同一集群内keepalived id 应该相同
    priority 100 # 优先级
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass ceni@pg@2025
    }
    virtual_ipaddress {
        192.192.192.240
    }
}
EOF

备节点配置文件:

cat > /etc/keepalived/keepalived.conf << EOF
global_defs {
   router_id LVS_DEVEL
}

vrrp_script chk_pgdb {
    script "/etc/keepalived/check.sh"
    interval 5
    weight 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens192 # 网卡编号
    virtual_router_id 99 # 同一集群内keepalived id 应该相同
    priority 99 # 优先级
    advert_int 1
    nopreemt
    authentication {
        auth_type PASS
        auth_pass ceni@pg@2025
    }
    virtual_ipaddress {
        192.192.192.240
    }
}
EOF

创建存活检查脚本和通知脚本

cat > /etc/keepalived/check.sh << EOF
#!/bin/sh
# 本脚本用于 Keepalived 检查 PostgreSQL 状态,通过 su - postgres -c "psql -qtAX -c \"select pg_is_in_recovery();\""
# 执行查找 pg_is_in_recovery() 的结果。如果结果为 "f",表示当前节点为主节点;否则为备节点。
#
# 请确保:
# 1. psql 已安装,且当前系统中 postgres 用户正常配置了访问 PostgreSQL 的权限。
# 2. 日志输出使用 logger (可选) 以便调试。

# 执行 SQL 查询,获取 pg_is_in_recovery() 返回值
ROLE=$(su - postgres -c "psql -qtAX -c \"select pg_is_in_recovery();\"" 2>/dev/null)

# 记录日志
logger "Keepalived check: pg_is_in_recovery() returned '${ROLE}'"

# 判断结果,如果返回 f 则为主节点
if [ "$ROLE" = "f" ]; then
    exit 0
else
    exit 1
fi
EOF
cat > /etc/keepalived/notify.sh << EOF
#!/bin/bash
# notify.sh

echo $1 $2 is in $3 state > /etc/keepalived/keepalive.$1.$2.state
EOF
cat >  /etc/keepalived/getstate.sh << EOF
#!/bin/bash
# getstate.sh

cat /etc/keepalived/keepalive.*.*.state
EOF

启动服务

systemctl enable --now keepalived
systemctl status keepalived.service
posted @ 2025-03-24 10:35  带着泥土  阅读(522)  评论(0)    收藏  举报