龙空技术网

MySQL高可用架构-MHA(下)

程序猿集锦 233

前言:

此时我们对“supervisor管理mysql”大约比较讲究,兄弟们都需要学习一些“supervisor管理mysql”的相关资讯。那么小编在网络上收集了一些对于“supervisor管理mysql””的相关内容,希望小伙伴们能喜欢,朋友们快快来学习一下吧!

关注我「程序猿集锦」,获取更多分享。

VIP漂移的三种方式通过MHA自定义master_ip_failover维护通过keepalive来维护安装keepalive配置keepalive启动keepalive通过keepalive和master_ip_failover结合来使用验证VIP漂移验证VIP是否可用验证VIP是否可以漂移如何恢复失败节点读服务怎么解决思考总结

前置阅读:MySQL高可用架构-MHA(上)

VIP漂移的三种方式

MySQL高可用集群已经搭建好,但是当master1节点宕机之后,从节点提升为主节点后,应用连接到MySQL服务的主节点地址就变成了某一个slave从节点,那如何保证应用可用是一个固定的IP来连接MySQL数据库呢?这就需要使用到VIP虚拟IP地址。

这个VIP地址就是应用中配置的IP地址,它可用在活着的主节点上面绑定,如果主宕机了,这个VIP可用漂移到新的主上。这就是VIP要完成功能。那么如何让这个VIP自动漂移到活着的主节点呢?目前可有三种方式:

通过脚本自己去维护VIP绑定的节点,这要在MHA的扩展脚本中去做,使用perl脚本去做。或者使用keepalive插件自己去维护,这种方式是在MHA做故障迁移的时候,顺便把发生故障的主机上面的keepalive服务给停止掉,这样VIP就可以从故障的节点迁移到指定的节点了。就是上面两种1和2的方式结合的一种方式。在Perl脚本中不去维护VIP的创建绑定等操作,而是交给keepalive服务去做,只要在perl脚本中,决定什么时候在发生主节点MySQL服务不能用的时候,把该主节点上面的keepalive服务给停止,就可以把对应VIP漂移到其他主机上去了。通过MHA自定义master_ip_failover维护

这个脚本就是master_ip_failover自定义脚本。因为我不会写Perl,下面是网上扒来的,供参考。

大体的逻辑就是:我们在脚本中定义一个VIP地址,然后定义一个在远程服务器创建VIP,还有一个取消VIP的方法。然后MAH的manager进程在进行主从切换的时候,会调用这个自定义的master_ip_failover脚本,并给这个脚本传入对应的参数和命令,这个脚本就根据manager进程转入的命令走对应的逻辑,来调用自定义的创建和取消VIP的方法,从而来实现VIP从原主节点到新节点的漂移。

在使用这个脚本之前,需要原先正常的master节点上,先使用下面的命令来创建一个VIP,然后才能将这个VIP漂移到新的主节点上。

root@master1:/var/log/mha/app1# ifconfig eth0:1 172.21.0.10 uproot@master1:/var/log/mha/app1# ifconfigeth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.11  netmask 255.255.255.0  broadcast 172.21.0.255        ether 02:42:ac:15:00:0b  txqueuelen 0  (Ethernet)        RX packets 1251  bytes 162986 (159.1 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 3342  bytes 342702 (334.6 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.10  netmask 255.255.0.0  broadcast 172.21.255.255        ether 02:42:ac:15:00:0b  txqueuelen 0  (Ethernet)lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536        inet 127.0.0.1  netmask 255.0.0.0        loop  txqueuelen 1000  (Local Loopback)        RX packets 154  bytes 21841 (21.3 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 154  bytes 21841 (21.3 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

master_ip_failover的示例脚本如下:

