PostgreSQL高可用实战:repmgr+keepalived主从复制与自动故障转移
1. 先说结论:这套架构到底解决什么问题
看到这个标题,很多刚接触PostgreSQL的同学第一反应是:数据库不是装好就能用吗?为什么要搞主从复制,还要再套一个keepalived?我2018年第一次在生产环境做PG高可用时也是这个想法,直到某天凌晨两点主库磁盘满掉、业务直接停摆,才真正理解“高可用”这三个字的分量。
PostgreSQL本身是一个单机数据库,它没有内置的自动故障转移能力。你可以用自带的流复制搭出一主一备,但备库不会主动变成主库,应用也不能自动把连接切过去。这套方案要解决的,就是两个核心问题:
- 数据冗余 :主库的数据实时同步到备库,主库出故障时数据不丢或尽量少丢。
- 自动切换 :主库故障后,备库自动提升为新主库,业务通过VIP(虚拟IP)无感知地切换到新主库上继续跑。
这套方案的主角是三个:PostgreSQL负责数据存储和流复制,repmgr负责主备关系管理、监控和故障时的角色提升,keepalived负责VIP漂移。repmgr管“谁能当主”,keepalived管“流量怎么过去”,两者各管一层,配合起来就是一个完整的数据库高可用闭环。
我写的这篇东西,适合三类人:一是刚接手PG数据库运维、想搭一套靠谱高可用架构的DBA;二是公司没有专职DBA、需要自己动手的运维或后端开发;三是为了应付技术面试、想把这个经典组合讲明白的求职者。我会按从零开始的顺序,把安装、配置、故障演练、踩坑全部走一遍,所有命令都是我在CentOS 7.9和Rocky Linux 8上实测过可用的。
提示:本文所有命令和配置均在RHEL系发行版上验证,Debian/Ubuntu系的路径和包管理器略有差异,但思路完全一致,照着调整即可。
2. 方案选型:为什么是repmgr + keepalived,而不是Patroni或Pacemaker
2.1 三个方案的横向对比
在动手安装之前,先花点时间聊方案选型。PostgreSQL的高可用方案现在主流有三类: repmgr + keepalived 、 Patroni + etcd/consul 、 Pacemaker + corosync (通常配DRBD或共享存储)。我见过太多人上来就装,装到中途发现架构不匹配,推倒重来,所以在第一步就把优劣说清楚。
| 特性 | repmgr + keepalived | Patroni + etcd/consul | Pacemaker + corosync |
|---|---|---|---|
| 架构复杂度 | 低,两个组件 | 较高,需额外维护一致性组件 | 高,集群资源管理复杂 |
| 对脑裂的处理 | 依赖repmgr判断+脚本兜底 | 通过etcd/consul分布式锁 | 有Fencing机制,更严格 |
| 学习成本 | 中等 | 较高 | 高 |
| 适用规模 | 中小型业务,2~3节点 | 中大型业务,多节点动态选主 | 传统企业,与存储强绑定 |
| 自动故障转移 | 支持,repmgrd守护进程 | 支持,Raft共识 | 支持,资源约束 |
2.2 我这套方案的优势和边界
repmgr + keepalived最核心的优势是 轻 。它不需要额外引入一套分布式协调组件,也没有那么多资源约束概念,一台主库、一台备库、一个VIP,三个组件就能转起来。对于用户量几万、QPS几千的业务,这个架构完全够用,而且排障链路短:数据库层的问题看repmgr日志,网络层的问题看keepalived日志,心智负担很小。
那它的边界在哪里?最大的风险是 脑裂 ,也就是主备都认为自己是主、都尝试对外提供服务。keepalived的VRRP协议只能在网络层面保证同一时刻只有一个节点持有VIP,但如果网络出现瞬断,两个节点可能同时进入MASTER状态。repmgr这边,repmgrd在检测到主库失联后会尝试将备库提升为primary,但网络抖动也可能触发误切换。
应对这个风险,业界通用的做法是 加仲裁节点 。最少用三台机器,仲裁节点不跑数据库,只参与keepalived的投票和repmgr的见证。条件不允许的话,至少要在repmgr配置里关闭自动切换、改为半自动(failover=manual),或者把repmgrd的检测时间和重试次数调大,用时间维度过滤瞬断。
2.3 什么时候不要选这套方案
我必须说清楚,如果你的业务对数据一致性要求极其苛刻——比如金融交易、订单流水,RPO接近零,那这套架构不达标。repmgr的默认流复制是异步的,主库崩溃瞬间未同步的事务可能丢失。这时候你该考虑 同步复制 (synchronous_commit = on + synchronous_standby_names),或者直接上Patroni配同步节点、甚至上共享存储+Pacemaker。另外如果节点超过三个、需要动态扩缩容,Patroni的Raft选主机制明显更顺手。
我实际的经验是: 3节点以下、没有专职DBA、想快速交付一套能用的高可用环境,repmgr + keepalived是最优解;节点多、要求强一致、有专门基础架构团队,直接Patroni别犹豫。 剩下的内容都围绕前者展开。
3. 环境准备:三件套缺一不可
3.1 主机规划与版本确认
我先给出一套我在生产环境验证过的标准规划。两台数据库节点加一台可选的监控(见证)节点,CPU内存按业务量来,至少2C4G起步。操作系统用的是Rocky Linux 8.8(CentOS 7.9同样适用,只是PG编译依赖会老一点)。
| 主机名 | IP | 角色 | 说明 |
|---|---|---|---|
| pg-primary | 192.168.10.11 | 主库节点 | 初始主库,跑PG + repmgr + keepalived |
| pg-standby | 192.168.10.12 | 备库节点 | 初始备库,跑PG + repmgr + keepalived |
| pg-witness | 192.168.10.13 | 见证节点 | 可选,跑repmgr witness + keepalived仲裁 |
| VIP | 192.168.10.100 | 虚拟IP | 应用连接入口,正常时绑定在主库节点 |
这里有个很多人踩过的坑: VIP网段必须和业务网卡在同一个二层网络 ,不然VRRP广播报文传不过去,keepalived漂移根本不会发生。如果你跨机房做多活,需要单独规划三层路由,那是另一套方案了,本文不展开。
PostgreSQL版本方面,我做这套环境时用的16.3。选16的原因有三个:逻辑复制在16上重大改进、流复制协议稳定、repmgr官方对16的支持已经非常成熟。如果你有历史包袱在跑11或12,repmgr 5.x也有对应支持,别盲目升大版本,PG的大版本升级不是小事。
3.2 系统基础配置:10分钟搞定
主机名、hosts解析、免密SSH、时间同步、防火墙,这五项是集群的“地基”,任何一个出问题都会让你在后面排查到怀疑人生。
第一,设置主机名并写入hosts。keepalived和repmgr都是靠主机名通信的,主机名不一致直接报错。
hostnamectl set-hostname pg-primary
cat >> /etc/hosts <<EOF
192.168.10.11 pg-primary
192.168.10.12 pg-standby
192.168.10.13 pg-witness
EOF
第二,配置双节点免密SSH登录。repmgr的standby clone操作要通过SSH在备库上执行,密码交互会导致脚本卡死。
ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa
ssh-copy-id pg-primary
ssh-copy-id pg-standby
第三,时间同步。流复制对时间偏差没那么敏感,但事务时间戳和日志排查需要一致的时间线,用chrony同步一下更稳妥。
dnf install -y chrony
systemctl enable --now chronyd
chronyc sources -v
第四,关闭SELinux,放行(或关闭)防火墙。生产环境安全要求高的话,只放行必要的端口:PostgreSQL的5432,repmgr的SSH通信端口,keepalived的VRRP协议(通常是IP协议112号以及Windows下组播地址224.0.0.18)。我为了演示方便直接关掉了防火墙,但你要知道哪些端口是必需的。
setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
systemctl stop firewalld
systemctl disable firewalld
第五,确认两台节点的locale和时区一致,避免复制时文本编码问题。
timedatectl set-timezone Asia/Shanghai
localectl set-locale LANG=en_US.UTF-8
3.3 PostgreSQL安装:编译还是二进制包
这一步我纠结过很久。二进制包安装快、省心,但自定义编译参数受限;源码编译灵活、可控,但费时且依赖多。如果你用的是CentOS 7.9,PGDG官方源里就有编译好的RPM包,直接装;如果你用的是Rocky 8/9或者想用PG 16的最新小版本,RPM包也覆盖到了。我的建议是: 能用RPM包就别编译 ,生产环境稳定优先,别在安装上折腾。
dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
dnf -y install postgresql16-server postgresql16
但要注意,PGDG源在安装后不会自动初始化数据目录,需要手动执行:
/usr/pgsql-16/bin/postgresql-16-setup initdb
systemctl enable --now postgresql-16
初始化完成的第一步就是修改postgres用户密码和设置默认连接:
su - postgres
psql -c "ALTER USER postgres WITH PASSWORD 'YourStrongPass';"
exit
到这里,单机PG已经跑起来了。但注意,这只是起点。接下来要做的所有配置,都是为了让它具备“被管理”的条件。
4. PostgreSQL基础配置:高可用的地基
4.1 必须改的postgresql.conf参数
repmgr的主从复制基于PostgreSQL的WAL日志流复制。要让复制正常工作,主库必须开启wal_level、配置足够的max_wal_senders和wal_keep_size,备库要配置hot_standby。这些参数在PG 16里默认值比老版本友好,但高可用场景下仍要显式确认。
su - postgres
vi /var/lib/pgsql/16/data/postgresql.conf
我贴上生产环境经过验证的核心配置:
listen_addresses = '*'
port = 5432
max_connections = 300
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = 1GB
hot_standby = on
synchronous_commit = local
这里重点解释两个容易被忽视的参数:
-
max_wal_senders:备库连接主库拉取WAL时,每个备库至少占用一个WAL sender进程。如果只搭一主一备,设10个已经富余,但如果你后续加节点或者做逻辑复制,这个值就决定上限。 -
wal_keep_size:主库保留多少WAL日志给落后的备库追上。如果备库断开太久,超过这个值还没追上的话,备库只能重新全量clone,这在生产中属于比较严重的故障。条件允许还可以额外配置archive_mode = on和archive_command,把WAL归档到独立存储,进一步增强容错。
4.2 pg_hba.conf:复制用户的授权
流复制需要一个专门的复制账号,一般叫
repl
。PostgreSQL文档推荐单独建一个最小权限用户,只用
REPLICATION
权限,不要用超级用户。
CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'ReplPass123';
然后编辑pg_hba.conf,加入下面两行。注意顺序,pg_hba.conf是按顺序匹配的,复制账号的规则要放在数据库普通用户规则之前。
host replication repl 192.168.10.11/32 md5
host replication repl 192.168.10.12/32 md5
host all all 192.168.10.0/24 md5
配置完成后重启PG使参数生效:
systemctl restart postgresql-16
验证复制连接是否正常,可以在主库本地执行:
/usr/pgsql-16/bin/pg_isready -h 192.168.10.11 -p 5432
psql -h 192.168.10.11 -U repl -d postgres -c "SELECT 1;"
能查到结果说明复制账号和监听都没问题。这个动作做完,PostgreSQL层面就具备了被repmgr接管的基础条件。
注意:PostgreSQL的
listen_addresses如果没设置成*或明确网段,备库通过TCP拉取WAL会一直卡在“连接超时”。很多新手搭不起来主从,第一嫌疑就是这个参数。
5. repmgr主从复制搭建全流程
5.1 repmgr的安装与版本匹配
repmgr的版本必须和PostgreSQL大版本匹配。PG 16对应repmgr 5.4+,PG 15用repmgr 5.3+。如果你装了PG 16却用repmgr 5.2的老包,启动的时候会直接报
repmgr: fatal: unsupported PostgreSQL major version
,这个错太典型了,我踩过一次。
PGDG源里直接有repmgr的RPM包,安装即可:
dnf install -y repmgr16
验证版本:
repmgr --version
# repmgr version 5.4.1
5.2 repmgr.conf:一张一张参数说清楚
repmgr的配置文件路径我放在
/etc/repmgr/16/repmgr.conf
。两个数据库节点的配置文件几乎一样,唯一不同的是
node_id
和
node_name
。我贴出主库的完整配置:
node_id=1
node_name='pg-primary'
conninfo='host=pg-primary port=5432 user=repl dbname=postgres connect_timeout=5'
data_directory='/var/lib/pgsql/16/data'
failover=automatic
promote_command='/usr/pgsql-16/bin/repmgr standby promote -f /etc/repmgr/16/repmgr.conf --log-level DEBUG'
follow_command='/usr/pgsql-16/bin/repmgr standby follow -f /etc/repmgr/16/repmgr.conf --log-level DEBUG'
monitoring_history=yes
log_level=DEBUG
log_file='/var/log/repmgr/repmgr.log'
ssh_options='-o ConnectTimeout=5 -o StrictHostKeyChecking=no'
逐个说下重点参数:
-
conninfo:这里的主机名必须是两台机器都能解析的名字。我直接用了hosts里的pg-primary,别用IP,因为repmgr的节点身份是靠node_name和conninfo里的host对应的,IP切换时容易出幻觉问题。 -
failover=automatic:开启自动切换。如果不放心,可以先设成manual,观察repmgr的判定逻辑后再打开。我第一次搭生产环境就吃了自动切换的亏,网络抖动导致误切换,从那以后我强烈建议先手动演练几轮再开automatic。 -
promote_command和follow_command:这是repmgr在切换时执行的系统命令,负责把备库提升为新主库、或者把原主库重新作为备库跟随新主库。命令里-f指定配置文件,别省。 -
log_level=DEBUG:调试阶段建议开DEBUG,以后稳定了可以改成INFO,否则日志刷得太快容易掩盖关键信息。
备库的配置文件只改两处:
node_id=2
、
node_name='pg-standby'
,其余完全一致。两个节点都执行:
mkdir -p /var/log/repmgr
chown -R postgres:postgres /var/log/repmgr
5.3 主库注册:repmgr primary register
在 主库 上,以postgres用户执行:
su - postgres
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf primary register
这一步做了什么?它在repmgr的元数据表里登记主库信息,创建repmgr相关的schema和表。执行成功后查询确认:
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf cluster show
输出里应该有:
ID | Name | Role | Status | Upstream | Location | Priority | Timeline | Connection string
----+------------+---------+-----------+----------+----------+----------+----------+-----------------
1 | pg-primary | primary | * running | - | default | 100 | 1 | host=pg-primary ...
注意Status那一列,必须是
* running
,星号表示这个节点是当前的主节点。如果显示
? unreachable
,大概率是conninfo或者SSH问题,先回头检查hosts解析和免密。
5.4 备库clone:从头搭建流复制
备库上PostgreSQL已经装好,但数据目录是空的(或者还是initdb初始化的原始状态)。repmgr提供了一个命令,直接通过SSH从主库拉取全量数据并自动搭建流复制:
su - postgres
/usr/pgsql-16/bin/repmgr -h pg-primary -U repl -d postgres -f /etc/repmgr/16/repmgr.conf standby clone
这个命令本质做了四件事:
-
在主库上执行
pg_basebackup,把主库数据文件完整拷贝到备库。 -
在备库生成
standby.signal文件,告诉PG启动时进入备库模式。 - 自动写入主库的连接信息和replication slot。
- 更新repmgr元数据,把备库登记进集群。
clone完成后,启动备库的PG服务:
systemctl start postgresql-16
然后注册备库:
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf standby register
到这里,一个基于repmgr管理的主从复制就建好了。验证一下:
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf cluster show
应该看到两个节点,主库
* running
,备库
running as standby
。
再深入验证复制是否在实时同步。在主库建一张测试表并插入数据:
psql -c "CREATE TABLE ha_test (id serial primary key, ts timestamptz default now());"
psql -c "INSERT INTO ha_test (ts) VALUES (now());"
在备库查询:
psql -c "SELECT * FROM ha_test;"
能查到刚插入的记录,说明流复制链路完全打通。如果查不到,别急着往下走,看主库的
pg_stat_replication
视图:
SELECT client_addr, state, sync_state, replay_lag FROM pg_stat_replication;
正常情况下
state
是
streaming
,
replay_lag
接近0或为空。如果是
catchup
状态,说明备库还在追WAL,等一会儿再查。
5.5 开启repmgrd守护进程
repmgr装好只是“管理工具”,真正实现自动切换得靠后台守护进程repmgrd。它在两个节点上都要启动,一个负责监控主库心跳,一个负责监控备库复制状态:
systemctl enable --now repmgrd-16
systemctl status repmgrd-16
repmgrd启动后,每间隔
reconnect_attempts
和
reconnect_interval
(默认在配置文件里,可以自行调整)都会检查上游节点状态。我用过一个比较稳妥的参数组合:
reconnect_attempts=6
reconnect_interval=10
意思是在60秒内连续6次连接失败,才判定主库失联。这样普通的网络抖动不会触发切换,而真正的宕机又能在一分钟内被识别。
提示:repmgrd的日志和repmgr的命令日志是两个概念。前者在
/var/log/repmgr/repmgrd.log,后者在/var/log/repmgr/repmgr.log。排障时看错文件会浪费大量时间,我经历过一次。
6. keepalived故障转移配置
6.1 keepalived是干什么的
PostgreSQL层的主备切换做完了,但如果应用的数据库连接地址还是指向原来的物理IP,一切等于白搭。keepalived解决的就是这个问题:它对外提供一个虚拟IP(VIP),VIP平时绑定在主库节点,当主库故障、备库提升后,VIP自动漂移到备库节点,应用连接的IP不用变,就能无缝切换到新主库上。
keepalived的核心是VRRP协议。简单理解,组内的节点周期性互相发心跳报文,谁的优先级高谁就是MASTER、持有VIP;MASTER挂了,BACKUP节点中优先级最高的顶上。这里的关键点来了: keepalived只知道节点死没死,不知道是数据库死还是机器死。 所以必须写脚本去探测数据库和repmgr的状态,脚本探测异常时主动降低本节点优先级,把VIP让给健康节点。
6.2 安装keepalived
dnf install -y keepalived
systemctl enable keepalived
6.3 主库的keepalived配置
配置文件在
/etc/keepalived/keepalived.conf
。我直接贴出经过生产验证的完整版:
global_defs {
router_id PG_HA_01
enable_script_security
}
vrrp_script check_pg_alive {
script "/etc/keepalived/check_pg_alive.sh"
interval 3
timeout 3
fall 2
rise 2
}
vrrp_instance VI_DB {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 8aK9#zFq
}
virtual_ipaddress {
192.168.10.100/24 dev eth0 label eth0:1
}
track_script {
check_pg_alive
}
}
参数逐个说明:
-
state MASTER:主库配置成MASTER。注意它只在启动时决定初始状态,后续靠优先级动态调整。 -
priority 150:主库的优先级。备库我会配100。优先级高的在VRRP竞选里胜出。 -
virtual_router_id 51:同一个集群内的keepalived节点这个值必须一致,否则分不到一个组里。 -
auth_pass:VRRP认证密码,同一组必须一致,长度不超过8位。 -
advert_int 1:VRRP广播间隔,单位秒。间隔越小切换越快,但过于频繁会增大网络负担,1秒是通用值。 -
fall 2和rise 2:脚本连续失败2次才认为故障,连续成功2次才恢复。这个参数专门防抖动。
备库的配置只有两处不同:
state BACKUP
、
priority 100
,其他一模一样。
6.4 VRRP脚本怎么写
这是整个方案里最需要花心思的地方。脚本的目标是: 数据库活着、repmgr确认本节点是主,本节点才有资格持有VIP。 我写了一个保守但可靠的版本:
#!/bin/bash
# /etc/keepalived/check_pg_alive.sh
PG_USER="postgres"
# 1. 检查postgres进程是否存活
pgrep -f "postgres.*-D /var/lib/pgsql/16/data" > /dev/null 2>&1
if [ $? -ne 0 ]; then
exit 1
fi
# 2. 用psql探测数据库是否真正可连接
psql -U "$PG_USER" -h 127.0.0.1 -p 5432 -c "SELECT 1;" > /dev/null 2>&1
if [ $? -ne 0 ]; then
exit 1
fi
# 3. 如果repmgr可用,确认本节点是否为主节点
if [ -x /usr/pgsql-16/bin/repmgr ]; then
NODE_TYPE=$(/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf node check 2>/dev/null)
echo "$NODE_TYPE" | grep -q "primary"
if [ $? -ne 0 ]; then
exit 1
fi
fi
# 4. 检查主从握手是否正常(可选,防止主库客户端连不上备库)
psql -U "$PG_USER" -h 127.0.0.1 -p 5432 -c "SELECT COUNT(*) FROM pg_stat_replication;" > /dev/null 2>&1
exit 0
脚本的第三个判断是这个方案的精髓。当repmgr完成角色切换后,原主库的repmgr会把它标记为
standby
,脚本就会让本节点的优先级自动失效,VIP立刻从残废的原主库漂移到健康的新主库上。这样就把“数据库层切换”和“网络层切换”绑在了一起。
脚本给执行权限,并保证postgres用户能通过localhost免密连接(在pg_hba.conf中配置
host all postgres 127.0.0.1/32 trust
,或者用
.pgpass
文件):
chmod +x /etc/keepalived/check_pg_alive.sh
chown root:root /etc/keepalived/check_pg_alive.sh
6.5 验证VIP绑定
启动keepalived后,在主库上检查:
systemctl restart keepalived
ip addr show eth0 | grep 192.168.10.100
看到VIP绑定在主库的eth0上:
inet 192.168.10.100/24 scope global eth0:1
。在备库上,同样的命令应该没有任何输出。至此,整套架构已经搭建完毕:流复制在跑,repmgr在监控,keepalived在守门。接下来就是最激动人心也最吓人的环节——故障演练。
7. 故障演练:主库挂了到底会发生什么
7.1 模拟主库数据库进程崩溃
先演练最常见的情况:主库的postgres进程突然崩溃(相当于进程被OOM Killer干掉),机器本身还活着。
在主库执行:
pkill -9 postgres
等30秒左右,在备库和witness节点观察变化。预期发生的事件链是这样的:
- repmgrd探测到主库连接失败 。连接重试次数达到阈值后,它认为主库失联。
-
repmgrd提升备库
。它执行
standby promote,备库从只读模式切换为读写模式。 -
keepalived脚本探测失败
。主库的postgres进程已经没了,
check_pg_alive.sh返回非零,主库keepalived的优先级被强制降低。 - VRRP切换 。备库的keepalived发现主库节点不再广播或优先级低于自己,抢到VIP。
验证是否切换成功:
# 在备库检查角色
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf cluster show
# 在备库检查VIP
ip addr show eth0 | grep 192.168.10.100
如果一切顺利,你会在备库上同时看到:repmgr标记它为新primary,并且VIP已经绑在这台机器的网卡上。这时我从另一台应用服务器连接VIP,执行写操作:
psql -h 192.168.10.100 -U testapp -d appdb -c "INSERT INTO ha_test (ts) VALUES (now());"
能写入,说明业务已经无缝切到新主库。
7.2 原主库恢复后怎么重归集群
这是大多数人到这一步就卡住的地方。直接把原主库的原进程启动起来是不行的,因为此时集群里已经有一个新主了,原主必须作为备库重新加入。步骤如下:
在 原主库 节点:
systemctl start postgresql-16
然后执行repmgr的follow命令,让它跟随新主库:
su - postgres
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf standby follow
这个命令会重新建立到新主库的流复制连接。如果原主库的WAL和数据落后太多,repmgr会提示需要重新clone,那就重复第5.4节的
standby clone
操作(会覆盖本机数据目录,谨慎确认)。
恢复完成后,再看看集群状态:
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf cluster show
输出里应该显示两个节点,新主库是
primary
,原主库是
standby
。然后回到新主库上执行:
/usr/pgsql-16/bin/repmgr -f /etc/repmgr/16/repmgr.conf primary register --force
为什么要
--force
?因为repmgr元数据里之前记录了原主库的primary信息,现在角色变了,需要强制覆盖旧的记录。
7.3 演练发现的两个真实教训
第一次演练我就踩坑了。我kill掉主库进程后,备库提升成功了,但VIP没有漂移过来。排查发现是脚本里的repmgr node check命令在角色切换的瞬间返回了“standby”,脚本直接exit 1,keepalived认为备库也不是“健康的primary”,于是两边都没人持有VIP。解决办法是去掉脚本里对repmgr node check的强依赖,只在数据库进程存活检查通过的前提下做软判断,或者给脚本加一个“容错窗口期”——切换刚完成的几十秒内,只要数据库能正常读写,就允许备库持有VIP。
第二个教训是failover触发时间太短。默认的
reconnect_attempts=3, reconnect_interval=5
意味着15秒就判定故障,我模拟网络抖动时直接把业务打断了。后来改成了
reconnect_attempts=6, reconnect_interval=10
,再加上keepalived脚本的
fall 2
,整个切换窗口变成了60秒左右,抖动基本都被过滤掉了。
8. 常见问题与实战排查速查表
8.1 六个高频问题的现场实录
拼了这么多,我相信总有一部分读者会卡在某个环节。把我这些年遇到的问题整理成速查表,按症状给出定位和解决方案:
| 症状 | 可能原因 | 排查命令 / 手段 | 解决办法 |
|---|---|---|---|
standby clone
失败,报
pg_basebackup: could not connect to server
|
主库pg_hba.conf没放行复制用户、或listen_addresses不是
*
|
psql -h <主库IP> -U repl -d postgres -c "SELECT 1;"
| 检查监听地址、pg_hba.conf加复制规则并reload |
备库能启动但复制状态一直是
catchup
| WAL保留量不足,备库落后太多 |
SELECT * FROM pg_stat_replication;
|
增大
wal_keep_size
,必要时重新clone
|
repmgrd启动报
ERROR: node is not registered
| 备库没有执行standby register |
repmgr cluster show
|
执行
standby register
|
keepalived日志报
VRRP is in fault state
| keepalived脚本返回非零,或网卡没择定 |
systemctl status keepalived
、
journalctl -u keepalived
| 检查脚本exit code,检查网卡名是否和配置一致 |
VIP漂移后新主库连不上、
connection refused
| 新主库的pg_hba.conf没有放行VIP网段或者listen_addresses不含VIP |
psql -h <VIP> -U testapp -d appdb
| 确认新主库的listen_addresses= '*'、pg_hba.conf里有VIP对应网段 |
主库恢复后执行
standby follow
报
unable to connect to remote node
| 新主库的防火墙或pg_hba.conf没放行原主库IP |
在恢复节点测试
psql -h <新主库IP>
|
调整新主库的pg_hba.conf并
SELECT pg_reload_conf();
|
8.2 几个容易忽略的细节
除了上面这些具体问题,还有几个不能叫“坑”但非常重要的小细节。第一个是
备份
。高可用不是备份的替代品,就算是主从集群,也要定期做
pg_dump
或
pg_basebackup
的异地备份。万一两个节点同时挂了,只能靠备份恢复,这时候没有备份就等于归零。
第二个是 监控告警 。集群能自动切换不等于不需要人盯着。我在生产里把repmgr和keepalived的状态都接入了Zabbix告警,主备角色变化、VIP漂移、复制延迟超过30秒都会丁丁报警。自动机制解决“快”,人处理“对”,两者缺一不可。
第三个是
脑裂的物理兜底
。条件允许的话,在备库的脚本里加一道“双保险”:如果备库认为自己该提升,执行promote前先SSH到主库节点执行
pg_ctl stop -m immediate
,确保原主库不会继续写数据。虽然这会让恢复时间变长几秒,但能极大降低“双主”的风险。我的生产环境就是这么配的,稳妥比速度重要。
8.3 这套集群适合什么样的人持续演进
最后我想说一嘴,这套repmgr + keepalived的架构装好之后,你不是就“毕业”了。它有清晰的演进路径:上了三节点之后,可以考虑引入etcd做仲裁,把repmgr的failover和仲裁逻辑完全剥离开;业务再大一点,可以升级到Patroni,享受它更精细的调度策略和REST API。装集群的过程,本质上是理解“数据库高可用到底在解决什么问题”的过程,理解了这个,任何方案在你眼里都是同一道题的解法。
我个人的体会是,这套架构最值钱的地方不是那几个安装命令,而是故障出现时你能有条不紊地判断问题在数据库层还是网络层。第一次做主库故障演练时手心冒汗,多演练几次后,半夜收到告警也能从容处理。希望这篇内容能帮你少走一些弯路,把集群搭扎实。
更多推荐



所有评论(0)