Top > MySQL5.6のGTIDとheartbeatを活用して双方向レプリケーションなHAクラスタを構築する

キーワード: MySQL クラスタ GTID レプリケーション HA heartbeat マルチマスタ 双方向レプリケーション


概要図

      -----------------                            -----------------
      | dbm01         | ----replication----------> | dbm02         |
      |               |                            |               |
      | 192.168.23.61 | ---[vip: 192.168.23.60]--- | 192.168.23.62 |
      |               |                            |               |
      |               | <----------replication---- |               |
      -----------------                            -----------------
              |                                             |
 >------------------------------------------------------------------------< 192.168.23.0/24
             |                     |                   |   
      ------------------   ------------------   ------------------
      | dbs01          |   | dbs02          |   | dbs03          |
      |  192.168.23.71 |   |  192.168.23.72 |   |  192.168.23.73 |
      | slave of       |   | slave of       |   | slave of       |
      |  192.168.23.60 |   |  192.168.23.60 |   |  192.168.23.60 |
      ------------------   ------------------   ------------------

構成

  • mysql5.6のGTIDをつかう
  • dbm01, dbm02は相互に双方向 レプリケーション設定する.
  • dbm01, dbm02のどちらにWriteしても全てのスレーブにデータがレプリカされる, マルチマスタを構成する.
    • ただし, auto_incrementやトランザクションが2台に分散すると衝突が発生する.
    • heartbeat (またはVRRP) によるVIPの付け替えで ACTIVE/BACKUP構成 を取り, リクエストは片方に寄せる.
  • dbm01, dbm02は heartbeat (またはVRRP)で vip:192.168.23.60 を共有する
  • dbsXXのスレーブはロードバランサでReadリクエストの負荷を分散する
  • dbsXXのスレーブは vip:192.168.23.60 をマスターとしてレプリケーション設定する
  • アプリケーションからのwriteは, vip:192.168.23.60 に対しておこなう
  • アプリケーションからのreadは, ロードバランサ経由でdbsXX に対しておこなう
  • GTIDのお陰でマスタが切り替わってもスレーブはレプリを継続してくれる

最小構成

マスタ2台Write, Read, Backup
スレーブ0台
バックアップ0台
  • 書込・参照ともにマスタのVIPをつかう.
  • どちらかに障害が発生して再起不能に陥ってもサービスを継続できる.
  • 障害から復帰させるためにはサービスを停止する必要がある.
  • 非アクティブ側にて定時バックアップした方が良い.

推奨構成

マスタ2台Write
スレーブ2台以上Read
バックアップスレーブ1台 or 2台Backup
  • 書込はマスタのVIPを, 参照はスレーブのVIPをそれぞれつかう.
  • どれか1台に障害が発生して再起不能に陥ってもサービスを継続できる.
  • 障害から復帰させるためにサービス停止を伴わない.

必要なもの

  • CentOS 6.x or ScientificLinux 6.x
  • MySQL-server-5.6.x (mysql.com, MySQL Community Server 5.6.x)
  • heartbeat (epel)
  • check_mysql (epel, nagios-plugins-mysql)

フェイルオーバー設計

マスタDB (ACTIVE/BACKUPのHA構成)

  • 常にマスタクラスタのどちらかがアクティブなマスタとして最新のデータを維持し続ける
  • 監視と動作
    • heartbeatの相互監視
      • heartbeatの異常終了, サーバダウンを検知できる
      • 1秒間隔で相互のudp-unicast監視をしている
      • 監視が失敗すれば, バックアップ機はアクティブに昇格する
    • port監視
      • アクティブ側でmysqlの異常終了を検知できる
      • アクティブ側でVIPのmysql connectivity応答監視をしている
      • nagios-pluginのcheck_mysqlを使って実装する
      • 10秒間隔で監視している
      • arp/routeが見つからない or 40秒以内に応答が無い場合, アクティブは自殺(heartbeatプロセスを終了)する
  • crmは使わない
  • fail backは設定しない
    • どちらかがアクティブになっていればOK

スレーブDB

  • スレーブは, アクティブなマスタが誰であろうがレプリケーションを継続し続ける
  • マスタがfailover中に運悪くマスタへのreconnectが失敗した場合, master_connect_retry設定値に従って10秒以内に再接続しレプリを継続する.

リスクとリスクの排除

  • アクティブではない方のマスタにwriteをおこなった場合, タイミングが悪いとidの採番等が衝突し, 不整合が発生する可能性がある.
  • dbm01,dbm02にてiptablesを設定し, 誤操作を防御する
