MHA搭建

发布时间 2023-06-19 17:08:53作者: bingo-HF

介绍

MHA是一套MySQL高可用管理软件,除了检测Master宕机后,提升候选Slave为New Master之外(漂虚拟IP),还会自动让其他Slave与New Master建立复制关系。MHA Manager可以单独部署在一台独立的机器上,并管理多个master-slave集群。

架构

 

 

原理

MHA Manager:MHA的管理节点,负责检测MySQL的状态,以及管理MySQL的复制关系

Master:MySQL对外提供的服务( 通过 VIP )的主节点

Slave(M):MySQL候选主节点,本质上为一个Slave节点,只是在Master宕机后,会被提升为 New Master

Slave N:MySQL从机节点,对外可以提供只读服务

当Master宕机后,MHA Manager会让 Slave(M) 提升为 New Master ,然后修改 Slave N 的复制关系( CHANGE MASTER ),指向 New Master

MHA Manager检测Master是否宕机, 不会简单的只检查自身与Master之间的直连状态

MHA Manager会 透过其他Slave节点去检测Master,进行二次探测 ,最大限度的避免脑裂的发生

比如说,检测到Master宕机了,MHA Manager会 透过Slave(M) 检测是否为宕机

如果检测后仍为宕机状态,会继续 透过Slave1...SlaveN 进行探测,只有在 透过所有节点 检测到Master宕机了,才真的认为Master宕机了

以上操作步骤的前提是,MHA Manager到所有MySQL节点的 SSH免密码登录要打通

最后将提供对外服务的 VIP漂移到New Master 上,继续对外提供服务

一个MHA Manager可以 管理多个 MySQL的Master-Slave集群(通过不同的app.conf)

 

搭建步骤

MHA安装

 

MHA由两个部分组成MHA Manager和MHA Node,Manager依赖Node,所以需要先安装Node。

MHA Node的安装,所有服务器均要安装 (包括MHA Manager)

安装Perl依赖

yum install perl-DBI perl-DBD-MySQL -y

wget https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58-0.el7.centos.noarch.rpm

rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm

安装成功后在/usr/bin/目录下生成以下文件

apply_diff_relay_logs
filter_mysqlbinlog
purge_relay_logs
save_binary_logs

 

MHA Manager的安装,仅仅只需要在MHA Manager服务器上安装

安装Perl依赖

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

安装成功后在/usr/bin/目录下生成以下文件

/usr/bin/masterha_check_repl
/usr/bin/masterha_check_ssh
/usr/bin/masterha_check_status
/usr/bin/masterha_conf_host
/usr/bin/masterha_manager
/usr/bin/masterha_master_monitor
/usr/bin/masterha_master_switch
/usr/bin/masterha_secondary_check
/usr/bin/masterha_stop

 

创建目录存放配置文件

mkdir -p /etc/masterha

 

配置SSH免密登录

SSH免密码登录是为了能够让MHA Manager可以远程登录到各个MySQL Server,以及各个MySQL Server之间可以相互远程登录,然后进行相关操作

MHA Manager生成Key

ssh-keygen

公钥分发

执行ssh.sh,需要修改里面的ip地址

sh ssh.sh

MHA Manager

将 MHA Manager 服务器的公钥,依次分发给 Master 、 Slave1 和 Slave2

MySQL Master

将 Master 的公钥依次分发给 Slave-M 和 Slave-1

MySQL Slave-M

将 Slave-M 的公钥依次分发给 Master 和 Slave-1

MySQL Slave2-1

将 Slave-1 的公钥依次分发给 Master 和 Slave-M

 

修改hosts

为了方便使用主机名登录, 同时MHA Manager在Failover阶段会做resolve name 的操作,所以需要把下列信息加入到 所有的节点 中

cat /etc/hosts
10.100.172.45 MHA-MySQL-M
10.100.172.43 MHA-MySQL-S1
10.100.172.44 MHA-MySQL-S2

 

MHA环境说明

