MHA


一、MHA介绍 1

二、部署MHA 2

1.部署MHA Node 2

2.安装MHA Manager 3

3.配置SSH登录无密码验证 3

4.搭建主从复制环境 3

5.配置MHA 4

6.检查SSH配置 8

7.检查SSH配置 8

8.检查MHA Manager状态 8

9.开启MHA Manager监控 8

10.关闭MHA Manager监控 8

11.MHA引入VIP 8

三、自动Failover 17

四、手动Failover(MHA Manager必须没有运行) 19

五、在线进行切换 19

六、修复宕机的Master 20

七、报警脚本 20

八、MHA常用命令 21






一、MHA介绍

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

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

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

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

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

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


MHA软件由两部分组成,Manager工具包和Node工具包,具体的说明如下。

Manager工具包主要包括以下几个工具:


masterha_check_ssh              检查MHA的SSH配置状况

masterha_check_repl             检查MySQL复制状况

masterha_manger                 启动MHA

masterha_check_status           检测当前MHA运行状态

masterha_master_monitor         检测master是否宕机

masterha_master_switch          控制故障转移(自动或者手动)

masterha_conf_host              添加或删除配置的server信息


Node工具包(这些工具通常由MHA Manager的脚本触发,无需人为操作)主要包括以下几个工具:

save_binary_logs                保存和复制master的二进制日志

apply_diff_relay_logs           识别差异的中继日志事件并将其差异的事件应用于其他的slave

filter_mysqlbinlog              去除不必要的ROLLBACK事件(MHA已不再使用这个工具)

purge_relay_logs                清除中继日志(不会阻塞SQL线程)



二、部署MHA

接下来部署MHA,具体的搭建环境如下(所有操作系统均为centos 7 64bit,不是必须,server03和server04是server02的从,复制环境搭建后面会简单演示,但是相关的安全复制不会详细说明,需要的童鞋请参考前面的文章,MySQL Replication需要注意的问题):

角色                    ip地址           主机名             server_id                 类型

Monitor host            10.47.11.34    epay_mysql             -                      监控复制组

Master                  10.47.11.34:3306    epay_mysql             3306                   写入

Candicate master        10.27.120.96:3307   mysql_slave            3307                   读

Slave2                  10.27.120.96:3308   mysql_slave            3308                   读

Slave3                  10.27.120.96:3309   mysql_salve            3309                   读


vim /etc/hosts

10.47.11.34 master

10.27.120.96 slave


1.部署MHA Node

(1)在所有节点安装MHA node所需的perl模块(DBD:mysql):

yum install epel-release perl-DBD-MySQL perl-ExtUtils-Embed perl-CPAN -y



(2)在所有的节点安装mha node:

wget http://mysql-master-ha.googlecode.com/files/mha4mysql-node-0.56.tar.gz

tar xf mha4mysql-node-0.56.tar.gz

cd mha4mysql-node-0.56

perl Makefile.PL

make && make install


2.安装MHA Manager

yum install perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes -y



wget http://mysql-master-ha.googlecode.com/files/mha4mysql-node-0.56.tar.gz

tar xf mha4mysql-node-0.56.tar.gz

cd mha4mysql-node-0.56

perl Makefile.PL

make && make install


wget http://mysql-master-ha.googlecode.com/files/mha4mysql-manager-0.56.tar.gz

tar xf mha4mysql-manager-0.56.tar.gz 

cd mha4mysql-manager-0.56

perl Makefile.PL

make && make install


cp /root/mha4mysql-manager-0.56/samples/scripts   /usr/local/bin/



3.配置SSH登录无密码验证

不能禁止 password 登陆,否则会出现错误

ssh-keygen –t rsa

ssh-copy-id –i /root/.ssh/id_rsa.pub root@ip


4.搭建主从复制环境

注意:binlog-do-db 和 replicate-ignore-db 设置必须相同。 MHA 在启动时候会检测过滤规则,如果过滤规则不同,MHA 不启动监控和故障转移。

(1)在master上执行备份

mysqldump --master-data=2 --single-transaction –B > all.sql