root@manager:/var/log/mha/app1# cat /etc/mha/app1/master_ip_failover#!/usr/bin/env perluse strict;use warnings FATAL => 'all';use Getopt::Long;use MHA::DBHelper;my (  $command,        $ssh_user,         $orig_master_host,  $orig_master_ip, $orig_master_port, $new_master_host,  $new_master_ip,  $new_master_port,  $new_master_user,  $new_master_password);# MySQL cluster VIP for write requestmy $vip = '172.21.0.10';my $key = '1';my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";my $ssh_stop_vip = "/sbin/ifconfig eth0:$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,  'new_master_user=s'     => \$new_master_user,  'new_master_password=s' => \$new_master_password,);exit &main();sub main {  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 "Disabling the VIP on old master: $orig_master_host \n";      &stop_vip();      # updating global catalog, etc      $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 {      my $new_master_handler = new MHA::DBHelper();      # args: hostname, port, user, password, raise_error_or_not      $new_master_handler->connect( $new_master_ip, $new_master_port,        $new_master_user, $new_master_password, 1 );      ## Set read_only=0 on the new master      $new_master_handler->disable_log_bin_local();      print "Set read_only=0 on the new master.\n";      $new_master_handler->disable_read_only();      ## Creating an app user on the new master      #print "Creating app user on the new master..\n";      #FIXME_xxx_create_user( $new_master_handler->{dbh} );      $new_master_handler->enable_log_bin_local();      $new_master_handler->disconnect();      ## Update master ip on the catalog database, etc      #FIXME_xxx;      print "Enabling the VIP - $vip on the new master - $new_master_host \n";      &start_vip();      $exit_code = 0;    };    if ($@) {      warn $@;      # If you want to continue failover, exit 10.      exit $exit_code;    }    exit $exit_code;  }  elsif ( $command eq "status" ) {    print "Checking the Status of the script.. OK \n";    # do nothing    exit 0;  }  else {    &usage();    exit 1;  }}# A simple system call that enable the VIP on the new mastersub start_vip() {    `ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;}# A simple system call that disable the VIP on the old_mastersub 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@manager:/var/log/mha/app1#
通过keepalive来维护

我们接下来安装keepalive插接,目的是为了在MySQL数据集群中配置一个虚拟的VIP,然后让这个VIP可以根据主节点的状态来自动的在可用的主节点进行漂移。此时我们不需要自己在master节点上创建VIP,当我们在master节点和备选master节点上,配置好keepalive的配置文件/etc/keepalived/keepalived.conf之后,启动keepalive服务后会自动在对应的节点创建VIP地址。

安装keepalive

我们在master1节点和slave1节点上面都安装keepalive插件,master1作为默认的主节点,slave1作为备用主节点(如果是有多个备选主节点,那么所有的备选主节点都要安装并启动keepalive服务),使用apt-get命令来安装keepalive软件,如果是centos使用yum来安装。

root@master1:/# apt-get install keepalived -yroot@slave1:/# apt-get install keepalived -y

keepalive安装完成之后,会在/etc/init.d目录里下面生成一个启动脚本文件keepalived,如下所示,我们只是master1节点上示例展示。查看这个脚本文件,可用看到它的配置文件是在/etc/keepalived/keepalived.conf这里。所以,后续配置keepalive的时候,我们需要在这个配置文件中进行配置。

# 查看启动和停止keepalive服务的脚本文件root@master1:/etc/keepalived# ls -lstr /etc/init.d/keepalived4 -rwxr-xr-x 1 root root 2121 Aug 15  2015 /etc/init.d/keepalived# 查看keepalive的默认配置文件配置路径是什么root@master1:/etc/keepalived# cat /etc/init.d/keepalived | grep CONFIGCONFIG=/etc/keepalived/keepalived.conftest -f $CONFIG || exit 0root@master1:/etc/keepalived#
配置keepalive

在master1节点和slave1节点上面分别创建配置文件/etc/keepalived/keepalived.conf,然后分别编辑配置他们。

在maste1上的keepalived.conf`配置文件如下:

root@master1:/etc/keepalived# cat /etc/keepalived/keepalived.conf! Configuration File for keepalivedglobal_defs {    router_id 172.21.0.11}vrrp_script chk_mysql_status {    script "/etc/keepalived/check_mysql_port.sh 3306"    interval 2    weight -20}vrrp_instance VI_1 {    state BACKUP    interface eth0    virtual_router_id 251    priority 100    advert_int 1    mcast_src_ip 172.21.0.11    nopreempt    authentication {        auth_type PASS        auth_pass 11111111    }    track_script {        chk_mysql_status    }    virtual_ipaddress {        172.21.0.10    }}root@master1:/etc/keepalived#
在slave1上的keepalived.conf`配置文件如下:
root@slave1:/etc/keepalived# cat keepalived.conf! Configuration File for keepalivedglobal_defs {    router_id 172.21.0.12}vrrp_script chk_mysql_status {    script "/etc/keepalived/check_mysql_port.sh 3306"    interval 2    weight -20}vrrp_instance VI_1 {    state BACKUP    interface eth0    virtual_router_id 251    priority 90    advert_int 1    mcast_src_ip 172.21.0.12    nopreempt    authentication {        auth_type PASS        auth_pass 11111111    }    track_script {        chk_mysql_status    }    virtual_ipaddress {        172.21.0.10    }}root@slave1:/etc/keepalived#

现在,针对上面的配置文件的参数,使用master1节点上面的配置文件,做如下简单介绍说明,以便于立即keepalive的配置文件怎么配置。

