一、问题提出
由于公司服务器硬件已临界损耗期限,最近数据库主机频繁出现故障,不得不手工执行主从库切换操作。这种方式的问题是全程需要人工干预,宕机时间长,严重影响线上业务。于是开始调研MySQL 高可用解决方案,要达到两个主要目标:主库出现问题时快速自动切换到从库;对应用透明。

二、方案选择
MySQL的高可用方案有很多,比如Cluster、MMM、MHA、DRBD,以及Oracle官方推出的Fabric等,这些方案各有优劣,但都比较复杂,安装配置有一定难度,对线上库实施动静太大。就我们的具体情况而言,并不需要这么复杂的环境,实施简单、对现有架构影响最小、能迅速解决问题的方案才是最适合的。比如我们现在只是配置了MySQL Replication,加上如Keepalived这样的高可用软件,就能实现我们的需求。

  1. MySQL架构为Master/Slave,当Master故障时,虚IP漂移到Slave上提供服务,简单环境如图1所示。在这种架构中,故障自动切换以后,需要采取手动操作的方式与新的Master进行复制。当然也可以设置为Active-Passive模式下的双Master复制。

图1

三、Keepalived简介
Keepalived的作用是检测服务器的状态,如果有一台服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其它服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。

  1. VRRP
    1. Keepalived是对VRRP的一个实现,因此在理解Keepalived的工作原理之前,有必要先了解VRRP的原理。

1.1 VRRP协议
在现实的网络环境中,两台需要通信的主机(end-host)大多数情况下并没有直接地物理连接。对于这样的情况,它们之间的路由怎么选择?通常有两种方法解决如何选定到达目的主机的下一跳路由问题:

使用动态路由协议,如RIP、OSPF等。
配置静态路由。
很明显,在主机上配置动态路由,因为管理、维护成本以及是否支持等诸多问题是不切实际的。那么配置静态路由就变得很流行。实际上,这种方式一直沿用至今。但是,路由器,或者说默认网关(default gateway)却经常成为单故障点。就算配置了多个静态路由,却因为必须重启网络才能生效而变得不实用。

  1. VRRP(虚拟路由冗余协议,Virtual Router Redundancy Protocol)的目的就是为了解决静态路由单点故障问题。它通过一种竞选(election)协议动态地将路由任务交给LAN中虚拟路由器中的某台VRRP路由器。这里有两个关键名词:VRRP路由器和虚拟路由器。

VRRP路由器:VRRP路由器就是一台路由器,只不过上面运行了VRRPD程序来实现VRRP协议而已,是物理的路由器。一台VRRP路由器可以位于多个虚拟路由器中。
虚拟路由器:所谓虚拟,就是说并不是实际存在的,是一个逻辑而不是物理的路由器。虚拟路由器通常由多台VRRP路由器通过某种方式组成,就好像将这些物理路由器都丢到一个池里面去,整个池对外看起来就像是一台路由器,但其实内部有多台。虚拟路由器的标识称为VRID。
在一个VRRP虚拟路由中,有多台物理的VRRP路由器,但是这多台物理路由并不同时工作,而是由一台称为master的负责路由工作,其它的都是backup。master并非一成不变,VRRP协议让每个VRRP路由器参与竞选,最终获胜的就是master。master有一些特权,比如拥有虚拟路由器的IP地址,我们的主机就是用这个IP地址作为静态路由的。拥有特权的master要负责转发发送给网关地址的包和响应ARP请求。

1.2 工作机制
VRRP通过竞选协议来实现虚拟路由器的功能,所有的协议报文都是通过IP多播(multicast)包形式发送的,多播地址为224.0.0.18。虚拟路由器由VRID(范围0-255)和一组IP地址组成,对外表现为一个众所周知的MAC地址:00-00-5E-00-01-{VRID}。所以,在一个虚拟路由器中,不管谁是master,对外都是相同的MAC和IP(称之为VIP)。客户端主机并不需要因为master的改变而修改自己的路由配置,对它们来说,这种主从的切换是透明的。

  1. 在一个虚拟路由器中,只有作为masterVRRP路由器会一直发送VRRP广告包(VRRP Advertisement Message),backup不会抢占master,除非它的优先级更高。当master不可用时,backup收不到广告包,多台backup中优先级最高的这台会抢占为master。这种抢占是非常快速的(<1秒),以保证服务的连续性。出于安全性考虑,VRRP包使用了加密协议进行加密。
  1. Keepalived设计与实现
    1. Keepalived是一个高度模块化设计的软件,从源代码结构似乎也很容易看出这一点。Keepalived 1.2.13源代码中只有如下目录:check core etc include libipvs-2.4 libipvs-2.6 vrrp

