7. PostgreSQL + repmgr + witness 高可用架构
简介
repmgr是一套开源的PostgreSQL集群管理工具,具有非常轻量级的使用特性。具体表现有以下特点:
- 配置操作简单,可一键式完成相关部署操作;
- 支持Auto Failover和Manual Switchover;
- 分布式管理集群节点,易扩展,可在线增删集群节点。
- witness节点是处理集群主库和备库之间可能存在网络拥塞、延迟、路由等问题影响,导致主库还在正常工作,而备库无法联系主库的场景。
- 通过设置witness节点可以针对主库与备库之间切换的检查完整性,即辅助备节点监控程序实施主节点网络可见性检查,避免因网络问题导致的脑裂现象。
环境准备
节点 | 角色 | 软件 |
---|---|---|
10.10.8.176 | primary | pg+repmgr |
10.10.8.106 | standby | pg+repmgr |
10.10.8.177 | standby | pg+repmgr |
10.10.8.180 | standby | pg+repmgr |
安装postgresql
安装详见标题 2 源码安装配置
免密登录
rm -rf ~/.ssh
ssh-keygen #一路回车
cd ~/.ssh
mv id_rsa.pub authorized_keys
scp -r ~/.ssh 10.10.8.176:/home/postgres/
scp -r ~/.ssh 10.10.8.106:/home/postgres/
scp -r ~/.ssh 10.10.8.177:/home/postgres/
scp -r ~/.ssh 10.10.8.180:/home/postgres/
ssh postgres@10.10.8.176 date
ssh postgres@10.10.8.106 date
ssh postgres@10.10.8.177 date
ssh postgres@10.10.8.180 date
postgresql 重点参数配置 //主库操作
cat >/pgdata/12/data/postgresql.conf<<'EOF'
# 监听地址
listen_addresses = '0.0.0.0'
# 最大连接数
max_connections = 1000
# 缓冲区大小
shared_buffers = 2GB
# 动态共享内存类型
dynamic_shared_memory_type = posix
# 日志详细程度
wal_level = replica
# 检查点之后第一次页面更改(即使是对提示位进行非关键性的修改)写 FPI 到 WAL 中
wal_log_hints = on
# 重做日志最大物理大小
max_wal_size = 1GB
# 重做日志最小物理大小
min_wal_size = 80MB
# 开启归档
archive_mode = on
# 检查归档
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
# 最大从库数 多设置点没事
max_wal_senders = 30
# 重做日志保存数量
wal_keep_segments = 128
# 流复制主机发送数据的超时时间
wal_sender_timeout = 60s
# 开启说明如果作为从库可以读
hot_standby = on
# 流复制的最大延迟时间
max_standby_streaming_delay = 30s
# 复制槽的最大数量(复制槽是自动检查行冲突的)
max_replication_slots = 128
# 向主库报告从库当前状态的最大时间
wal_receiver_status_interval = 10s
# 如果有错误的数据复制,是否向主库反馈
hot_standby_feedback = on
# 日志时区(prc 中华人民共和国)
log_timezone = 'PRC'
# 时间格式
datestyle = 'iso, mdy'
# 数据库时区
timezone = 'PRC'
# 系统错误消息的区域设置
lc_messages = 'C'
# 货币格式的区域设置
lc_monetary = 'C'
# 数字格式设置的区域设置
lc_numeric = 'C'
# 时间格式的区域设置
lc_time = 'C'
# 默认文本搜索配置
default_text_search_config = 'pg_catalog.english'
# 日志输出
log_destination = 'stderr'
# 开启日志采集
logging_collector = on
# 日志路径
log_directory = '/pgdata/12/log'
# 日志格式
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
# 日志权限
log_file_mode = 0600
EOF
# 添加防火墙规则
cat >>/pgdata/12/data/pg_hba.conf<<'EOF'
host all all 0.0.0.0/0 trust
host replication repmgr 0.0.0.0/0 trust
EOF
安装repmgr
/ 所有节点安装repmgr
# 下载包
wget https://repmgr.org/download/repmgr-5.2.0.tar.gz
# 解压
tar xf repmgr-5.2.0.tar.gz
# 进入目录
cd repmgr-5.2.0
# 配置
./configure
# 编译
make
# 安装
make install
# 创建配置文件目录
mkdir -p /pgdata/repmgr
# 报错 cannot create regular file ‘/usr/local/pg12/lib/postgresql/repmgr.so’: Permission denied
su - root
chown -R postgres:postgres /usr/local/pg12
配置repmgr
配置用户 // 主库
# 创建用户
createuser -s repmgr
# 创建库
createdb repmgr -O repmgr
# 修改密码
PGPASSWORD=dyh666 psql -c "alter user repmgr with password 'dyh666';"
# 修改查找目录
PGPASSWORD=dyh666 psql -c "alter user repmgr set search_path to repmgr ,\"\$user\",public;"
添加配置文件 // 所有节点执行
cat >/pgdata/repmgr/repmgr.conf<<'EOF'
# 节点ID,高可用集群各节点标识 // 每个节点不同
node_id=1
# 节点名称,高可用集群各节点名称 // 每个节点不同
node_name='pg01-182'
# 本节点数据库连接信息 // 填写本机的IP地址即可
conninfo='host=10.10.8.182 user=repmgr dbname=repmgr connect_timeout=2'
# pg数据目录
data_directory='/pgdata/12/data'
# 流复制数据库用户,默认使用repmgr
#replication_user='replica'
# repmgr软件目录
repmgr_bindir='/usr/local/pg12/bin/'
# pg软件目录
pg_bindir='/usr/local/pg12/bin'
# 停机超时时间
#shutdown_check_timeout=10
# 日志输出
log_level=INFO
log_file='/pgdata/repmgr/repmgrd.log'
log_status_interval=10
EOF
配置数据库免密 // 所有机器配置
# 配置主备库的密码文件 .pgpass 互相通信使用
cat >~/.pgpass<<'EOF'
10.10.8.176:5432:repmgr:repmgr:dyh666
10.10.8.106:5432:repmgr:repmgr:dyh666
10.10.8.177:5432:repmgr:repmgr:dyh666
10.10.8.180:5432:repmgr:repmgr:dyh666
EOF
# 授权 // 所以机器配置
chmod 0600 ~/.pgpass
注册完成后可以使用repmgr用户登录数据库repmgr默认库下查看生成的表来确定集群的状态
或者通过repmgr的命令查看相关的状态信息
主库加入集群
# 主库注册到集群
repmgr primary register -f /pgdata/repmgr/repmgr.conf
# 查看集群状态
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
克隆到其他节点
# 测试克隆 // 非必要操作如果你想使用现有主从做repmgr 则可以忽略
repmgr standby clone -h 10.10.8.183 -Urepmgr -d repmgr -f /pgdata/repmgr/repmgr.conf --dry-run
# 正式执行 // 这里如果提示你输入密码说明你的配置有问题
repmgr standby clone -h 10.10.8.182 -Urepmgr -d repmgr -f /pgdata/repmgr/repmgr.conf
# 启动pgsql
pg_ctl -D /pgdata/12/data -l logfile start
从库加入集群
# 将 standby 节点注册到集群 --upstream-node-id=1 //指定primary 节点的ID号
repmgr standby register -f /pgdata/repmgr/repmgr.conf --upstream-node-id=1
# 查看集群状态
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
配置 witness 节点
以上操作虽然实现了PostgreSQL的切换功能,但是需要手动去执行才能进行切换,witness 节点是监控节点,他可以监控集群的状态,当主库访问不可达或宕机时,可以通过指定的命令自动切换主库,并在代码中实现了防止脑裂的功能。
如果备节点网络上只是和witness或主节点中的一个节点不通,,则很可能存在网络中断,它不应该切换为主节点。如果备节点和witness节点相通,但和主节点不通,这证明不是网络中断,而是主节点本身不可用,因此它可以切换为主节点。
⚠️: witness节点不能在集群内,必须是独立的节点,必须为独立主库!!!
witness节点上初始化一个实例
# 清理旧实例
pg_ctl stop
rm -rf /pgdata/12/data/*
rm -rf /archive/*
# 生产初始化
initdb -A md5 -D $PGDATA -E utf8 --locale=C -W # 配置管理员密码
配置实例
cat >/pgdata/12/data/postgresql.conf<<'EOF'
# 监听地址
listen_addresses = '0.0.0.0'
# 最大连接数
max_connections = 1000
# 缓冲区大小
shared_buffers = 2GB
# 动态共享内存类型
dynamic_shared_memory_type = posix
# 日志详细程度
wal_level = replica
# 检查点之后第一次页面更改(即使是对提示位进行非关键性的修改)写 FPI 到 WAL 中
wal_log_hints = on
# 重做日志最大物理大小
max_wal_size = 1GB
# 重做日志最小物理大小
min_wal_size = 80MB
# 开启归档
archive_mode = on
# 检查归档
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
# 最大从库数 多设置点没事
max_wal_senders = 30
# 重做日志保存数量
wal_keep_segments = 128
# 流复制主机发送数据的超时时间
wal_sender_timeout = 60s
# 开启说明如果作为从库可以读
hot_standby = on
# 流复制的最大延迟时间
max_standby_streaming_delay = 30s
# 复制槽的最大数量(复制槽是自动检查行冲突的)
max_replication_slots = 128
# 向主库报告从库当前状态的最大时间
wal_receiver_status_interval = 10s
# 如果有错误的数据复制,是否向主库反馈
hot_standby_feedback = on
# 日志时区(prc 中华人民共和国)
log_timezone = 'PRC'
# 时间格式
datestyle = 'iso, mdy'
# 数据库时区
timezone = 'PRC'
# 系统错误消息的区域设置
lc_messages = 'C'
# 货币格式的区域设置
lc_monetary = 'C'
# 数字格式设置的区域设置
lc_numeric = 'C'
# 时间格式的区域设置
lc_time = 'C'
# 默认文本搜索配置
default_text_search_config = 'pg_catalog.english'
# 日志输出
log_destination = 'stderr'
# 开启日志采集
logging_collector = on
# 日志路径
log_directory = '/pgdata/12/log'
# 日志格式
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
# 日志权限
log_file_mode = 0600
# 自动切换
shared_preload_libraries ='repmgr'
EOF
# 添加防火墙规则
cat >>/pgdata/12/data/pg_hba.conf<<'EOF'
host all all 0.0.0.0/0 trust
host replication repmgr 0.0.0.0/0 trust
EOF
# 启动
pg_ctl -D /pgdata/12/data -l logfile start
创建用户
# 创建用户
createuser -s repmgr
# 创建库
createdb repmgr -O repmgr
# 修改密码
PGPASSWORD=dyh666 psql -c "alter user repmgr with password 'dyh666';"
# 修改查找目录
PGPASSWORD=dyh666 psql -c "alter user repmgr set search_path to repmgr ,\"\$user\",public;"
添加 repmgr 配置
cat >/pgdata/repmgr/repmgr.conf<<'EOF'
# 节点ID,高可用集群各节点标识 // 每个节点不同
node_id=4
# 节点名称,高可用集群各节点名称 // 每个节点不同
node_name='pg04-180'
# 本节点数据库连接信息 // 填写本机的IP地址即可
conninfo='host=10.10.8.180 user=repmgr dbname=repmgr connect_timeout=2'
# pg数据目录
data_directory='/pgdata/12/data'
# 流复制数据库用户,默认使用repmgr
#replication_user='replica'
# repmgr软件目录
repmgr_bindir='/usr/local/pg12/bin/'
# pg软件目录
pg_bindir='/usr/local/pg12/bin'
# 停机超时时间
#shutdown_check_timeout=10
# 日志输出
log_level=INFO
log_file='/pgdata/repmgr/repmgrd.log'
log_status_interval=10
EOF
注册witness 节点到集群
# 将 witness 节点注册到集群
repmgr witness register -f /pgdata/repmgr/repmgr.conf -h 10.10.8.183 --upstream-node-id=2 -Urepmgr -d repmgr
集群检查
# 集群状态拓扑
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
# ssh 连接检查
repmgr -f /pgdata/repmgr/repmgr.conf cluster matrix
# repmgr连接检查
repmgr -f /pgdata/repmgr/repmgr.conf cluster crosscheck
# 显示节点基本信息和复制状态
repmgr -f /pgdata/repmgr/repmgr.conf node status
# 复制状况检查
repmgr -f /pgdata/repmgr/repmgr.conf node check
故障手动切换
主从切换的效果是将执行的命令的从库提升为主库,其他从库指向新主库,旧主库也指向新主库。
# 切换测试
repmgr -f /pgdata/repmgr/repmgr.conf standby switchover --siblings-follow --force-rewind --dry-run
# 正式切换
repmgr -f /pgdata/repmgr/repmgr.conf standby switchover --siblings-follow --force-rewind
# # 集群状态拓扑
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
可能遇见的问题:
fe_sendauth: no password supplied
一般是 pg_hba.conf 文件的防火墙权限配置的有问题
故障自动切换
pgsql 添加配置 // 所有节点
# 添加如下配置
cat >>/pgdata/12/data/postgresql.conf<<'EOF'
# 加载repmgr
shared_preload_libraries = 'repmgr'
EOF
# 重启pg
pg_ctl restart
repmgr配置 // 所有节点
# 配置repmgr 配置文件
vim /pgdata/repmgr/repmgr.conf
# 节点ID 唯一的
node_id=4
# 节点名 唯一的
node_name='pg-04-180'
# 本地连接信息
conninfo='host=10.10.8.180 port=5432 user=repmgr dbname=repmgr connect_timeout=2'
# pg数据目录路径
data_directory='/pgdata/12/data/'
# repmgr软件目录
repmgr_bindir='/usr/local/pg12/bin/'
# pg软件目录
pg_bindir='/usr/local/pg12/bin'
# 故障转移方法 自动
failover=automatic
# 等待提升配置
promote_command='/usr/local/pg12/bin/repmgr standby promote -f /pgdata/repmgr/repmgr.conf --log-to-file'
# 故障转移配置
follow_command='/usr/local/pg12/bin/repmgr standby follow -f /pgdata/repmgr/repmgr.conf --log-to-file --upstream-node-id=%n'
# pg启动命令
service_start_command='pg_ctl -D /pgdata/12/data -l logfile start'
# pg关闭命令
service_stop_command='pg_ctl -D /pgdata/12/data -l logfile stop'
# pg重启命令
service_restart_command='pg_ctl -D /pgdata/12/data -l logfile restart'
# pg重载命令
service_reload_command='pg_ctl -D /pgdata/12/data -l logfile reload'
# pid文件位置
repmgrd_pid_file='/pgdata/repmgr/repmgrd.pid'
# 优先级权重
priority=100
# 日志输出
log_level=ERROR
log_file='/pgdata/repmgr/repmgrd.log'
log_status_interval=10
# 检查主库状态间隔时间
monitor_interval_secs=2
# 探测主库是否可用
connection_check_type='query'
# 尝试连接主库次数
reconnect_attempts=6
# 连接主库失败后再次连接的间隔时间
reconnect_interval=2
# repmgr 启动命令
repmgrd_service_start_command='repmgrd -f /pgdata/repmgr/repmgr.conf'
# repmgr 关闭命令
repmgrd_service_stop_command='kill -9 $(cat /pgdata/repmgr/repmgrd.pid)'
# 启动
repmgrd -f /pgdata/repmgr/repmgr.conf
# 关闭
kill -9 `cat /pgdata/repmgr/repmgrd.pid`
集群检查
# 集群状态拓扑
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
# ssh 连接检查
repmgr -f /pgdata/repmgr/repmgr.conf cluster matrix
# repmgr连接检查
repmgr -f /pgdata/repmgr/repmgr.conf cluster crosscheck
# 显示节点基本信息和复制状态
repmgr -f /pgdata/repmgr/repmgr.conf node status
# 复制状况检查
repmgr -f /pgdata/repmgr/repmgr.conf node check
自动切换
# 主库执行
pg_ctl stop
# 查看集群状态
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
恢复的时候执行不了的直接强制执行
但是一定要注意是什么问题,主从同步的问题就不能强制执行,执行了也报错
repmgr 常用命令
# 查看集群状态
repmgr -f /pgdata/repmgr/repmgr.conf cluster show
# primary 节点注册到集群 //主库
repmgr -f /pgdata/repmgr/repmgr.conf primary register
# 将 standby 节点注册到集群 --upstream-node-id=1 //指定primary 节点的ID号
repmgr standby register -f /pgdata/repmgr/repmgr.conf --upstream-node-id=1
# 强制注册一个standby 节点
repmgr standby register --force -d 'host=10.10.8.176 port=5432 user=repmgr password=dyh666'
# 将 witness 节点注册到集群
repmgr -f /pgdata/repmgr/repmgr.conf -h 10.10.8.176 --upstream-node-id=1 -Urepmgr -d repmgr witness register
# 集群删除一个standby节点
repmgr standby unregister -f /pgdata/repmgr/repmgr.conf --node-id=1
# 集群删除一个primary节点
repmgr primary unregister -f /pgdata/repmgr/repmgr.conf --node-id=1
# 集群删除 witness节点
repmgr witness unregister -f /pgdata/repmgr/repmgr.conf --node-id=4
# 检查切换 // 从节点执行
repmgr -f /pgdata/repmgr/repmgr.conf standby switchover --siblings-follow --force-rewind --dry-run
# 手动切换切换 // 从节点执行
repmgr -f /pgdata/repmgr/repmgr.conf standby switchover --siblings-follow --force-rewind
# 清理监控数据
repmgr cluster cleanup -f /pgdata/repmgr/repmgr.conf --node-id=4