MHA的组成:

  • MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上管理多个Master-Slave集群,也可以部署在一台Slave上。当Master出现故障时,它可以自动将最新数据的Slave提升为新的Master,然后将所有其他的Slave重新指向新的Master。整个故障转移过程对应用程序是完全透明的。

MHA的工作流程:

  1. 把宕机的master二进制日志保存下来。
  2. 找到binlog位置点最新的slave。
  3. 在binlog位置点最新的slave上用relay log(差异日志)修复其它slave。
  4. 将宕机的master上保存下来的二进制日志恢复到含有最新位置点的slave上。
  5. 将含有最新位置点binlog所在的slave提升为master。
  6. 将其它slave重新指向新提升的master,并开启主从复制。

搭建实验环境

搭建三台mysql数据库,IP分别为

  • mysql-db01: 192.168.106.10
  • mysql-db02: 192.168.106.20
  • mysql-db03: 192.168.106.30

此处展示192.168.106.10的配置,其他同理

[root@mysql-db01 ~]# wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz
[root@mysql-db01 ~]# tar xzvf mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz
[root@mysql-db01 ~]# mkdir /application
[root@mysql-db01 ~]# mv mysql-5.6.40-linux-glibc2.12-x86_64 /application/mysql-5.6.40
[root@mysql-db01 ~]# ln -s /application/mysql-5.6.40 /application/mysql
[root@mysql-db01 ~]# cd /application/mysql/support-files
[root@mysql-db01 ~]# cp my-default.cnf /etc/my.cnf
cp:是否覆盖"/etc/my.cnf"? y
[root@mysql-db01 ~]# cp mysql.server /etc/init.d/mysqld
[root@mysql-db01 ~]# cd /application/mysql/scripts
[root@mysql-db01 ~]# useradd mysql -s /sbin/nologin -M
[root@mysql-db01 ~]# yum -y install autoconf
[root@mysql-db01 ~]# ./mysql_install_db --user=mysql --basedir=/application/mysql --data=/application/mysql/data
[root@mysql-db01 ~]# vim /etc/profile.d/mysql.sh
export PATH="/application/mysql/bin:$PATH"
[root@mysql-db01 ~]# source /etc/profile
[root@mysql-db01 ~]# sed -i 's#/usr/local#/application#g' /etc/init.d/mysqld /application/mysql/bin/mysqld_safe
[root@mysql-db01 ~]# vim /usr/lib/systemd/system/mysqld.service