root@master1:/etc/keepalived# cat /etc/keepalived/keepalived.conf! Configuration File for keepalivedglobal_defs {    router_id 172.21.0.11 # keepalive所在主机的IP地址,一个名字而已,这里用IP地址代替了。}vrrp_script chk_mysql_status {		# 检查主机3306端口的shell脚本,发生VIP漂移的触发原因就是某一个主机的某个端口不可用。这个脚本需要自定义去写一下,比较简单,后面会给出示例。    script "/etc/keepalived/check_mysql_port.sh 3306"     interval 2 # 端口检查的频次,每2秒中一次    weight -20}vrrp_instance VI_1 {    state BACKUP # 当前keepalive节点的角色    interface eth0 # 当前主机的物理网卡名称    virtual_router_id 251    priority 100    advert_int 1    mcast_src_ip 172.21.0.11 # 当前主机的物理IP地址    nopreempt			# keepalive多个节点互相通信的认证方式,用于标识哪些keepalive节点是同一个keepalive集群。    authentication {        auth_type PASS        auth_pass 11111111    }    track_script {        chk_mysql_status # 检查服务端口的脚本,VIP发生漂移的时候就是从这里触发的。    }    virtual_ipaddress {        172.21.0.10 # 这就是我们设置的VIP的地址    }}root@master1:/etc/keepalived#

注意:上面两台服务器的keepalived都设置为了BACKUP模式,目的是为了尽量减少VIP漂移的次数。具体原因如下:

在keepalived中2种模式,分别是master->backup模式和backup->backup模式。这两种模式有很大区别。

在master->backup模式下,一旦主库宕机,虚拟ip会自动漂移到从库,当主库修复后,keepalived启动后,还会把虚拟ip抢占过来,即使设置了非抢占模式(nopreempt)抢占ip的动作也会发生。在backup->backup模式下,当主库宕机后虚拟ip会自动漂移到从库上,当原主库恢复和keepalived服务启动后,并不会抢占新主的虚拟ip,即使是优先级高于从库的优先级别,也不会发生抢占。

所以,为了减少ip漂移次数,通常是把修复好的主库当做新的备库。同时把keepalive中的角色都设置为backup角色。

在配置好keepalive的配置文件后,配置文件中有引用到一个监控某个服务是否可以的脚本,这里我们要监控的是MySQL数据库服务是否可用,所以我们监控的是MySQL的3306端口是否在提供监听服务。如果是要监听其他服务的端口,只要作出对应的端口作出修改即可。下面给出监听MySQL服务的shell脚本,这个脚本在每一个keepalive节点上都是一样的,在我们的master1和slave1上使用相同的脚本文件。

root@master1:/etc/keepalived# cat chk_mysql_status.sh#!/bin/bashCHK_PORT=$1if [ -n "$CHK_PORT" ]; then    PORT_PROCESS=`ss -lnt | grep $CHK_PORT | wc -l`    if [ $PORT_PROCESS -eq 0 ]; then        echo "请注意:端口号$CHK_PORT现在已经不可用,下面将退出keepalive的服务,VIP即将发生漂移。" >> ./chk_mysql_status.log        # 发现当前主机的MySQL端口不能访问后,把当前主机的keepalive服务也停止,让这个主机上的VIP漂移到备选主节点上        /etc/init.d/keepalived stop        exit 1    fielse    echo "待监控的服务端口号不能为空!" >> ./chk_mysql_status.logfiroot@master1:/etc/keepalived# pwd/etc/keepalivedroot@master1:/etc/keepalived#

需要注意的是脚本的放置目录,我们是放在和keepalive配置文件相同的目录/etc/keepalive目录下面了。

启动keepalive

在master1节点上启动keepalive服务,如下所示:

root@master1:/etc/keepalived# /etc/init.d/keepalived start[ ok ] Starting keepalived: keepalived.# 查看启动keepalive后,VIP已经创建成功root@master1:/etc/keepalived# ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000    link/ipip 0.0.0.0 brd 0.0.0.03: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000    link/tunnel6 :: brd ::59: eth0@if60: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default    link/ether 02:42:ac:15:00:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0    inet 172.21.0.11/24 brd 172.21.0.255 scope global eth0       valid_lft forever preferred_lft forever    inet 172.21.0.10/32 scope global eth0  # 这里就是我们配置的VIP,默认是master1上面,当master1节点的3306端口挂掉,或漂移到slave1上。       valid_lft forever preferred_lft foreverroot@master1:/etc/keepalived#

在slave1节点上,也启动keepalive服务,如下所示:

