1. MHA集群概述  
 集群的定义:多台服务器一起提供相同的服务,如(web集群)等。 常见集群的分类:   LB(负载均衡集群):服务器共同平均分摊处理客户端的多次连接请求。  HA(高可用集群):主备模式,主宕机后备用服务器自动接替工作。   常见集群服务软件:  LB:LVS、Nginx、haproxy等。  HA:Keepalived、heartbeat、   
  
 1.1. 软件介绍  
MHA(Master High Availability)   
 由日本DeNA公司youshimaton通过perl脚本语言开发。 是一套优秀的实现MySQL高可用的解决方案。 数据库的自动故障切换操作能做到在0~30秒之内完成。 MHA能确保在故障切换过程中最大限度保证数据的一致性,以达到真正意义的高可用。   
  
 1.2. MHA组成  
 
 管理所有数据库服务器 可以单独部署在一台独立的机器上 也可以部署在某台数据库服务器上   
  
 
 存储数据的MySQL服务器 运行在每台MySQL服务器上   
  
 1.3. MHA工作过程  
MHA集群架构图:   MHA 工作过程:   
 由Manager 定时探测集群中的master节点 当master故障时,Manager自动将拥有最新数据的slave提升为新的master,另一台slave主机将自动变为新master的从库,所以集群组里面的主机应该是一主多从结构。   
  
 1.4. IP规划  
IP地址 主从同步角色 集群角色 主机名 192.168.2.10 客户端 无 client10 192.168.2.20 无 管理主机 mha20 192.168.2.30 主库 当前主库 mysql30 192.168.2.40 从库 备用主库 mysql40 192.168.2.50 从库 备用主库 mysql50 192.168.2.100 无 VIP地址 无 
 2. 部署MHA集群  
 2.1. 准备集群环境  
 2.1.1. 安装依赖包  
 
[ root@mha20 ~] 
  
 
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Email-Date-Format-1.002-15.el7.noarch.rpm
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-MIME-Lite-3.030-1.el7.noarch.rpm
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-MIME-Types-1.38-2.el7.noarch.rpm
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
wget  http://rpmfind.net/linux/centos/7.9.2009/os/x86_64/Packages/perl-Config-Tiny-2.14-7.el7.noarch.rpm
wget  http://rpmfind.net/linux/epel/7/x86_64/Packages/p/perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm[ root@mha20 ~] 
perl-Config-Tiny-2.14-7.el7.noarch.rpm          perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm  perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm       perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Mail-Sender-0.8.23-1.el7.noarch.rpm        perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
[ root@mha20 ~] 
  
 2.1.2. 配置ssh密钥对认证登录  
配置管理主机mha20可以ssh免密登录所有数据库服务器   
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
  
 
[ root@mysql30 ~] 
[ root@mysql30 ~] 
[ root@mysql30 ~] 
[ root@mysql40 ~] 
[ root@mysql40 ~] 
[ root@mysql40 ~] 
[ root@mysql50 ~] 
[ root@mysql50 ~] 
[ root@mysql50 ~] 
  
 2.1.3. 配置一主多从  
 
 2.1.3.1. 配置主库(mysql30)  
[ root@mysql30 ~] 
[ mysqld] 
.. .. 
log_bin = mysql30
server_id = 30 
plugin-load= rpl_semi_sync_master= semisync_master.so; rpl_semi_sync_slave = semisync_slave.so
rpl_semi_sync_master_enabled = 1 
rpl_semi_sync_slave_enabled = 1 
relay_log_purge = 0 
[ root@mysql30 ~] 
mysql>  show master status; 
+----------------+----------+--------------+------------------+-------------------+
|  File           |  Position |  Binlog_Do_DB |  Binlog_Ignore_DB |  Executed_Gtid_Set | 
+----------------+----------+--------------+------------------+-------------------+
|  mysql30.000001 |       154  |               |                   |                    | 
+----------------+----------+--------------+------------------+-------------------+
1  row in  set  ( 0.00  sec) 
mysql>  grant replication slave on *.* to 'master' @'%'  identified by '1234' ; 
  
 2.1.3.2. 配置从服务器(mysql40)  