iptables -A INPUT -d 192.168.23.60 -p tcp --dport 3306 -j ACCEPT # vipへのアクセスは自由
iptables -A INPUT -s 192.168.23.61 -p tcp --dport 3306 -j ACCEPT # dbm01からのwriteは許可
iptables -A INPUT -s 192.168.23.62 -p tcp --dport 3306 -j ACCEPT # dbm02からのwriteは許可
iptables -A INPUT                  -p tcp --dport 3306 -j REJECT # それ以外は禁止
/etc/init.d/iptables save
chkconfig iptables on
ip6tables -A INPUT -p tcp --dport 3306 -j REJECT # IPv6でのアクセスは禁止
/etc/init.d/ip6tables save
chkconfig ip6tables on

heartbeat設定

  • /etc/rsyslog.conf
    # heartbeat
    local6.* /var/log/ha-log
    • # /etc/init.d/rsyslog restart
    • logrotateはデフォルトで設定されている: /etc/logrotate.d/heartbeat
  • /usr/local/bin/mysql-ha-check.sh
    #!/bin/sh
    
    VIP=$1
    USER=$2
    PASS=$3
    CMD=/usr/lib64/nagios/plugins/check_mysql
    INTERVAL=10
    TIMEOUT=40
    
    while true
    do
    
    if [ ! -f $CMD ]; then
            exit -1
    fi
    
    ## sleep if backup
    CVIP=`/sbin/ip addr | grep $VIP | wc -l`
    if [ 0 -eq $CVIP ] ; then
       #echo "Zzz..." 
       sleep $INTERVAL
       continue
    fi
    
    ## die if active is not responding 
    $CMD -H $VIP -u "${USER}" -p "${PASS}" -c $TIMEOUT 2>&1 > /dev/null
    RES=$?
    
    if [ 2 -eq $RES ] ; then
        /usr/lib64/heartbeat/heartbeat -k
    fi
    
    sleep $INTERVAL
    done
    
  • # chmod 755 /usr/local/bin/mysql-ha-check.sh
  • /etc/ha.d/ha.cf
    crm            off
    use_logd       no
    logfacility    local6
    
    keepalive      1
    warntime       5
    deadtime       15
    initdead       60
    
    udpport        694
    auto_failback  off
    ucast          eth0  192.168.23.62 # dbm01はこの行を使う
    #ucast          eth0  192.168.23.61 # dbm02はこっち
    
    node           dbm01
    node           dbm02
    
    # 自分がactv時にmysqlが落ちていたら自殺する    self-ip       user pass
    respawn root /usr/local/bin/mysql-ha-check.sh  192.168.23.61 repl repl
    #respawn root /usr/local/bin/mysql-ha-check.sh  192.168.23.62 repl repl
  • /etc/ha.d/authkeys
    auth 1
    1 crc
    • # chmod 600 /etc/ha.d/authkeys
  • /etc/ha.d/haresources
    dbm01 IPaddr::192.168.23.60/24
    • 両方に同じ設定をする. heaerbeatが同時に起動したときに誰がマスタになるのかが決められるのでdbm01をマスタノードして書いておく.

my.cnf設定

log-bin= mysql-bin
log-slave-update
gtid-mode = ON
enforce-gtid-consistency

mysql レプリケーション設定

  • すべてで流す
    GRANT REPLICATION SLAVE ON *.*  TO  'repl'@'%' IDENTIFIED BY 'repl';
    GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'127.0.0.1'  IDENTIFIED BY 'repl';
    grant all privileges on *.* to 'root'@'%' identified by 'password';
  • dbm01
    change master to
    master_host = '192.168.23.62',
    master_port = 3306,
    master_user='repl',
    master_password='repl',
    master_connect_retry=10,
    master_auto_position=1;
  • dbm02
    change master to
    master_host = '192.168.23.61',
    master_port = 3306,
    master_user='repl',
    master_password='repl',
    master_connect_retry=10,
    master_auto_position=1;
  • dbsXX
    change master to
    master_host = '192.168.23.60',
    master_port = 3306,
    master_user='repl',
    master_password='repl',
    master_connect_retry=10,
    master_auto_position=1;

テスト1 期待通りに動くかテスト

  • アクティブなマスタがdbm01のときにdbm01がダウンし, 復旧したときの想定テスト.
  • データ損失が無く, キレイに切り替わって, クラスタを維持できていればOK.
  • サービスの停止時間は1秒未満とする.
  • 下記3台のテスト環境でテストをおこなう:
    マスタ1dbm01
    マスタ2dbm02
    スレーブdbs01