root@slave1:/etc/keepalived# /etc/init.d/keepalived start[ ok ] Starting keepalived: keepalived.root@slave1:/etc/keepalived## 此时slave1上面是没有我们配置的VIP的,因为master1目前还是可用状态。root@slave1:/etc/keepalived# ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00    inet 127.0.0.1/8 scope host lo       valid_lft forever preferred_lft forever2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000    link/ipip 0.0.0.0 brd 0.0.0.03: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000    link/tunnel6 :: brd ::61: eth0@if62: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default    link/ether 02:42:ac:15:00:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0    inet 172.21.0.12/24 brd 172.21.0.255 scope global eth0       valid_lft forever preferred_lft foreverroot@slave1:/etc/keepalived#
通过keepalive和master_ip_failover结合来使用

这种方式就结合前面使用MHA的master_ip_failover脚本和keepalive两种方式的变形。实现的方式就是:VIP的创建我们交给keepalive来做,但是这个VIP要向完成漂移的动作,发生故障的服务器上面的keepalive进程必须终止后,VIP才会发生漂移,那么如何让这个发生故障的服务器上面的keepalive终止进程呢?就在我们的master_ip_failover脚本中去终止对应的服务器上面的keepalive进程服务,这样这个服务器上面的VIP就会发生漂移了。

脚本的实现方式和第一种master_ip_failover的脚本类似,只是此时我们不需要只是用keepalive的时候自定义的那个chk_mysql_status.sh脚本来终止keepalive进程了。在master_ip_failover脚本中去终止对应的keepalive服务进程。脚本如下:

root@manager:/var/log/mha/app1# cat /etc/mha/app1/master_ip_failover#!/usr/bin/env perl## Note: This is a sample script and is not complete. Modify the script based on your environment.use strict;use warnings FATAL => 'all';use Getopt::Long;use MHA::DBHelper;my (  $command,        $ssh_user,         $orig_master_host,  $orig_master_ip, $orig_master_port, $new_master_host,  $new_master_ip,  $new_master_port,  $new_master_user,  $new_master_password);# MySQL集群中的VIP地址my $vip = '172.21.0.10';# 开启和关闭VIP的命令,下面是通过停止对应的服务器上面的keepalive的命令,来完成VIP的漂移的。my $ssh_start_vip = "/etc/init.d/keepalived start";my $ssh_stop_vip = "/etc/init.d/keepalived stop";# 也可以使用下面的方式,自己通过ifconfig命令在对应的服务器上面增加和删除VIP,这样在master节点和备选master节点就不用安装keepalive服务了。#my $key = '1';#my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip up";#my $ssh_stop_vip = "/sbin/ifconfig eth0:$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,  'new_master_user=s'     => \$new_master_user,  'new_master_password=s' => \$new_master_password,);exit &main();sub main {  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 "Disabling the VIP on old master: $orig_master_host \n";      &stop_vip();  # 调用停止VIP的方法      # updating global catalog, etc      $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 {      my $new_master_handler = new MHA::DBHelper();      # args: hostname, port, user, password, raise_error_or_not      $new_master_handler->connect( $new_master_ip, $new_master_port,        $new_master_user, $new_master_password, 1 );      ## Set read_only=0 on the new master      $new_master_handler->disable_log_bin_local();      print "Set read_only=0 on the new master.\n";      $new_master_handler->disable_read_only();      ## Creating an app user on the new master      #print "Creating app user on the new master..\n";      #FIXME_xxx_create_user( $new_master_handler->{dbh} ); # 如果应用使用的MySQL用户并没有在salve节点创建,可以在切换的时候,在这里定义并创建。但是一般情况下,整个集群中的所有用户都是相同的,权限也是相通的,所以一般情况用不上这个操作。      $new_master_handler->enable_log_bin_local();      $new_master_handler->disconnect();      ## Update master ip on the catalog database, etc      #FIXME_xxx;      print "Enabling the VIP - $vip on the new master - $new_master_host \n";      &start_vip(); # 调用开启VIP的方法      $exit_code = 0;    };    if ($@) {      warn $@;      # If you want to continue failover, exit 10.      exit $exit_code;    }    exit $exit_code;  }  elsif ( $command eq "status" ) {    print "Checking the Status of the script.. OK \n";    # do nothing    exit 0;  }  else {    &usage();    exit 1;  }}# 在远程服务器开启VIP的方法sub start_vip() {    `ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;}# 在远程服务器停止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@manager:/var/log/mha/app1#
验证VIP漂移

在验证VIP是否可以正常漂移的实验中,针对上面提出来的3种实现方式,我们选择第一种方式来验证一下。其他两种方式,我也一并做过,但是这里不再一一贴出实验过程。

第一种方式:通过master_ip_failover脚本来维护VIP的漂移。在master1节点上创建一个VIP,如下所示,用于对外提供写的服务。然后我们看下在master1节点宕机的时候,这个上面的VIP是否可以自动漂移到salve1备用主主节点上。

