前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Keepalived实现PostgreSQL高可用

用Keepalived实现PostgreSQL高可用

作者头像
数据和云
发布2021-10-28 15:38:31
2.7K0
发布2021-10-28 15:38:31
举报
文章被收录于专栏:数据和云

点击上方"蓝字"

关注我们,享更多干货!

一、Keepalived工作原理

Keepalived可提供VRRP以及health-check功能,可以只用它提供双机浮动的vip(VRRP虚拟路由功能),这样可以简单实现一个双机热备高可用功能。

Keepalived是以VRRP虚拟路由冗余协议为基础实现高可用的,可以认为是实现路由器高可用的协议,即将N台提供相同功能的路由器组成一个路由器组,这个组里面有一个master和多个backup,master上面有一个对外提供服务的vip(该路由器所在局域网内其他机器的默认路由为该vip),master会发组播,当backup收不到VRRP包时就认为master宕掉了,这时就需要根据VRRP的优先级来选举一个backup当master。这样的话就可以保证路由器的高可用了。

下载地址:(复制链接至浏览器中浏览)

https://www.keepalived.org/download.html https://github.com/acassen/keepalived

Keepalived工作在TCP/IP参考模型的三层、四层、五层(物理层,链路层):

  • 网络层(3):Keepalived通过ICMP协议向服务器集群中的每一个节点发送一个ICMP数据包(有点类似与Ping的功能),如果某个节点没有返回响应数据包,那么认为该节点发生了故障,Keepalived将报告这个节点失效,并从服务器集群中剔除故障节点。
  • 传输层(4):Keepalived在传输层里利用了TCP协议的端口连接和扫描技术来判断集群节点的端口是否正常,比如对于常见的WEB服务器80端口。或者SSH服务22端口,Keepalived一旦在传输层探测到这些端口号没有数据响应和数据返回,就认为这些端口发生异常,然后强制将这些端口所对应的节点从服务器集群中剔除掉。
  • 应用层(5):Keepalived的运行方式也更加全面化和复杂化,用户可以通过自定义Keepalived工作方式,例如:可以通过编写程序或者脚本来运行Keepalived,而Keepalived将根据用户的设定参数检测各种程序或者服务是否允许正常,如果Keepalived的检测结果和用户设定的不一致时,Keepalived将把对应的服务器从服务器集群中剔除。

二、架构

2.1 主机规划

2.2 架构图

三、环境准备

3.1 关闭防火墙和selinux

代码语言:javascript
复制
[root@localhost ~]# systemctl stop firewalld
[root@localhost keepalived]# vi /etc/selinux/config
SELINUX=disabled
[root@localhost keepalived]# getenforce
Disabled

3.2 安装Keepalived

代码语言:javascript
复制
[root@localhost ~]# yum install gcc openssl openssl‐devel libnl libnl‐devel ipvsadm -y
[root@localhost ~]# cd /usr/local/src/
[root@localhost src]# tar -zxf keepalived-2.2.4.tar.gz 
[root@localhost src]# cd keepalived-2.2.4
[root@localhost keepalived-2.2.4] ./configure --prefix=/usr/local/keepalived
[root@localhost keepalived-2.2.4] make && make install
[root@localhost keepalived]#  cd /usr/local/keepalived
[root@localhost keepalived]# 
ln -s /usr/local/src/keepalived-2.2.4/keepalived/etc/init.d/keepalived /etc/init.d/
[root@localhost keepalived]# ln -s /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
[root@localhost keepalived]# ln -s /usr/local/keepalived/sbin/keepalived /usr/local/sbin/
[root@localhost keepalived]# mkdir /etc/keepalived/
[root@localhost keepalived]# ln -s  /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
[root@localhost ~]# cat  /etc/keepalived/keepalived.conf 

[root@localhost keepalived-2.2.4]# systemctl start keepalived
[root@localhost keepalived-2.2.4]# systemctl status keepalived


