MHA笔记

作者: mysia | 来源:发表于2018-09-26 21:38 被阅读120次

    简介

    MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。

    该软件由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。

    在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制,可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。

    目前MHA主要支持一主多从的架构,要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库,因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝TMHA已经支持一主一从。

    工作原理

    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线程)
    

    安装部署

    具体的搭建环境如下:

    角色                    ip地址          主机名          server_id                   类型
    Monitor host            192.168.0.20    server01            -                      监控复制组
    Master                  192.168.0.50    server02            1                      写入
    Candicate master        192.168.0.60    server03            2                      读
    Slave                   192.168.0.70    server04            3                      读
    

    创建用户:

    chattr -i /etc/group
    chattr -i /etc/gshadow
    chattr -i /etc/passwd
    chattr -i /etc/shadow
    
    useradd -m -d /home/mha mha
    passwd mha
    
    chattr +i /etc/passwd
    chattr +i /etc/shadow
    chattr +i /etc/group
    chattr +i /etc/gshadow
    

    修改权限:

    #visudo
    末尾添加:
    mha ALL=(ALL) NOPASSWD: ALL
    
    加入root组:
    ## Allow root to run any commands anywhere
    root    ALL=(ALL)       ALL
    mha     ALL=(ALL)       ALL
    

    创建、下发公钥,已MHA Server为例:

    ssh-keygen -t rsa
    ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.0.50
    ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.0.60
    ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.0.70
    

    修改mha用户环境变量,添加perl、mha、mysqlbinlog:

    #vim /home/mha/.bashrc
    export PTAH=$PATH:/usr/share/perl5:/opt/soft/mha/bin/:/opt/soft/mysql57/bin/
    export LC_ALL=C
    

    修改对应目录权限:

    chown -R mha.mha /opt/soft/mha/*
    chown -R mha.mha /usr/share/perl5/vendor_perl/MHA/*
    

    安装MHA:

    #mha server
    rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
    rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm
    
    #mha node
    rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
    

    rpm包是重新打包的,安装后环境如下:

    #mha server
    #tree /opt/soft/mha/
    /opt/soft/mha/
    |-- bin
    |   |-- apply_diff_relay_logs
    |   |-- filter_mysqlbinlog
    |   |-- masterha_check_repl
    |   |-- masterha_check_ssh
    |   |-- masterha_check_status
    |   |-- masterha_conf_host
    |   |-- masterha_manager
    |   |-- masterha_master_monitor
    |   |-- masterha_master_switch
    |   |-- masterha_secondary_check
    |   |-- masterha_stop
    |   |-- purge_relay_logs
    |   `-- save_binary_logs
    |-- binlog
    |-- conf
    |   |-- app1.cnf
    |   `-- masterha_default.cnf
    |-- logs
    |   `-- log
    `-- scripts
    
    #mha node
    #tree /opt/soft/mha/
    /opt/soft/mha/
    └── bin
        ├── apply_diff_relay_logs
        ├── filter_mysqlbinlog
        ├── purge_relay_logs
        └── save_binary_logs
    

    添加配置文件:

    #masterha_default.cnf
    [server default]
    user=mha
    password=mha
    ssh_user=mha
    
    repl_user=repl
    repl_password=repl
    
    manager_workdir=/opt/soft/mha
    manager_log=/opt/soft/mha/logs/log
    master_binlog_dir=/work/mysql6666/var/
    remote_workdir=/work/dba/
    secondary_check_script=/opt/soft/mha/bin/masterha_secondary_check -s 192.168.0.60 -s 192.168.0.70 --user=mha --master_ip=192.168.0.50 --master_port=6666
    
    # master_ip_failover_script=/opt/soft/mha/scripts/master_ip_failover
    # master_ip_online_change_script=/opt/soft/mha/scripts/master_ip_online_change
    # report_script=/opt/soft/mha/scripts/send_report
    # shutdown_script=/opt/soft/mha/scripts/power_manager
    
    ping_interval=1
    
    #app.conf
    [server1]
    hostname=192.168.0.50
    port=6666
    
    [server2]
    hostname=192.168.0.60
    port=6666
    candidate_master=1
    check_repl_delay=0
    
    [server3]
    hostname=192.168.0.70
    port=6666
    no_master
    

    检查SSH配置:

    /opt/soft/mha/bin/masterha_check_ssh --global_conf=/opt/soft/mha/conf/masterha_default.cnf --conf=/opt/soft/mha/conf/app.cnf
    

    检查复制环境:

    /opt/soft/mha/bin/masterha_check_status --global_conf=/opt/soft/mha/conf/masterha_default.cnf --conf=/opt/soft/mha/conf/app.cnf
    

    开启mha监控

    /opt/soft/mha/bin/masterha_manager --global_conf=/opt/soft/mha/conf/masterha_default.cnf --conf=/opt/soft/mha/conf/app.cnf --ignore_last_failover
    

    脚本含义

    Manager

    • masterha_check_ssh:检查mha的ssh配置情况;
    • masterha_check_repl:检查MySQL复制情况;
    • masterha_manager:启动mha;
    • masterha_check_status:检查当前mha运行状态;
    • masterha_master_monitor:检测master是否宕机;
    • masterha_check_switch:控制故障转移(自动或手动);
    • masteha_conf_host:添加或删除配置的server信息;

    Node

    • save_binary_logs:保存和复制master的二进制文件;
    • apply_diff_relay_logs:识别差异的relay log并将其差异event应用于其他slave;
    • filter_mysqlbinlog:去除不必要的rollback event;
    • purge_relay_logs:清除relay log;

    工作流程

    masterha_manager

    mha启动脚本为masterha_manager,可选参数为remove_dead_master_conf、manger_log、ignore_last_failover。
    masterha_manager主要流程为:

    1. 调用MasterMonitor,监控MySQL master状态;
    2. 发现master状态异常后,调用MasterFailover进行切换;

    具体细节如下:

    my ( $exit_code, $dead_master, $ssh_reachable ) =
      MHA::MasterMonitor::main( "--interactive=0", @ARGV );
    

    manager通过monitor监测master状态,一旦获得返回值,则表明monitor状态异常。通过判断exit_code确定是否应切换。

    if ( $exit_code && $exit_code != $MHA::ManagerConst::MASTER_DEAD_RC ) {
      exit $exit_code;
    }
    if ( !$dead_master->{hostname}
      || !$dead_master->{ip}
      || !$dead_master->{port}
      || !defined($ssh_reachable) )
    {
      exit 1;
    }
    

    检测通过后,调用MasterFailover进执行切换操作。

    $exit_code = MHA::MasterFailover::main(
      "--master_state=dead",
      "--interactive=0",
      "--dead_master_host=$dead_master->{hostname}",
      "--dead_master_ip=$dead_master->{ip}",
      "--dead_master_port=$dead_master->{port}",
      "--ssh_reachable=$ssh_reachable",
      @ARGV
    );
    
    MasterHA_Manager.png

    MasterMonitor

    MasterHA_Manager调用MasterMonitor的main方法对MySQL进行监控。流程图如下:


    MasterMonitor.png

    核心方法是一个死循环,不断调用wait_until_master_is_dead方法监测主库状态,部分代码如下:

    while (1) {
      my ( $exit_code, $dead_master, $ssh_reachable ) =
        wait_until_master_is_dead();
      my $msg = sprintf( "Got exit code %d (%s).",
        $exit_code,
        $exit_code == $MHA::ManagerConst::MASTER_DEAD_RC
        ? "Master dead"
        : "Not master dead" );
      $log->info($msg) if ($log);
      if ($g_check_only) {
        finalize();
        return $exit_code;
      }
      if ( $exit_code && $exit_code == $RETRY ) {
        prepare_for_retry();
      }
      else {
        if ( $exit_code && $exit_code != $MHA::ManagerConst::MASTER_DEAD_RC ) {
          finalize_on_error();
        }
        elsif ($g_monitor_only) {
          finalize();
        }
        return ( $exit_code, $dead_master, $ssh_reachable );
      }
    }
    

    wait_until_master_is_dead方法的返回值中,exit_code有的值有四种,分别是0、1、20、retry。其中只有当exit_code=MHA::ManagerConst::MASTER_DEAD_RC,也就是20时,后续才会调用failover方法。

    wait_until_master_is_dead方法中,核心方法是调用wait_until_master_is_unreachable方法并处理其返回值。逻辑关系如下:


    wait_until_master_is_dead.png

    拿到wait_until_master_is_unreachable的返回值后,会再次根据配置文件探活,确认主库连接失败后,根据配置文件检测slave状态和数量,有合适新主库后,exit_code返回20,否则返回0或者1;

    再次检测代码存活的代码如下:

    $_server_manager->connect_all_and_read_server_status(
      $dead_master->{hostname},
      $dead_master->{ip}, $dead_master->{port} );
    my @dead_servers  = $_server_manager->get_dead_servers();
    my @alive_servers = $_server_manager->get_alive_servers();
    $log->info("Dead Servers:");
    $_server_manager->print_dead_servers();
    $log->info("Alive Servers:");
    $_server_manager->print_alive_servers();
    $log->info("Alive Slaves:");
    $_server_manager->print_alive_slaves();
    $_server_manager->print_failed_slaves_if();
    $_server_manager->print_unmanaged_slaves_if();
    

    wait_until_master_is_unreachable方法的返回值有三个,分别是ret、dead_master和ssh_reachable。该方法的逻辑如下:


    wait_until_master_is_unreachable.png

    wait_until_master_is_unreachable调用MHA::ServerManager对主库进行实时检测,包括deadservers、aliveservers、aliveslaves等。

    $_server_manager = new MHA::ServerManager( servers => \@servers_config );
    $_server_manager->set_logger($log);$_server_manager->connect_all_and_read_server_status();
    @dead_servers  = $_server_manager->get_dead_servers();
    @alive_servers = $_server_manager->get_alive_servers();
    @alive_slaves  = $_server_manager->get_alive_slaves();
    $log->info("Dead Servers:");
    $_server_manager->print_dead_servers();
    $log->info("Alive Servers:");
    $_server_manager->print_alive_servers();
    $log->info("Alive Slaves:");
    $_server_manager->print_alive_slaves();
    $_server_manager->print_failed_slaves_if();
    $_server_manager->print_unmanaged_slaves_if();
    $current_master = $_server_manager->get_current_alive_master();
    

    如果启用GTID,则检查binlog server,否则进行ssh和slave版本检测。

        if ( !$_server_manager->is_gtid_auto_pos_enabled() ) {
          $log->info("GTID (with auto-pos) is not supported");
          MHA::SSHCheck::do_ssh_connection_check( \@alive_servers, $log,
            $servers_config[0]->{log_level}, $g_workdir )
            unless ($g_skip_ssh_check);
          $log->info("Checking MHA Node version..");
          foreach my $slave (@alive_slaves) {
            MHA::ManagerUtil::check_node_version(
              $log,             $slave->{ssh_user}, $slave->{ssh_host},
              $slave->{ssh_ip}, $slave->{ssh_port}
            );
          }
          $log->info(" Version check ok.");
        }
        else {
          $log->info(
    "GTID (with auto-pos) is supported. Skipping all SSH and Node package checking."
          );
          check_binlog_servers( $binlog_server_ref, $log );
        }
    

    后续使用MHA::HealthCheck对主库进行ping检查,如果定义了secondary_check_script,则运行该脚本。检查确认主库的确不可达后,返回func_rc, current_master, ssh_reachable。

    $master_ping = new MHA::HealthCheck(
      user                   => $current_master->{user},
      password               => $current_master->{password},
      ip                     => $current_master->{ip},
      hostname               => $current_master->{hostname},
      port                   => $current_master->{port},
      interval               => $current_master->{ping_interval},
      ssh_user               => $current_master->{ssh_user},
      ssh_host               => $current_master->{ssh_host},
      ssh_ip                 => $current_master->{ssh_ip},
      ssh_port               => $current_master->{ssh_port},
      ssh_connection_timeout => $current_master->{ssh_connection_timeout},
      ssh_check_command      => $ssh_check_command,
      status_handler         => $_status_handler,
      logger                 => $log,
      logfile                => $g_logfile,
      workdir                => $g_workdir,
      ping_type              => $current_master->{ping_type},
    );
    $log->info(
      sprintf( "Set master ping interval %d seconds.",
        $master_ping->get_ping_interval() )
    );
    if ( $current_master->{secondary_check_script} ) {
      $master_ping->set_secondary_check_script(
        $current_master->{secondary_check_script} );
      $log->info(
        sprintf( "Set secondary check script: %s",
          $master_ping->get_secondary_check_script() )
      );
    }
    else {
      $log->warning(
    "secondary_check_scriptis not defined. It is highly recommended setting it to check master reachability from two or more routes."
      );
    }
    

    添加vip检测功能

    config.pm负责mha的参数处理,如果想添加vip检测参数,则可在my @PARAM_ARRAY 后添加master_vip和master_vip_port。并在parse_server函数中添加解析:

    $value{master_vip} = $param_arg->{master_vip};
    $value{master_vip_port} = $param_arg->{master_vip_port};
    

    检查vip状态函数可放在MasterMonitor.pm中:

    sub check_vip_status {
      my @servers_config;
      my ( $sc_ref, $binlog_server_ref ) = new MHA::Config(
        logger     => $log,
        globalfile => $g_global_config_file,
        file       => $g_config_file
      )->read_config();
      @servers_config = @$sc_ref;
      my $master_vip = $servers_config[0]->{master_vip};
      my $master_vip_port = $servers_config[0]->{master_vip_port};
      my $user = $servers_config[0]->{user};
      my $password = $servers_config[0]->{password};
      my $logfile = $servers_config[0]->{manager_log};
      $log = MHA::ManagerUtil::init_log($logfile);
      if ( !defined($master_vip) || !defined($master_vip_port))
      {
        $log->info("no master_vip or master_vip_port in config file,skip VIP check.") if ($log);
        return 2;
      }
      eval {
        my $dbhelper = DBI->connect("DBI:mysql:;host=$master_vip;" . "port=$master_vip_port;",$user,$password,{ PrintError => 0, RaiseError => 1 });
        my $sth = $dbhelper->prepare("SELECT 1 As Value");
        $sth->execute();
        my $href = $sth->fetchrow_hashref;
      };
      if ($@) {
        $log->info($@) if ($log);
        $log->info("connect VIP error...") if ($log);
        return 0;
      }
      else {
        $log->info("check VIP config success.") if ($log);
        return 1;
      }
    }
    

    对主库进行状态检查时,可以先检测vip状态,若vip存活,则进行下一轮检测。该逻辑可放置在MasterMonitor的main函数中。

    while (1) {
        my $VIPStatus =  check_vip_status();
        if ($VIPStatus ne 0)
        {
            my ( $exit_code, $dead_master, $ssh_reachable ) = wait_until_master_is_dead();
            ......
        }
    }
    

    项目地址
    未完待续……

    哨兵实现多路探测
    ETCD构建MHA Cluster
    主库恢复后自动挂载
    中途失败完全回滚

    相关文章

      网友评论

        本文标题:MHA笔记

        本文链接:https://www.haomeiwen.com/subject/pvddoftx.html