Role

IP

Hostname

Server_id

Desc

Master & MHA Manager

10.100.172.45

MHA-MySQL-M

11

MHA 管理服务器,用于监控,MySQL Master服务器,用于read-write

Candicate Master (Slave)

10.100.172.43

MHA-MySQL-S1

44

MySQL Slave服务器,read-only,当Master宕机后,被提升为Master(New)

Slave

10.100.172.44

MHA-MySQL-S2

55

MySQL Slave服务器,read-only

 

 

MySQL建立复制,此处不赘述

 

MHA配置

MHA配置文件和说明 配置文件统一放在 /etc/masterha/ 中,针对当前这个MySQL复制组,我们定义为 jfrog-cn.conf ,如果还有 其他MySQL复制组 ,可以 继续定义

[server default]
log_level=debug
manager_log=/var/log/masterha/jfrog-cn/manager.log
manager_workdir=/var/log/masterha/jfrog-cn
master_binlog_dir=/data/mysql/
master_ip_failover_script=/usr/local/bin/master_ip_failover
master_ip_online_change_script=/usr/local/bin/master_ip_online_change
password=*******
ping_interval=3
ping_type=INSERT
remote_workdir=/tmp
repl_password=*******
repl_user=repl
secondary_check_script=/usr/bin/masterha_secondary_check -s 10.100.172.43 -s 10.100.172.44 --user=root --master_host=10.100.172.45 --master_port=3306
ssh_user=root
user=root

[server1]
candidate_master=1
check_repl_delay=0
hostname=10.100.172.45
port=3306

[server2]
candidate_master=1
check_repl_delay=0
hostname=10.100.172.43
port=3306

[server3]
hostname=10.100.172.44
no_master=1
port=3306

 

MHA脚本说明

#!/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 = '10.100.172.47/24';
my $key = '88';
my $ssh_start_vip = "/sbin/ifconfig ens192:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens192:$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";
}

 

master_ip_online_change

#!/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 (
$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,
);
my $vip = '10.100.172.47/24';

注意修改系统对应的接口名称(CentOS比较变态)

my $eth = 'ens192';
my $key = '47';
my $ssh_start_vip = "/sbin/ifconfig $eth:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig $eth:$key down";
my $ssh_user = "root";
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,
'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" ) {
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 --ssh-user=user --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";
}

 

  1. 修改上述两个脚本中的 $vip ,以符合自己实际的需求

  2. 将上述两个文件复制到 MHA Manager 节点的 /usr/local/bin 中(同masterha_*在同一个目录)

  3. 将上述两个文件赋予执行权限: ◦ chmod +x master_ip_failover ◦ chmod +x master_ip_online_change

 

定时清理relay-log的脚本

将下面这个脚本以及定时任务放入 所有的Slave节点,并添加到定时任务

#!/bin/bash

Filename:/opt/mha/cron_purge_relay_logs.sh

user=root
passwd=*********

port=3306

host=localhost
socket=/data/mysql/mysql.sock
log_dir='/data/mysql/purge_relay_logs'
work_dir='/data/mysql/relay_log_hardlink'
purge='/usr/bin/purge_relay_logs'
if [[ ! -d ${work_dir} ]];then
mkdir ${work_dir} -p
fi
if [[ ! -d ${log_dir} ]];then
mkdir ${log_dir} -p
fi
${purge} --user=${user} --password=${passwd} --host=${host} --socket=${socket} --workdir=${work_dir} --disable_relay_log_purge >> ${log_dir}/purge_relay_logs.log 2>&1

 

SSH检测

masterha_check_ssh --conf=/etc/masterha/jfrog-cn.conf

 

复制关系检测

masterha_check_repl --conf=/etc/masterha/jfrog-cn.conf

 

启动MHA

在MySQL Master上添加虚拟ip

ifconfig ens192:88 10.100.172.47/24

 

自动Failover测试

停止MySQL Master,虚拟ip自动切换到Slave-M上