# 在master1节点上创建VIP地址root@master1:/var/log/mha/app1# ifconfig eth0:1 172.21.0.10 up# VIP地址已经在master1节点上创建完成root@master1:/var/log/mha/app1# ifconfigeth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.11  netmask 255.255.255.0  broadcast 172.21.0.255        ether 02:42:ac:15:00:0b  txqueuelen 0  (Ethernet)        RX packets 1251  bytes 162986 (159.1 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 3342  bytes 342702 (334.6 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.10  netmask 255.255.0.0  broadcast 172.21.255.255        ether 02:42:ac:15:00:0b  txqueuelen 0  (Ethernet)lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536        inet 127.0.0.1  netmask 255.0.0.0        loop  txqueuelen 1000  (Local Loopback)        RX packets 154  bytes 21841 (21.3 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 154  bytes 21841 (21.3 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0# 此时的salve1节点上是没有VIP地址的root@slave1:/# ifconfigeth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.12  netmask 255.255.255.0  broadcast 172.21.0.255        ether 02:42:ac:15:00:0c  txqueuelen 0  (Ethernet)        RX packets 9  bytes 726 (726.0 B)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 0  bytes 0 (0.0 B)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536        inet 127.0.0.1  netmask 255.0.0.0        loop  txqueuelen 1000  (Local Loopback)        RX packets 0  bytes 0 (0.0 B)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 0  bytes 0 (0.0 B)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0root@slave1:/#

对于master_ip_failover脚本,参考上面提到VIP漂移第一种实现方式的贴出来的脚本内容,这不再重复贴出。

验证VIP是否可用

现在的VIP已经在master1上面创建成功了,下面我们来尝试使用这个VIP是否可以正常方式到MySQL服务。我们在安装MHA服务的manager节点上,尝试通过VIP来访问MySQL数据库服务,看是否可以成功。通过如下结果可以看出通过VIP是可以正常方式MySQL服务的。

root@manager:/etc/mha# mysql -uroot -proot -h172.21.0.10mysql: [Warning] Using a password on the command line interface can be insecure.Welcome to the MySQL monitor.  Commands end with ; or \g.Your MySQL connection id is 48Server version: 5.7.31-log MySQL Community Server (GPL)Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> select @@hostname;+---------------+| @@hostname    |+---------------+| master1.mysql |+---------------+1 row in set (0.00 sec)mysql>

通过上面的实验可以看出,在manager节点172.21.0.100上是可以通过172.21.0.10这个VIP来访问MySQL数据库的。

验证VIP是否可以漂移

manager节点的操作

首先,我们在manager节点上面,启动MHA的manager服务。启动方式如下所示,我们选择后台启动。

# 以后进程的方式启动MHA的manager服务root@manager:/var/log/mha/app1# nohup masterha_manager --conf=/etc/mha/app1.cnf &[1] 1230root@manager:/var/log/mha/app1# nohup: ignoring input and appending output to 'nohup.out'

查看manager服务启动后,日志目录下面的文件有哪些。日志文件的目录在/etc/mha/app1.cnf配置文件中定义。

# 启动后,发现会在/var/log/mha/app1目录下面生如下3个文件,一个是nohup的日志输出,一个是manager服务的日志输出文件,一个是MySQL数据库集群中master节点监控状态标识文件。root@manager:/var/log/mha/app1# ls -lstrtotal 164 -rw------- 1 root root  299 Mar  7 09:56 nohup.out8 -rw-r--r-- 1 root root 5784 Mar  7 09:56 app1.log4 -rw-r--r-- 1 root root   33 Mar  7 09:56 app1.master_status.health

用如下的命令动态监控manager服务的日志输出是什么,以方便我们在测试主从切换的时候,动态查看manager服务的日志内容。

# 用tail -f的命令动态的观察manager服务日志的输出内容,发现目前已经启动manager服务,它在等待MySQL服务不可用的时候,才会有动作以及日志的输出。root@manager:/var/log/mha/app1# tail -f app1.logSun Mar  7 09:56:55 2021 - [info] Checking master_ip_failover_script status:Sun Mar  7 09:56:55 2021 - [info]   /etc/mha/app1/master_ip_failover --command=status --ssh_user=root --orig_master_host=172.21.0.11 --orig_master_ip=172.21.0.11 --orig_master_port=3306Checking the Status of the script.. OKSun Mar  7 09:56:55 2021 - [info]  OK.Sun Mar  7 09:56:55 2021 - [warning] shutdown_script is not defined.Sun Mar  7 09:56:55 2021 - [info] Set master ping interval 1 seconds.Sun Mar  7 09:56:55 2021 - [warning] secondary_check_script is not defined. It is highly recommended setting it to check master reachability from two or more routes.Sun Mar  7 09:56:55 2021 - [info] Starting ping health check on 172.21.0.11(172.21.0.11:3306)..Sun Mar  7 09:56:55 2021 - [info] Ping(SELECT) succeeded, waiting until MySQL doesn't respond..

