MySQL数据库集群实战(6)——MySQL主从复制高可用之MHA

一、理解MHA

1、什么是MHA

MHA(MySQL-master-ha)是目前广泛使用的MySQL主从复制的高可用方案。MHA设计目标是自动实现主实例宕机后,从机切换为主,并尽量降低切换时延(通常在10-30s内切换完成)。同时,由MHA保证在切换过程中的数据一致性。MHA对MySQL的主从复制集群非常友好,没有对集群做任何侵入性的修改。

MHA的一个重点特性是:在主实例宕机后,MHA可以自动的判断主从复制集群中哪个从实例的relaylog是最新的,并将最新从实例的差异log“应用”到其余的从实例中,从而保证每个实例的数据一致。通常情况下,MHA需要10s左右检测主实例异常,并将主实例关闭从而避免脑裂。然后再用10s左右将差异的log event同步,并启用新的Master。整个MHA的RTO时间大约在30s。

2、MHA组成

该软件由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。

3、MHA自动故障切换过程

在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器 硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制,可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最 新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。

4、MHA的要求和原理

要求:
(1)需要奇数个节点

(2)当一个master节点挂掉之后投票选出一个新的master,偶数个节点会出现票数相同的状况,但奇数个就不会

(3)所有数据节点的数据一致,每一台数据节点都有可能作为master

(4)每个节点都要安装master和slave插件;

当master节点挂了之后,一般选出数据最近的slave节点作为新的master节点。数据最近指的是数据的差异性小

原理:
(1)从宕机崩溃的master保存二进制日志事件(binlog events);

(2)识别含有最新更新的slave;

(3)apply差异的中继日志(relay log)到其他的slave;

(4)apply从master保存的二进制日志事件(binlog events);

(5)提升一个slave为新的master;

(6)使其他的slave连接新的master进行复制。

二、搭建MySQL高可用之MHA

实验环境

主机名 ip 服务
srver1 172.25.1.1 master
server2 172.25.1.2 slave(备master)
server3 172.25.1.3 slave
server4 172.25.1.4 MHA

注意:在做MHA之前,不要和之前的实验混淆,做好gtid复制就好。在这里我从头开始做一下

1、搭建MHA

在server1上
步骤一:下载mysql社区版服务器安装包
下载地址:mysql-community
在这里插入图片描述
步骤二:安装软件包

yum install -y mysql-community-client.x86_64 mysql-community-common.x86_64 mysql-community-libs.x86_64 mysql-community-libs-compat.x86_64 mysql-community-server.x86_64

在这里插入图片描述
步骤三:更改配置文件/etc/my.cnf,添加以下内容

server-id=1
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=binlog
log_slave_updates=ON

在这里插入图片描述
步骤四:开启mysqld服务,查看随机生成的密码

systemctl start mysqld
cat /var/log/mysqld.log | grep password

在这里插入图片描述
步骤五:安全初始化

mysql_secure_installation

在这里插入图片描述
步骤六:登陆数据库,创建并授权用来做复制的用户,查看master状态

mysql -uroot -pWestos+007
mysql> CREATE USER 'repl'@'172.25.1.%' IDENTIFIED BY  'Westos+007';
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'172.25.1.%';
mysql> flush privileges;
mysql> show master status;

在这里插入图片描述
在server2上

步骤一:下载mysql社区版服务器安装包
下载地址:mysql-community
在这里插入图片描述

步骤二:安装软件包

yum install -y mysql-community-client.x86_64 mysql-community-common.x86_64 mysql-community-libs.x86_64 mysql-community-libs-compat.x86_64 mysql-community-server.x86_64

在这里插入图片描述
步骤三:更改配置文件/etc/my.cnf,添加以下内容

server-id=2
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=binlog
log_slave_updates=ON

在这里插入图片描述

步骤四:开启mysqld服务,查看随机生成的密码

systemctl start mysqld
cat /var/log/mysqld.log | grep password

在这里插入图片描述
步骤五:安全初始化

mysql_secure_installation

在这里插入图片描述
步骤六:登陆数据库启用gtid

mysql -uroot -pWestos+007
mysql> CHANGE MASTER TO
    -> MASTER_HOST = '172.25.1.1',
    -> MASTER_USER = 'repl',
    -> MASTER_PASSWORD = 'Westos+007',
    -> MASTER_AUTO_POSITION = 1;
mysql> start slave;
mysql> show slave status\G

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
此时可以看到gtid配置成功

在server3上
与server2配置相同
步骤一:下载mysql社区版服务器安装包
下载地址:mysql-community

