MySql的集群与高可用 -- MHA的几种切换方式

实验环境:
server1 :172.25.254.1 是server2和server3的master结点
server2 :172.25.254.2
server3 :172.25.254.3
server4 :172.25.254.4 高可用结点

简介

在企业的日常生产运营中,为了保证日常的业务稳定,通常会注意以下几个部分:

  • 如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断。
  • 用作备份、只读副本等功能的非主节点的数据应该和主节点的数据实时或者最终保持一致。
  • 当业务发生数据库切换时,切换前后的数据库内容应当一致,不会因为数据缺失或者数据不一致而影响业务。

我们就需要对数据库进行集群,并做高可用的相关配置。

MHA 分为两种结点: manager 和 node 结点。
manager可以单独部署到一台服务器上,也可以部署到slave结点上,它可以管理多个master和slave结点,

目前大多的MHA部署会遵从 一主多从的架构,集群中最少需要有三台mysql服务器。

server1
server2
server3

工作原理:
在主节点的切换过程中,MHA会最大程度上去缓存二进制日志,识别拥有最新日志的slave结点,让其充当新的master结点,让其它的slave结点去同步新master结点的 relaylog 和 binlog ,更新差异,保证数据不丢失。同时MHA 可以和 半同步复制共同使用,以最大化的降低数据丢失,保持数据的一致性。

配置

集群与高可用的配置需要两个重要的内容,就是主从复制和结点之间的免密登陆(因为i通过ssh连接)。
我们先配置

主从复制:

#server1:
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
log_bin=binlog
##下面这三行写在配置文件中就不用每次在mysql中进行设置了,必须先在mysql中安装这两个插件才可以,不然会报错

rpl_semi_sync_master_enabled = 1
rpl_semi_sync_slave_enabled = 1
rpl_semi_sync_master_timeout = 1000000000000000            #超时时间可以不用加,会影响实验。

#server2:
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
log_bin=binlog


#server3:
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
log_bin=binlog


重启mysqld。

在server1和2中配置半同步
server1:

mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
安装半同步插件
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
由于也可能成为slave结点,所以也安装slave插件
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
设置使用半同步复制
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;

mysql> SET GLOBAL rpl_semi_sync_master_timeout = 1000000000000000;
设置超时时间。

server2中;

mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

mysql> stop slave io_thread;

mysql> start slave io_thread;
重启io线程
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;

mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;

此时我们在server1中插入数据,
在这里插入图片描述
在server2中查看
在这里插入图片描述
说明半同步复制操作已经配置好。

在进行绵密登陆的配置

server3 中也配置为slave结点,因为集群中至少两个slave结点:

mysql> change master to master_host='172.25.254.1',master_user='repl',master_password='Cc990718-+',master_auto_position=1;

mysql> start slave;

#################################
如果在开启slave时IO线程没有启动(使用show slave status\G查看),则是因为前面server1已经产生数据的缘故,这里我教大家怎样解决:
mysql> show global variables like '%gtid%';  查看数据。

server1:
命令行中,手动备份数据给server3。
mysqldump --all-databases --single-transaction --triggers --routines --events --host=127.0.0.1 --port=3306 --user=root --password=Cc990718-+ > cay.sql     这就是数据库中全部的数据了。
scp cay.sql server3:            复制给server3.

server3:
mysql> stop slave;
Query OK, 0 rows affected (0.08 sec)

mysql> reset master;         清除master信息。
Query OK, 0 rows affected (0.37 sec)
 
 命令行:
 [root@server3 ~]# mysql -p < cay.sql 
Enter password:  输入密码就ok了。
在重新change master
mysql> change master to master_host='172.25.254.1',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
mysql> start slave;
就ok了  此时IO线程开启。
################################

在server4上进行高可用配置:
这里server4既做manager结点,又做node结点。
安装MHA的rpm包如下:

mha4mysql-manager-0.58-0.el7.centos.noarch.rpm 
perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
mha4mysql-node-0.58-0.el7.centos.noarch.rpm   
perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm       
perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm 
perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm

跟server1 2 3做免密登陆

我们用到了4台主机,我们要保证每一台主机都可以和其它主机进行免密登陆。

#在server4中
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 2 3 上安装node软件包

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

然后配置server4上的配置文件:

mkdir /etc/masterha     			建立MHA工作目录
vim /etc/masterha/masterha.cnf      编写配置文件
[server default]
manager_workdir=/etc/masterha
manager_log=/var/log/masterha.log
master_binlog_dir=/etc/masterha
#master_ip_failover_script= /usr/local/bin/master_ip_failover
#master_ip_online_change_script= /usr/local/bin/master_ip_online_change
password=Cc990718-+
user=root
ping_interval=1
remote_workdir=/tmp
repl_password=Cc990718-+
repl_user=repl
#report_script=/usr/local/send_report
#secondary_check_script= /usr/local/bin/master_secondary_check -s server03 -s server02
#shutdown_script=""
ssh_user=root


[server1]
hostname=172.25.254.1
port=3306


[server2]
hostname=172.25.254.2
port=3306
candidate_master=1       可以成为新的master
check_repl_delay=0

[server3]
hostname=172.25.254.3
port=3306
no_master=1         表示不可以成为新master

然后我们就可以去检查我们的主从复制和免密登陆了:

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