[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/application/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE = 5000
[root@mysql-db01 ~]# systemctl start mysqld
[root@mysql-db01 ~]# systemctl enable mysqld
[root@mysql-db01 ~]# mysqladmin -uroot password '123456'
[root@mysql-db01 ~]# mysql -uroot -p123456
[root@mysql-db01 ~]# systemctl stop mysqld
[root@mysql-db01 ~]# /etc/init.d/mysqld start
#下面用配置文件的方式重启,所以这里用配置文件的方式打开

搭建基于GTID的主从复制

主库操作

  • 修改配置文件
[root@mysql-db01 ~]# vim /etc/my.cnf
#编辑mysql配置文件

[mysqld]
#在mysqld标签下配置
server_id =1
#主库server-id为1,从库不等于1
log_bin=mysql-bin
#开启binlog日志
skip-name-resolv
#跳过域名解析

gtid_mode=ON
#开启GTID
log_slave_updates
#开启slave binlog同步
enforce_gtid_consistency
#不允许任何违反GTID一致性

[root@mysql-db01 ~]# /etc/init.d/mysqld restart
#重启数据库

查看配置文件是否生效

mysql> show variables like "server_id";
mysql> show global variables like '%gtid%';
#检查GTID状态
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| enforce_gtid_consistency | ON    | #执行GTID一致
| gtid_executed            |       |
| gtid_mode                | ON    | #开启GTID模块
| gtid_owned               |       |
| gtid_purged              |       |
+--------------------------+-------+

注:主库从库都需要开启GTID否则在做主从复制的时候就会报错,

ERROR 1777 (HY000): CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when @@GLOBAL.GTID_MODE = ON.

从库操作

  • 修改配置文件

配置192.168.106.20

[root@mysql-db02 ~]# vim /etc/my.cnf
#修改mysql-db02配置文件

[mysqld]
#在mysqld标签下配置
server_id =5
#主库server-id为1,从库必须大于1
log_bin=mysql-bin
#开启binlog日志

gtid_mode=ON
#开启GTID
log_slave_updates
#开启slave binlog同步
enforce_gtid_consistency
#不允许任何违反GTID一致性
[root@mysql-db02 ~]# /etc/init.d/mysqld restart
#重启mysql

查看配置文件是否生效

mysql> show variables like "server_id";
mysql> show global variables like '%gtid%';
#检查GTID状态
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| enforce_gtid_consistency | ON    | #执行GTID一致
| gtid_executed            |       |
| gtid_mode                | ON    | #开启GTID模块
| gtid_owned               |       |
| gtid_purged              |       |
+--------------------------+-------+

配置192.168.106.30

[root@mysql-db03 ~]# vim /etc/my.cnf
#修改mysql-db03配置文件
[mysqld]
#在mysqld标签下配置
server_id =10
#主库server-id为1,从库必须大于1
log_bin=mysql-bin
#开启binlog日志

gtid_mode=ON
#开启GTID
log_slave_updates
#开启slave binlog同步
enforce_gtid_consistency
#不允许任何违反GTID一致性
[root@mysql-db03 ~]# /etc/init.d/mysqld restart
#重启mysql

查看配置文件是否生效

mysql> show variables like "server_id";
mysql> show global variables like '%gtid%';
#检查GTID状态
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| enforce_gtid_consistency | ON    | #执行GTID一致
| gtid_executed            |       |
| gtid_mode                | ON    | #开启GTID模块
| gtid_owned               |       |
| gtid_purged              |       |
+--------------------------+-------+

创建主从复制用户(所有主机)

此处展示192.168.106.10的配置,其他同理

[root@mysql-db01 ~]# mysql -uroot -p123456
#登录数据库
mysql> grant replication slave on *.* to slave@'192.168.106.%' identified by '123456';
#创建slave用户

配置主从复制(所有从库)

配置192.168.106.20

[root@mysql-db02 ~]# mysql -uroot -p123456
#登录数据库
mysql> change master to
#配置复制主机信息
-> master_host='192.168.106.10',
#主库IP
-> master_user='slave',
#主库复制用户
-> master_password='123456',
#主库复制用户的密码
-> master_auto_position=1;
#GTID位置点
mysql> start slave;
#开启slave
mysql> show slave status\G
#查看slave状态
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.175.10
                  Master_User: rep
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 403
               Relay_Log_File: mysql-db02-relay-bin.000002
                Relay_Log_Pos: 613
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes      #注意关闭防火墙,否则Slave_IO_Running: Connecting
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 403
              Relay_Log_Space: 822
              Until_Condition: None


mysql> set global relay_log_purge = 0;
#禁用自动删除relay log 功能
mysql> set global read_only=1;
#设置只读

[root@mysql-db02 ~]# vim /etc/my.cnf
#编辑配置文件

[mysqld]
#在mysqld标签下添加
relay_log_purge = 0
#禁用自动删除relay log 永久生效

配置192.168.106.30

[root@mysql-db03 ~]# mysql -uroot -p123456
#登录数据库
mysql> change master to
 master_host='192.168.106.10',
 master_user='slave',
 master_password='123456',
 master_auto_position=1;

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

mysql> set global relay_log_purge = 0;
#禁用自动删除relay log 功能
mysql> set global read_only=1;
#设置只读

[root@mysql-db03 ~]# vim /etc/my.cnf
#编辑配置文件

[mysqld]
#在mysqld标签下添加
relay_log_purge = 0
#禁用自动删除relay log 永久生效

部署MHA

环境准备(所有节点)

此处展示192.168.106.10的配置,其他同理

[root@mysql-db01 ~]# cd && wget https://download.s21i.faiusr.com/23126342/0/0/ABUIABBPGAAg3OHUiAYolpPt7AQ.zip?f=mysql-master-ha.zip&v=1628778716
[root@mysql-db01 ~]# mv ABUIABBPGAAg3OHUiAYolpPt7AQ.zip\?f\=mysql-master-ha.zip  mysql-master-ha.zip
#改名
[root@mysql-db01 ~]# yum install perl-DBD-MySQL -y
#安装依赖包
[root@mysql-db01 ~]# yum install  -y unzip
[root@mysql-db01 ~]# unzip mysql-master-ha.zip
[root@mysql-db01 ~]# cd mysql-master-ha
#进入安装包存放目录
[root@mysql-db01 mysql-master-ha]# rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
Preparing...                ########################################### [100%]
   1:mha4mysql-node         ########################################### [100%]
#安装node包

添加mha管理账号(仅主库)

#登录数据库
mysql> grant all privileges on *.* to mha@'192.168.106.%' identified by 'mha';
#添加mha管理账号
mysql> select user,host from mysql.user;
#查看是否添加成功
#可以在从库上使用同一命令查看是否同步成功

命令软连接(所有节点)

此处展示192.168.106.10的配置,其他同理

[root@mysql-db01 ~]# ln -s /application/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
[root@mysql-db01 ~]# ln -s /application/mysql/bin/mysql /usr/bin/mysql
#如果不创建命令软连接,检测mha复制情况的时候会报错

部署管理节点(在从库db03上)

从库中选择一个作为管理节点,这里选192.168.106.30

环境准备

[root@mysql-db03 ~]# yum -y install epel-release
#使用epel源
[root@mysql-db03 ~]# yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
#安装manager依赖包
[root@mysql-db03 mysql-master-ha]# cd  ~/mysql-master-ha && rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm 
Preparing...              ########################################### [100%]
1:mha4mysql-manager       ########################################### [100%]
#安装manager包

编辑配置文件(manage节点)

[root@mysql-db03 ~]# mkdir -p /etc/mha
#创建配置文件目录
[root@mysql-db03 ~]# mkdir -p /var/log/mha/app1
#创建日志目录
[root@mysql-db03 ~]# vim /etc/mha/app1.cnf
#编辑mha配置文件

[server default]
manager_log=/var/log/mha/app1/manager.log
#设置manager的日志
manager_workdir=/var/log/mha/app1
#设置manager的工作目录
master_binlog_dir=/application/mysql/data
#设置master 保存binlog的位置,以便MHA可以找到master的日志
user=mha
#设置监控用户mha
password=mha
#设置监控用户密码
ping_interval=2
#设置监控主库,发送ping包的时间间隔,尝试三次没有回应的时候自动进行failover
repl_password=123456
#设置复制用户的密码
repl_user=slave
#设置复制环境中的复制用户名
ssh_user=root
#设置ssh的登录用户名
[server1]
hostname=192.168.106.10
port=3306
[server2]
hostname=192.168.106.20
port=3306
candidate_master=1
#设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave。
check_repl_delay=0
#默认情况下如果一个slave落后master100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master
[server3]
hostname=192.168.106.30
port=3306

配置ssh信任(所有节点)

此处展示192.168.106.10的配置,其他同理

[root@mysql-db01 ~]# ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1
#创建秘钥对
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.106.10
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.106.20
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.106.30
#发送公钥,包括自己

启动测试(manage节点)

[root@mysql-db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
#测试ssh
#看到如下字样,则测试成功
Tue Mar  7 01:03:33 2017 - [info] All SSH connection tests passed successfully.

[root@mysql-db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
#测试复制
#看到如下字样,则测试成功
MySQL Replication Health is OK.

启动MHA

[root@mysql-db03 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &
#启动命令

[root@mysql-db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
#检测状态

切换master测试

关闭现主库192.168.106.10的mysql

[root@mysql-db01 ~]# /etc/init.d/mysqld stop
#停掉主库
Shutting down MySQL..... SUCCESS!

发现从库192.168.106.20变成了主库

[root@mysql-db02 ~]# mysql -uroot -p123456
#登录数据库(db02)
mysql> show slave status\G
#查看slave状态
Empty set (0.00 sec)
#db02的slave已经为空,因为变成了主库,不是slave了
[root@mysql-db03 ~]# mysql -uroot -p123456
#登录数据库(db03)
mysql> show slave status\G
#查看slave状态
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host:192.168.175.20
                  Master_User: rep
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000006
          Read_Master_Log_Pos: 191
               Relay_Log_File: mysql-db03-relay-bin.000002
                Relay_Log_Pos: 361
        Relay_Master_Log_File: mysql-bin.000006
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
但是即使重新开启主库,主库也不会自动加回去了,必须手动的方式加回去才可以

加回192.168.106.10,变为从库

[root@mysql-db01 ~]# /etc/init.d/mysqld start
[root@mysql-db01 ~]# mysql -uroot -p123456
#登录数据库
mysql> change master to
 master_host='192.168.106.20',
 master_user='slave',
 master_password='123456',
 master_auto_position=1;

mysql> start slave;
mysql> show slave status\G
#发现Master_Host: 192.168.175.20,master已经变了

在manage节点的/etc/mha/app1.cnf加回192.168.106.10的配置,因为主库转换后,在app1.cnf里面相应的配置文件会消失

[root@mysql-db03 ~]# vim /etc/mha/app1.cnf

[server1]
hostname=192.168.106.10
port=3306

配置vip漂移

VIP漂移的两种方式

  • 通过keepalived的方式,管理虚拟IP的漂移
  • 通过MHA自带脚本方式,管理虚拟IP的漂移(本实验采用)

修改配置文件(manage节点)

[root@mysql-db03 ~]# vim /etc/mha/app1.cnf
#编辑配置文件
[server default]
#在[server default]标签下添加
master_ip_failover_script=/etc/mha/master_ip_failover
#使用MHA自带脚本
脚本在我们自己下载的工具包里面有,但是我们要修改#cd ~/mysql-master-ha
#tar zxvf mha4mysql-manager-0.56.tar.gz
#cd mha4mysql-manager-0.56/samples/scripts 中有master_ip_failover

编辑master_ip_failover脚本(manage节点)

[root@mysql-db03 ~]# vim /etc/mha/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 = '192.168.106.88/24';   #此为配置的虚拟IP
my $key = '0';
my $ssh_start_vip = "/sbin/ifconfig ens33:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens33:$key down";

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";
}
[root@mysql-db03 ~]# chmod +x /etc/mha/master_ip_failover
#添加执行权限,否则mha无法启动

手动绑定vIP(master节点)

[root@mysql-db02 ~]# yum install -y net-tools 
#装一下工具包,要不然会没有ifconfig命令
[root@mysql-db02 ~]# ifconfig ens33:0 192.168.106.88/24
  #绑定vip
#[root@mysql-db02 ~]# ifconfig ens33:0 down
  #取消绑定vip
[root@mysql-db02 ~]# ip a
#查看vip
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:61:78:8d brd ff:ff:ff:ff:ff:ff
    inet 192.168.106.20/24 brd 192.168.106.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet 192.168.106.88/24 brd 192.168.106.255 scope global secondary ens33:0
       valid_lft forever preferred_lft forever

重启mha(manage节点)

[root@mysql-db03 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &
#启动
masterha_check_status --conf=/etc/mha/app1.cnf
#检测状态

测试vip漂移

[root@mysql-db02 ~]# /etc/init.d/mysqld stop
Shutting down MySQL..... SUCCESS!
#停掉主库

在192.168.106.30上查看从库slave信息

[root@mysql-db03 ~]# mysql -uroot -p123456
mysql> show slave status\G\
Empty set (0.00 sec)
#无slave信息,因为192.168.106.30成为了主库

[root@mysql-db03 ~]# ip a
#查看vip
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:61:78:8d brd ff:ff:ff:ff:ff:ff
    inet 192.168.106.30/24 brd 192.168.106.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet 192.168.106.88/24 brd 192.168.106.255 scope global secondary ens33:0
       valid_lft forever preferred_lft forever
#发现VIP漂移到了192.168.106.30上
#master_ip_failover脚本有一个问题,每次切换完成后,脚本就会退出运行,如果希望继续可以自动切换要重新运行脚本,并且去修改/etc/mha/app1.cnf配置文件,因为配置文件里面会把坏掉的server节点给删除掉,在[server default]标签下添加master_ip_failover_script=/etc/mha/master_ip_failover也会被消除,需要重新添加

若是想要把关闭的192.168.106.20加回来,与上面加回192.168.106.10,变为从库同理,就是需要改现主库master_host='192.168.106.30'