master1主节点的操作

下面我们可以尝试把master1节点MySQL服务停止掉,看下VIP是否会自动漂移到slave1上。在master1节点上执行如下操作来停止MySQL数据库服务。由于我们的MySQL服务是用docker容器启动的,当我们停止容器中的MySQL之后,这个容器也就退出了。

root@master1:~# /etc/init.d/mysql stop...........%                                                                                                 ➜  ~

查看manager节点的日志

当发生主从切换之后,manager节点上面的manager服务会停止,如下可以查看manager服务以及停止,需要在我们恢复好MySQL数据失败的节点之后,再次以后台进程的方式启动起来。

# 检查manager节点上的manager服务的状态,发现在完成主从切换之后,它已经停止了root@manager:/var/log/mha/app1# masterha_check_status --conf=/etc/mha/app1.cnfapp1 is stopped(2:NOT_RUNNING).root@manager:/var/log/mha/app1#

同时,我们可以查看在manager节点manager服务的日志目录下面,有如下文件生成,需要注意的是,其中的app1.failover.complete标识文件,需要删除之后,才可以再次启动manager服务。否则我们的manager服务是不能启动成功的。

root@manager:/var/log/mha/app1# ls -lstrtotal 28 4 -rw------- 1 root root   773 Mar  7 10:06 nohup.out # nohup的输出日志文件24 -rw-r--r-- 1 root root 22206 Mar  7 10:06 app1.log # 主从切换过程的日志文件 0 -rw-r--r-- 1 root root     0 Mar  7 10:06 app1.failover.complete # 主从切换完成的标识文件,再次启动manager服务之前,需要将这个文件删除之后,才可以再次启动manager服务。root@manager:/var/log/mha/app1# pwd/var/log/mha/app1root@manager:/var/log/mha/app1#

在manager节点上,查看manager节点的主从切换的日志输出如下,由于日志内容太长,这里我们只列出最后几行的日志内容。

root@manager:/var/log/mha/app1# tail -f app1.log# ...省略...Master 172.21.0.11(172.21.0.11:3306) is down!Check MHA Manager logs at manager.mysql:/var/log/mha/app1/app1.log for details.Started automated(non-interactive) failover.Invalidated master IP address on 172.21.0.11(172.21.0.11:3306)The latest slave 172.21.0.12(172.21.0.12:3306) has all relay logs for recovery.Selected 172.21.0.12(172.21.0.12:3306) as a new master.172.21.0.12(172.21.0.12:3306): OK: Applying all logs succeeded.172.21.0.12(172.21.0.12:3306): OK: Activated master IP address.172.21.0.22(172.21.0.22:3306): This host has the latest relay log events.Generating relay diff files from the latest slave succeeded.172.21.0.22(172.21.0.22:3306): OK: Applying all logs succeeded. Slave started, replicating from 172.21.0.12(172.21.0.12:3306)172.21.0.12(172.21.0.12:3306): Resetting slave info succeeded.Master failover to 172.21.0.12(172.21.0.12:3306) completed successfully.

slave1节点的操作

查看备选主节点slave1上面的是否有VIP的地址,通过如下命令可以查看VIP已经漂移到了slave1节点上了。这个漂移的动作就是我们在master_ip_failover脚本中,通过ifconfig命令来实现的,这个脚本会在MHA的manager服务在完整主从切换之后自动调用的。至于这个脚本的具体位置和路径,则是定义在/etc/mha/app1.cnf配置文件中。

root@slave1:/# ifconfigeth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.12  netmask 255.255.255.0  broadcast 172.21.0.255        ether 02:42:ac:15:00:0c  txqueuelen 0  (Ethernet)        RX packets 433  bytes 61965 (60.5 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 395  bytes 96296 (94.0 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500        inet 172.21.0.10  netmask 255.255.0.0  broadcast 172.21.255.255        ether 02:42:ac:15:00:0c  txqueuelen 0  (Ethernet)lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536        inet 127.0.0.1  netmask 255.0.0.0        loop  txqueuelen 1000  (Local Loopback)        RX packets 59  bytes 8093 (7.9 KiB)        RX errors 0  dropped 0  overruns 0  frame 0        TX packets 59  bytes 8093 (7.9 KiB)        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0root@slave1:/#