check:Keepalived的healthchecker子进程的目录,包括了所有的健康检查方式以及对应配置的解析,LVS的配置解析也在这个里面。
core:Keepalived的核心程序,如全局配置的解析,进程启动等等。
etc:包含Keepalived的配置模板和启动脚本等文件。
include:头文件目录。
libipvs*:LVS使用的库文件。
vrrp:Keepalived的vrrpd子进程以及相关的代码。
Keepalived架构如图2所示。

图2

1.1 多进程模式
Keepalived采用了多进程的设计模式,每个进程负责不同的功能,我们在使用LVS的机器上通常可以看到三个Keepalived进程:
111 keepalived < 父进程,负责内存管理、监控子进程等
112 _ keepalived < VRRP子进程
113 _ keepalived < healthchecker子进程

1.2 控制面板
这里所谓的控制面板就是对配置文件的编译和解析。Keepalived的配置文件解析比较另类,并不是一次解析所有配置,而是只在用到某模块时才解析相应的配置。在源文件里面可以看到类似XXX_parser.c的文件,就是做这个用的。

1.3 看门狗
WatchDog框架提供了对VRRP和healthchecker子进程的监控。

1.4 IPVS封装
Keepalived里面所有对LVS的相关操作并不直接使用ipvsadm客户端程序,而是使用IPVS提供的函数进行操作,这些代码都在check/ipwrapper.c中。

四、安装配置
环境:
MySQL Master 172.16.1.126
MySQL Slave 172.16.1.127
VIP 172.16.1.100

  1. MySQL主从复制配置从略。
  1. 安装keepalived软件
    1. root执行下面的命令,主从操作一样。

wget -q http://www.keepalived.org/software/keepalived-1.2.13.tar.gz
tar -zxvf keepalived-1.2.13.tar.gz
cd keepalived-1.2.13
./configure && make && make install
cp /usr/local/etc/rc.d/init.d/keepalived /etc/rc.d/init.d/
cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/
mkdir /etc/keepalived
cp /usr/local/etc/keepalived/keepalived.conf /etc/keepalived/
cp /usr/local/sbin/keepalived /usr/sbin/
chkconfig —add keepalived
chkconfig —level 345 keepalived on

  1. 主从的配置文件修改
    1. masterkeepalived配置文件如下:

[root@hdp3~]#more /etc/keepalived/keepalived.conf
global_defs {
router_id MySQL-HA
}

vrrp_script check_run {
script “/home/mysql/mysql_check.sh”
interval 60
}

vrrp_sync_group VG1 {
group {
VI_1
}
}

vrrp_instance VI_1 {
state BACKUP
interface ens32
virtual_router_id 51
priority 100
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1234
}
track_script {
check_run
}
notify_master /home/mysql/master.sh
notify_stop /home/mysql/stop.sh

  1. virtual_ipaddress {
  2. 172.16.1.100
  3. }

}
[root@hdp3~]#

  1. slavekeepalived配置文件如下:

[root@hdp4~]#more /etc/keepalived/keepalived.conf
global_defs {
router_id MySQL-HA
}

vrrp_script check_run {
script “/home/mysql/mysql_check.sh”
interval 60
}

vrrp_sync_group VG1 {
group {
VI_1
}
}

vrrp_instance VI_1 {
state BACKUP
interface ens32
virtual_router_id 51
priority 90
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1234
}
track_script {
check_run
}
notify_master /home/mysql/master.sh
notify_stop /home/mysql/stop.sh

  1. virtual_ipaddress {
  2. 172.16.1.100
  3. }

}
[root@hdp4~]#
master与slave的keepalived配置文件中只有priority设置不同,master为100,slave为90,其它全一样。配置文件是以块形式组织的,每个块都在{}包围的范围内,#和!开头的行都是注释。

  1. global_defs为全局定义,对整个Keepalived起作用,而不管是否使用LVS

router_id:运行Keepalived的机器的一个标识。
vrrp_script配置业务进程监控脚本。

script:设置脚本文件名。
interval:设置脚本执行的时间间隔,这里为每60秒执行一次。
/home/mysql/mysql_check.sh文件用以检测MySQL服务是否正常,当发现连接不上mysql,自动把keepalived进程杀掉,让VIP进行漂移。文件内容如下。

[root@hdp3~]#more /home/mysql/mysql_check.sh

!/bin/bash