#対象操作種別実行動作と確認
1dbm02mysqlstop slave;スレーブを止める
2dbs01mysqlstop slave;スレーブを止める
3dbm01mysqlcreate database testdb1;dbm01にだけ testdb1 が作成される
4dbm02mysqlshow databases;testdb1が無いのを確認
5dbs01mysqlshow databases;testdb1が無いのを確認
6dbm01shell/etc/init.d/heartbeat stop; /etc/init.d/mysql stop;アクティブなマスタを停止する. スタンバイがマスタに昇格する.
7dbm02mysqlstart slave;スレーブを開始
8dbs01mysqlstart slave;スレーブを開始
9dbm02mysqlcreate database testdb2;アクティブなマスタで testdb2 を作成
10dbs01mysqlshow databases;スレーブに testdb2 がレプリカされたのを確認. testdb1が無いのを確認.
11dbm01shell/etc/init.d/mysql start; /etc/init.d/heartbeat start;スタンバイに成り下がった旧アクティブを復旧する.
12dbm01mysqlshow databases;dbm01で作成した testdb2 が存在するのを確認
13dbm02mysqlshow databases;dbm01 → dbm02 に testdb1 がレプリカされたのを確認
14dbs01mysqlshow databases;dbm01 → dbm02 → dbs01 に testdb1 がレプリカされたのを確認

テスト2 トランザクション中のクラッシュを想定してクラスタを復旧する

  • サービスの停止時間は1秒未満とする.
  • 滅多におこりえないレアケースだけど, トランザクションを処理中のアクティブなマスタがdbm01のときにdbm01がダウンし, 復旧したときの想定テスト.
  • トランザクションが消失してクラスタがクラッシュするが, 無事に復旧できるかどうかをたしかめる.
  • 複数のスレーブを稼働させ, 単一のスレーブを停止 (レプリ復旧のために停止) してもサービスを継続できる.
  • 下記4台のテスト環境でテストをおこなう:
    マスタ1dbm01
    マスタ2dbm02
    スレーブ1dbs01
    スレーブ2dbs02
dbm02: stop slave;
dbs01: stop slave;
dbm01: create database crashdb;
dbm02: show databases; (crashdbが無いのを確認)
dbs01: show databases; (crashdbが無いのを確認)
dbs02: show databases; (crashdbが無いのを確認)
dbm01: /etc/init.d/heartbeat stop; /etc/init.d/mysql stop; (heartbeatの停止時にアクティブがdbm02に切り替わる. その間1秒未満.)
dbm02: start slave;
dbs01: start slave;
dbs02: start slave;
dbm02: create database crashdb;
dbs01: show databases; (crashdbがあるのを確認)
dbs02: show databases; (crashdbがあるのを確認)
dbm01: /etc/init.d/mysql start; /etc/init.d/heartbeat start;
dbm01: show databases; (当然 crashdb がある. だけどdbm02でcreateした crashdb もあるはず = クラッシュ)
dbm01: show slave status\G (クラッシュしたのを確認)
Slave_IO_Running: Yes
Slave_SQL_Running: No
Last_SQL_Error: Error 'Can't create database 'crashdb'; database exists' on query. Default database: 'crashdb'. Query: 'create database crashdb')
dbm01: cd /var/lib; /etc/init.d/mysql stop; /etc/init.d/heartbeat stop; mv mysql mysql.bk; nc -l 9999 | tar xf
dbs01: cd /var/lib; /etc/init.d/mysql stop; tar cf - mysql | nc dbm01 9999; /etc/init.d/mysql start
dbm01: rm -f /var/lib/mysql/auto.cnf; /etc/init.d/mysql start; /etc/init.d/heartbeat start;
dbm01: start slave;
dbm01: show slave status (正常を確認)
dbm01: rm -rf /var/lib/mysql.bk
dbm02: drop database crashdb;
dbm01: show databases; (crashdbが消えているのを確認)
dbs01: show databases; (crashdbが消えているのを確認)
dbs02: show databases; (crashdbが消えているのを確認)

監視

  • 最低限下記の監視が必要
    • slave io running
    • slave sql running
    • heaertbeat processes (マスタクラスタのみ)

参考: http://downloads.mysql.com/presentations/MySQLTechTour_Replication.pdf



新規 編集 添付 名前変更 バックアップ   ホーム バックアップ リンク元   最終更新のRSS