[ root@mysql40 ~] 
[ mysqld] 
.. .. 
server_id = 40 
log_bin = master40
plugin-load= rpl_semi_sync_master= semisync_master.so; rpl_semi_sync_slave = semisync_slave.so
rpl_semi_sync_master_enabled = 1 
rpl_semi_sync_slave_enabled = 1 
relay_log_purge = 0 
[ root@mysql40 ~] 
mysql>  change master to->  master_host = "192.168.2.30" ,->  master_user = "master" ,->  master_password = "1234" ,->  master_log_file = "mysql30.000001" ,->  master_log_pos = 154 ; 
mysql>  start slave; 
Query OK, 0  rows affected ( 0.01  sec) 
mysql>  show slave status\ G;   
查看Slave_IO_Running和Slave_SQL_Running是yes那就配置成功,这里不展示了。
  
 2.1.3.3. 配置从服务器(mysql50)  
[ root@mysql50 ~] 
[ mysqld] 
.. .. 
server_id = 50 
log_bin = master50
plugin-load= rpl_semi_sync_master= semisync_master.so; rpl_semi_sync_slave = semisync_slave.so
rpl_semi_sync_master_enabled = 1 
rpl_semi_sync_slave_enabled = 1 
relay_log_purge = 0 
[ root@mysql50 ~] 
mysql>  change master to->  master_host = "192.168.2.30" ,->  master_user = "master" ,->  master_password = "1234" ,->  master_log_file = "mysql30.000001" ,->  master_log_pos = 154 ; 
mysql>  start slave; 
Query OK, 0  rows affected ( 0.01  sec) 
mysql>  show slave status\ G;   
查看Slave_IO_Running和Slave_SQL_Running是yes那就配置成功,这里不展示了。
  
 2.2. 配置管理节点(mha20)  
 2.2.1. 安装mha软件包  
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
  
 2.2.2. 管理集群命令  
[ root@mha20 ~] 
masterha_check_repl  masterha_check_status  masterha_manager         masterha_master_switch    masterha_stop
masterha_check_ssh   masterha_conf_host     masterha_master_monitor  masterha_secondary_check
  
命令 作用 masterha_check_ssh 检查MHA的SSH配置状态 masterha_check_repl 检查MySQL复制状态 masterha_manager 启动MHA masterha_check_status 检测MHA运行状态 masterha_stop 停止MHA masterha_master_monitor 检测master是否宕机 masterha_master_switch 控制故障转移(自动或者手动) masterha_conf_host 添加或删除配置的server信息 
 2.2.3. 修改主配置文件  
[ root@mha20 ~] 
[ root@mha20 ~] 
[ root@mha20 mha] 
修改内容如下:
[ server default]           ---管理服务的默认配置
manager_workdir = /etc/mha      ---指定工作目录路径
manager_log = /etc/mha/manager.log    ---指定管理服务运行后日志文件的名称和存放路径
master_ip_failover_script = /etc/mha/master_ip_failover      ---指定故障切换脚本
ssh_user = root    ---指定ssh连接时候的用户名和端口号
ssh_port = 22 repl_user = master       ---指定主库授权的用户名和密码
repl_password = 1234 user = mysqldb				---指定监控用户和密码,三台mysql需要一样。
password = 1234 ping_interval = 1          ---ping间隔时长
[ server1]        			---指定第一台mysql服务器
hostname = 192.168 .2.30       ---指定第一台mysqlIP地址
port = 3306                    ---端口号
candidate_master = 1           ---指定该数据库服务器参与竞选主库,1代表参与。[ server2] 
hostname = 192.168 .2.40
port = 3306 
candidate_master = 1 [ server3] 
hostname = 192.168 .2.50
port = 3306 
candidate_master = 1   
 2.2.4. 创建故障切换脚本  
[ root@mha20 mha] 
[ root@mha20 mha] 
[ root@mha20 mha] 
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.2.100/24' ;      
my $key  =  '1' ; 
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 \n IN 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 " ; 
}   
 2.2.5. 在主库上创建VIP地址(mysql30)  
[ root@mysql30 ~] 
[ root@mysql30 ~] 
ens33:1: flags = 416 3 < UP,BROADCAST,RUNNING,MULTICAST>   mtu 1500 inet 192.168 .2.100  netmask 255.255 .255.0  broadcast 192.168 .2.255ether 00:0c:29:88:4f:b0  txqueuelen 1000   ( Ethernet) 
  
 2.3. 配置数据库服务器(mysql30-50)  
 2.3.1. 在数据库服务器安装mha4mysql-node软件包  
[ root@mysql30 ~] 
[ root@mysql30 ~]   
 2.3.2. 创建监控用户  
mysql>  grant all on *.* to 'mysqldb' @'%'  identified by '1234' ; 
Query OK, 0  rows affected, 1  warning ( 0.00  sec) 
  
 3. 验证配置  
 3.1. 测试ssh配置  