. /home/mysql/.bashrc
count=1

while true
do

mysql -uroot -S /data/mysql.sock -e “show status;” > /dev/null 2>&1
i=$?
ps aux | grep mysqld | grep -v grep > /dev/null 2>&1
j=$?
if [ $i = 0 ] && [ $j = 0 ]
then
exit 0
else
if [ $i = 1 ] && [ $j = 0 ]
then
exit 0
else
if [ $count -gt 5 ]
then
break
fi
let count++
continue
fi
fi

done

/etc/init.d/keepalived stop
[root@hdp3~]#
vrrp_sync_group配置VRRP同步组。不使用Sync Group的话,如果机器有两个网段,一个内网一个外网,每个网段开启一个VRRP实例。假设VRRP配置为检查内网,那么当外网出现问题时,VRRPD认为自己仍然健康,那么不会触发Master和Backup的切换,从而导致问题。Sync Group解决这个问题,可以把两个实例都放进一个Sync Group,这样的话,Sync Group里面任何一个实例出现问题都会发生切换。

group:设置同一组中的VRRP实例名,这里只有一个实例VI_1。
vrrp_instance配置VRRP实例。VRRP实例表示在上面开启了VRRP协议。这个实例说明了VRRP的一些特性,比如主从、VRID等等。可以在每个网卡上开启一个实例。VRRP实例主要定义vrrp_sync_group里面的每个组的漂移IP等。

state:指定实例的初始状态。在两台路由都启动后,马上会发生竞争,高priority的会竞选为Master,所以这里的state并不表示这台就一直是Backup。
interface:实例绑定的网卡。
virtual_router_id:VRID标记,值为0..255,这里使用默认的51。
priority:高优先级竞选为Master,Master要高于Backup至少50。这里MySQL主从库两个优先级分别设置为100和90,因此当Keepalived启动后,MySQL主库会被选为Master。
advert_int:检查间隔,这里设置为默认的1秒。
nopreempt:设置为不抢占,注意这个配置只能设置在state为BACKUP的主机上。当MASTER出现问题后,BACKUP会竞选为新的MASTER,那么当之前的MASTER重新在线后,是继续成为MASTER还是变成BACKUP呢?默认不设置不抢占,那么之前的MASTER起来后会继续抢占成为MASTER。这样的频繁切换对于业务是不能容忍的,我们希望MASTER起来后成为BACKUP,所以要设置不抢占。又因为nopreempt配置只能用在state为BACKUP的主机上,因此MASTER的state也得设置为BACKUP,也就是说172.16.1.126和172.16.1.127都要将state设置为BACKUP。通过在两台BACKUP上面设置不同的priority,让它们一起来就抢占,高priority的172.16.1.126成为最初的MASTER。
authentication:设置认证类型和认证密码。
auth_type:认证类型,支持PASS、AH两种,通常使用PASS类型。
auth_pass:明文认证密码。同一VRRP实例的MASTER与BACKUP使用相同的密码才能正常通信。
track_script:设置追踪脚本,这里为check_run,即调用vrrp_script中定义的脚本。
notify_master:指定当切换到MASTER时执行的脚本。
notify_stop:VRRP停止以后执行的脚本。
virtual_ipaddress:指定漂移地址(VIP),也就是切换到MASTER时,这些IP或被添加,切换到BACKUP时,这些IP会被删除。因此每台服务器上可以不绑定任何虚拟地址,而都把它们放到virtual_ipaddress里面,可以都多个。Keepalived会自动使用ip addr进行绑定。
/home/mysql/master.sh的作用是状态改为master以后执行的脚本。首先判断复制是否有延迟,如果有延迟,等1分钟后,不论是否有延迟,都并停止复制,并且记录binlog和pos点。文件内容如下。

[root@hdp3~]#more /home/mysql/master.sh

!/bin/bash

. /home/mysql/.bashrc

Master_Log_File=$(mysql -uroot -S /data/mysql.sock -e “show slave status\G” | grep -w Master_Log_File | awk -F”: “ ‘{print $2}’)
Relay_Master_Log_File=$(mysql -uroot -S /data/mysql.sock -e “show slave status\G” | grep -w Relay_Master_Log_File | awk -F”: “ ‘{print $2}’)
Read_Master_Log_Pos=$(mysql -uroot -S /data/mysql.sock -e “show slave status\G” | grep -w Read_Master_Log_Pos | awk -F”: “ ‘{print $2}’)
Exec_Master_Log_Pos=$(mysql -uroot -S /data/mysql.sock -e “show slave status\G” | grep -w Exec_Master_Log_Pos | awk -F”: “ ‘{print $2}’)