其中--master-data=2代表备份时刻记录master的Binlog位置和Position,--single-transaction意思是获取一致性快照,-R意思是备份存储过程和函数,--triggres的意思是备份触发器,-A代表备份所有的库。更多信息请自行mysqldump --help查看。

server-id = 3306

log-bin = mysql-bin

binlog_format = row

replicate-ignore-db = mysql

replicate-ignore-db = performance_schema

replicate-ignore-db = information_schema

replicate-ignore-db = test


(2)在salve上创建复制用户:

mysql> grant replication slave,replication on *.* to 'rep'@'10.%' identified by '';

Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;

Query OK, 0 rows affected (0.00 sec)


(3)查看主库备份时的binlog名称和位置,MASTER_LOG_FILE和MASTER_LOG_POS:

head -n 30 all.sql | grep 'CHANGE MASTER TO'

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000010', MASTER_LOG_POS=112;


(4)把备份复制到server03和server04,也就是192.168.0.60和192.168.0.70

scp all.sql mysql_slave:/data/


(5)导入备份到server03,执行复制相关命令

mysql < /data/all.sql

mysql> CHANGE MASTER TO MASTER_HOST=' 10.47.11.34',MASTER_USER='repl', MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000010',MASTER_LOG_POS=112;

Query OK, 0 rows affected (0.02 sec)


(6) slave服务器设置read_only(从库对外提供读服务,只所以没有写进配置文件,是因为随时slave会提升为master)

mysql -e 'set global read_only=1'


(7)创建监控用户(在master上执行,也就是10.47.11.34):

mysql> grant all privileges on *.* to 'mha_monitor'@'10.%' identified  by 'rinimei';

Query OK, 0 rows affected (0.00 sec)


(8)开启半同步复制

install plugin rpl_semi_sync_master SONAME ‘semisync_master.so’;

set global rpl_semi_sync_master_enabled = 1;

set global rpl_semi_sync_master_timeout = 1000;


install plugin rpl_semi_sync_slave SONAME ‘semisync_slave.so’;

set global rpl_semi_sync_slave_enabled = 1;




5.配置MHA

(1)创建MHA的工作目录,并且创建相关配置文件(在软件包解压后的目录里面有样例配置文件)。

mkdir -p /usr/local/mha/{conf,tmp}

cp mha4mysql-manager-0.56/samples/conf/app1.cnf /usr/local/mha/conf/mha.conf


修改mha.cnf配置文件,修改后的文件内容如下(注意,配置文件中的注释需要去掉,我这里是为了解释清楚):


cat /usr/local/mha/conf/mha.conf 

[server default]

manager_workdir=/usr/local/mha/tmp/              //设置manager的工作目录

manager_log=/eh_data/mha/manager.log       //设置manager的日志

#master_binlog_dir=/data/mysql                         //设置master 保存binlog的位置,以便MHA可以找到master的日志,我这里的也就是mysql的数据目录

#master_ip_failover_script= /usr/local/bin/master_ip_failover    //设置自动failover时候的切换脚本

master_ip_online_change_script= /usr/local/bin/master_ip_online_change  //设置手动切换时候的切换脚本

user=mha_monitor               设置监控用户root

password=         //设置mysql中root用户的密码,这个密码是前文中创建监控用户的那个密码

ping_interval=1         //设置监控主库,发送ping包的时间间隔,默认是3秒,尝试三次没有回应的时候自动进行railover

#remote_workdir=/tmp    //设置远端mysql在发生切换时binlog的保存位置

repl_user=rep           //设置复制环境中的复制用户名

repl_password=          //设置复制用户的密码

report_script=/usr/local/bin/weixin.sh    //设置发生切换后发送的报警的脚本

secondary_check_script= /usr/local/bin/masterha_secondary_check -s epay_mysql -s mysql_slave            

shutdown_script=""      //设置故障发生后关闭故障主机脚本(该脚本的主要作用是关闭主机放在发生脑裂,这里没有使用)

ssh_user=root           //设置ssh的登录用户名


[server1]

hostname=epay_mysql

port=3306

candidate_master=1

check_repl_delay=0

master_binlog_dir=/eh_data/mysql_data

remote_workdir=/eh_data/mha/3306


[server2]

hostname=mysql_slave