3.3 PostgreSQL数据库异步主从流复制搭建

192.168.254.128服务器上配置:

1、创建流复制用户

postgres=# create user replia replication superuser password ‘123qwert’;

给superuser权限是为了后面做pg_rewind操作。

2、数据目录下配置pg_hba.conf文件添加(192.168.254.129也需要添加)

代码语言:javascript
复制
     host    replication     all             192.168.254.1/24      md5

3、配置环境变量

代码语言:javascript
复制
[postgres@localhost pg_data]$ cat ~/.bash_profile 
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then 	. ~/.bashrc; fi
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH
export PGHOME=/usr/local/pgsql
export LD_LIBRARY_PATH=$PGHOME/lib:/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib:$LD_LIBRARY_PATH
export DATE=`date +"%Y%m%d%H%M"`
export PATH=$PGHOME/bin:$PATH:.
export MANPATH=$PGHOME/share/man:$MANPATH
export PGUSER=postgres
export PGDATA=/data/pg_data
export PGDATABASE=postgres
alias rm='rm -i'
alias ll='ls -lh'
export LANG="zh_CN.UTF8"

在192.168.254.129上进行:

4、~/.bash_profile 和上面输入一样的内容

5、制作基础备份

代码语言:javascript
复制
 [postgres@localhost ~]$ pg_basebackup -h192.168.254.128 -Ureplia -R -Fp -P --verbose  -c fast -D /data/pg_data

6、启动从库

代码语言:javascript
复制
 [postgres@localhost ~]$ pg_ctl -D /data/pg_data/ start

192.168.254.128上查看主库信息:

7、查看主库信息

代码语言:javascript
复制
 postgres=# select * from pg_stat_replication ;

3.4 PostgreSQL数据库配置

创建数据库Keepalived,并且创建表探测表sr_delay,后续Keepalived探测,刷新sr_delay表的last_alive字段为当前探测时间。这张表用来判断主备延迟情况,数据库故障切换时会用到这张表。

数据库配置

代码语言:javascript
复制
postgres=# create user keepalived password '123qwert' CONNECTION LIMIT 4 ;
CREATE ROLE
postgres=# create database keepalived owner keepalived;
CREATE DATABASE
postgres=# \c keepalived keepalived
You are now connected to database "keepalived" as user "keepalived".
keepalived=> create table sr_delay(id int4, last_alive timestamp(0) without time zone);
CREATE TABLE

表sr_delay只允许写入一条记录,并且不允许删除此表数据,通过触发器实现。创建触发器函数,如下所示:

代码语言:javascript
复制
CREATE FUNCTION cannt_delete () 
RETURNS trigger 
LANGUAGE plpgsql AS $$ 
BEGIN 
RAISE EXCEPTION 'Table sr_delay can not delete !';
 END; 
$$;

创建触发器:

代码语言:javascript
复制
CREATE TRIGGER trigger_sr_delay_del 
BEFORE DELETE ON sr_delay 
FOR EACH ROW EXECUTE PROCEDURE cannt_delete() ; 
CREATE TRIGGER trigger_sr_delay_tru BEFORE TRUNCATE ON sr_delay FOR STATEMENT EXECUTE PROCEDURE cannt_delete() ;

插入数据:

代码语言:javascript
复制
 INSERT INTO sr_delay VALUES(1,now()) ;

参数部分:

代码语言:javascript
复制
hot_standby = on
wal_log_hints = on 
archive_mode=on
archive_command='test ! -f /data/pg_archive/%f && cp %p /data/pg_archive/%f'

后续Keepalived会每隔指定时间探测PostgreSQL数据库存活, 并且以Keepalived用户登录Keepalived数据库刷新这张表, 配置主备库pg_hba.conf,增加如下内容

代码语言:javascript
复制
host    keepalived    keepalived        192.168.254.128/32      md5
host    keepalived    keepalived        192.168.254.129/32      md5