在这里插入图片描述
步骤二:安装软件包

yum install -y mysql-community-client.x86_64 mysql-community-common.x86_64 mysql-community-libs.x86_64 mysql-community-libs-compat.x86_64 mysql-community-server.x86_64

在这里插入图片描述
步骤三:更改配置文件/etc/my.cnf,添加以下内容

server-id=3
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=binlog
log_slave_updates=ON

在这里插入图片描述
步骤四:开启mysqld服务,查看随机生成的密码

systemctl start mysqld
cat /var/log/mysqld.log | grep password

在这里插入图片描述
步骤五:安全初始化

mysql_secure_installation

在这里插入图片描述
步骤六:登陆数据库启用gtid

mysql -uroot -pWestos+007
mysql> CHANGE MASTER TO
    -> MASTER_HOST = '172.25.1.1',
    -> MASTER_USER = 'repl',
    -> MASTER_PASSWORD = 'Westos+007',
    -> MASTER_AUTO_POSITION = 1;
mysql> start slave;
mysql> show slave status\G

在这里插入图片描述在这里插入图片描述在这里插入图片描述
此时可以看到gtid配置成功

在server4上
步骤一:安装MHA所需安装包及依赖项
在这里插入图片描述
步骤二:生成ssh密钥,管理节点是不需要输入密码,将生成的公钥和私钥传给数据节点

ssh-keygen
ssh-copy-id server1
ssh-copy-id server2
ssh-copy-id server3

在这里插入图片描述
在这里插入图片描述
注意:我的四台主机均做好了本地解析
在这里插入图片描述
步骤三:复制公钥私钥给数据节点,实现互相免密连接

scp -r .ssh server1:
scp -r .ssh server2:
scp -r .ssh server3:

在这里插入图片描述
在server1、server2和server3上,安装节点数据包

yum install -y mha4mysql-node-0.58-0.el7.centos.noarch.rpm

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在管理节点server4上
步骤一:创建一个目录,编辑配置文件

mkdir -p /etc/masterha
cd /etc/masterha/
vim app1.cnf

[server default]
manager_workdir=/etc/masterha
manager_log=/var/log/masterha.log   # manager 日志文件
master_binlog_dir=/etc/masterha

password=Westos+007   #MySQL管理帐号和密码
user=root
ping_interval=1
remote_workdir=/tmp
repl_password=Westos+007
repl_user=repl   # 复制帐号和密码
ssh_user=root  # 系统ssh用户

[server1]
hostname=172.25.1.1
port=3306

[server2]
hostname=172.25.1.2
port=3306
candidate_master=1  #设置优先级,master故障时会成为新的master
check_repl_delay=0  #默认如果候选master落后master100M的relay logs,MHA就不选改slave作为新的master

[server3]
hostname=172.25.1.3
port=3306
no_master=1  ##no_master表示这个节点不能作为master

在这里插入图片描述
步骤二:检查数据节点的ssh连接

masterha_check_ssh --conf=/etc/masterha/app1.cnf 

在这里插入图片描述
注意:如果发现报错,说明server1 2 3互相之间不免密
拷贝server4上的密钥给server1 2 3
我在上面已经做过

步骤三:检测数据节点的复制功能

masterha_check_repl --conf=/etc/masterha/app1.cnf

在这里插入图片描述
此时发现报错,这是因为server4默认是用root远程连接数据库,但是在配置数据库是已经禁用了root的远程连接
解决办法:
在主库(server1)上授权用户

mysql> grant all on *.* to root@'%' identified by 'Westos+007';

在这里插入图片描述
此时必须将两个slave节点设置只读(不写在配置文件里,因为slave随时可能切换为master)

在server2上:

将slave节点设置只读

set global read_only=1;

在这里插入图片描述
在server3上:

将slave节点设置只读

set global read_only=1;

在这里插入图片描述
在server4上

此时,再次检查数据节点复制功能:

masterha_check_repl --conf=/etc/masterha/app1.cnf

在这里插入图片描述

2、测试手动failover切换

在serer4上

先关闭manager,不关的话切不了,manager就是自动切换的工具

masterha_stop --conf=/etc/masterha/app1.cnf

在这里插入图片描述
在server1上

systemctl stop mysqld

在这里插入图片描述
在server4上

masterha_master_switch --master_state=dead --conf=/etc/masterha/app1.cnf --dead_master_host=172.25.1.1 --dead_master_ip=172.25.1.1 --dead_master_port=3306 --new_master_host=172.25.1.2 --new_master_port=3306

