【PostgreSQL】主从集群之keepalived实现HA和故障自动转移

前言

书接上回,我们已经有了一套集群环境,(参考文章:CSDN)目前工作正常,但还不能高枕无忧,突然哪一天主库发生故障了,业务系统就会无法使用,我们的电话会被打爆,如何睡个安稳觉呢,那就需要继续增加组件实现高可用(HA)。今天介绍的是keepalived方案。闲话少说,马上进入正题。

环境介绍

我们有如下集群,还没有的可以参考之前的文章动手操作

主机 角色 OS db
192.168.0.111 Ubuntu20 postgresql14
192.168.0.112 Ubuntu20 postgresql14
192.168.0.113 Ubuntu20 postgresql14

另外需要keepalived工具,如果还没装的,在集群所有主机上执行下面的命令进行安装 

 apt-get -y install keepalived

一些keepalived的基本操作命令如下

sudo service keepalived [start | stop | reload | restart ] #启停操作
keepalived -v #检查是否安装成功

方案介绍

 虽然本方案使用了一主两备的集群,但是目前现有资料都是针对一主一备的,多备的情况更复杂一点,所以本方案在此基础上简化了一些功能,即实现一主两备集群,但当发生主库故障时,只实现单备的故障转移,另一台备机需要手工切换到新主库。这个方案可以满足对实时性要求不高的读场景,也就是说即使另一台备机没有切换,也能支持正常的查询业务,但数据不再更新,直至手工切换后数据再次同步。

部署步骤

设置自启动【主、备】

sudo vim /etc/rc.local 

 加入以下内容

#!/bin/sh -e
service keepalived start &
 
exit 0

配置keepalived.conf【主、备】

注意ubuntu中通过这种方式安装以后目录是/etc/keepalived,初始是没有keepalived.conf文件的,服务也是没起来的,网上找了一些处理过程,参考如下:

在/etc/keepalived目录下自己创建配置文件

cd /etc/keepalived
vi keepalived.conf

# 初始内容如下,这个是最简易的,具体可阅读官方说明文档

#  Keepalived for Linux简易配置

###主机1
global_defs {
router_id 192.168.0.111
script_user root
enable_script_security
}
vrrp_script chk_http_port {
script "/etc/keepalived/pg_alive.sh"
interval 10
weight 2
}
vrrp_instance VI_1 {
state MASTER
interface enp0s3
virtual_router_id 51
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.50
}
track_script {
chk_http_port
}
smtp_alert
notify_master '/etc/keepalived/failover.sh'
notify_fault '/etc/keepalived/fault.sh'
}

###备库【1】配置信息
global_defs {
router_id 192.168.0.112
script_user root
enable_script_security
}
vrrp_script chk_http_port {
script "/etc/keepalived/pg_alive.sh"
interval 10
weight 2
}
vrrp_instance VI_1 {
state BACKUP
interface enp0s3
virtual_router_id 51
priority 90 
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.50
}
track_script {
chk_http_port
}
smtp_alert
notify_master '/etc/keepalived/failover.sh'
notify_fault '/etc/keepalived/fault.sh'
}

###备库【2】配置信息
global_defs {
router_id 192.168.0.113
script_user root
enable_script_security
}
vrrp_script chk_http_port {
script "/etc/keepalived/pg_alive.sh"
interval 10
weight 2
}
vrrp_instance VI_1 {
state BACKUP
interface enp0s3
virtual_router_id 51
priority 80 
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.0.50
}
track_script {
chk_http_port
}
}

 这里几个参数简单说明如下:
router_id 192.168.223.131//你的ip地址
script "/etc/keepalived/a.sh"  健康检查的脚本,这个要自己写
interval 2 #(检测脚本执行的间隔)
state BACKUP # 主机上为 MASTER;备机上为 BACKUP
interface ens33 #网卡,可以通过ifconfig或ip a查看
virtual_router_id 51 # 主、备机的 virtual_router_id 必须相同
priority 90 # 主、备机取不同的优先级,主机值较大,备份机值较小
virtual_ipaddress { 192.168.0.50 // VRRP H 虚拟地址,这里最好用和实体机一个网段的,否则容易出现联不通的情况

smtp_alert # 如果主机状态变成Master|Backup|Fault之后会去执行的通知脚本,脚本要自己写

注意:这里的内容直接复制到linux里可能无法识别某些字符要当心 

编写健康检查脚本 【主、备】

健康检查的脚本这里也给出一段供参考

cd /etc/keepalived
vi pg_alive.sh
# 建好文件还要授权
chmod +x pg_alive.sh

#!/bin/bash
# FILENAME pg_alive.sh
# define DB parameters
PGDATABASE=postgres
PGPORT=5432
PGUSER=postgres
PGHOME=/usr/lib/postgresql/14/bin
LOGFILE=/etc/keepalived/pg_alive.log
PATH=$PATH:$PGHOME
keeplognums=30000

# define sql scripts
SQL_UPDATE_HEART="update sr_delay set sr_date = now() where id =1;"
SQL_ROLE="SELECT pg_is_in_recovery from pg_is_in_recovery();"
SQL_CHK_DBALIVED="SELECT 1;"

# process step
db_role=`echo $SQL_ROLE  | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w`
if [ $db_role == 't' ];then
echo -e `date +"%F %T"` "Attention1:the current database is standby DB!" >> $LOGFILE
exit 0
fi

#备库不检查存活,主库更新状态
echo $SQL_CHK_DBALIVED | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w
if [ $? -eq 0 ] ;then
echo $SQL_UPDATE_HEART | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w
echo -e `date +"%F %T"` "Success: update the master sr_delay successed!" >> $LOGFILE
exit 0
else
echo -e `date +"%F %T"` "Error:Is the server is running?" >> $LOGFILE
echo -e `date +"%F %T"` "Error:Postgresql is tring stop..." >> $LOGFILE
systemctl stop postgresql >> $LOGFILE 2>&1
sleep 5
echo -e `date +"%F %T"` "Error:keepalived is tring stop..." >> $LOGFILE
service keepalived stop >>$LOGFILE 2>&1
exit 1
fi
#日志保留 keeplognums 行
if [ ! -f ${MONITOR_LOG} ] ;then touch ${MONITOR_LOG};fi 
lognums=`cat ${MONITOR_LOG} |wc -l`
catnum=$((${lognums} -${keeplognums}))
if [[ $lognums -gt ${keeplognums} ]] ; then 
sed -i "1,${catnum}d" ${MONITOR_LOG}; 
fi

这是一个很简单的集群健康检查演示脚本,它每隔2秒去检查一下主库的响应,如果正常则更新心跳标记,同时把检查结果输出到日志中

注意:这里的内容直接复制到linux里可能无法识别某些字符要当心 

要让上面这段脚本正常运行,还需要在【主库】中新建一个心跳表,执行下面的脚本

create table sr_delay(id serial,sr_date timestamp default now(),new_primary_ip varchar(50),standby_num int);
insert into sr_delay(id,sr_date) values(1,now());

字段说明:

sr_date心跳时间,主机健康检查时更新
new_primary_ip 新主库IP,当备库升级为主库时填入,主库健康检查时如果standby_num归0则清空
standby_num 备库数量,不在故障转移阶段(即new_primary_ip为空时)由健康检查时更新,当故障转移时数量自动-1;还在故障转移阶段时(即备库升级为主库后,主库健康检查时获取到的从库数>standby_num),在健康检查时自动-已有从库数,直至0

 确认日志正常【主 备】

root@host111:/etc/keepalived# tail -f pg_alive.log
2022-04-26 08:53:22 Success: update the master sr_delay successed!
2022-04-26 08:53:24 Success: update the master sr_delay successed!

 所有主库备库上都部署一遍以后,我们就完成了HA的第一步,自动检测和VIP服务。接下来就是重点了,通过自定义脚本结合keepalived实现自动故障转移。

故障后处理(fault.sh)

这是keepalived在健康检查时遇到异常时要执行的脚本

cd //etc/keepalived
vi fault.sh
# 编辑完成后授权
chmod +x fault.sh

内容如下:

#!/bin/bash
# FILENAME fault.sh
LOGFILE=/etc/keepalived/fault.log
PGDATA=/var/lib/postgresql/14/main
echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE

PGPID="`head -n1 $PGDATA/postmaster.pid`"
if [ $? -ne 0 ];then
echo -e `date +"%F %T"` "Error:$PGDATA/postmaster.pid not found!" >> $LOGFILE
fi
# systemctl stop keepalived
service keepalived stop

kill -9 $PGPID
if [ $? -eq 0 ] ;
then
echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE
# systemctl stop keepalived
service keepalived stop
exit 1
fi

 需要确认PGDATA是否在正确的目录,安装默认在/var/lib/postgresql/14/main

部署自动故障转移(failover.sh)

创建一个故障转移的脚本

cd //etc/keepalived
vi failover.sh
# 编辑完成后授权
chmod +x failover.sh

内容如下:

#!/bin/bash
# FILENAME failover.sh
# define DB parameters
PGPORT=5432
PGUSER=postgres
PG_OS_USER=postgres
PGDBNAME=postgres
LANG=en_US.UTF-8
PGPATH=/usr/lib/postgresql/14/bin
PGDBPATH=/var/lib/postgresql/14/main
LOGFILE=/etc/keepalived/failover.log
PATH=$PATH:$PGPATH

# 主备数据库同步时延,单位为秒
sr_allowed_delay_time=100
# define SQL scripts
SQL_ROLE="select pg_is_in_recovery from pg_is_in_recovery();"
SQL_DELAY_TIME="select sr_date as delay_time from sr_delay where now()>(sr_date + interval '$sr_allowed_delay_time seconds');"
SWITCH_COMMAND="pg_ctl promote -D $PGDBPATH"
echo -e `date +"%F %T"` "Attention:failover process start ..." >> $LOGFILE
echo su - postgres -c "psql" >> $LOGFILE  2>&1
if [ $? -ne 0 ];then
echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is disconnect!" >> $LOGFILE
exit 1
else
echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is OK!" >> $LOGFILE
fi

# process step
db_role=`echo $SQL_ROLE | psql -At -p $PGPORT -U $PGUSER -d $PGDBNAME -w`
# Switch Rule: only standby node and db delay greater than defined delay-time allowed switch to primary node
if [ $db_role != 't' ];then
echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is master database,cannot switched!" >> $LOGFILE
exit 0
fi
slp_sec=$[$sr_allowed_delay_time+10]
echo "wait $slp_sec sec..." >> $LOGFILE
sleep $slp_sec
db_sr_delaytime=`echo $SQL_DELAY_TIME | psql -p $PGPORT -d $PGDBNAME -U $PGUSER -At -w`
if [ -z "$db_sr_delaytime" ];then
echo -e `date +"%F %T"` "Attention:The current master database is health,the standby DB cannot switched!" >> $LOGFILE
exit 0
fi

if [ -n "$db_sr_delaytime" ];then
echo -e `date +"%F %T"` "Attention:The current database is statndby,ready to switch master database!" >> $LOGFILE
echo -e `date +"%F %T"` "su - $PG_OS_USER -c $SWITCH_COMMAND" >>$LOGFILE
echo "su - $PG_OS_USER -c $SWITCH_COMMAND" >>$LOGFILE
su - $PG_OS_USER -c "$SWITCH_COMMAND" >> $LOGFILE 2>&1
if [ $? -eq 0 ];then
echo -e `date +"%F %T"` "success:The current standby database successed to switched the primary PG database !" >> $LOGFILE
exit 0
else
echo -e `date +"%F %T"` "Error: the standby database failed to switch the primary PG database !,pelease checked it!" >> $LOGFILE
exit 1
fi
fi

 切换流程:

当心跳延迟超过设定上限时,开始启动备库升级;备库升级完成后,更新心跳表的IP字段,将新主库的IP地址写进去。 

上述脚本在当前主机上编辑验证完成后传输到其他节点,在所有节点上执行下面的命令

scp postgres@192.168.0.111:/etc/keepalived/*.sh /etc/keepalived

最后启动keepalived服务

service keepalived start

模拟故障转移

这里主要模拟三种场景

  • 主库停止服务,主服务器还在运行
  • 主服务器丢失 
  • 任意备库丢失(不会发生转移)

主库停止服务

在主库(192.168.111)执行下面的语句,停止postgresql服务

# root

systemctl stop postgresql

观察【主库】日志

022-04-27 07:51:00 Success: update the master sr_delay successed!
2022-04-27 07:51:02 Success: update the master sr_delay successed!
2022-04-27 07:51:04 Success: update the master sr_delay successed!
2022-04-27 07:51:06 Error:Is the server is running?
2022-04-27 07:51:08 Error:Is the server is running?
2022-04-27 07:51:10 Error:Is the server is running?
2022-04-27 07:51:12 Error:Is the server is running?

 可以看到日志出现状态异常。此时备库日志还是正常的,在限定时间内(这里我们设的是100秒)不会发生转移。

在看主库的failover.log

root@host111:/etc/keepalived# tail -f failover.log

2022-04-27 08:28:00 Attention:The current postgreSQL DB is disconnect!

再看主库的fault.log

root@host111:/etc/keepalived# cat fault.log
2022-04-27 08:33:12 Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!

如果在目录里找不到日志就要检查一下脚本里面可能有报错,手工执行一下根据报错信息修正脚本。

功能展望

下一步要实现多备集群中所有备机自动切换到新主库上,初步设想如下:

新增一个集群信息表,用来记录整个集群的主机信息

create table sr_cluster(node_ip varchar(50),role_name varchar(30),status varchar(20));

核心字段介绍:

node_ip:集群中的节点IP 

role_name:节点当前角色,值: master-主库;standby-备库;

status:节点状态,值 online-正常在线;offline-脱离集群

处理流程简介:

1. master自动扫描新节点并注册到集群表,扫描现有节点并更新集群表状态

2.在故障转移过程中,同时把本节点更新为master,原来的master更新为standby

3. standby节点检测master节点,一旦发现master节点离线,则从集群表中寻找新的master,找到后切换到新master

原文地址:https://blog.csdn.net/bendywu/article/details/124428194

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读601次。Oracle的数据导入导出是一项基本的技能,但是对于懂数据库却不熟悉Oracle的同学可能会有一定的障碍。正好在最近的一个项目中碰到了这样一个任务,于是研究了一下Oracle的数据导入导出,在这里跟大家分享一下。......_oracle 迁移方法 对比
文章浏览阅读553次。开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共700多人左右 1 + 2)。最近我们在使用MYSQL 8 的情况下(8.025)在数据库运行中出现一个问题 参数prefer_order_i..._mysql prefer_ordering_index
文章浏览阅读3.5k次,点赞3次,收藏7次。折腾了两个小时多才成功连上,在这分享一下我的经验,也仅仅是经验分享,有不足的地方欢迎大家在评论区补充交流。_navicat连接opengauss
文章浏览阅读2.7k次。JSON 代表 JavaScript Object Notation。它是一种开放标准格式,将数据组织成中详述的键/值对和数组。_postgresql json
文章浏览阅读2.9k次,点赞2次,收藏6次。navicat 连接postgresql 注:navicat老版本可能报错。1.在springboot中引入我们需要的依赖以及相应版本。用代码生成器生成代码后,即可进行增删改查(略)安装好postgresql 略。更改配置信息(注释中有)_mybatisplus postgresql
文章浏览阅读1.4k次。postgre进阶sql,包含分组排序、JSON解析、修改、删除、更新、强制踢出数据库所有使用用户、连表更新与删除、获取今年第一天、获取近12个月的年月、锁表处理、系统表使用(查询所有表和字段及注释、查询表占用空间)、指定数据库查找模式search_path、postgre备份及还原_pgsql分组取每组第一条
文章浏览阅读3.3k次。上一篇我们学习了日志清理,日志清理虽然解决了日志膨胀的问题,但就无法再恢复检查点之前的一致性状态。因此,我们还需要日志归档,pg的日志归档原理和Oracle类似,不过归档命令需要自己配置。以下代码在postmaster.c除了开启归档外,还需要保证wal_level不能是MINIMAL状态(因为该状态下有些操作不会记录日志)。在db启动时,会同时检查archive_mode和wal_level。以下代码也在postmaster.c(PostmasterMain函数)。......_postgresql archive_mode
文章浏览阅读3k次。系统:ubuntu22.04.3目的:利用向日葵实现windows远程控制ubuntu。_csdn局域网桌面控制ubuntu
文章浏览阅读1.6k次。表分区是解决一些因单表过大引用的性能问题的方式,比如某张表过大就会造成查询变慢,可能分区是一种解决方案。一般建议当单表大小超过内存就可以考虑表分区了。1,继承式分区,分为触发器(trigger)和规则(rule)两种方式触发器的方式1)创建表CREATE TABLE "public"."track_info_trigger_partition" ( "id" serial, "object_type" int2 NOT NULL DEFAULT 0, "object_name..._pg数据表分区的实现
文章浏览阅读3.3k次。物联网平台开源的有几个,就我晓得的有、、thingskit、JetLink、DG-iot(还有其他开源的,欢迎在评论区留言哦!),然后重点分析了下ThingsBoard、ThingsPanel和JetLink,ThingsBoard和Jetlinks是工程师思维产品,可以更多的通过配置去实现开发的目的,ThingsPanel是业务人员思路产品,或者开发或者用,避免了复杂的配置带来的较高学习门槛。ThingsBoard和Jetlinks是Java技术体系的,ThingsPanel是PHP开发的。_jetlinks和thingsboard
文章浏览阅读3.8k次。PostgreSQL 数据类型转换_pgsql数字转字符串
文章浏览阅读7k次,点赞3次,收藏14次。在做数据统计页面时,总会遇到统计某段时间内,每天、每月、每年的数据视图(柱状图、折线图等)。这些统计数据一眼看过去也简单呀,不就是按照时间周期(天、月、年)对统计数据进行分个组就完了嘛?但是会有一个问题,简单的写个sql对周期分组,获取到的统计数据是缺失的,即没有数据的那天,整条记录也都没有了。如下图需求:以当前月份(2023年2月)为起点,往后倒推一年,查询之前一年里每个月的统计数据。可见图中的数据其实是缺少的,这条sql只查询到了有数据的月份(23年的1月、2月,22年的12月)_如何用一条sql查出按年按月按天的汇总
文章浏览阅读3.8k次,点赞66次,收藏51次。PostgreSQL全球开发小组与2022年10月13日,宣布发布PostgreSQL15,这是世界上最先进的开源数据库的最新版本_mysql8 postgresql15
文章浏览阅读1.3k次。上文介绍了磁盘管理器中VFD的实现原理,本篇将从上层角度讲解磁盘管理器的工作细节。_smgrrelationdata
文章浏览阅读1.1k次。PostgreSQL设置中文语言界面和局域网访问_postgressql汉化
文章浏览阅读4.2k次。PostgreSQL 修改数据存储路径_如何设置postgresql 数据目录
文章浏览阅读4.7k次。在项目中用到了多数据源,在连接postgres数据库时,项目启动报错,说数据库连接错误,说dual不存在,网上好多教程都是说数据库查询的时候的大小写问题,而这个仅仅是连接,咋鞥却处理方法是修改application-dev.yml中的配置文件.项目中的druid参数是这样的:确实在配置文件中有个查询语句。_relation "dual" does not exist
文章浏览阅读4.9k次。PostgreSQL是一款强大的关系型数据库,但在实际使用过程中,许多用户经常会遇到慢SQL的问题。这些问题不仅会降低数据库性能,还会直接影响业务流程和用户体验。因此,本文将会深入分析PostgreSQL慢SQL的原因和优化方案,帮助用户更好地利用这个优秀的数据库系统。无论你是初学者还是专业开发者,本文都将为你提供实用的技巧和方法,让你的PostgreSQL数据库始终保持高效快速。_postgresql数据库优化
文章浏览阅读1.6k次。Linux配置postgresql开机自启_linux 启动pgsql
文章浏览阅读2k次。本篇介绍如何在centos7系统搭建一个postgresql主备集群实现最近的HA(高可用)架构。后续更高级的HA模式都是基于这个最基本的主备搭建。_postgresql主备