[ root@mha20 ~] 
.. .. 
All SSH connection tests passed successfully    ---最后出现这个就说明配置正确。  
 3.2. 测试主从配置  
[ root@mha20 ~] 
.. .
MySQL Replication Health is OK.    ---出现这个说明MySQL复制运行状况正常
  
 3.3. 启动管理服务  
使用 masterha_manager 命令启动管理服务   
 —— --remove_dead_master_conf是指当主库宕机的时候,会删除宕机主库的配置,否则主库宕机后服务就会无法启动;–ignore_last_failover是指忽略 xxx.health 文件,意思是当主库宕机之后,服务会在规定的时间内连接剩下的从服务器来选举出主库。如果启动服务的时候不加这个选项,那么超过规定时间还没连接上的时候,就不会再去切换主库了,加上这个选项后,既是在规定时间内没能连接上剩下的从服务器,之后也会继续尝试连接,知道选举出主库进行切换。
 
  
[ root@mha20 ~] 
nohup   masterha_manager --conf = /etc/mha/app1.cnf --remove_dead_master_conf    --ignore_last_failover   & 
[ root@mha20 ~] 
app1 ( pid:39934)  is running( 0 :PING_OK) , master:192.168.2.30
[ root@mha20 ~]   
 4 测试高可用  
 4.1. 模拟主库服务器故障  
 
 停止mysqld服务 关机 故障切换过程:  当管理主机连接不上mysql主服务器时,就认为mysql主服务器宕机了,然后管理主机的服务自动停掉,然后调用故障切换脚本,删除管理主机中关于mysql主服务器的内容(/etc/mha/app1.cnf)然后脚本会在选举出来的主服务器创建VIP地址。   
  
[ root@mysql30 ~] 
[ root@mha20 ~] 
[ root@mha20 ~] 
Fri Feb 24  14 :58:16 2023  - [ info]  Searching new master from slaves.. 
192.168 .2.40( 192.168 .2.40:3306)  ( new master) 
Fri Feb 24  14 :58:16 2023  - [ info]  Getting new master's binlog name and position.. 
Enabling the VIP - 192.168 .2.100/24 on the new master - 192.168 .2.40
Fri Feb 24  14 :58:17 2023  - [ info]   Resetting slave 192.168 .2.50( 192.168 .2.50:3306)  and starting replication from the new master 192.168 .2.40( 192.168 .2.40:3306) .. 
Fri Feb 24  14 :58:18 2023  - [ info]  Resetting slave info on the new master.. 
Selected 192.168 .2.40( 192.168 .2.40:3306)  as a new master.
[ root@mysql40 ~] 
ens33:1: flags = 416 3 < UP,BROADCAST,RUNNING,MULTICAST>   mtu 1500 inet 192.168 .2.100  netmask 255.255 .255.0  broadcast 192.168 .2.255ether 00:0c:29:83:e3:d4  txqueuelen 1000   ( Ethernet) 
[ root@mysql50 ~] 
mysql: [ Warning]  Using a password on the command  line interface can be insecure.Master_Host: 192.168 .2.40     ---可以看的出来mysql40已经是masterl Master_User: masterMaster_Port: 3306 Connect_Retry: 60 Master_Log_File: master40.000001Read_Master_Log_Pos: 154 Relay_Log_File: mysql50-relay-bin.000002Relay_Log_Pos: 319 Relay_Master_Log_File: master40.000001Slave_IO_Running: YesSlave_SQL_Running: Yes	
[ root@mha20 ~] 
[ root@mha20 ~] 
app1 ( pid:57758)  is running( 0 :PING_OK) , master:192.168.2.40
  
 4.2. 修复故障的数据库服务器  
 4.2.1. 数据服务器的配置  
 
 启动mysql服务 与主服务器数据一致 指定主服务器信息 启动slave进程 查看slave状态信息   
  
[ root@mysql30 ~] 
mysql>  change master to->  master_host = "192.168.2.40" ,->  master_user = "master" ,->  master_password = "1234" ,->  master_log_file = "master40.000001" ,->  master_log_pos = 154 ; 
Query OK, 0  rows affected, 2  warnings ( 0.00  sec) 
mysql>  start slave; 
  
 4.2.2. 配置管理主机  
[ root@mha20 ~] 
[ root@mha20 ~] 
.. .. 
[ server1] 
candidate_master = 1 
hostname = 192.168 .2.30
port = 3306 
[ root@mha20 ~] 
[ root@mha20 ~] 
app1 ( pid:60567)  is running( 0 :PING_OK) , master:192.168.2.40