pg数据库repmgr主备双节点见证者方案
pg数据库repmgr主备双节点见证者方案
pg数据库repmgr主备双节点见证者方案
主备场景
参考keepalived+timescaladb主备切换高可用方案
- 使用keepalived管理虚拟ip
- 使用repmgr组件对pg数据库主备状态和主从复制做管理
双主脑裂场景
例如在主服务器拔网线或把网卡禁用。当前主不会切换,备机检测到主的网络丢失而自动升主。网络恢复后,出现双主。
需要人工介入修复,如把原来的主数据库服务重启,触发自动降为备。
repmgr见证者机制
repmgr的见证者机制,相当于裁判的观察者角色,用于规避双主脑裂,自动管理和恢复。一般需要奇数个节点,至少部署3个。
使用自定义系统服务+脚本定时检测在双节点实现见证者机制
部署说明
服务项需要分别部署在主、备服务器上。脚本用于本机主备状态自检和恢复(而不是对端)。
自定义检测和恢复脚本
check_pg_is_split_brain_and_restart.sh
脚本示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/bin/bash
# 检查pg实例是否脑裂并尝试自动重启恢复
# 从入参获取网卡名称、虚拟 IP 和容器名称
NETWORK_INTERFACE=$1
VIP=$2
CONTAINER_NAME=$3
# repmgr守护进程pid文件docker映射路径
REPMGR_PID_FILE=/xxx/pg-tmp/repmgrd.pid
# 脚本日志路径
WITNESS_LOG_FILE=/xxx/repmgr-witness/pid.log
# 检查参数是否齐全
if [ -z "$NETWORK_INTERFACE" ] || [ -z "$VIP" ] || [ -z "$CONTAINER_NAME" ]; then
echo "Usage: $0 <network_interface> <vip> <container_name>"
exit 1
fi
# 获取网卡的 IP 地址列表
IP_LIST=$(ip addr show dev "$NETWORK_INTERFACE" | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
# 判断是否包含 VIP
if echo "$IP_LIST" | grep -qw "$VIP"; then
echo "VIP $VIP is present on $NETWORK_INTERFACE."
HAS_VIP=true
else
echo "VIP $VIP is NOT present on $NETWORK_INTERFACE."
HAS_VIP=false
fi
# 执行命令并捕获输出
output=$(docker exec "$CONTAINER_NAME" psql -U postgres -c "SELECT pg_is_in_recovery();" 2>/dev/null | awk 'NR==3 {print $1}')
# 检查命令是否成功
if [ $? -ne 0 ]; then
echo "Failed to execute command in container $CONTAINER_NAME."
exit 1
fi
# 确认当前 PostgreSQL 实例是否处于备库模式
if [ "$output" = "t" ]; then
echo "PostgreSQL is in recovery mode (standby)."
elif [ "$output" = "f" ]; then
echo "PostgreSQL is not in recovery mode (primary)."
else
echo "Unexpected output: $output"
exit 1
fi
# 如果没有虚拟 IP 且当前是主库,则重启容器
if [ "$HAS_VIP" = false ] && [ "$output" = "f" ]; then
echo "No VIP found and PostgreSQL is primary. Restarting container $CONTAINER_NAME..."
# 删除repmgr服务守护进程pid再重启,否则后续可能会各种原因无法加入节点集群。该文件需要从docker容器内映射到磁盘上
# [ERROR] PID file "/tmp/repmgrd.pid" exists and seems to contain a valid PID
# [HINT] if repmgrd is no longer alive, remove the file and restart repmgrd
rm -f "$REPMGR_PID_FILE"
docker restart "$CONTAINER_NAME"
if [ $? -eq 0 ]; then
echo "Container $CONTAINER_NAME restarted successfully."
# 记录最近一次执行裁决的时间
date > "$WITNESS_LOG_FILE"
else
echo "Failed to restart container $CONTAINER_NAME."
exit 1
fi
fi
自定义系统服务
repmgr-witness.service
示例,指定虚拟ip、虚拟ip绑定的网卡、pg容器名称,定时执行检测脚本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=Repmgr Witness Service
[Service]
StandardOutput=null
StandardError=null
# 指定脚本路径,注意授权可执行权限
# 定时检测pg数据库主备和vip状态是否匹配,避免脑裂出现双主;定时单位秒;脑裂时重启pg尝试自动恢复
ExecStart=/bin/bash -c 'while true; do /xxx/check_pg_is_split_brain_and_restart.sh eth0 192.168.0.20 postgres ; sleep 120; done'
# 无论退出状态如何都重启
Restart=always
# 服务失败后等待5秒再尝试重启
RestartSec=5
[Install]
WantedBy=multi-user.target
本文由作者按照 CC BY 4.0 进行授权