port=3307

candidate_master=1

check_repl_delay=0

master_binlog_dir=/eh_data/3307/data

remote_workdir=/eh_data/mha/3307/


[server3]

hostname=mysql_slave

port=3308

candidate_master=1

check_repl_delay=0

master_binlog_dir=/eh_data/3308/data

remote_workdir=/eh_data/mha/3308/


[server4]

hostname=mysql_slave

port=3309

candidate_master=1

check_repl_delay=0

master_binlog_dir=/eh_data/3309/data

remote_workdir=/eh_data/mha/3309/



/usr/local/bin/master_ip_online_change   152行注释



candidate_master=1   //设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave

check_repl_delay=0   //默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master




(2)设置relay log的清除方式(在每个slave节点上):

mysql -e 'set global relay_log_purge=0'

mysql -e 'set global relay_log_purge=0'



update mysql.user set password=password("") where user="root";


注意:如果MHA已经进行过一次failover,MHA进程就会自动退出


ln -s /usr/local/mysql/bin/mysqlbinlog /usr/local/bin/mysqlbinlog

ln -s /usr/local/mysql/bin/mysql /usr/local/bin/mysql


注意:

MHA在发生切换的过程中,从库的恢复过程中依赖于relay log的相关信息,所以这里要将relay log的自动清除设置为OFF,采用手动清除relay log的方式。在默认情况下,从服务器上的中继日志会在SQL线程执行完毕后被自动删除。但是在MHA环境中,这些中继日志在恢复其他从服务器时可能会被用到,因此需要禁用中继日志的自动删除功能。定期清除中继日志需要考虑到复制延时的问题。在ext3的文件系统下,删除大的文件需要一定的时间,会导致严重的复制延时。为了避免复制延时,需要暂时为中继日志创建硬链接,因为在linux系统中通过硬链接删除大文件速度会很快。(在mysql数据库中,删除大表时,通常也采用建立硬链接的方式)

--user mysql                      用户名

--password mysql                  密码

--port                            端口号

--workdir                         指定创建relay log的硬链接的位置,默认是/var/tmp,由于系统不同分区创建硬链接文件会失败,故需要执行硬链接具体位置,成功执行脚本后,硬链接的中继日志文件被删除

--disable_relay_log_purge         默认情况下,如果relay_log_purge=1,脚本会什么都不清理,自动退出,通过设定这个参数,当relay_log_purge=1的情况下会将relay_log_purge设置为0。清理relay log之后,最后将参数设置为OFF。


(3)设置定期清理relay脚本

cat purge_relay_log.sh

#!/bin/bash

user=root

password=

port=3306

log_dir=/eh_data/mha/purge_log/

work_dir=’/eh_data/3307/data’

purge=’/usr/local/bin/purge_relay_logs’


if [ ! –d $log_dir ];then

mkdir $log_dir –p

fi


$purge –user=$user –password=$password –disable_relay_log_purge –port=$port –workdir=$work_dir >> $log_dir/purge_relay_logs.log 2>&1


添加到crontab定期执行

crontab –l

0 4 * * * /bin/bash /root/purge_relay_log.sh



6.检查SSH配置

masterha_check_ssh –conf=/usr/local/mha/conf/mha.conf


7.检查SSH配置

masterha_check_repl –conf=/usr/local/mha/conf/mha.conf


如果发现如下错误:

Can't exec "mysqlbinlog": No such file or directory at /usr/local/share/perl5/MHA/BinlogManager.pm line 99.

mysqlbinlog version not found!

Testing mysql connection and privileges..sh: mysql: command not found

解决方法如下,添加软连接(所有节点)

ln -s /usr/local/mysql/bin/mysqlbinlog /usr/local/bin/mysqlbinlog

ln -s /usr/local/mysql/bin/mysql /usr/local/bin/mysql


8.检查MHA Manager状态

masterha_check_status –conf=/usr/local/mha/conf/mha.conf


9.开启MHA Manager监控

Nohup masterha_manager –conf=/usr/local/mha/conf/mha.conf 


启动参数

--remove_dead_master_conf      该参数代表当发生主从切换后,老的主库的ip将会从配置文件中移除。