以上,我们就验证了通过master_ip_failover在定义的脚本来完成VIP在发生主从切换的时候漂移的过程。至于另外两种VIP漂移的方式,实验过程大同小异。大家可以自行式样。

如何恢复失败节点

在manger节点上的操作

在manager节点中的app1.log中,包含具体恢复失败节点的命令提示。其实现的原理则是把失败的主节点,以一个从节点的角色再次加入到集群当中去。

当前启动好失败的主节点之后,如何把这个恢复的节点加入到集群当中去,可以参考app1.log日志文件中的输出内容。

root@manager:/var/log/mha/app1# cat app1.log | grep "CHANGE"Sun Mar  7 10:06:25 2021 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='172.21.0.12', MASTER_PORT=3306, MASTER_LOG_FILE='slave1-mysql-bin.000007', MASTER_LOG_POS=154, MASTER_USER='repl', MASTER_PASSWORD='xxx';Sun Mar  7 10:06:27 2021 - [info]  Executed CHANGE MASTER.root@manager:/var/log/mha/app1#

在失败节点上的操作

根据上面在app1.log日志文件中输出的内容,改为如下内容,在失败的节点上,执行改变主从复制链路的命令之后,就可以把刚恢复的节点以从节点的角色加入到MHA考可用集群当中。

/* 在失败节点执行如下命令,设置新的复制链路 */mysql>  CHANGE MASTER TO MASTER_HOST='172.21.0.12', MASTER_PORT=3306, MASTER_LOG_FILE='slave1-mysql-bin.000007', MASTER_LOG_POS=154, MASTER_USER='repl', MASTER_PASSWORD='repl';Query OK, 0 rows affected, 2 warnings (0.03 sec)/* 启动复制链路 */mysql> start slave;Query OK, 0 rows affected (0.03 sec)/* 查看主从复制后的链路,发现链路已经切换为slave1作为主节点了 */mysql> show slave status\G*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: 172.21.0.12                  Master_User: repl                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: slave1-mysql-bin.000007          Read_Master_Log_Pos: 154               Relay_Log_File: master1-relay-bin.000002                Relay_Log_Pos: 327        Relay_Master_Log_File: slave1-mysql-bin.000007             Slave_IO_Running: Yes            Slave_SQL_Running: Yes           	/* ...省略输出内容... */             Master_Server_Id: 12                  Master_UUID: 3ae2324e-7bef-11eb-bc99-0242ac15000c             Master_Info_File: mysql.slave_master_info                    SQL_Delay: 0          SQL_Remaining_Delay: NULL      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates           Master_Retry_Count: 864001 row in set (0.00 sec)mysql>

在manager节点上操作

前面的步骤我们已经恢复了主从复制的链路,接下来就是要再次启动我们的MHA在manager节点上的manager服务。

在启动之前,需要把manager节点上的主从切换完成的标识文件删除掉,如下所示:

root@manager:/var/log/mha/app1# rm /var/log/mha/app1/app1.failover.completeroot@manager:/var/log/mha/app1#

然后再次启动MAH监控服务,

# 后台启动MHA的manager服务root@manager:/var/log/mha/app1# nohup masterha_manager --conf=/etc/mha/app1.cnf &[1] 1877root@manager:/var/log/mha/app1# nohup: ignoring input and appending output to 'nohup.out'# 查看MHA的manager服务启动的结果root@manager:/var/log/mha/app1# masterha_check_status --conf=/etc/mha/app1.cnfapp1 (pid:1877) is running(0:PING_OK), master:172.21.0.12root@manager:/var/log/mha/app1#
读服务怎么解决

对于一个高可用的MHA集群,对外提供写的服务,可以通过VIP来解决应用层连接的问题。那么对于读的服务,多个slave节点如何对应用层提供服务?

其实,对于这样的高可用的集群,它是一个读写分离的高可用集群。主节点提供写的服务,从节点提供读的服务。在应用层连接数据库的时候,就需要配置两个IP地址,一个用于写,一个用于读。这样对应用层是有侵入性的,因为应用层要自己确定每一个SQL请求到底是该发送到写的IP地址,还是发送到读的IP地址上。

为了解决这种对应用层侵入的问题,有现有的数据库中间件来解决这样问题。MyCat就是这样一个可以使用读写分离的数据库中间件。我们把上面我们提到的VIP地址,和多个slave节点IP地址,分别配置在MyCat中,MyCat会根据接收到的SQL语句自动识别出这是一个写请求还是一个读请求,进而把对应的SQL发送给VIP还是salve的IP。至于到底发送到哪个salve节点上,MyCat里有各种可以选择的配置方式,轮休分发的策略是常用的配置方式。MyCat对外提供一个IP地址,供应用层来连接MyCat。这样就解决了MHA高可用集群的读写分离。

