文章目录
环境
192.168.17.141 manager
192.168.17.131 master
192.168.17.132 slave1
192.168.17.134 slave2
所有节点添加hosts
[root@manager ~]# vi /etc/hosts
192.168.17.141 manager
192.168.17.131 master
192.168.17.132 slave1
192.168.17.134 slave2
[root@master ~]# vi /etc/hosts
192.168.17.141 manager
192.168.17.131 master
192.168.17.132 slave1
192.168.17.134 slave2
[root@slave1 ~]# vi /etc/hosts
192.168.17.141 manager
192.168.17.131 master
192.168.17.132 slave1
192.168.17.134 slave2
[root@slave2 ~]# vi /etc/hosts
192.168.17.141 manager
192.168.17.131 master
192.168.17.132 slave1
192.168.17.134 slave2
修改mysql节点配置
[root@master ~]# vi /etc/my.cnf
server-id = 1 # 复制集群中的各节点的id均必须唯一
log-bin = mysql-bin # 开启二进制日志
relay-log = relay-bin # 开启中继日志
skip_name_resolve # 关闭名称解析(非必须)
[root@slave1 ~]# vi /etc/my.cnf
server-id = 2 # 复制集群中的各节点的id均必须唯一;
relay-log = relay-bin # 开启中继日志
log-bin = mysql-bin # 开启二进制日志
read_only = ON # 启用只读属性
relay_log_purge = 0 # 是否自动清空不再需要中继日志
skip_name_resolve # 关闭名称解析(非必须)
log_slave_updates = 1 # 使得更新的数据写进二进制日志中
[root@slave2 ~]# vi /etc/my.cnf
server-id = 3 # 复制集群中的各节点的id均必须唯一;
relay-log = relay-bin # 开启中继日志
log-bin = mysql-bin # 开启二进制日志
read_only = ON # 启用只读属性
relay_log_purge = 0 # 是否自动清空不再需要中继日志
skip_name_resolve # 关闭名称解析(非必须)
log_slave_updates = 1 # 使得更新的数据写进二进制日志中
mysql节点上创建复制用户
先在master上创建同步用户
[root@master ~]# mysql -uroot -p000000
mysql> grant replication slave,replication client on *.* to 'mharepl'@'%' identified by '000000';
mysql> flush privileges;
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 | 608 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.03 sec)
在slave节点上也创建同步用户
[root@slave1 ~]# mysql -uroot -p000000
mysql> grant replication slave,replication client on *.* to 'mharepl'@'%' identified by '000000';
mysql> flush privileges;
[root@slave2 ~]# mysql -uroot -p000000
mysql> grant replication slave,replication client on *.* to 'mharepl'@'%' identified by '000000';
mysql> flush privileges;
slave节点上配置同步
[root@slave1 ~]# mysql -uroot -p000000
mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.17.131',
-> MASTER_USER='mha',
-> MASTER_PASSWORD='000000',
-> MASTER_PORT=3306,
-> MASTER_LOG_FILE='mysql-bin.000003',
-> MASTER_LOG_POS=608;
mysql> start slave;
mysql> show slave status\G;
[root@slave2 ~]# mysql -uroot -p000000
mysql> CHANGE MASTER TO
-> MASTER_HOST='192.168.17.131',
-> MASTER_USER='mha',
-> MASTER_PASSWORD='000000',
-> MASTER_PORT=3306,
-> MASTER_LOG_FILE='mysql-bin.000003',
-> MASTER_LOG_POS=608;
mysql> start slave;
mysql> show slave status\G;
在master上进行授权
[root@master ~]# mysql -uroot -p000000
mysql> grant all privileges on *.* to 'mhaadmin'@'%' identified by '000000';
mysql> flush privileges;
准备ssh互通环境
必须保证所有集群都能ssh互通
manager创建公钥并发送到其他主机
[root@manager ~]# ssh-keygen -t rsa
[root@manager ~]# ssh-copy-id -i .ssh/id_rsa.pub root@master
[root@manager ~]# ssh-copy-id -i .ssh/id_rsa.pub root@slave1
[root@manager ~]# ssh-copy-id -i .ssh/id_rsa.pub root@slave2
master创建公钥并发送到其他主机
[root@master ~]# ssh-keygen -t rsa
[root@master ~]# ssh-copy-id -i .ssh/id_rsa.pub root@manager
[root@master ~]# ssh-copy-id -i .ssh/id_rsa.pub root@slave1
[root@master ~]# ssh-copy-id -i .ssh/id_rsa.pub root@slave2
slave1创建公钥并发送到其他主机
[root@slave1 ~]# ssh-keygen -t rsa
[root@slave1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@manager
[root@slave1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@master
[root@slave1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@slave2
slave2创建公钥并发送到其他主机
[root@slave2 ~]# ssh-keygen -t rsa
[root@slave2 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@manager
[root@slave2 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@master
[root@slave2 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@slave1
下载MHA安装包
下载地址
https:# github.com/yoshinorim/mha4mysql-manager/releases
https:# github.com/yoshinorim/mha4mysql-node/releases
所有节点上安装mha4mysql-node
mysql节点上先安装mysql-community-libs-compat
[root@master src]# rpm -ivh mysql-community-libs-compat-5.7.26-1.el7.x86_64.rpm
[root@slave1 src]# rpm -ivh mysql-community-libs-compat-5.7.26-1.el7.x86_64.rpm
[root@slave2 src]# rpm -ivh mysql-community-libs-compat-5.7.26-1.el7.x86_64.rpm
所有节点安装依赖包
yum install -y perl-DBD-MySQL perl-ExtUtils-MakeMaker perl-CPAN
#解压
tar -zxvf mha4mysql-node-0.58.tar.gz
#移动到/usr/local/mha4mysql-node-0.58目录下
mv mha4mysql-node-0.58 /usr/local/mha4mysql-node-0.58
#编译
cd /usr/local/mha4mysql-node-0.58
perl Makefile.PL
#make并安装
make
make install
安装完成后会在/usr/local/bin/生成以下脚本文件,Node脚本说明:
这些工具通常由MHA Manager的脚本触发,无需人为操作
save_binary_logs # 保存和复制master的二进制日志
apply_diff_relay_logs # 识别差异的中继日志事件并将其差异的事件应用于其他的slave
filter_mysqlbinlog # 去除不必要的ROLLBACK事件(MHA已不再使用这个工具)
purge_relay_logs # 清除中继日志(不会阻塞SQL线程)
管理节点上安装mha4mysql-manager
#安装依赖包
yum -y install epel-release perl-CPAN* perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager
#解压
tar -zxf mha4mysql-manager-0.58.tar.gz
#移动目录
mv mha4mysql-manager-0.58 /usr/local/mha4mysql-manager-0.58
编译安装
cd /usr/local/mha4mysql-manager-0.58/
perl Makefile.PL
make
make install
安装完成后,在/usr/local/bin会多出以下相关的命令脚本:
masterha_check_repl # 检查MySQL复制健康状况
masterha_check_ssh # 检查ssh健康状况
masterha_check_status # 检测当前MHA运行状态
masterha_conf_host # 添加或者删除配置的server信息
masterha_manager # 启动MHA
masterha_master_monitor # 检测master是否宕机
masterha_master_switch # 控制故障转移(自动或者手动)
masterha_secondary_check # 如果从manager节点发现
masterha_stop # 停止MHA
在/usr/local/mha4mysql-manager-0.58/samples/scripts 目录下会有相关脚本
master_ip_failover # 自动切换时vip管理的脚本,不是必须,如果我们使用keepalived的,我们可以自己编写脚本完成对vip的管理,比如监控mysql,如果mysql异常,我们停止keepalived就行,这样vip就会自动漂移
master_ip_online_change # 在线切换时vip的管理,不是必须,同样可以可以自行编写简单的shell完成
power_manager # 故障发生后关闭主机的脚本,不是必须
send_report # 因故障切换后发送报警的脚本,不是必须,可自行编写简单的shell完成
定义MHA管理配置文件
MHA manager有两个配置文件模板,在路径$MHA_BASE/samples/conf/下的app1.cnf和masterha_default.cnf。
app1.cnf #是对某个复制组的配置文件。
masterha_default.cnf #MHA manager的全局配置文件,可以通过这一个配置文件管理多个复制组。
[root@manager mha4mysql-manager-0.58]# cp /usr/local/mha4mysql-manager-0.58/samples/conf/app1.cnf /etc/mha/
创建mha管理配置文件
[root@manager src]# mkdir /etc/mha
vi /etc/mha/app1.cnf
[server default]
#mha manager日志文件
manager_log=/usr/local/mha/app1/manager.log
#manager工作目录
manager_workdir=/usr/local/mha/app1
#master节点存放binlog日志路径,以便MHA找到binlog,这里就是MySQL的数据目录
master_binlog_dir=/var/lib/mysql
#发生切换时slave节点binlog日志存放路径
remote_workdir=/usr/local/mha/app1
#自动切换脚本
master_ip_failover_script=/usr/local/mha4mysql-manager-0.58/samples/scripts/master_ip_failover
#手动切换脚本
master_ip_online_change_script=/usr/local/mha4mysql-manager-0.58/samples/scripts/master_ip_online_change
#监控主节点时间间隔
ping_interval=1
#设置故障发生后关闭故障主机脚本(该脚本的主要作用是关闭主机防止发生脑裂,这里没有使用)
#shutdown_script=""
#数据库监控用户
user=mhaadmin
password=000000
#复制用户
repl_user=mha
repl_password=000000
#ssh登录用户
ssh_user=root
[server1]
hostname=192.168.17.131
port=3306
[server2]
#设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave
candidate_master=1
#默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master
check_repl_delay=0
hostname=192.168.17.132
port=3306
[server3]
hostname=192.168.17.134
port=3306
创建所需目录
[root@manager src]# mkdir -p /usr/local/mha/app1
对所有个节点进行检测
在manager上检测各节点间ssh互信通信配置是否ok
[root@manager ~]# /usr/local/bin/masterha_check_ssh --conf=/etc/mha/app1.cnf
检查管理的MySQL复制集群的连接配置参数是否OK
[root@manager ~]# /usr/local/bin/masterha_check_repl --conf=/etc/mha/app1.cnf
启动MHA
manager节点上执行以下命令来启动MHA
[root@manager ~]# nohup /usr/local/bin/masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover &> /usr/local/mha/manager.log &
启动成功以后,查看master节点的状态
[root@manager ~]# masterha_check_status --conf=/etc/mha/app1.cnf
mha (pid:18829) is running(0:PING_OK), master:192.168.17.131
停止MHA命令
[root@manager ~]# /usr/local/bin/masterha_stop --conf=/etc/mha/app1.cnf
创建自动清除relay log脚本
MHA在发生切换的过程中,从库的恢复过程中依赖于relay log的相关信息,所以这里要将relay log的自动清除设置为OFF,采用手动清除relay log的方式。在默认情况下,从服务器上的中继日志会在SQL线程执行完毕后被自动删除。但是在MHA环境中,这些中继日志在恢复其他从服务器时可能会被用到,因此需要禁用中继日志的自动删除功能。定期清除中继日志需要考虑到复制延时的问题。在ext3的文件系统下,删除大的文件需要一定的时间,会导致严重的复制延时。为了避免复制延时,需要暂时为中继日志创建硬链接,因为在linux系统中通过硬链接删除大文件速度会很快。(在mysql数据库中,删除大表时,通常也采用建立硬链接的方式)
MHA节点中包含了pure_relay_logs命令工具,它可以为中继日志创建硬链接,执行SET GLOBAL relay_log_purge=1,等待几秒钟以便SQL线程切换到新的中继日志,再执行SET GLOBAL relay_log_purge=0。
清理日志脚本:
[root@manager ~]# vi /usr/local/mha/purge_relay_log.sh
#!/bin/bash
#数据库用户名密码端口
user=root
passwd=000000
port=3306
#脚本日志存放路径
log_dir='/usr/local/mha/app1'
#指定创建relay log的硬链接的位置,默认是/var/tmp。由于系统不同分区创建硬链接文件会失败,故需要执行硬链接具体位置,成功执行脚本后,硬链接的中继日志文件被删除。
work_dir='/usr/local/mha'
#删除中继日志脚本
purge='/usr/local/bin/purge_relay_logs'
if [ ! -d $log_dir ]
then
mkdir -p $log_dir
fi
#--disable_relay_log_purge :默认情况下,如果relay_log_purge=1,脚本会什么都不清理,自动退出。通过设定这个参数,当relay_log_purge=1的情况下会将relay_log_purge设置为0。清理relay log之后,最后将参数设置为OFF。
$purge --user=$user --password=$passwd --disable_relay_log_purge --port=$port --workdir=$work_dir >> $log_dir/purge_relay_logs.log 2>&1
授予可执行权限
[root@manager ~]# chmod +x purge_relay_log.sh
添加至定时任务
00 03 * * * /bin/bash /usr/local/mha/purge_relay_log.sh
2种配置VIP的方式
使用keepalived配置VIP
keepalived安装略
在master和candidate master上安装,即在master、slave1上安装.
master的keepalived.conf
! Configuration File for keepalived
global_defs {
router_id master
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 100
nopreempt
advert_int 1
authentication {
auth_type PASS
auth_pass 000000
}
virtual_ipaddress {
192.168.17.130
}
}
slave1的keepalived.conf
! Configuration File for keepalived
global_defs {
router_id slave1
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 90
nopreempt
advert_int 1
authentication {
auth_type PASS
auth_pass 000000
}
virtual_ipaddress {
192.168.17.130
}
}
MHA引入keepalived
MySQL服务进程挂掉时通过MHA停止keepalived,需要修改切换是触发的脚本文件master_ip_failover即可,在该脚本中添加在master发生宕机时对keepalived的处理。
[root@manager scripts]# vi /usr/local/bin/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.17.130";
my $ssh_start_vip = "systemctl start keepalived";
my $ssh_stop_vip = "systemctl stop keepalived";
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 \"`;
}
# A simple system call that disable the VIP on the old_master
sub stop_vip() {
`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";
}
使用脚本引入VIP
通过脚本的方式管理VIP。这里是修改/usr/local/bin/master_ip_failover。修改完成后内容如下,而且如果使用脚本管理vip的话,需要手动在master服务器上绑定一个vip。
/sbin/ifconfig ens160:1 10.238.162.100/24
通过脚本引入VIP的实验不在演示,我自己做了自动切换的实验是可以的。大家可以自己做做实验看看日志输出理解原理。脚本如下供参考,需要根据自己环境修改VIP与网卡等信息
# cat 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 = '10.238.162.100/24';
my $key = '1';
my $ssh_start_vip = "/sbin/ifconfig ens160:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens160:$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";
}