--manger_log                            日志存放位置

--ignore_last_failover                 在缺省情况下,如果MHA检测到连续发生宕机,且两次宕机间隔不足8小时的话,则不会进行Failover,之所以这样限制是为了避免ping-pong效应。该参数代表忽略上次MHA触发切换产生的文件,默认情况下,MHA发生切换后会在日志目录,也就是上面我设置的/data产生app1.failover.complete文件,下次再次切换的时候如果发现该目录下存在该文件将不允许触发切换,除非在第一次切换后收到删除该文件,为了方便,这里设置为--ignore_last_failover。


10.关闭MHA Manager监控

masterha_stop –conf=/usr/local/mha/conf/mha.conf


11.MHA引入VIP

vip配置可以采用两种方式,一种通过keepalived的方式管理虚拟ip的浮动;另外一种通过脚本方式启动虚拟ip的方式(即不需要keepalived或者heartbeat类似的软件)。

(1)下载软件进行并进行安装(两台master,准确的说一台是master,另外一台是备选master,在没有切换以前是slave):

[root@192.168.0.50 ~]# wget http://www.keepalived.org/software/keepalived-1.2.12.tar.gz

tar xf keepalived-1.2.12.tar.gz           

cd keepalived-1.2.12

./configure --prefix=/usr/local/keepalived

make &&  make install

cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/

cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/

mkdir /etc/keepalived

cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/

cp /usr/local/keepalived/sbin/keepalived /usr/sbin/


(2)配置keepalived的配置文件,在master上配置(192.168.0.50)

[root@192.168.0.50 ~]# cat /etc/keepalived/keepalived.conf

! Configuration File for keepalived


global_defs {

     notification_email {

     saltstack@163.com

   }

   notification_email_from dba@dbserver.com

   smtp_server 127.0.0.1

   smtp_connect_timeout 30

   router_id MySQL-HA

}


vrrp_instance VI_1 {

    state BACKUP

    interface eth1

    virtual_router_id 51

    priority 150

    advert_int 1

    nopreempt


    authentication {

    auth_type PASS

    auth_pass 1111

    }


    virtual_ipaddress {

        192.168.0.88

    }

}


其中router_id MySQL HA表示设定keepalived组的名称,将192.168.0.88这个虚拟ip绑定到该主机的eth1网卡上,并且设置了状态为backup模式,将keepalived的模式设置为非抢占模式(nopreempt),priority 150表示设置的优先级为150。下面的配置略有不同,但是都是一个意思。

在候选master上配置(192.168.0.60)


[root@192.168.0.60 ~]# cat /etc/keepalived/keepalived.conf 

! Configuration File for keepalived


global_defs {

     notification_email {

     saltstack@163.com

   }

   notification_email_from dba@dbserver.com

   smtp_server 127.0.0.1

   smtp_connect_timeout 30

   router_id MySQL-HA

}


vrrp_instance VI_1 {

    state BACKUP

    interface eth1

    virtual_router_id 51

    priority 120

    advert_int 1

    nopreempt


    authentication {

    auth_type PASS

    auth_pass 1111

    }


    virtual_ipaddress {

        192.168.0.88

    }

}


(3)启动keepalived服务,在master上启动并查看日志

[root@192.168.0.50 ~]# /etc/init.d/keepalived start

Starting keepalived:                                       [  OK  ]

[root@192.168.0.50 ~]# tail -f /var/log/messages

Apr 20 20:22:16 192 Keepalived_healthcheckers[15334]: Opening file '/etc/keepalived/keepalived.conf'.

Apr 20 20:22:16 192 Keepalived_healthcheckers[15334]: Configuration is using : 7231 Bytes

Apr 20 20:22:16 192 kernel: IPVS: Connection hash table configured (size=4096, memory=64Kbytes)

Apr 20 20:22:16 192 kernel: IPVS: ipvs loaded.

Apr 20 20:22:16 192 Keepalived_healthcheckers[15334]: Using LinkWatch kernel netlink reflector...

Apr 20 20:22:19 192 Keepalived_vrrp[15335]: VRRP_Instance(VI_1) Transition to MASTER STATE

