2013年2月13日水曜日

masterha_master_switchについて(後編)

前回の「masterha_master_switchについて(前編) 」からの続きで、
masterha_master_switchコマンド実行時に--master_state=aliveを指定した場合のファイルオーバーについてになります。

--master_state=aliveは前回と違いmasterを停止しなくても
フェイルオーバーを実行できるため検証が楽チンです( ´∀`)bグッ!

■--master_state=aliveが指定された場合に利用されそうなオプションについて
※誤解などがあるかもしれないので詳細やその他オプションは公式のドキュメントを読んでください!
masterha_master_switchのドキュメント
  - conf
 設定ファイルの指定

  - master_state
    alive or deadが設定できる。
    deadはmasterが停止している場合に設定する。
    
    aliveはmasterが起動している場合にフェイルオーバーした場合に設定する
    masterが生きている必要あり。
    実行されるスクリプトはmaster_ip_failover_scriptではなくて、master_ip_online_change_scriptで指定したものになる。
    
    スケジュールされたマスタースイッチのためのプログラム·フローは、マスターのフェイルオーバーとは若干異なります。
    たとえば、マスターサーバーの電源をオフにする必要はありませんが、書き込みクエリがマスター上で実行されていないことを確認する必要があります。
    設定することによりmaster_ip_online_change_scriptを、新しいマスターにトラフィックを記述できるようにする方法のREADロックを持つFLUSH TABLESを実行する前に、
    現在のマスター(つまりREAD_ONLY = 1などを設定し、書き込み可能なユーザーの削除など)に書き込みトラフィックを禁止する方法を制御したり、することができます。
    オンラインマスタースイッチは、次のすべての条件が満たされた場合にのみ開始されます。

    ・すべてのslaveでI/O threads が実行されていること。
    ・すべてのslaveでSQL threads が実行されていること。
    ・running_updates_limitで指定された秒数より、全てのslave上のSeconds_Behind_Masterが小さいこと。
    ・master上で実行されている更新系のSQLの実行時間が、running_updates_limitで指定された秒数より小さいこと。

  - new_master_host =(ホスト名)
   新しいmasterを指定する場合。

  - orig_master_is_new_slave
   フェイルオーバー完了後に、slaveとして稼動させる。(デフォルトでは稼動させない設定)
   ※利用する場合には設定ファイルでrepl_user,repl_passwordの設定が必要となる。

  - running_updates_limit =(秒)
   現在masterで実行中のSQLで指定された秒数より時間がかかっている、
   または、slave側で同期の遅延が指定された秒数よりかかってしまっている場合に、
   手動フェイルオーバーを実行させないための秒数。(デフォルトは1秒)

  - remove_orig_master_conf
   フェイルオーバー完了時に、設定ファイル上から元masterサーバーの情報削除する。(デフォルトは削除しない)

  - skip_lock_all_tables
   フェイルオーバー時に、FLUSH TABLES WITHをスキップする。(デフォルトはスキップしない)
■注意点
masterha_master_switchコマンド実行時に、
--master_stateの値ごとに設定ファイルで設定した拡張用スクリプトで呼び出されるものが違うっぽい。

・--master_state=deadでは、master_ip_failover_scriptで定義したものが呼ばれる。
・--master_state=aliveでは、master_ip_online_change_scriptで定義したものが呼ばれる。

master_ip_online_change_scriptを設定しなくても動作しますが、
mha-managerで提供されていない、仮想IPの切り替えなどの追加処理が行えない。
※keepalivedやheartbeatなどを使って、仮想IPを切り替える手段を使えばこのスクリプトは要らないかも?

■master_ip_online_change_script用のファイルを作成する。
MySQL-MHAのインストール&設定で設定した
master_ip_failoverと同じで、MySQL-MHAは仮想IPの切り替えまで自動で行ってくれるわけではないので、
その部分は自身で実装する必要がある。

サンプルは公式サイトに置かれているのでそこからダウンロードしてきて、
処理の追加は部分はmaster_ip_failoverと殆ど変わらないと思います。
MySQLレプリケーションをMHAとHAProxyでフェイルオーバさせてみた

こちらからmaster_ip_online_changeをダウンロードしてくる。
※mha4mysql-manager / samples / scripts / master_ip_online_change

ファイルは/usr/bin直下に新規作成します。
vi /usr/bin/master_ip_online_change
#!/usr/bin/env perl

#  Copyright (C) 2011 DeNA Co.,Ltd.
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#  Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

## 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;
use MHA::NodeUtil;
use Time::HiRes qw( sleep gettimeofday tv_interval );
use Data::Dumper;

my $_tstart;
my $_running_interval = 0.1;
my (
  $command,          $orig_master_host, $orig_master_ip,
  $orig_master_port, $orig_master_user, $orig_master_password,
  $new_master_host,  $new_master_ip,    $new_master_port,
  $new_master_user,  $new_master_password,
  #変数の追加(mha.cnfの追加オプション用)
  $virtual_ip,       $orig_master_vip_eth, $new_master_vip_eth
);

#GetOptions 関数
#コマンドラインオプションを処理する
GetOptions(
  'command=s'              => \$command,
  '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,
  '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,
  #追加した変数への値代入
  #仮想IP
  'virtual_ip=s'       => \$virtual_ip,
  #masterの仮想IP用のネットワークインターフェイス名
  'orig_master_vip_eth=s'  => \$orig_master_vip_eth,
  #新masterの仮想IP用のネットワークインターフェイス名
  'new_master_vip_eth=s'   => \$new_master_vip_eth
);

exit &main();

sub current_time_us {
  my ( $sec, $microsec ) = gettimeofday();
  my $curdate = localtime($sec);
  return $curdate . " " . sprintf( "%06d", $microsec );
}

sub sleep_until {
  my $elapsed = tv_interval($_tstart);
  if ( $_running_interval > $elapsed ) {
    sleep( $_running_interval - $elapsed );
  }
}

sub get_threads_util {
  my $dbh                    = shift;
  my $my_connection_id       = shift;
  my $running_time_threshold = shift;
  my $type                   = shift;
  $running_time_threshold = 0 unless ($running_time_threshold);
  $type                   = 0 unless ($type);
  my @threads;

  my $sth = $dbh->prepare("SHOW PROCESSLIST");
  $sth->execute();

  while ( my $ref = $sth->fetchrow_hashref() ) {
    my $id         = $ref->{Id};
    my $user       = $ref->{User};
    my $host       = $ref->{Host};
    my $command    = $ref->{Command};
    my $state      = $ref->{State};
    my $query_time = $ref->{Time};
    my $info       = $ref->{Info};
    $info =~ s/^\s*(.*?)\s*$/$1/ if defined($info);
    next if ( $my_connection_id == $id );
    next if ( defined($query_time) && $query_time < $running_time_threshold );
    next if ( defined($command)    && $command eq "Binlog Dump" );
    next if ( defined($user)       && $user eq "system user" );
    next
      if ( defined($command)
      && $command eq "Sleep"
      && defined($query_time)
      && $query_time >= 1 );

    if ( $type >= 1 ) {
      next if ( defined($command) && $command eq "Sleep" );
      next if ( defined($command) && $command eq "Connect" );
    }

    if ( $type >= 2 ) {
      next if ( defined($info) && $info =~ m/^select/i );
      next if ( defined($info) && $info =~ m/^show/i );
    }

    push @threads, $ref;
  }
  return @threads;
}

sub main {

  # Print out parameters
  print(">>>>>>>>master_ip_online_change\n" );
  print("DEBUG PARAMETERS***********\n");
  if ( defined $command ) {print("command => $command\n")};
#  if ( defined $ssh_user ) {print("ssh_user=s => $ssh_user\n")};
  if ( defined $orig_master_host ) {print("orig_master_host => $orig_master_host\n")};
  if ( defined $orig_master_ip ) {print("orig_master_ip => $orig_master_ip\n")};
  if ( defined $orig_master_port ) {print("orig_master_port => $orig_master_port\n")};
  if ( defined $new_master_host ) {print("new_master_host => $new_master_host\n")};
  if ( defined $new_master_ip ) {print("new_master_ip => $new_master_ip\n")};
  if ( defined $new_master_port ) {print("new_master_port => $new_master_port\n")};
  if ( defined $virtual_ip ) {print("virtual_ip => $virtual_ip\n")};
  if ( defined $orig_master_vip_eth ) {print("orig_master_vip_eth => $orig_master_vip_eth\n")};
  if ( defined $new_master_vip_eth ) {print("new_master_vip_eth => $new_master_vip_eth\n")};

  #コマンドのオプションがstop or stopsshだったら
  if ( $command eq "stop" ) {
    ## Gracefully killing connections on the current master
    # 1. Set read_only= 1 on the new master
    # 2. DROP USER so that no app user can establish new connections
    # 3. Set read_only= 1 on the current master
    # 4. Kill current queries
    # * Any database access failure will result in script die.
    my $exit_code = 1;
    eval {

      ###################
      #新masterへ処理
      ###################

      ## Setting read_only=1 on the new master (to avoid accident)
      my $new_master_handler = new MHA::DBHelper();

      # args: hostname, port, user, password, raise_error(die_on_error)_or_not
      $new_master_handler->connect( $new_master_ip, $new_master_port,
        $new_master_user, $new_master_password, 1 );

      print current_time_us() . " Set read_only on the new master.. ";
      #read_only=1を設定する
      $new_master_handler->enable_read_only();
      #
      if ( $new_master_handler->is_read_only() ) {
        print "ok.\n";
      }
      else {
        die "Failed!\n";
      }
      #新masterのコネクションを切る
      $new_master_handler->disconnect();

      # updating global catalog, etc

      #障害の発生したマスタに付加されている仮想IPを剥奪、もし接続できない場合はタイムアウトまで待つ
     `ssh $orig_master_host -o "ConnectTimeout=5" '/sbin/ifconfig $orig_master_vip_eth down'`;

      ###################
      #元masterへの処理
      ###################

      # Connecting to the orig master, die if any database error happens
      my $orig_master_handler = new MHA::DBHelper();
      #元masterのコネクション取得
      $orig_master_handler->connect( $orig_master_ip, $orig_master_port,
        $orig_master_user, $orig_master_password, 1 );

      ## Drop application user so that nobody can connect. Disabling per-session binlog beforehand
      #バイナリーログ出力を無効化
      $orig_master_handler->disable_log_bin_local();

      #元masterのデータベースに対して何か行いたい場合にはここに記述する。
#      print current_time_us() . " Drpping app user on the orig master..\n";
#      FIXME_xxx_drop_app_user($orig_master_handler);

      ## Waiting for N * 100 milliseconds so that current connections can exit
      my $time_until_read_only = 15;
      $_tstart = [gettimeofday];
      my @threads = get_threads_util( $orig_master_handler->{dbh},
        $orig_master_handler->{connection_id} );
      while ( $time_until_read_only > 0 && $#threads >= 0 ) {
        if ( $time_until_read_only % 5 == 0 ) {
          printf
"%s Waiting all running %d threads are disconnected.. (max %d milliseconds)\n",
            current_time_us(), $#threads + 1, $time_until_read_only * 100;
          if ( $#threads < 5 ) {
            print Data::Dumper->new( [$_] )->Indent(0)->Terse(1)->Dump . "\n"
              foreach (@threads);
          }
        }
        sleep_until();
        $_tstart = [gettimeofday];
        $time_until_read_only--;
        @threads = get_threads_util( $orig_master_handler->{dbh},
          $orig_master_handler->{connection_id} );
      }

      ## Setting read_only=1 on the current master so that nobody(except SUPER) can write
      print current_time_us() . " Set read_only=1 on the orig master.. ";

      #read_only=1を設定する。
      $orig_master_handler->enable_read_only();
      if ( $orig_master_handler->is_read_only() ) {
        print "ok.\n";
      }
      else {
        die "Failed!\n";
      }

      ## Waiting for M * 100 milliseconds so that current update queries can complete
      my $time_until_kill_threads = 5;
      @threads = get_threads_util( $orig_master_handler->{dbh},
        $orig_master_handler->{connection_id} );
      while ( $time_until_kill_threads > 0 && $#threads >= 0 ) {
        if ( $time_until_kill_threads % 5 == 0 ) {
          printf
"%s Waiting all running %d queries are disconnected.. (max %d milliseconds)\n",
            current_time_us(), $#threads + 1, $time_until_kill_threads * 100;
          if ( $#threads < 5 ) {
            print Data::Dumper->new( [$_] )->Indent(0)->Terse(1)->Dump . "\n"
              foreach (@threads);
          }
        }
        sleep_until();
        $_tstart = [gettimeofday];
        $time_until_kill_threads--;
        @threads = get_threads_util( $orig_master_handler->{dbh},
          $orig_master_handler->{connection_id} );
      }

      ## Terminating all threads
      print current_time_us() . " Killing all application threads..\n";

      #
      $orig_master_handler->kill_threads(@threads) if ( $#threads >= 0 );
      print current_time_us() . " done.\n";

      #バイナリーログ出力を有効化
      $orig_master_handler->enable_log_bin_local();

      #コネクションを切る
      $orig_master_handler->disconnect();

      ## After finishing the script, MHA executes FLUSH TABLES WITH READ LOCK
      $exit_code = 0;
    };
    if ($@) {
      warn "Got Error: $@\n";
      exit $exit_code;
    }
    exit $exit_code;
  }
  elsif ( $command eq "start" ) {
    ## Activating master ip on the new master
    # 1. Create app user with write privileges
    # 2. Moving backup script if needed
    # 3. Register new master's ip to the catalog database

    # We don't return error even though activating updatable accounts/ip failed so that we don't interrupt slaves' recovery.
    # If exit code is 0 or 10, MHA does not abort
    my $exit_code = 10;
    eval {

      #新masterへ処理
      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 current_time_us() . " Set read_only=0 on the new master.\n";

      #read_only=0を設定する。
      $new_master_handler->disable_read_only();

      ## Creating an app user on the new master
      print current_time_us() . " Creating app user on the new master..\n";
#      FIXME_xxx_create_app_user($new_master_handler);

      #バイナリーログ出力を有効化
      $new_master_handler->enable_log_bin_local();

      #コネクションを切る
      $new_master_handler->disconnect();

      ## Update master ip on the catalog database, etc
      #ここから拡張したい処理を記述する

      #仮想IPを割り振るNICの情報を取得
      my $real_eth=$new_master_vip_eth;
      $real_eth=~ s/:.*//g;
      my $new_master_network= `ssh $new_master_host '/sbin/ifconfig $real_eth'`;
      #ネットマスクの取得
      my $vip_netmask = join(".", $new_master_network =~ /Mask\:(\d+)\.(\d+)\.(\d+)\.(\d+)/ );
      #ブロードキャストアドレスの取得
      my $vip_broadcast = join(".", $new_master_network =~ /Bcast\:(\d+)\.(\d+)\.(\d+)\.(\d+)/ );
      #フェイルオーバ先に仮想IPを割り振る、そのとき仮想IPの切り替わりのためpingを発行する
      `ssh $new_master_host -o "ConnectTimeout=15" '/sbin/ifconfig $new_master_vip_eth $virtual_ip netmask $vip_netmask up; ping -I $virtual_ip -b $vip_broadcast -c 3 -c 3 -i 0.01 -q -W 10 > /dev/null'`;

      $exit_code = 0;
    };
    if ($@) {
      warn "Got Error: $@\n";
      exit $exit_code;
    }
    exit $exit_code;
  }
  elsif ( $command eq "status" ) {

    # do nothing
    exit 0;
  }
  else {
    &usage();
    exit 1;
  }
}

sub usage {
  print
"Usage: master_ip_online_change --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 --virtual_ip=ip --orig_master_vip_eth=eth --new_master_vip_eth=eth\n";
#"Usage: master_ip_online_change --command=start|stop|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";
  die;
}
作成が終わったら、このスクリプトを設定ファイルに設定を行います。
vi /etc/mha.conf

#master_ip_online_change_scriptを[server default]セクションに追加します。
master_ip_online_change_script=/usr/bin/master_ip_online_change --virtual_ip=192.168.10.234 --orig_master_vip_eth=eth0:234 --new_master_vip_eth=eth0:234
実行前に、現在のサーバーの状態の確認します
・mha-managerサーバー
 IP:192.168.10.227)
・masterサーバー
  IP:192.168.10.228
  VIP:192.168.10.234
・salve1
  IP:192.168.10.229
・salve2
  IP:192.168.10.233

それでは、実際に実行して試してみますド━━━━m9(゚∀゚)━━━━ン!!

次のコマンドでフェイルオーバーを行いたいとおもいます。
各オプションの意味は次のようになります
--master_state=aliveは、masterが生きているという意味になります。
--orig_master_is_new_slaveは、masterをフェイルオーバー後に
slaveとして稼動させるという意味になります。
フェイルオーバー時の新masterを選ぶ際の選択はmha-managerにお任せする。(指定無しのため)
# masterha_master_switch --master_state=alive --conf=/etc/mha.cnf --orig_master_is_new_slave
Tue Feb 12 10:45:08 2013 - [info] MHA::MasterRotate version 0.55.
Tue Feb 12 10:45:08 2013 - [info] Starting online master switch..
Tue Feb 12 10:45:08 2013 - [info]
Tue Feb 12 10:45:08 2013 - [info] * Phase 1: Configuration Check Phase..
Tue Feb 12 10:45:08 2013 - [info]
Tue Feb 12 10:45:08 2013 - [info] Reading default configuratoins from /etc/masterha_default.cnf..
Tue Feb 12 10:45:08 2013 - [info] Reading application default configurations from /etc/mha.cnf..
Tue Feb 12 10:45:08 2013 - [info] Reading server configurations from /etc/mha.cnf..
Tue Feb 12 10:45:08 2013 - [info] Current Alive Master: 192.168.10.228(192.168.10.228:3306)
Tue Feb 12 10:45:08 2013 - [info] Alive Slaves:
Tue Feb 12 10:45:08 2013 - [info]   192.168.10.229(192.168.10.229:3306)  Version=5.5.29-log (oldest major version between slaves) log-bin:enabled
Tue Feb 12 10:45:08 2013 - [info]     Replicating from 192.168.10.228(192.168.10.228:3306)
Tue Feb 12 10:45:08 2013 - [info]   192.168.10.233(192.168.10.233:3306)  Version=5.5.29-log (oldest major version between slaves) log-bin:enabled
Tue Feb 12 10:45:08 2013 - [info]     Replicating from 192.168.10.228(192.168.10.228:3306)

It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.10.228(192.168.10.228:3306)? (YES/no): y
Tue Feb 12 10:45:09 2013 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
Tue Feb 12 10:45:09 2013 - [info]  ok.
Tue Feb 12 10:45:09 2013 - [info] Checking MHA is not monitoring or doing failover..
Tue Feb 12 10:45:09 2013 - [info] Checking replication health on 192.168.10.229..
Tue Feb 12 10:45:09 2013 - [info]  ok.
Tue Feb 12 10:45:09 2013 - [info] Checking replication health on 192.168.10.233..
Tue Feb 12 10:45:09 2013 - [info]  ok.
Tue Feb 12 10:45:09 2013 - [info] 192.168.10.233 can be new master.
Tue Feb 12 10:45:09 2013 - [info]
From:
192.168.10.228 (current master)
 +--192.168.10.229
 +--192.168.10.233

To:
192.168.10.233 (new master)
 +--192.168.10.229
 +--192.168.10.228

Starting master switch from 192.168.10.228(192.168.10.228:3306) to 192.168.10.233(192.168.10.233:3306)? (yes/NO): y
Tue Feb 12 10:45:10 2013 - [info] Checking whether 192.168.10.233(192.168.10.233:3306) is ok for the new master..
Tue Feb 12 10:45:10 2013 - [info]  ok.
Tue Feb 12 10:45:10 2013 - [info] 192.168.10.228(192.168.10.228:3306): SHOW SLAVE STATUS returned empty result. To check replication filtering rules, temporarily executing CHANGE MASTER to a dummy host.
Tue Feb 12 10:45:10 2013 - [info] 192.168.10.228(192.168.10.228:3306): Resetting slave pointing to the dummy host.
Tue Feb 12 10:45:10 2013 - [info] ** Phase 1: Configuration Check Phase completed.
Tue Feb 12 10:45:10 2013 - [info]
Tue Feb 12 10:45:10 2013 - [info] * Phase 2: Rejecting updates Phase..
Tue Feb 12 10:45:10 2013 - [info]
Tue Feb 12 10:45:10 2013 - [info] Executing master ip online change script to disable write on the current master:
Tue Feb 12 10:45:10 2013 - [info]   /usr/bin/master_ip_online_change --virtual_ip=192.168.10.234 --orig_master_vip_eth=eth0:234 --new_master_vip_eth=eth0:234 --command=stop --orig_master_host=192.168.10.228 --orig_master_ip=192.168.10.228 --orig_master_port=3306 --orig_master_user='repl' --orig_master_password='repl' --new_master_host=192.168.10.233 --new_master_ip=192.168.10.233 --new_master_port=3306 --new_master_user='repl' --new_master_password='repl'
>>>>>>>>master_ip_online_change
DEBUG PARAMETERS***********
command => stop
orig_master_host => 192.168.10.228
orig_master_ip => 192.168.10.228
orig_master_port => 3306
new_master_host => 192.168.10.233
new_master_ip => 192.168.10.233
new_master_port => 3306
virtual_ip => 192.168.10.234
orig_master_vip_eth => eth0:234
new_master_vip_eth => eth0:234
Tue Feb 12 10:45:10 2013 240276 Set read_only on the new master.. ok.
Tue Feb 12 10:45:10 2013 791322 Set read_only=1 on the orig master.. ok.
Tue Feb 12 10:45:10 2013 794276 Killing all application threads..
Tue Feb 12 10:45:10 2013 794484 done.
Tue Feb 12 10:45:10 2013 - [info]  ok.
Tue Feb 12 10:45:10 2013 - [info] Locking all tables on the orig master to reject updates from everybody (including root):
Tue Feb 12 10:45:10 2013 - [info] Executing FLUSH TABLES WITH READ LOCK..
Tue Feb 12 10:45:10 2013 - [info]  ok.
Tue Feb 12 10:45:10 2013 - [info] Orig master binlog:pos is mysql-bin.000011:3185.
Tue Feb 12 10:45:10 2013 - [info]  Waiting to execute all relay logs on 192.168.10.233(192.168.10.233:3306)..
Tue Feb 12 10:45:10 2013 - [info]  master_pos_wait(mysql-bin.000011:3185) completed on 192.168.10.233(192.168.10.233:3306). Executed 0 events.
Tue Feb 12 10:45:10 2013 - [info]   done.
Tue Feb 12 10:45:10 2013 - [info] Getting new master's binlog name and position..
Tue Feb 12 10:45:10 2013 - [info]  mysql-bin.000011:929
Tue Feb 12 10:45:10 2013 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='192.168.10.233', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000011', MASTER_LOG_POS=929, MASTER_USER='repl', MASTER_PASSWORD='xxx';
Tue Feb 12 10:45:10 2013 - [info] Executing master ip online change script to allow write on the new master:
Tue Feb 12 10:45:10 2013 - [info]   /usr/bin/master_ip_online_change --virtual_ip=192.168.10.234 --orig_master_vip_eth=eth0:234 --new_master_vip_eth=eth0:234 --command=start --orig_master_host=192.168.10.228 --orig_master_ip=192.168.10.228 --orig_master_port=3306 --orig_master_user='repl' --orig_master_password='repl' --new_master_host=192.168.10.233 --new_master_ip=192.168.10.233 --new_master_port=3306 --new_master_user='repl' --new_master_password='repl'
>>>>>>>>master_ip_online_change
DEBUG PARAMETERS***********
command => start
orig_master_host => 192.168.10.228
orig_master_ip => 192.168.10.228
orig_master_port => 3306
new_master_host => 192.168.10.233
new_master_ip => 192.168.10.233
new_master_port => 3306
virtual_ip => 192.168.10.234
orig_master_vip_eth => eth0:234
new_master_vip_eth => eth0:234
Tue Feb 12 10:45:10 2013 964072 Set read_only=0 on the new master.
Tue Feb 12 10:45:10 2013 965733 Creating app user on the new master..
Tue Feb 12 10:45:12 2013 - [info]  ok.
Tue Feb 12 10:45:12 2013 - [info]
Tue Feb 12 10:45:12 2013 - [info] * Switching slaves in parallel..
Tue Feb 12 10:45:12 2013 - [info]
Tue Feb 12 10:45:12 2013 - [info] -- Slave switch on host 192.168.10.229(192.168.10.229:3306) started, pid: 14031
Tue Feb 12 10:45:12 2013 - [info]
CHANGE MASTER TO MASTER_HOST='192.168.10.233', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='repl', MASTER_LOG_FILE='mysql-bin.000011', MASTER_LOG_POS=929Tue Feb 12 10:45:12 2013 - [info] Log messages from 192.168.10.229 ...
Tue Feb 12 10:45:12 2013 - [info]
Tue Feb 12 10:45:12 2013 - [info]  Waiting to execute all relay logs on 192.168.10.229(192.168.10.229:3306)..
Tue Feb 12 10:45:12 2013 - [info]  master_pos_wait(mysql-bin.000011:3185) completed on 192.168.10.229(192.168.10.229:3306). Executed 0 events.
Tue Feb 12 10:45:12 2013 - [info]   done.
Tue Feb 12 10:45:12 2013 - [info]  Resetting slave 192.168.10.229(192.168.10.229:3306) and starting replication from the new master 192.168.10.233(192.168.10.233:3306)..
Tue Feb 12 10:45:12 2013 - [info]  Executed CHANGE MASTER.
Tue Feb 12 10:45:12 2013 - [info]  Slave started.
Tue Feb 12 10:45:12 2013 - [info] End of log messages from 192.168.10.229 ...
Tue Feb 12 10:45:12 2013 - [info]
Tue Feb 12 10:45:12 2013 - [info] -- Slave switch on host 192.168.10.229(192.168.10.229:3306) succeeded.
Tue Feb 12 10:45:12 2013 - [info] Unlocking all tables on the orig master:
Tue Feb 12 10:45:12 2013 - [info] Executing UNLOCK TABLES..
Tue Feb 12 10:45:12 2013 - [info]  ok.
Tue Feb 12 10:45:12 2013 - [info] Starting orig master as a new slave..
Tue Feb 12 10:45:12 2013 - [info]  Resetting slave 192.168.10.228(192.168.10.228:3306) and starting replication from the new master 192.168.10.233(192.168.10.233:3306)..
CHANGE MASTER TO MASTER_HOST='192.168.10.233', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='repl', MASTER_LOG_FILE='mysql-bin.000011', MASTER_LOG_POS=929Tue Feb 12 10:45:12 2013 - [info]  Executed CHANGE MASTER.
Tue Feb 12 10:45:12 2013 - [info]  Slave started.
Tue Feb 12 10:45:12 2013 - [info] All new slave servers switched successfully.
Tue Feb 12 10:45:12 2013 - [info]
Tue Feb 12 10:45:12 2013 - [info] * Phase 5: New master cleanup phase..
Tue Feb 12 10:45:12 2013 - [info]
Tue Feb 12 10:45:12 2013 - [info]  192.168.10.233: Resetting slave info succeeded.
Tue Feb 12 10:45:12 2013 - [info] Switching master to 192.168.10.233(192.168.10.233:3306) completed successfully.
フェイルオーバーキタ――(゚∀゚)――!!

って、感じにあっさりフェイルオーバーされてしまいます。
10:42:26に開始されて、10:42:41に終了とは・・・かかった時間は15秒Σ(´∀`;)