在这里插入图片描述在这里插入图片描述
手动切换之前,需要保证主从同步正常,repl复制用户能够远程连接.

在server3测试
在server3上看到它的master已经变成了server2
在这里插入图片描述
在server1上
手动开启server1,作为slave加入集群

步骤一:开启数据库mysqld

systemctl start mysqld

在这里插入图片描述
步骤二:进入数据库添加slave连接信息并开启slave

mysql -uroot -pWestos+007
mysql> CHANGE MASTER TO MASTER_HOST = '172.25.1.2', MASTER_USER = 'repl', MASTER_PASSWORD = 'Westos+007', MASTER_AUTO_POSITION = 1;
mysql> start slave;
mysql> show slave status\G

在这里插入图片描述在这里插入图片描述
现在就完成了一次手动切换,这时会在MHA节点/etc/masterha目录下生成一个app1.failover.complete文件,是来记录failover情况的,再进行failover时必须先把这个文件删除,不然不会failover
在这里插入图片描述

3、测试手动在线切换

在server4上

masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=172.25.1.1 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000

在这里插入图片描述在这里插入图片描述
在server3上测试

mysql> show slave status\G

在这里插入图片描述
查看到master已经成为172.25.1.1,切换成功

4、测试半自动failover

在server4上

步骤一:首先清理app1.failover.complete

rm -fr /etc/masterha/app1.failover.complete 

在这里插入图片描述
步骤二:开启mha manager管理节点

nohup masterha_manager --conf=/etc/masterha/app1.cnf &>/dev/null &

在这里插入图片描述
mha manager自带守护进程,会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。

在server1上查看

ps ax | grep mysql
kill -9 12476
ps ax | grep mysql

在这里插入图片描述
发现进程杀不掉

手动关闭mysqld

 systemctl stop mysqld
 ps ax | grep mysql  #查看进程退出

在这里插入图片描述
在server4上

cat /var/log/masterha.log

在这里插入图片描述
发现已经切换,同时manager进程退出,所以全自动需要脚本

先把server1加回集群
在server1上

systemctl start mysqld
mysql -uroot -pWestos+007
mysql>  CHANGE MASTER TO MASTER_HOST = '172.25.1.2', MASTER_USER = 'repl', MASTER_PASSWORD = 'Westos+007', MASTER_AUTO_POSITION = 1;
mysql> start slave;
mysql> show slave status\G

在这里插入图片描述在这里插入图片描述

5、配置全自动故障转移脚本和vip漂移

因为用户访问入口只能有一个,所以需要配置vip

步骤一:编辑master_ip_failover 和 master_ip_online_change 两个故障切换脚本

官网上下载脚本,修改如下内容

my $vip = "172.25.1.100/24";
my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";

修改master_ip_failover脚本:
在这里插入图片描述修改master_ip_online_change脚本:
在这里插入图片描述
具体脚本内容如下:
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.25.1.100/24';
my $key = '1';
my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";

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 $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
sub stop_vip() {
return 0 unless ($ssh_user);
`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";
}

master_ip_online_change脚本内容如下:

#!/usr/bin/env perl
use strict;  
use warnings FATAL =>'all';  
  
use Getopt::Long;  
  
my $vip = '172.25.1.100/24';  # Virtual IP  
my $key = "1";  
my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";  
my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";  
my $exit_code = 0;  
  
my (  
  $command,              $orig_master_is_new_slave, $orig_master_host,  
  $orig_master_ip,       $orig_master_port,         $orig_master_user,  
  $orig_master_password, $orig_master_ssh_user,     $new_master_host,  
  $new_master_ip,        $new_master_port,          $new_master_user,  
  $new_master_password,  $new_master_ssh_user,  
);  
GetOptions(  
  'command=s'                => \$command,  
  'orig_master_is_new_slave' => \$orig_master_is_new_slave,  
  'orig_master_host=s'       => \$orig_master_host,  
  'orig_master_ip=s'         => \$orig_master_ip,  
  'orig_master_port=i'       => \$orig_master_port,  
  'orig_master_user=s'       => \$orig_master_user,  
  'orig_master_password=s'   => \$orig_master_password,  
  'orig_master_ssh_user=s'   => \$orig_master_ssh_user,  
  'new_master_host=s'        => \$new_master_host,  
  'new_master_ip=s'          => \$new_master_ip,  
  'new_master_port=i'        => \$new_master_port,  
  'new_master_user=s'        => \$new_master_user,  
  'new_master_password=s'    => \$new_master_password,  
  'new_master_ssh_user=s'    => \$new_master_ssh_user,  
);  
  
  
exit &main();  
  
sub main {  
  
#print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";  
  
if ( $command eq "stop" || $command eq "stopssh" ) {  
  
        # $orig_master_host, $orig_master_ip, $orig_master_port are passed.  
        # If you manage master ip address at global catalog database,  
        # invalidate orig_master_ip here.  
        my $exit_code = 1;  
        eval {  
            print "\n\n\n***************************************************************\n";  
            print "Disabling the VIP - $vip on old master: $orig_master_host\n";  
            print "***************************************************************\n\n\n\n";  
&stop_vip();  
            $exit_code = 0;  
        };  
        if ($@) {  
            warn "Got Error: $@\n";  
            exit $exit_code;  
        }  
        exit $exit_code;  
}  
elsif ( $command eq "start" ) {  
  
        # all arguments are passed.  
        # If you manage master ip address at global catalog database,  
        # activate new_master_ip here.  
        # You can also grant write access (create user, set read_only=0, etc) here.  
my $exit_code = 10;  
        eval {  
            print "\n\n\n***************************************************************\n";  
            print "Enabling the VIP - $vip on new master: $new_master_host \n";  
            print "***************************************************************\n\n\n\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";  
        `ssh $orig_master_ssh_user\@$orig_master_host \" $ssh_start_vip \"`;  
        exit 0;  
}  
else {  
&usage();  
        exit 1;  
}  
}  
  