i=1

while true
do

if [ $Master_Log_File = $Relay_Master_Log_File ] && [ $Read_Master_Log_Pos -eq $Exec_Master_Log_Pos ]
then
echo “ok”
break
else
sleep 1

if [ $i -gt 60 ]
then
break
fi
continue
let i++
fi
done

mysql -uroot -S /data/mysql.sock -e “stop slave;”
mysql -uroot -S /data/mysql.sock -e “reset slave all;”
mysql -uroot -S /data/mysql.sock -e “reset master;”
mysql -uroot -S /data/mysql.sock -e “show master status;” > /tmp/masterstatus$(date “+%y%m%d-%H%M”).txt

[root@hdp3~]#
/home/mysql/stop.sh表示Keepalived停止以后需要执行的脚本。检查是否还有复制写入操作,最后无论是否执行完毕都退出。文件内容如下。

[root@hdp3~]#more /home/mysql/stop.sh

!/bin/bash

. /home/mysql/.bashrc

M_File1=$(mysql -uroot -S /data/mysql.sock -e “show master status\G” | awk -F’: ‘ ‘/File/{print $2}’)
M_Position1=$(mysql -uroot -S /data/mysql.sock -e “show master status\G” | awk -F’: ‘ ‘/Position/{print $2}’)
sleep 1
M_File2=$(mysql -uroot -S /data/mysql.sock -e “show master status\G” | awk -F’: ‘ ‘/File/{print $2}’)
M_Position2=$(mysql -uroot -S /data/mysql.sock -e “show master status\G” | awk -F’: ‘ ‘/Position/{print $2}’)

i=1

while true
do

if [ $M_File1 = $M_File1 ] && [ $M_Position1 -eq $M_Position2 ]
then
echo “ok”
break
else
sleep 1

if [ $i -gt 60 ]
then
break
fi
continue
let i++
fi
done

[root@hdp3~]#
五、测试

  1. 分别在master上和slave上启动keepalived进程。

/etc/init.d/keepalived start
成功启动后可以看到如图3所示的3个Keepalived进程。

图3

  1. 查看master上的VIP

[root@hdp3~]#ip addr
结果如图4所示,可以看到VIP已经绑定成功。

图4

  1. 客户端使用VIP连接数据库,创建测试库,插入数据。

C:\WINDOWS\system32>mysql -u wxy -p -h 172.16.1.100
Enter password: **
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2958
Server version: 5.6.14-log Source distribution

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.

mysql> create database test;
Query OK, 1 row affected (0.00 sec)

mysql> use test;
Database changed
mysql> create table t1(a int);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t1 values (1);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1;
+———+
| a |
+———+
| 1 |
+———+
1 row in set (0.00 sec)

mysql>

  1. 模拟mysqld crash
    1. master上执行以下命令:

[root@hdp3~]#pkill -9 mysqld

  1. 再次使用VIP连接,数据没有异常,复制停止了,因为已经切换为主库。

C:\WINDOWS\system32>mysql -u wxy -p -h 172.16.1.100
Enter password: **
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1568
Server version: 5.6.14-log Source distribution

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.

mysql> use test;
Database changed
mysql> select * from t1;
+———+
| a |
+———+
| 1 |
+———+
1 row in set (0.00 sec)

mysql> show slave status;
Empty set (0.00 sec)

mysql> show master status;
+—————————+—————+———————+—————————+—————————-+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+—————————+—————+———————+—————————+—————————-+
| mysql-bin.000001 | 120 | | | |
+—————————+—————+———————+—————————+—————————-+
1 row in set (0.00 sec)

mysql>
binlog和pos点记录如下:

[root@hdp4~]#more /tmp/master_status_180704-1751.txt
File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set
mysql-bin.000001 120
[root@hdp4~]#
此时VIP漂移到172.16.1.127,而172.16.1.126上绑定的VIP已经删除。在172.16.1.126查看IP地址如图5所示。

图5

  1. 同时172.16.1.126keepalived进程也已经被杀掉:

[root@hdp3~]#ps -ef | grep keepalived | grep -v grep
[root@hdp3~]#

六、总结

  1. MySQL高可用方案配置简单,对现有MySQL架构无任何影响,也不需要停止数据库服务,完全联机操作即可。有一点需要注意,主从库的端口必须一样。

————————————————
版权声明:本文为CSDN博主「wzy0623」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wzy0623/article/details/80916567