一応、正常にフェイルオーバーされているか確認する
まず元master(192.168.10.228)がslaveになっているか確認する。
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.10.233
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 10
              Master_Log_File: mysql-bin.000011
          Read_Master_Log_Pos: 107
               Relay_Log_File: mysqld-relay-bin.000002
                Relay_Log_Pos: 253
        Relay_Master_Log_File: mysql-bin.000011
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 107
              Relay_Log_Space: 410
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 30
1 row in set (0.00 sec)
次に元master(192.168.10.228)の仮想IPが外されているか確認する(eth0:234が無いこと)
# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:16:3E:47:5E:B8
          inet addr:192.168.10.228  Bcast:192.168.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2089310 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1374365 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:706218242 (673.5 MiB)  TX bytes:4231639033 (3.9 GiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:252 errors:0 dropped:0 overruns:0 frame:0
          TX packets:252 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:39267 (38.3 KiB)  TX bytes:39267 (38.3 KiB)
次は、slave1(192.168.10.229)が新master(192.168.10.233)と同期されているか確認する
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.10.233
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 10
              Master_Log_File: mysql-bin.000011
          Read_Master_Log_Pos: 107
               Relay_Log_File: mysql-relay-bin.000002
                Relay_Log_Pos: 253
        Relay_Master_Log_File: mysql-bin.000011
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 107
              Relay_Log_Space: 409
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 30
1 row in set (0.00 sec)
次は、slave2(192.168.10.233)が新masterになっているか確認する
mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000011 |      107 |              |                  |
+------------------+----------+--------------+------------------+
masterになった場合にはslave statusが空になる。
mysql> show slave status\G
Empty set (0.00 sec)

認識しているslave一覧を取得する
mysql> show slave hosts;
+-----------+---------+------+-----------+
| Server_id | Host    | Port | Master_id |
+-----------+---------+------+-----------+
|        20 | slave20 | 3306 |        30 |
|        10 | slave10 | 3306 |        30 |
+-----------+---------+------+-----------+
2 rows in set (0.00 sec)
read_onlyが外されているか確認する
※my.cnfは修正されていないので再起動後に設定が戻らないように手動で編集してあげる必要がある。
元もslaveにread_only=1が設定されていない場合にはそのまま。
mysql> SELECT @@read_only;
+-------------+
| @@read_only |
+-------------+
|           0 |
+-------------+
1 row in set (0.00 sec)
新master(192.168.10.233)に仮想IPが設定されているかネットワークインターフェイスを確認する。
# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:16:3E:48:5F:B8
          inet addr:192.168.10.233  Bcast:192.168.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:181059 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8707 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:55296611 (52.7 MiB)  TX bytes:1163893 (1.1 MiB)

eth0:234  Link encap:Ethernet  HWaddr 00:16:3E:48:5F:B8
          inet addr:192.168.10.234  Bcast:192.168.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:214 errors:0 dropped:0 overruns:0 frame:0
          TX packets:214 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:22924 (22.3 KiB)  TX bytes:22924 (22.3 KiB)

lo:0      Link encap:Local Loopback
          inet addr:192.168.10.223  Mask:255.255.255.255
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
設定上は3つとも問題無しド━━━━m9(゚∀゚)━━━━ン!!

仮想IPで接続できるか確認してみる。
# mysql -u repl -h 192.168.10.234 -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 312
Server version: 5.5.29-log MySQL Community Server (GPL) by Remi

Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show slave hosts;
+-----------+---------+------+-----------+
| Server_id | Host    | Port | Master_id |
+-----------+---------+------+-----------+
|        20 | slave20 | 3306 |        30 |
|        10 |         | 3306 |        30 |
+-----------+---------+------+-----------+
2 rows in set (0.00 sec)
問題無し(゚д゚)(。_。)(゚д゚)(。_。) ウンウン

実際に新master(192.168.10.233)にデータを入れて、レプリケーションされるか確認すること。
mysql> use test;
Database changed

mysql> show tables;
Empty set (0.00 sec)

mysql> CREATE TABLE IF NOT EXISTS `dummy` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  `insert_date` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

mysql> INSERT INTO dummy( id, name, insert_date ) VALUES ( NULL, 1, now() );
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM dummy;
+----+------+---------------------+
| id | name | insert_date         |
+----+------+---------------------+
|  1 | aaa  | 2013-02-09 11:24:52 |
+----+------+---------------------+
1 row in set (0.00 sec)
slave(192.168.10.229),slave(192.168.10.228)で、データの同期が取られているか確認する。
mysql> use test;
Database changed

mysql> select * from dummy;
+----+------+---------------------+
| id | name | insert_date         |
+----+------+---------------------+
|  1 | 1    | 2013-02-13 06:36:05 |
+----+------+---------------------+
1 row in set (0.00 sec)

(+・`ー'・)ドヤ

masterha_master_switchコマンドは、
--interactive=0をつけずに実行すると、デフォルトの対話式で実行されます。
その際に次の点の確認を聞かれます。

■1)It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.10.229(192.168.10.229:3306)? (YES/no):

適当な訳:NO_WRITE_TO_BINLOGを実行して、バイナリーログを出力させない設定にしますか?
これは、フェイルオーバー中の処理などで実行したSQLなどが
バイナリーログに出力されてしまい、そのSQLをフェイルオーバーされた後のslave側などで実行されてしまうのを防ぐ?

■2)Starting master switch from 192.168.10.223(192.168.10.223:3306) to 192.168.10.229(192.168.10.229:3306)? (yes/NO):

適当な訳:masterを192.168.10.233から192.168.10.229に変更して(・∀・)イイ!!ですか?

■3)master_ip_online_change_script is not defined. If you do not disable writes on the current master manually, applications keep writing on the current master. Is it ok to proceed? (yes/NO):

適当な訳:master_ip_online_change_scriptが定義されていないので、
このまま続けるとアプリケーション側からmasterに書き込み処理を実行してしまう可能性がありますが続けますか?((((;゚Д゚))))ガクガクブルブル

これは、設定ファイルでmaster_ip_online_change_scriptが定義されていないせいで発生するようです。
ただしく設定してあげることで、masterの書き込みなどを正常にブロックしてフェイルオーバーを行ってくれるそうです。

もし問題が無ければ全て「yes」と入力して、フェイルオーバーを実行する。
「no」を入力すると処理が中断されます。

今回はmasterになるslaveの選択を、mha-managerの自動選択で行いましたが、
コマンドにオプションで指定することも可能です。

また、対話式が(゚⊿゚)イラネって方は--interactive=0をつけることで、
一切確認されずにフェイルオーバーが実行されます。

■フェイルオーバー先のmasterを指定するコマンド
たとえば、次のようなコマンドの場合には、
--new_master_hostで、masterになるslaveを指定するという意味になり、
さらに、--interactive=0で、非対話式で実行するという意味になります。
masterha_master_switch --master_state=alive --conf=/etc/mha.cnf --new_master_host=192.168.10.233 --orig_master_is_new_slave --interactive=0
上記のオプションの説明にも書きましたが、
--master_state=aliveの設定時にフェイルオーバーが行える条件として、
いくつか注意点があるみたいです。

・すべてのslaveでI/O threads が実行されていること。
・すべてのslaveでSQL threads が実行されていること。
・running_updates_limitで指定された秒数より、全てのslave上のSeconds_Behind_Masterが小さいこと。
・master上で実行されている更新系のSQLの実行時間が、running_updates_limitで指定された秒数より小さいこと。

上記の点のどれかが満たされていない場合には恐らくエラーが発生しフェイルオーバーが行われないと思います。φ(`д´)メモメモ...

たとえば、slaveの1つをstop slave;で停止した状態で、
masterha_master_switchで、--master_state=aliveのオプションを指定して実行しようとすると、
次のようなエラーが発生し、処理が中断されました。

# masterha_master_switch --master_state=alive --conf=/etc/mha.cnf --orig_master_is_new_slave
Tue Feb 12 09:00:20 2013 - [info] MHA::MasterRotate version 0.55.
Tue Feb 12 09:00:20 2013 - [info] Starting online master switch..
Tue Feb 12 09:00:20 2013 - [info]
Tue Feb 12 09:00:20 2013 - [info] * Phase 1: Configuration Check Phase..
Tue Feb 12 09:00:20 2013 - [info]
Tue Feb 12 09:00:20 2013 - [info] Reading default configuratoins from /etc/masterha_default.cnf..
Tue Feb 12 09:00:20 2013 - [info] Reading application default configurations from /etc/mha.cnf..
Tue Feb 12 09:00:20 2013 - [info] Reading server configurations from /etc/mha.cnf..
Tue Feb 12 09:00:21 2013 - [warning] SQL Thread is stopped(no error) on 192.168.10.229(192.168.10.229:3306)
Tue Feb 12 09:00:21 2013 - [info] Current Alive Master: 192.168.10.228(192.168.10.228:3306)
Tue Feb 12 09:00:21 2013 - [info] Alive Slaves:
Tue Feb 12 09:00:21 2013 - [info]   192.168.10.229(192.168.10.229:3306)  Version=5.5.29-log (oldest major version between slaves) log-bin:enabled
Tue Feb 12 09:00:21 2013 - [info]     Replicating from 192.168.10.228(192.168.10.228:3306)
Tue Feb 12 09:00:21 2013 - [info]   192.168.10.233(192.168.10.233:3306)  Version=5.5.29-log (oldest major version between slaves) log-bin:enabled
Tue Feb 12 09:00:21 2013 - [info]     Replicating from 192.168.10.228(192.168.10.228:3306)

It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.10.228(192.168.10.228:3306)? (YES/no): y
Tue Feb 12 09:00:23 2013 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
Tue Feb 12 09:00:23 2013 - [info]  ok.
Tue Feb 12 09:00:23 2013 - [info] Checking MHA is not monitoring or doing failover..
Tue Feb 12 09:00:23 2013 - [info] Checking replication health on 192.168.10.229..
Tue Feb 12 09:00:23 2013 - [error][/usr/lib/perl5/vendor_perl/MHA/Server.pm, ln472] Slave IO thread is not running on 192.168.10.229(192.168.10.229:3306)
既にmasterなのに新masterにも同じHOSTを指定した場合には、
次のエラーが発生して処理が中断していました。
Tue Feb 12 09:12:55 2013 - [info] Locking all tables on the orig master to reject updates from everybody (including root):
Tue Feb 12 09:12:55 2013 - [info] Executing FLUSH TABLES WITH READ LOCK..
Tue Feb 12 09:12:55 2013 - [info]  ok.
Tue Feb 12 09:12:55 2013 - [info] Orig master binlog:pos is mysql-bin.000011:3185.
Tue Feb 12 09:12:55 2013 - [info]  Waiting to execute all relay logs on 192.168.10.228(192.168.10.228:3306)..
Tue Feb 12 09:12:55 2013 - [error][/usr/lib/perl5/vendor_perl/MHA/Server.pm, ln924] master_pos_wait(mysql-bin.000011:3185) returned NULL on 192.168.10.228(192.168.10.228:3306). Maybe SQL thread was aborted?
Tue Feb 12 09:12:55 2013 - [error][/usr/lib/perl5/vendor_perl/MHA/MasterRotate.pm, ln389] Failed to get new master's binlog and/or position!
Tue Feb 12 09:12:55 2013 - [error][/usr/lib/perl5/vendor_perl/MHA/ManagerUtil.pm, ln178] Got ERROR:  at /usr/bin/masterha_master_switch line 53
■作業中につまづいた箇所

フェイルオーバーを行った際に、masterからslaveに変わろうとするサーバーで必ず毎回発生していて、
slaveからslaveの場合に、CHANGE MASTER TOでmasterの情報を変更するだけのサーバーでは発生しない現象がありました。

エラーが発生しているサーバーのshow slave status\Gで見ると次のようなエラーになっていました。
                Last_IO_Errno: 1045
                Last_IO_Error: error connecting to master 'repl@192.168.10.233:3306' - retry-time: 60  retries: 86400
※このエラーが出ている状態で、stop slave、start slaveをやってもダメでした。

仕方ないのでソースコードを見てみたら、
CHANGE MASTER TOのコマンド実行時に、次のように実行されていました。
CHANGE MASTER TO 
 MASTER_HOST='192.168.10.229', 
 MASTER_PORT=3306, 
 MASTER_USER='repl', 
 MASTER_LOG_FILE='mysql-bin.000009', 
 MASTER_LOG_POS=107
あれ・・・MASTER_PASSWORDが設定されていない!!!(´・ω`・)エッ?

何でMASTER_PASSWORDが設定されていないのか、
公式のドキュメントを見てみると、
repl_user、repl_passwordという設定項目をハヶ━m9( ゚д゚)っ━ン!!

repl_user、repl_passwordが設定されていない場合には、現在のパスワードと同じのを使います( ノ゚Д゚)ヨッ!
ただし、元々masterだった場合には、slaveの情報がリセットされているので、
パスワードが無い状態になっているので、MASTER_PASSWORD付きのCHANGE MASTER TOコマンドじゃないと正しく動作しない・・・orz

あれほど、ドキュメントは読めと教わったのに・・・(´д`)ママン…

恐らくこれを設定すれば、ちゃんとMASTER_PASSWORDが設定されるに違いない!!

って、ことで、/etc/mha.cnfを次のように編集して実行してみたところ、
思惑通りに設定したパスワードが利用されて、それ以降はこの現象が発生することはありませんでした!!ъ(゚Д゚)グッジョブ!!
repl_user=repl
repl_password=repl
slaveからslaveのCHANGE MASTER TOコマンドの場合には、
MASTER_PASSWORDが設定されていなくても問題ない(元々replユーザーに設定されているパスワードが使われる?)けど、
masterからslaveに変わる場合のCHANGE MASTER TOコマンドの場合には、
ちゃんとMASTER_PASSWORDを設定しないと、正しくslaveのI/Oスレッドがmasterに接続できなくなってしまう!!ド━━━━m9(゚∀゚)━━━━ン!!

ってことですかね。
公式ドキュメントにもそんなような事が書いてあるっぽい感じでした。
--orig_master_is_new_slaveオプションを使う場合には、ちゃんと設定してね!(はーと

以上(`・ω・´)ゞビシッ!!

今までの「masterha_master_switchについて(前編) 」「masterha_master_switchについて(後編)」
実際に行われるフェイルオーバーの流れが何となく理解できたので、
次回はmha-managerをバックグラウンドなどで動かして、
実際にmasterが急に落ちた場合などの挙動を確認したいと思います(ΦωΦ)フフフ…

参考URL

0 件のコメント:

コメントを投稿