在这里插入图片描述
在这里插入图片描述
但是在测试复制时就报错了,因为我们最开始安装mysql的时候设置了禁止root用户远程登陆,所以这里我们要在server1 2 3中重新授权root用户。

mysql> grant all on *.* to root@'%' identified by 'Cc990718-+';
Query OK, 0 rows affected, 1 warning (0.28 sec)
mysql> flush privileges;             刷新授权表
Query OK, 0 rows affected (0.28 sec)

再次测试:
在这里插入图片描述
然后我们就可以测试在server4中进行切换了。

测试切换

手动切换master结点

我们先测试手动切换 master 结点:

要注意每次切换都会生成/etc /masterha/masterha.failover.error|complete 文件,下次切换前必须删除这个文件才可以进行新的切换,

在这里插入图片描述
目前我们的master结点是server1,我们手动切换是需要挂掉server1的mysql服务,然后server4上执行:
server1:

systemctl stop mysqld

server4命令行中执行:

masterha_master_switch \
--master_state=dead \            状态为挂掉的
--conf=/etc/masterha/masterha.cnf \		配置文件
--dead_master_host=172.25.254.1 \		挂掉的master地址
--dead_master_ip=172.25.254.1 \
--dead_master_port=3306 \				
--new_master_host=172.25.254.2 \		新的master地址
--new_master_port=3306

在这里插入图片描述
可以看出重置了master结点,切换到了server2。
在这里插入图片描述
此时server4的目录中就出现了 .complete .这个文件,这就是切换的数据,代表切换成功,下次手动切换前需要删除这个文件。

而且在server2中已经没有slave的信息,而且server3 的master已经切换:
在这里插入图片描述
在这里插入图片描述
刚才的是离线切换,现在我们尝试在线切换:

先开启server1的mysql,在把它加回到集群中去:

 systemctl start mysqld.service
 #设置server1的主节点为server2:
mysql> change master to master_host='172.25.254.2',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.51 sec)

mysql> start slave;
mysql> show slave status\G

命令行中执行:

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

在这里插入图片描述
就切换成功了。
server1:
在这里插入图片描述
server2:
在这里插入图片描述
server3:

在这里插入图片描述
这就是我们手动切换的两种方式。

自动切换

删除 /etc/masterha/masterha.failover.complete 刚刚转换完成产生的文件。

自动切换的工具为: masterha_manager ,它会检测数据库的master结点的状态,如果出现故障,会自动进行切换。

它只能执行一次,因为每次执行都回生成我们上面删除的文件,它自带守护进程,不能kill -9 杀掉。

我们启动:
在server4中:

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

nohup 静默输出,& 后台运行。

测试:

这时我们挂掉当前的master主机 server1 ,

systemctl stop mysqld.service 

这时我们发现server4 中的manager进程消失, 说明它只执行一次
然后我们在server2 和server3 中查看:
server2:
在这里插入图片描述
server3:
在这里插入图片描述
就切换成功了。
我们可以查看日志看看发生了神什么动作:
在这里插入图片描述
还是执行了changermaster的动作,和我们手动切换执行的命令相同,只不过自动执行。
在这里插入图片描述
并且这时manager进程自动结束了,这就是manager的自动切换.

VIP故障切换

在实际情况下,客户的访问都有一个固定的ip ,这个ip是对外的,我们不能告诉用户访问的地址变了,所以应做一个固定的接口,不论后端的数据库怎样改变,都让客户使用这个IP进行访问,所以我们应该设置VIP进行故障切换。

  1. 我们先将上面关闭的server1打开,在将其加入到集群中:
mysql> change master to master_host='172.25.254.2',master_user='repl',master_password='Cc990718-+',master_auto_position=1;
mysql> start slave;

VIP切换脚本

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.0.100/24';
my $ssh_start_vip = "/sbin/ip addr add $vip dev ens3";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev ens3";

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.0.100/24';  # Virtual IP  
my $ssh_start_vip = "/sbin/ip addr add $vip dev ens3";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev ens3";
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";
}

把他们放到配置文件中写的位置,并给与执行权限:
在这里插入图片描述

[root@server4 ~]# cp master_ip_* /usr/local/bin/
[root@server4 ~]# chmod +x /usr/local/bin/master_ip_*
[root@server4 ~]# ll /usr/local/bin/master_ip_*
-rwxr-xr-x 1 root root 2156 May 15 10:07 /usr/local/bin/master_ip_failover
-rwxr-xr-x 1 root root 3829 May 15 10:07 /usr/local/bin/master_ip_online_change

测试(半自动切换)

当前master结点为server2。server1 和 server3 是它的slave结点
先给server 添加172.25.254.100/24 的vip
在这里插入图片描述
测试我们的vip是否可以迁移到新的master结点中去,
在 serevr4中在先切换:

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

执行成功:
在这里插入图片描述
在这里插入图片描述
并且vip已经添加到了server1上:

在这里插入图片描述
且server2的ip消失:
在这里插入图片描述
此时的主结点已经切换为塞server1:
在这里插入图片描述

模拟故障全自动切换

我们还需要开启 masterha_manager 进程

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

我们挂掉server1的msyqld服务

[root@server1 ~]# systemctl stop mysqld

此时;
在这里插入图片描述
在这里插入图片描述
vip 又回到了server2上,而且master结点也已经切换。
server3:
在这里插入图片描述
这样对于用户来说只需要一直访问100 这个vip就可以持续访问了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章