Apr 20 20:22:20 192 Keepalived_vrrp[15335]: VRRP_Instance(VI_1) Entering MASTER STATE

Apr 20 20:22:20 192 Keepalived_vrrp[15335]: VRRP_Instance(VI_1) setting protocol VIPs.

Apr 20 20:22:20 192 Keepalived_vrrp[15335]: VRRP_Instance(VI_1) Sending gratuitous ARPs on eth1 for 192.168.0.88

Apr 20 20:22:20 192 Keepalived_healthcheckers[15334]: Netlink reflector reports IP 192.168.0.88 added

Apr 20 20:22:25 192 Keepalived_vrrp[15335]: VRRP_Instance(VI_1) Sending gratuitous ARPs on eth1 for 192.168.0.88


发现已经将虚拟ip 192.168.0.88绑定了网卡eth1上。

(4)查看绑定情况

[root@192.168.0.50 ~]# ip addr | grep eth1

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

    inet 192.168.0.50/24 brd 192.168.0.255 scope global eth1

    inet 192.168.0.88/32 scope global eth1

在另外一台服务器,候选master上启动keepalived服务,并观察

[root@192.168.0.60 ~]# /etc/init.d/keepalived start ; tail -f /var/log/messages

Starting keepalived:                                       [  OK  ]

Apr 20 20:26:18 192 Keepalived_vrrp[9472]: Registering gratuitous ARP shared channel

Apr 20 20:26:18 192 Keepalived_vrrp[9472]: Opening file '/etc/keepalived/keepalived.conf'.

Apr 20 20:26:18 192 Keepalived_vrrp[9472]: Configuration is using : 62976 Bytes

Apr 20 20:26:18 192 Keepalived_vrrp[9472]: Using LinkWatch kernel netlink reflector...

Apr 20 20:26:18 192 Keepalived_vrrp[9472]: VRRP_Instance(VI_1) Entering BACKUP STATE

Apr 20 20:26:18 192 Keepalived_vrrp[9472]: VRRP sockpool: [ifindex(3), proto(112), unicast(0), fd(10,11)]

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Netlink reflector reports IP 192.168.80.138 added

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Netlink reflector reports IP 192.168.0.60 added

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Netlink reflector reports IP fe80::20c:29ff:fe9d:6a9e added

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Netlink reflector reports IP fe80::20c:29ff:fe9d:6aa8 added

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Registering Kernel netlink reflector

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Registering Kernel netlink command channel

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Opening file '/etc/keepalived/keepalived.conf'.

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Configuration is using : 7231 Bytes

Apr 20 20:26:18 192 kernel: IPVS: Registered protocols (TCP, UDP, AH, ESP)

Apr 20 20:26:18 192 kernel: IPVS: Connection hash table configured (size=4096, memory=64Kbytes)

Apr 20 20:26:18 192 kernel: IPVS: ipvs loaded.

Apr 20 20:26:18 192 Keepalived_healthcheckers[9471]: Using LinkWatch kernel netlink reflector...


从上面的信息可以看到keepalived已经配置成功。

注意:

上面两台服务器的keepalived都设置为了BACKUP模式,在keepalived中2种模式,分别是master->backup模式和backup->backup模式。这两种模式有很大区别。在master->backup模式下,一旦主库宕机,虚拟ip会自动漂移到从库,当主库修复后,keepalived启动后,还会把虚拟ip抢占过来,即使设置了非抢占模式(nopreempt)抢占ip的动作也会发生。在backup->backup模式下,当主库宕机后虚拟ip会自动漂移到从库上,当原主库恢复和keepalived服务启动后,并不会抢占新主的虚拟ip,即使是优先级高于从库的优先级别,也不会发生抢占。为了减少ip漂移次数,通常是把修复好的主库当做新的备库。

(5)MHA引入keepalived(MySQL服务进程挂掉时通过MHA 停止keepalived):

要想把keepalived服务引入MHA,我们只需要修改切换是触发的脚本文件master_ip_failover即可,在该脚本中添加在master发生宕机时对keepalived的处理。

编辑脚本/usr/local/bin/master_ip_failover,修改后如下,我对perl不熟悉,所以我这里完整贴出该脚本(主库上操作,192.168.0.50)。