如果考虑到MyCat的高可用,可以在MySQL的MHA集群上层部署两台MyCat,做高可用的MyCat,然后通过keepalive在两个MyCat上启动一个VIP,让应用通过这个VIP来连接MyCat。最后的网络拓扑图,如下所示:

MHA高可用+读写分离

思考

MHA中的manager节点,本身就是一个单节点,该如何避免这个呢?

目前的MHA还不支持两个MHA的manager共同管理一个MySQL集群。从技术上讲,你可以部署两个manager去监控同一个MySQL集群。但是,当发生故障转移的时候两个MHA的manager节点都在对MySQL集群进行故障转移,如果他们选择的从节点不是同一个从节点将会发生什么情况?整个MySQL集群就有可能产生两个master节点,发生脑裂的现象。所以我们目前还没有一个很好的办法对MHA的manager节点做高可用。

比较推荐的一种做法是通过监控脚本去对MHA的manager服务是否正在运用进行监控,如果没有运行就把这个服务再次尝试启动。可以把这样的脚本配置在Linux服务器的crontab中。

另外一种做法就是使用类似于的supervisor这样的后端守护进程去监控manager节点的manager服务是否正常运行,如果发现没有在运行,supervisor会尝试把manager进程给启动起来。此时,我们不用担心在MySQL集群发生故障转移主从切换之后MHA的manager节点在自动停止运行之后,再次被supervisor这样的守护进程给再次拉取起来。因为此时的manager进程是启动不了的,因为manager进程在启动的时候,会检查是否有上一次MySQL宕机的时候生成的标识文件,如果没有这样的文件才会启动成功,有这样的文件,是不会启动成功的。

MySQL中集群虽然高可用了,那VIP漂移的问题怎么解决的呢?

我们可以使用两种方式来解决这个VIP漂移的问题。

可以使用基于MHA的Perl脚本去做VIP的绑定和漂移工作。可以借助keepalive这样的组件来完成VIP的绑定和漂移的工作,这里需要注意一点,keepalive管理的VIP发生漂移的触发动作是当前绑定VIP的主机上面的keepalive进程关闭退出之后,当前主机绑定的VIP才会发生漂移的动作。所以当我们的master节点如果没有发生宕机,只是MySQL进程出现问题不能访问的时候,我们是需要把当前的主机上面运行的keepalive进程给终止掉,才会发生VIP的漂移。所以,此时我们需要在MHA的master_ip_failover脚本整增加杀死已经死掉的主节点的keepalive进程,或在keepalive的配置文件中配置一个监控MySQL服务的脚本,根据监控的结果来决定是否要杀死当前主节点的keepalive进程。

MHA中如何保证候选的master是从数据最接近宕机的master中选择出来的呢?可以设置几个candidate_master节点?

可以设置多个候选主节点,但是感觉意义不大。我们设置多个候选节点的目的是担心只要一个候选节点,万一这个候选节点挂掉了,就不能完成主从切换了。因为候选的主节点以及处于宕机的状态,所以这个节点不能用于提供主节点的服务。

但是实验证明,即便使我们设置两个以上的候选节点,如果其中任意一个候选节点出现宕机的情况,此时MHA是不能完成主从切换的。会出现如下的错误信息,在MHA尝试自动完成主从切换的时候。言外之意,如果MySQL集群中,已经存在一个候选节点宕机的情况,是不能自动完成主从切换的,需要保证集群中所有节点都是可用状态。

Sun Mar  7 19:46:16 2021 - [info]     Replicating from 172.21.0.11(172.21.0.11:3306)Sun Mar  7 19:46:16 2021 - [info]     Primary candidate for the new Master (candidate_master is set)Sun Mar  7 19:46:16 2021 - [error][/usr/share/perl5/MHA/ServerManager.pm, ln492]  Server 172.21.0.33(172.21.0.33:3306) is dead, but must be alive! Check server settings.Sun Mar  7 19:46:16 2021 - [error][/usr/share/perl5/MHA/ManagerUtil.pm, ln178] Got ERROR:  at /usr/share/perl5/MHA/MasterFailover.pm line 269.

如果备选主节点先于主节点宕机了,MHA会发现这个现象吗?主从切换还能完成吗?

MHA不会发现候选节点宕机或者任何一个从节点宕机的情况。如果发生这种现象,将不能完成主从自动切换。

总结

关于MAH的高可用集群搭建、主从切换的实验就到这里了,如果你有什么疑问可以评论区留言,我们一起讨论。后续给大家分享MGR高可用集群的搭建和实验。

标签: #supervisor管理mysql #mhamysql #oracle插接式数据库 #mysql decimal62是什么意思