随后pg_ctl reload 操作使配置生效。

3.5 Keepalived 配置

代码语言:javascript
复制
[root@localhost ~]# cat /etc/keepalived/keepalived.conf 
! Configuration File for keepalived
global_defs {
   # 邮件通知信息
   notification_email {
     # 定义收件人
    394106346@qq.com 
   }
   # 定义发件人
   notification_email_from Alexandre.Cassen@firewall.loc
   # SMTP服务器地址
   smtp_server 192.168.254.128
   smtp_connect_timeout 30
   # 路由器标识,一般不用改,也可以写成每个主机自己的主机名
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

# 定义用于实例执行的脚本内容,比如可以在线降低优先级,用于强制切换
vrrp_script check_pg_alived { 
script "/data/scripts/pg_monitor.sh" 
interval 10 
fall 3 
}

# 一个vrrp_instance就是定义一个虚拟路由器的,实例名称
vrrp_instance VI_1 {
    # 定义初始状态,可以是MASTER或者BACKUP
    state MASTER
    #非抢占模式
    nopreempt
    # 工作接口,通告选举使用哪个接口进行
    interface ens33
    # 虚拟路由ID,如果是一组虚拟路由就定义一个ID,如果是多组就要定义多个,而且这个虚拟
    # ID还是虚拟MAC最后一段地址的信息,取值范围0-255
    virtual_router_id 51
    #权重 如果你上面定义了MASTER,这里的优先级就需要定义的比其他的高
    priority 100
    #通告频率 单位s
    advert_int 1
    # 通信认证机制,这里是明文认证还有一种是加密认证
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    # 设置虚拟VIP地址
    virtual_ipaddress {
        192.168.254.11
    }
    # 追踪脚本,通常用于去执行上面的vrrp_script定义的脚本内容
    track_script {
      check_pg_alived
    }
   # 如果主机状态变成Master|Backup|Fault之后会去执行的通知脚本,脚本要自己写
    notify_master "/data/scripts/failover.sh"
    notify_fault  "/data/scripts/fault.sh"
}

以上是Keepalived主节点的配置,Keepalived备节点的priority参数改成90 ,state改为BACKUP,以及smtp_server 改为192.168.254.129,其余参数配置一样。

3.6 数据库监控脚本

代码语言:javascript
复制
[postgres@localhost scripts]$ cat /data/scripts/pg_monitor.sh 
#!/bin/bash 
# 配置环境变量 
pgport=5432 
pguser=keepalived 
pgdb=keepalived 
pgpwd='123qwert'
LANG=en_US.utf8 
PGHOME=/usr/local/pgsql/ 
LD_LIBRARY_PATH=$PGHOME/lib:/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib 
PATH=$PGHOME/bin:$PATH:.
MONITOR_LOG="/data/scripts/pg_monitor.log" 
SQL1="UPDATE sr_delay SET last_alive= now();" 
SQL2='SELECT 1;' 
keeplognums=30000

#pg_port_status=`lsof ‐i :$pgport | grep LISTEN | wc ‐l` 
#pg_port_status=`ps ‐ef | grep LISTEN | wc ‐l` 
#if [ $pg_port_status ‐lt 1 ];then 
# echo ‐e `date +"%F %T"` "Error: The postgreSQL is not running,please chec k the postgreSQL server status!" >> $LOGFILE 
# exit 1 
#fi 

#此脚本不检查备库存活状态,如果是各库则退出 

standby_flg=`psql -p$pgport -Upostgres -At -c "SELECT pg_is_in_recovery();"` 
if [ ${standby_flg} == 't' ]; 
then 
 echo ‐e "`date +%F\ %T`: This is a standby database, exit!\n" > $MONITOR_LOG 
 exit 0 
fi
export PGPASSWORD=$pgpwd
#主库更新sr_delay 表 
echo $SQL1 | psql -At -p $pgport -U $pguser -d $pgdb >> $MONITOR_LOG 
#判断主库是否可用
echo $SQL2 | psql -At -p $pgport -U $pguser -d $pgdb 
if [ $? -eq 0 ] ;
then 
 echo -e "`date +%F\ %T`:Primary db is health." >> $MONITOR_LOG 
 exit 0 
else 
 echo -e "`date +%F\ %T`:Attention: Primary db is not health!" >> $MONITOR_LOG 
 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


此脚本每隔10秒执行一次,执行频率由keepalived.conf配置文件中interval参数设置, 脚本主要作用为:

  • 检测主库是否存活。
  • 更新sr_delay表last_alive字段为当前探测时间。
代码语言:javascript
复制
[postgres@localhost scripts]$ cat /data/scripts/failover.sh 
#!/bin/bash 
export PGPORT=5432 
export PGUSER=keepalived 
export PG_OS_USER=postgres 
export PGDBNAME=keepalived 
export LANG=en_US.UTF-8 
export PGPATH=/usr/local/pgsql/bin/ 
export PATH=$PATH:$PGPATH 
export PGMIP=127.0.0.1 
LOGFILE='/data/scripts/failover.log'
# 主备数据库同步时延,单位为秒 
sr_allowed_delay_time=100

SQL1='select pg_is_in_recovery from pg_is_in_recovery();' 
SQL2="select count(1) as delay_time from sr_delay where now()<(last_alive + interval '${sr_allowed_delay_time} seconds');"
#SQL2="select count(1) as delay_time from sr_delay where now()<(last_alive + interval '100 seconds');" 
sleep $sr_allowed_delay_time
db_role=`echo $SQL1 | psql -At -p $PGPORT -U $PGUSER -d $PGDBNAME -w` 
db_sr_delaytime=`echo $SQL2 | psql -p $PGPORT -d $PGDBNAME -U $PGUSER -At -w` 
SWITCH_COMMAND='pg_ctl promote -D /data/pg_data/' 
# 如果为备库,且延迟大于指定时间则切换为主库

if [ ${db_role} == 'f' ];
then 
	echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is master database,cannot switched!" >> $LOGFILE 
	exit 0 
fi

if [ $db_sr_delaytime -gt 0 ];
then 
	echo -e `date +"%F %T"` "Attention:The current master database is health,the standby DB cannot switched!" >> $LOGFILE 
	exit 0 
fi

if [ !$db_sr_delaytime ];
then 
	echo -e `date +"%F %T"` "Attention:The current database is statndby,ready to switch master database!" >> $LOGFILE 
	su - $PG_OS_USER -c "$SWITCH_COMMAND" 
elif [ $? -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

代码语言:javascript
复制
[postgres@localhost scripts]$ cat /data/scripts/fault.sh
#!/bin/bash
GFILE=/data/scripts/pg_db_fault.log 
PGPORT=5432 
PGMIP=192.168.254.128
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 /data/pg_data/postmaster.pid`" 

systemctl stop keepalived 

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 
	exit 1 
fi

代码语言:javascript
复制
[postgres@localhost scripts]$ chmod 755 /data/scripts/pg_monitor.sh /data/scripts/failover.sh /data/scripts/fault.sh

四、切换演练

4.1 在192.168.254.128上停掉数据库

代码语言:javascript
复制
[root@localhost ~]# su - postgres
[postgres@localhost ~]$ pg_ctl -D /data/pg_data/ stop

查看192.168.254.129上的数据库状态

代码语言:javascript
复制
[postgres@localhost ~]$ psql
psql (13.3)
Type "help" for help.
postgres=# insert into t5 (name) values ('b002');
INSERT 0 1

修复192.168.254.128。

使用pg_rewind 同步新主库的数据到原主库:

代码语言:javascript
复制
pg_rewind -R --target-pgdata '/data/pg_data' --source-server 'host=192.168.254.129 port=5432 user=replia password=123qwert dbname=postgres'

如果这个地方咱们的wal日志已经被覆盖了了很多,那么就需要将我们的备份日志/data/pg_archive/给scp过来。

代码语言:javascript
复制
[postgres@localhost ~]$ scp postgres@192.168.254.129:/data/pg_archive/00000002*  /data/pg_archive/

(此时需要注意新主和备上面TimeLineID的差异。并且这种情况需要在配置文件中加上:

代码语言:javascript
复制
restore_command='cp /data/pg_archive/%f %p'

启动数据库,然后检查主备wal sender和receiver是否正常:

代码语言:javascript
复制
[postgres@localhost ~]$ pg_ctl -D /data/pg_data/ start
[postgres@localhost ~]$ ps axu |grep walreceiver
postgres  53474  0.0  0.2 342008  2136 ?        Ss   14:45   0:01 postgres: walreceiver streaming 1/4A036EF0

192.168.254.128上启动Keepalived,查看Keepalived状态:

代码语言:javascript
复制
[root@localhost ~]# systemctl  start keepalived
[root@localhost ~]# systemctl  status keepalived

4.2 再次切换:

在做切换前,先要检查192.168.254.128和192.168.254.129上面的Keepalived正常以及vip和主从正常,在192.168.254.129上停掉数据库:

代码语言:javascript
复制
[postgres@localhost ~]$ pg_ctl -D /data/pg_data/ stop -mf

查看192.168.254.128上的数据库状态:

代码语言:javascript
复制
[postgres@localhost pg_data]$ psql
psql (13.3)
Type "help" for help.
postgres=# insert into t5 (name) values ('b005');
INSERT 0 1

修复192.168.254.129。

使用pg_rewind 同步新主库的数据到原主库:

代码语言:javascript
复制
pg_rewind -R --target-pgdata '/data/pg_data' --source-server 'host=192.168.254.128 port=5432 user=replia password=123qwert dbname=postgres'

如果这个地方咱们的wal日志已经被覆盖了了很多,那么就需要将我们的备份日志/data/pg_archive/给scp过来。

代码语言:javascript
复制
[postgres@localhost ~]$ scp postgres@192.168.254.128:/data/pg_archive/00000003*  /data/pg_archive/  

(此时需要注意新主和备上面TimeLineID的差异。并且这种情况需要在配置文件中加上:

代码语言:javascript
复制
restore_command='cp /data/pg_archive/%f %p'

启动数据库,然后检查主备wal sender和receiver是否正常:

代码语言:javascript
复制
[postgres@localhost ~]$ pg_ctl -D /data/pg_data/ start
[postgres@localhost ~]$ ps axu |grep walreceiver

192.168.254.129上启动Keepalived,查看Keepalived状态:

代码语言:javascript
复制
[root@localhost ~]# systemctl  start keepalived
[root@localhost ~]# systemctl  status keepalived

五、总结

1、可以完善failover时自己的延迟切换逻辑。

2、可以完善pg_rewind的实现,用脚本代替手动方式。

3、Keepalived较为灵活,能够在脚本上添加更多校验和自己的规则。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据和云 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2.1 主机规划
  • 2.2 架构图
  • 3.1 关闭防火墙和selinux
  • 3.2 安装Keepalived
  • 3.3 PostgreSQL数据库异步主从流复制搭建
    • 1、创建流复制用户
      • 2、数据目录下配置pg_hba.conf文件添加(192.168.254.129也需要添加)
        • 3、配置环境变量
          • 5、制作基础备份
            • 6、启动从库
            • 192.168.254.128上查看主库信息:
              • 7、查看主库信息
              • 3.4 PostgreSQL数据库配置
              • 3.5 Keepalived 配置
              • 3.6 数据库监控脚本
              • 4.1 在192.168.254.128上停掉数据库
              • 4.2 再次切换:
              相关产品与服务
              数据库
              云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档