目前的环境如下:centos7
有四台虚拟机,20,21,22,23
20为master,21,22,23 为20的从库,21 为管理节点。
搭建MySQL主从复制的,可以参考我之前的文章
MHA(Master High Availability)目前在 MySQL 高可用方面是一个相对成熟的解决方案,它由日本 DeNA 公司的 youshimaton(现就职于 Facebook 公司)开发,是一套优秀的作为 MySQL 高可用性环境下故障切换和主从提升的高可用软件。在 MySQL 故障切换过程中,MHA 能做到在 0~30 秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA 能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
(1) MHA Manager(管理节点)
MHA Manager 可以单独部署在一台独立的机器上管理多个 master/slave 集群,也可以部署在一台 slave 节点上;
(2) MHA Node(数据节点)
MHA Node 运行在每台 MySQL 服务器上,MHA Manager 会定时探测集群中的 master 节点,当 master 出现故障时,它可以自动将拥有最新数据的 slave 提升为新的 master,然后将所有其他的 slave 重新指向新的 master。整个故障转移过程对应用程序完全透明
在 MHA 自动故障切换过程中,MHA 试图从宕机的主服务器上获取保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过 ssh 访问,MHA 没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用 MySQL 5.5 的半同步复制,可以大大降低数据丢失的风险。MHA 可以与半同步复制结合起来。如果只有一个 slave 已经收到了最新的二进制日志,MHA 可以将最新的二进制日志应用于其他所有的 slave 服务器上,因此可以保证所有节点的数据一致性
# 4台机器都要安装 ifconfig命令
yum install -y net-tools
# 4台机器防火墙关闭
systemctl stop firewalld
systemctl disable firewalld
# 需要注意的是,关闭mysql从库的中继日志自动删除
# 这里将3台机器(20,22,23)设置该值,因为20也可能会作为从库,需要重启mysql
# /etc/my.cnf
relay_log_purge=0
# 创建软链接,因为mha内部代码使用了绝对路径导致的坑,你不做软链接,下面检查主从复制的时候会出现找不命令的报错
# 4台机器
ln -s /usr/local/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
ln -s /opt/mysql/mysql/bin/mysql /usr/bin/mysql
# 创建mha专用监控用户
# master节点上创建一个用户,从库自动会同步[我们这里4台机器都会同步]
CREATE USER 'mha'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
grant all privileges on *.* to mha@'%' ;
# 4台机器上下载node
wget https://qiniu.wsfnk.com/mha4mysql-node-0.58-0.el7.centos.noarch.rpm
# 21机器上下载manager
wget https://qiniu.wsfnk.com/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
# 4台机器安装DBD::MYSQL
yum install -y perl-DBD-MySQL
# 安装4台机器node
rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm
# 4台机器安装系统自带prel包
yum -y install perl-*
# 下载除系统自带外的perl包rpm包搜索
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Email-Date-Format-1.002-15.el7.noarch.rpm
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-MIME-Lite-3.030-1.el7.noarch.rpm
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-MIME-Types-1.38-2.el7.noarch.rpm
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
wget http://rpmfind.net/linux/centos/7.9.2009/os/x86_64/Packages/perl-Config-Tiny-2.14-7.el7.noarch.rpm
wget http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm
# 4台机器安装下载的prel包
yum -y install perl-*.rpm
# 以上这些包装好之后,开始在21 机器上安装manager
[root@mysql-back opt]# rpm -ivh mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
[root@mysql-back opt]# ll /usr/bin/masterha*
-rwxr-xr-x. 1 root root 1995 3月 23 2018 /usr/bin/masterha_check_repl
-rwxr-xr-x. 1 root root 1779 3月 23 2018 /usr/bin/masterha_check_ssh
-rwxr-xr-x. 1 root root 1865 3月 23 2018 /usr/bin/masterha_check_status
-rwxr-xr-x. 1 root root 3201 3月 23 2018 /usr/bin/masterha_conf_host
-rwxr-xr-x. 1 root root 2517 3月 23 2018 /usr/bin/masterha_manager
-rwxr-xr-x. 1 root root 2165 3月 23 2018 /usr/bin/masterha_master_monitor
-rwxr-xr-x. 1 root root 2373 3月 23 2018 /usr/bin/masterha_master_switch
-rwxr-xr-x. 1 root root 5172 3月 23 2018 /usr/bin/masterha_secondary_check
-rwxr-xr-x. 1 root root 1739 3月 23 2018 /usr/bin/masterha_stop
至此21机器上的manager,4台机器上的node都已经安装好了,接下来就是相关的配置了
#创建配置文件目录
mkdir -p /etc/mha
#创建日志目录,即工作目录
mkdir /var/log/mha/mysql_mha -p
#创建日志文件
touch /var/log/mha/mysql_mha/manager.log
#编辑mha配置文件,定义一个配置文件名即可,因为一个配置文件对应一组mha管理的mysql主从集群
#如果有多组mysql集群的话,可以创建多个文件进行管理
vim /etc/mha/mysql_mha.cnf
chmod +x mysql_mha.cnf
# 如下是:mysql_mha.cnf配置
[server default]
manager_log=/var/log/mha/mysql_mha/manager.log
manager_workdir=/var/log/mha/mysql_mha
#指定master节点binlog日志的目录,查看master节点/etc/my.cnf里log_bin = /usr/local/mysql/data/binlog,
#注意binlog只是文件的前缀,不是目录,所以这里我们写到data目录即可
master_binlog_dir=/usr/local/mysql/data/
#指定mha用户,刚才我们创建了
user=mha
#指定mha用户的密码
password=123456
#探测主库时发送ping包的时间间隔
ping_interval=2
#指定主从复制的用户,在创建mysql主从复制的时候创建了这个用户
repl_user=repl
#指定主从复制的用户密码
repl_password=123456
#ssh的用户
ssh_user=root
ssh_port=22
#下面几个就是定义mysql服务器的ip端口等信息
[server1]
hostname=172.16.185.20
port=3306
#指定该数据库服务器参与竞选主库,1代表参与
candidate_master=1
[server2]
hostname=172.16.185.22
port=3306
candidate_master=1
[server3]
hostname=172.16.185.23
port=3306
candidate_master=1
/usr/bin/masterha_manager #启动mha-manager
/usr/bin/masterha_stop #停止hma-manager
/usr/bin/masterha_check_repl #检查mysql的主从复制状况是否ok
/usr/bin/masterha_check_ssh #检查mysql的ssh配置是否正常
/usr/bin/masterha_check_status #检查当前mha-manager的运行状态
/usr/bin/masterha_conf_host #添加或删除配置文件的server信息
/usr/bin/masterha_master_monitor #检测master是否宕机
/usr/bin/masterha_master_switch #控制故障转移(自动或手动)
/usr/bin/masterha_secondary_check#检查多路由配置
/usr/bin/apply_diff_relay_logs #识别差异的中继日志事件并应用于其它Slave
/usr/bin/purge_relay_logs #清除中继日志(不会阻塞SQL线程)
/usr/bin/save_binary_logs #保存和复制master的二进制日志
/usr/bin/filter_mysqlbinlog #去除不必要的Rollback事件(MHA已不再使用该工具)
这里需要注意的是:4台机器ssh都能互相免密登陆,可以参考
如下地址:ssh免密登陆
#在manager上验证各个mysql节点的互信
[root@mysql-back etc]# masterha_check_ssh --conf=/etc/mha/mysql_mha.cnf
Wed Nov 8 14:04:06 2023 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping
................................................
Wed Nov 8 14:04:10 2023 - [info] All SSH connection tests passed successfully.
[root@mysql-back etc]#
[root@mysql-back etc]# masterha_check_repl --conf=/etc/mha/mysql_mha.cnf
Wed Nov 8 14:26:01 2023 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
....
....
MySQL Replication Health is OK
到现在为止,我们基础配置已经完成了
--remove_dead_master_conf是指当主库宕机的时候,会删除宕机主库的配置,
否则主库宕机后服务就会无法启动;
–ignore_last_failover是指忽略 xxx.health 文件,意思是当主库宕机之后,
服务会在规定的时间内连接剩下的从服务器来选举出主库。
如果启动服务的时候不加这个选项,那么超过规定时间还没连接上的时候,就不会再去切换主库了,
加上这个选项后,既是在规定时间内没能连接上剩下的从服务器,之后也会继续尝试连接,
知道选举出主库进行切换。
nohup: 这个指的是后台启动
[root@mysql-back etc]# nohup masterha_manager --conf=/etc/mha/mysql_mha.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/mysql_mha/manager.log 2>&1 &
[1] 8958
[root@mysql-back etc]# masterha_check_status --conf=/etc/mha/mysql_mha.cnf
mysql_mha (pid:8958) is running(0:PING_OK), master:172.16.185.20
##如果想停止服务可以输入下面命令
[root@mysql-back etc]# masterha_stop --conf=/etc/mha/mysql_mha.cnf
#查看进程,已经启动mha-manager了
[root@mysql-back etc]# ps -ef | grep mha
这里就不演示了,手动将20 机器的mysql 停掉,就会自动将22或者23机器提为新主库,22或者23为新的主库的从库了。
mha 实现了master故障,自动切换master,但是这对于应用程序而言,数据库IP变了,这怎么能行呢?,所以mha 自身提供了vip 来实现漂移机制。
mha 实现vip漂移也是使用脚本实现的,脚本可以在官网的源码包里面找到:
# 在github 下载mha4mysql-manager,将其上传到linux的/opt目录
[root@mysql-back opt]# wget https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-manager-0.58.tar.gz
[root@mysql-back opt]# tar -zxvf mha4mysql-manager-0.58.tar.gz
[root@mysql-back opt]# cp ./mha4mysql-manager-0.58/samples/scripts/* /etc/mha/
[root@mysql-back opt]# ll /etc/mha/
-rwxr-xr-x. 1 root root 3648 11月 8 14:45 master_ip_failover
-rwxr-xr-x. 1 root root 9870 11月 8 14:45 master_ip_online_change
-rw-r--r--. 1 root root 931 11月 8 14:04 mysql_mha.cnf
-rwxr-xr-x. 1 root root 11867 11月 8 14:45 power_manager
-rwxr-xr-x. 1 root root 1360 11月 8 14:45 send_report
# 这里我们将master_ip_failover 备份一份
[root@mysql-back mha]# cp master_ip_failover master_ip_failover_bak
[root@mysql-back mha]# echo '' > master_ip_failover #清空,将下面的内容填充到该文件中
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
my (
$command, $ssh_user, $orig_master_host,
$orig_master_ip,$orig_master_port, $new_master_host,
$new_master_ip, $new_master_port
);
my $vip = '172.16.185.30';#指定vip的地址
my $brdc = '172.16.185.255';#指定vip的广播地址
my $ifdev = 'ens33';#指定vip绑定的网卡
my $key = '1'; #指定vip绑定的虚拟网卡序列号
my $ssh_start_vip = "/sbin/ifconfig ens33:$key $vip"; #代表此变量值为ifconfig ens33:1 1172.16.185.30
my $ssh_stop_vip = "/sbin/ifconfig ens33:$key down"; #代表此变量值为ifconfig ens33:1 172.16.185.30 down
my $exit_code = 0;#指定退出状态码为0
GetOptions(
'command=s' => \$command,
'ssh_user=s' => \$ssh_user,
'orig_master_host=s' => \$orig_master_host,
'orig_master_ip=s' => \$orig_master_ip,
'orig_master_port=i' => \$orig_master_port,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
);
exit &main();
sub main {
print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";
if ( $command eq "stop" || $command eq "stopssh" ) {
my $exit_code = 1;
eval {
print "Disabling the VIP on old master: $orig_master_host \n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}elsif ( $command eq "start" ) {
my $exit_code = 10;
eval {
print "Enabling the VIP - $vip on the new master - $new_master_host \n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}elsif ( $command eq "status" ) {
print "Checking the Status of the script.. OK \n";
exit 0;
}else {
&usage();
exit 1;
}
}
sub start_vip() {
#这里指定ssh的用户时root,因为发现不指定会报错的
my $ssh_user = 'root';
`ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
# A simple system call that disable the VIP on the old_master
sub stop_vip() {
#这里指定ssh的用户时root,因为发现不指定会报错的
my $ssh_user = 'root';
`ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
sub usage {
print
"Usage: master_ip_failover --command=start|stop|stopssh|status --orig_master_host=host --orig_master_ip=ip --orig_master_port=port --new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}
#修改mha-manager的配置文件
[server default]
.............
#添加vip脚本
master_ip_failover_script=/etc/mha/master_ip_failover
#第一次配置,需要在master上创建vip
[root@mysql-master opt]# ifconfig ens33:1 172.16.185.30/24
接下来就是启动,可以参考上面的启动管理服务
# 我们先将20这台机器的mysql手动停掉,来看一下日志文件 /var/log/mha/mysql_mha/manager.log
# 出现这个说明故障自动切换已经可以
----- Failover Report -----
mysql_mha: MySQL Master failover 172.16.185.20(172.16.185.20:3306) to 172.16.185.22(172.16.185.22:3306) succeeded
Master 172.16.185.20(172.16.185.20:3306) is down!
Check MHA Manager logs at mysql-back:/var/log/mha/mysql_mha/manager.log for details.
Started automated(non-interactive) failover.
Invalidated master IP address on 172.16.185.20(172.16.185.20:3306)
The latest slave 172.16.185.22(172.16.185.22:3306) has all relay logs for recovery.
Selected 172.16.185.22(172.16.185.22:3306) as a new master.
172.16.185.22(172.16.185.22:3306): OK: Applying all logs succeeded.
172.16.185.22(172.16.185.22:3306): OK: Activated master IP address.
172.16.185.23(172.16.185.23:3306): This host has the latest relay log events.
Generating relay diff files from the latest slave succeeded.
172.16.185.23(172.16.185.23:3306): OK: Applying all logs succeeded. Slave started, replicating from 172.16.185.22(172.16.185.22:3306)
172.16.185.22(172.16.185.22:3306): Resetting slave info succeeded.
Master failover to 172.16.185.22(172.16.185.22:3306) completed successfully.
#查看22,23机器mysql的状态
#22机器
# 1. 这个可以说明22机器已经不是从库了
mysql> show slave status \G;
Empty set, 1 warning (0.00 sec)
ERROR:
No query specified
# 2. 出现这个说明是该机器有一个从库,因为我们这里就只有23了,所以23是从库
mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID |
+-----------+------+------+-----------+--------------------------------------+
| 4 | | 3306 | 3 | df697b44-787c-11ee-9923-000c29900fda |
+-----------+------+------+-----------+--------------------------------------+
1 row in set, 1 warning (0.00 sec)
#23机器
# 这个已经说明23机器是22机器的从库
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 172.16.185.22
# 接下来我们还得看一下虚拟ip 是否从20机器到22机器上了
#20机器,已经没有我们之前配这个了 ifconfig ens33:1 172.16.185.30/24
[root@mysql-master opt]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.185.20 netmask 255.255.255.0 broadcast 172.16.185.255
inet6 fe80::a245:faff:1cfc:ebb prefixlen 64 scopeid 0x20<link>
inet6 fd15:4ba5:5a2b:1002:2821:dfdd:6485:4ef prefixlen 64 scopeid 0x0<global>
ether 00:0c:29:0e:bc:4f txqueuelen 1000 (Ethernet)
RX packets 106118 bytes 131404826 (125.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 69936 bytes 6834138 (6.5 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 32 bytes 2592 (2.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 32 bytes 2592 (2.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
#22机器 可以看出这个 ens33:1 已经漂移到22机器了
[root@mysql-slave01 opt]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.185.22 netmask 255.255.255.0 broadcast 172.16.185.255
inet6 fe80::4f79:c1f3:2f3c:94dd prefixlen 64 scopeid 0x20<link>
inet6 fd15:4ba5:5a2b:1002:24c6:beab:f31c:1c2d prefixlen 64 scopeid 0x0<global>
ether 00:0c:29:2d:5b:50 txqueuelen 1000 (Ethernet)
RX packets 99583 bytes 131426306 (125.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 66377 bytes 6593894 (6.2 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens33:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.185.30 netmask 255.255.0.0 broadcast 172.16.255.255
ether 00:0c:29:2d:5b:50 txqueuelen 1000 (Ethernet)
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 407 bytes 56888 (55.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 407 bytes 56888 (55.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# 最后我们使用应用程序,还是可以正常访问30机器,因为30是我们虚拟出来的vip,
# 这样就实现了mha+vip故障自动切换,也不用更改应用程序的连接地址
当master故障之后,mha实现了master故障切换,应用只需要连接vip即可,但是我们恢复故障节点却有点麻烦,首先,当故障发生后,mha会删除配置文件的故障节点信息,如下所示:
# 我们的mha配置文件 /etc/mha/mysql_mha.cnf 中的变化
[server default]
manager_log=/var/log/mha/mysql_mha/manager.log
manager_workdir=/var/log/mha/mysql_mha
master_binlog_dir=/usr/local/mysql/data/
master_ip_failover_script=/etc/mha/master_ip_failover
password=123456
ping_interval=2
repl_password=123456
repl_user=repl
ssh_port=22
ssh_user=root
user=mha
### 这个地方的之前我们是有server1的也就是20机器的配置,现在没有了,
### 因为我们手动关闭了20机器的mysql,mha会自动将其移除
### 现在我们故障恢复的话,就需要手动加上去的
[server2]
candidate_master=1
hostname=172.16.185.22
port=3306
[server3]
candidate_master=1
hostname=172.16.185.23
port=3306
需要注意的是,故障发生后,masterha_manager进程还会自动退出。所以,现在我们需要做这么几件事:
# 1. 修复原来崩掉的mysql;
[root@mysql-master opt]# /usr/local/mysql/support-files/mysql.server start
# 2. 登陆mysql,让其重新加入现在的主从集群,而如何加入现在的主从集群呢?
# 这可以在manager的日志找到相关信息:
Thu Nov 9 16:59:22 2023 - [info] All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='172.16.185.22', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000006', MASTER_LOG_POS=157, MASTER_USER='repl', MASTER_PASSWORD='xxx';
Thu Nov 9 16:59:22 2023 - [info] Executing master IP activate script:
# 3. 即登陆20机器的mysql,执行CHANGE MASTER语句,启动slave,注意修改密码;
mysql> CHANGE MASTER TO MASTER_HOST='172.16.185.22', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000006', MASTER_LOG_POS=157, MASTER_USER='repl', MASTER_PASSWORD='123456';
mysql> start slave;
# 这样20机器就成22的从库了
mysql> show slave status \G;;
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 172.16.185.22
# 4. 把节点信息增加回配置文件 vim /etc/mha/mysql_mha.cnf
[server1]
candidate_master=1
hostname=172.16.185.20
port=3306
# 5、重启mha即可。