在MHA Manager修改脚本修改后的内容如下(参考资料比较少):

#!/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.0.88';

my $ssh_start_vip = "/etc/init.d/keepalived start";

my $ssh_stop_vip = "/etc/init.d/keepalived stop";


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";

        #`ssh $ssh_user\@cluster1 \" $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 $ssh_user\@$new_master_host \" $ssh_start_vip \"`;

}

# A simple system call that disable the VIP on the old_master

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@192.168.0.20 ~]# grep 'master_ip_failover_script' /etc/masterha/app1.cnf

master_ip_failover_script= /usr/local/bin/master_ip_failover

[root@192.168.0.20 ~]# 

[root@192.168.0.20 ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf  

Sun Apr 20 23:10:01 2014 - [info] Slaves settings check done.

Sun Apr 20 23:10:01 2014 - [info] 

192.168.0.50 (current master)

 +--192.168.0.60

 +--192.168.0.70


Sun Apr 20 23:10:01 2014 - [info] Checking replication health on 192.168.0.60..

Sun Apr 20 23:10:01 2014 - [info]  ok.

Sun Apr 20 23:10:01 2014 - [info] Checking replication health on 192.168.0.70..

Sun Apr 20 23:10:01 2014 - [info]  ok.

Sun Apr 20 23:10:01 2014 - [info] Checking master_ip_failover_script status:

Sun Apr 20 23:10:01 2014 - [info]   /usr/local/bin/master_ip_failover --command=status --ssh_user=root --orig_master_host=192.168.0.50 --orig_master_ip=192.168.0.50 --orig_master_port=3306 

Sun Apr 20 23:10:01 2014 - [info]  OK.

Sun Apr 20 23:10:01 2014 - [warning] shutdown_script is not defined.

Sun Apr 20 23:10:01 2014 - [info] Got exit code 0 (Not master dead).


MySQL Replication Health is OK.


可以看见已经没有报错了。哈哈

 /usr/local/bin/master_ip_failover添加或者修改的内容意思是当主库数据库发生故障时,会触发MHA切换,MHA Manager会停掉主库上的keepalived服务,触发虚拟ip漂移到备选从库,从而完成切换。当然可以在keepalived里面引入脚本,这个脚本监控mysql是否正常运行,如果不正常,则调用该脚本杀掉keepalived进程。

2.通过脚本的方式管理VIP。这里是修改/usr/local/bin/master_ip_failover,也可以使用其他的语言完成,比如php语言。使用php脚本编写的failover这里就不介绍了。修改完成后内容如下,而且如果使用脚本管理vip的话,需要手动在master服务器上绑定一个vip(发现修改修改对perl竟然有感觉了。难道我适合学Perl?^_^)

[root@192.168.0.50 ~]# /sbin/ifconfig eth1:1 192.168.0.88/24

通过脚本来维护vip的测试我这里就不说明了,童鞋们自行测试,脚本如下(测试通过)

#!/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.0.88/24';

my $key = '1';

my $ssh_start_vip = "/sbin/ifconfig eth1:$key $vip";

my $ssh_stop_vip = "/sbin/ifconfig eth1:$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";

}

为了防止脑裂发生,推荐生产环境采用脚本的方式来管理虚拟ip,而不是使用keepalived来完成。到此为止,基本MHA集群已经配置完毕。接下来就是实际的测试环节了。通过一些测试来看一下MHA到底是如何进行工作的。下面将从MHA自动failover,我们手动failover,在线切换三种方式来介绍MHA的工作情况。


三、自动Failover

(1)使用sysbench生成测试数据(使用yum快速安装)

 yum install sysbench -y

在主库(192.168.0.50)上进行sysbench数据生成,在sbtest库下生成sbtest表,共100W记录。

sysbench --test=oltp --oltp-table-size=1000000 --oltp-read-only=off --init-rng=on --num-threads=16 --max-requests=0 --oltp-dist-type=uniform --max-time=1800 --mysql-user=root --mysql-socket=/tmp/mysql.sock --mysql-password=123456 --db-driver=mysql --mysql-table-engine=innodb --oltp-test-mode=complex prepare