# A simple system call that enable the VIP on the new master  
sub start_vip() {  
`ssh $new_master_ssh_user\@$new_master_host \" $ssh_start_vip \"`;  
}  
# A simple system call that disable the VIP on the old_master  
sub stop_vip() {  
`ssh $orig_master_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";  
}

步骤二:配置自动添加和删除vip

cp master_ip_failover master_ip_online_change /usr/local/bin
cd /usr/local/
cd bin/
chmod +x master_ip_failover master_ip_online_change

在这里插入图片描述
步骤三:在配置文件/etc/masterha/app1.cnf中添加启动脚本路径

master_ip_failover_script= /usr/local/bin/master_ip_failover
master_ip_online_change_script= /usr/local/bin/master_ip_online_change

在这里插入图片描述
目前server2是master,所以先给server2添加vip

在server2上

ip addr add 172.25.1.100/24 dev eth0

在这里插入图片描述
测试vip漂移:
在server4上

masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=172.25.1.1 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000

在这里插入图片描述在这里插入图片描述
在server1上查看

ip addr show

在这里插入图片描述
测试全自动切换:
在server4上

步骤一:删除app1.failover.complete

cd /etc/masterha
rm -fr app1.failover.complete 

在这里插入图片描述
步骤二:开启manager(MHA管理)

nohup masterha_manager --conf=/etc/masterha/app1.cnf &>/dev/null &

在这里插入图片描述
在server1(master)上
模拟故障,关闭mysqld

systemctl stop mysqld

在这里插入图片描述
在server4上查看

cat /var/log/masterha.log

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
可以看到切换成功,vip也成功漂移

三、实验中遇到的报错解决

【1】检查数据节点复制功能时报错如下:
Sun Feb 9 11:37:36 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sun Feb 9 11:37:36 2020 - [info] Reading application default configuration from /etc/masterha/app1.cnf…
Sun Feb 9 11:37:36 2020 - [info] Reading server configuration from /etc/masterha/app1.cnf…
Sun Feb 9 11:37:36 2020 - [info] MHA::MasterMonitor version 0.58.
Sun Feb 9 11:37:37 2020 - [error][/usr/share/perl5/vendor_perl/MHA/ServerManager.pm, ln193] There is no alive slave. We can’t do failover
Sun Feb 9 11:37:37 2020 - [error][/usr/share/perl5/vendor_perl/MHA/MasterMonitor.pm, ln427] Error happened on checking configurations. at /usr/share/perl5/vendor_perl/MHA/MasterMonitor.pm line 329.
Sun Feb 9 11:37:37 2020 - [error][/usr/share/perl5/vendor_perl/MHA/MasterMonitor.pm, ln525] Error happened on monitoring servers.
Sun Feb 9 11:37:37 2020 - [info] Got exit code 1 (Not master dead).

MySQL Replication Health is NOT OK!

解决方法:

关闭所有节点的防火墙

systemctl stop firewalld
发布了174 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/CapejasmineY/article/details/104224757