(2)停掉slave sql线程,模拟主从延时。(192.168.0.60)

mysql> stop slave io_thread;

Query OK, 0 rows affected (0.08 sec)


(3)模拟sysbench压力测试。

在主库上(192.168.0.50)进行压力测试,持续时间为3分钟,产生大量的binlog。


[root@192.168.0.50 ~]# sysbench --test=oltp --oltp-table-size=1000000 --oltp-read-only=off --init-rng=on --num-threads=16 --max-requests=0 --oltp-dist-type=uniform --max-time=180 --mysql-user=root --mysql-socket=/tmp/mysql.sock --mysql-password=123456 --db-driver=mysql --mysql-table-engine=innodb --oltp-test-mode=complex run 



(4)开启slave(192.168.0.60)上的IO线程,追赶落后于master的binlog。

mysql> start slave io_thread;     

Query OK, 0 rows affected (0.00 sec)



(5)杀掉主库mysql进程,模拟主库发生故障,进行自动failover操作。

[root@192.168.0.50 ~]# pkill -9 mysqld



(6)查看MHA切换日志,了解整个切换过程,在192.168.0.20上查看日志:



四、手动Failover(MHA Manager必须没有运行)

masterha_master_switch --master_state=dead --conf=/usr/local/mha/conf/mha.conf --dead_master_host=10.47.11.34 --dead_master_port=3306 --new_master_host=10.27.120.96 --new_master_port=3307 --ignore_last_failover



五、在线进行切换

 在许多情况下, 需要将现有的主服务器迁移到另外一台服务器上。 比如主服务器硬件故障,RAID 控制卡需要重建,将主服务器移到性能更好的服务器上等等。维护主服务器引起性能下降, 导致停机时间至少无法写入数据。 另外, 阻塞或杀掉当前运行的会话会导致主主之间数据不一致的问题发生。 MHA 提供快速切换和优雅的阻塞写入,这个切换过程只需要 0.5-2s 的时间,这段时间内数据是无法写入的。在很多情况下,0.5-2s 的阻塞写入是可以接受的。因此切换主服务器不需要计划分配维护时间窗口。


1.检测复制设置和确定当前主服务器

2.确定新的主服务器

3.阻塞写入到当前主服务器

4.等待所有从服务器赶上复制

5.授予写入到新的主服务器

6.重新设置从服务器 


注意,在线切换的时候应用架构需要考虑以下两个问题:

1.自动识别master和slave的问题(master的机器可能会切换),如果采用了vip的方式,基本可以解决这个问题。

2.负载均衡的问题(可以定义大概的读写比例,每台机器可承担的负载比例,当有机器离开集群时,需要考虑这个问题)

为了保证数据完全一致性,在最快的时间内完成切换,MHA的在线切换必须满足以下条件才会切换成功,否则会切换失败。

1.所有slave的IO线程都在运行

2.所有slave的SQL线程都在运行

3.所有的show slave status的输出中Seconds_Behind_Master参数小于或者等于running_updates_limit秒,如果在切换过程中不指定running_updates_limit,那么默认情况下running_updates_limit为1秒。

4.在master端,通过show processlist输出,没有一个更新花费的时间大于running_updates_limit秒。


(1) 停止MHA监控

Masterha_stop –conf=/usr/local/mha/conf/mha.conf

(2) 进行在线切换

masterha_master_switch --conf=/usr/local/mha/conf/mha.conf --master_state=alive --new_master_host=10.27.120.96 --new_master_port=3307 --orig_master_is_new_slave --running_updates_limit=10000


--orig_master_is_new_slave 切换时加上此参数是将原 master 变为 slave 节点,如果不加此参数,原来的 master 将不启动

--running_updates_limit=10000,故障切换时,候选master 如果有延迟的话, mha 切换不能成功,加上此参数表示延迟在此时间范围内都可切换(单位为s),但是切换的时间长短是由recover 时relay 日志的大小决定 



六、修复宕机的Master

grep -i "All other slaves should start" /eh_data/mha/manager.log|tail -1



七、报警脚本

cat weixin.sh 

#!/bin/bash

###SCRIPT_NAME:weixin.sh###

###send message from weixin for mha monitor###

###gongy###

###V1-2016-12-12###


CropID=''

Secret=''

GURL="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$CropID&corpsecret=$Secret" 

Gtoken=$(/usr/bin/curl -s -G $GURL | awk -F\" '{print $4}')


PURL="https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$Gtoken"


function body() {

        local int AppID=                      # 企业号中的应用id

        #local UserID=                       # 部门成员id,zabbix中定义的微信接收者

local TagID=''

        #local PartyID=                         # 部门id,定义了范围,组内成员都可接收到消息

        local Msg=$(echo "$@" | cut -d" " -f3-) # 过滤出zabbix中传递的第三个参数

        printf '{\n'

        #printf '\t"touser": "'"$UserID"\"",\n"

        #printf '\t"toparty": "'"$PartyID"\"",\n"

        printf '\t"totag": "'"$TagID"\"",\n"

        printf '\t"msgtype": "text",\n'

        printf '\t"agentid": "'" $AppID "\"",\n"

        printf '\t"text": {\n'

        printf '\t\t"content": "'"$Msg"\""\n"

        printf '\t},\n'

        printf '\t"safe":"0"\n'

        printf '}\n'

}

MSG=`grep -i 'Master failover to' /eh_data/mha/manager.log|tail -1`

COMMAND=`grep -i "All other slaves should start" /eh_data/mha/manager.log |tail -1`

/usr/bin/curl --data-ascii "$(body 1 2 $MSG \\n $COMMAND)" $PURL





八、MHA常用命令



set globalsql_slave_skip_counter=1;

grant replication slave, replication client on *.* to repl@'172.16.0.%' identified by 'repl';

change master to master_host='172.16.0.202', MASTER_PORT=3306, master_user='repl', master_password='repl', master_log_file='mysql-bin.000001', master_log_pos=0;

install plugin rpl_semi_sync_master SONAME 'semisync_master.so';

install plugin rpl_semi_sync_slave SONAME 'semisync_slave.so';

set global rpl_semi_sync_slave_enabled = 1; 




nohup masterha_manager --conf=/etc/masterha/app1.cnf > /tmp/mha_manager.log  2>&1 &

nohup masterha_manager --conf=/etc/masterha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/masterha/app1/manager.log 2>&1 &  

update mysql.user set password=password("rinimei") where user="root";



1 检查mha的ssh免密登录状态


masterha_check_ssh --conf=/etc/mha/sys/sys.cnf


2 检查mha的运行状态


masterha_check_status --conf=/etc/mha/sys/sys.cnf


3 检查主备库的复制情况


masterha_check_repl --conf=/etc/mha/sys/sys.cnf


4 停止mha


masterha_stop --conf=/etc/mha/sys/sys.cnf


5 启动mha


nohup masterha_manager --conf=/etc/mha/sys/sys.cnf > sys.log 2>&1 &


6 mha手动切换主库


masterha_master_switch --conf=/etc/mha/sys/sys.cnf --master_state=alive --new_master_host=10.138.16.133 --new_master_port=3104 --orig_master_is_new_slave

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


7 mha重新绑定数据库实例


sudo /etc/mha/sys/master_ip_failover --command=status --ssh_user=xxxadmin --orig_master_host=10.138.16.133 --orig_master_ip=10.138.16.133 --orig_master_port=3104








Candidate_master=1

No_master=1

Remote_workdir

Master_binlog_dir

Client_bindir=/usr/local/mysql/bin

Client_libdir=/usr/lib/mysql

Secondary_check_script

Master_ip_failover_script

Master_ip_online_change_script

Shutdown_script

Report_script

Latest_priority

Ping_type=select|CONNECT|INSERT

Ping_interval

Multi_tier_slave



切换参数

Masterha_master_switch

--master_state=alive|dead

--remove_orig_master_conf

--skip_lock_all_tables

--orig_master_is_new_slave

--running_updates_limit

--running_seconds_limit



常用命令:

Masterha_check_ssh

Masterha_check_repl

Masterha_check_status

Masterha_conf_host

Masterha_manager

Masterha_master_monitor

Masterha_master_switch

Masterha_secondary_check

Masterha_stop