2017.03.31

mysqlfailoverを使ってみた


こんにちは。次世代システム研究室のM.Mです。
現在、関わっている開発案件にてMySQL Utilitiesにあるmysqlfailoverという機能を検証する機会があったので、今回はその際に実施した内容を共有したいと思います。

MySQL UtilitiesについてはMySQL Utilitiesを参照してください。

目次

  1. mysqlfailoverについて
  2. 検証構成について
  3. mysqlfailoverの起動
  4. mysqlfailoverの挙動確認
  5. フェイルオーバー後のDBの状態確認

1.mysqlfailoverについて

MySQL Utilitiesにはさまざまなツールが存在し、その中にmysqlfailoverというツールが存在しています。
mysqlfailoverはデーモンプロセスとして起動させることができ、MySQLのMaster DBの状態を監視し、Master DBに障害が発生した場合、有効なSlave DBに自動でフェイルオーバーさせる機能となります。
詳細については以下を参照してください。
mysqlfailover

2.検証構成について

システム構成は以下で検証しました。
■検証バージョン
MySQL 5.7.14
MySQL Utilities 1.6.5

■検証サーバー構成
以下の図のように
・管理サーバー:1台(mysqlfailoverを使ってDBサーバーを監視するサーバー)
・MySQL:3台(マスター1台、スレーブ2台のレプリケーション構成)
となっております。

■DB設定
・my.cnfについて
mysqlfailoverの検証なのでレプリケーションの構築自体の詳細は記載しませんが、
レプリケーションとしては以下のような設定をmy.cnfに追加してGTIDモードとして設定しました。

gtid_mode = on
enforce_gtid_consistency = on
master-info-repository = TABLE
relay-log-recovery = 1
relay-log-info-repository = TABLE

またSlaveのみmy.cnfにread_onlyも追加し読み込み専用として設定しました。

・利用ユーザーについて
root, appuser, replicaを使ってテストしました。
replicaユーザーには以下のようにREPLICATION SLAVE権限を与えています。

mysql> show grants for 'replica'@'192.168.33.%';
+------------------------------------------------------------+
| Grants for replica@192.168.33.% |
+------------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO 'replica'@'192.168.33.%' |
+------------------------------------------------------------+
1 row in set (0.00 sec)
・利用テーブルについて
以下のtestテーブルのみある状態でテストしました。
mysql> desc test;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(10) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
appuserで接続しSlave1/Slave2のDBではデータ登録できないことを確認
mysql> insert into test values(1, 'test');
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
(rootユーザーだとread_onlyでも更新できるので注意。super_read_onlyという設定にてrootでも更新できなくすることができるようですが未検証。)

3.mysqlfailoverの起動

管理サーバーにログインし、mysqlfailoverコマンドを以下のように実行します。
(表示の都合上適当に改行を入れています)

mysqlfailover
–master=root:’XXXXX’@192.168.33.41:3306
–slaves=root:’XXXXX’@192.168.33.42:3306,root:’XXXXX’@192.168.33.43:3306
–log=log.txt
–failover-mode=auto
–candidates=root:’XXXXX’@192.168.33.42:3306
–rpl-user=’replica:XXXXX’
–daemon=start

–masterには現在のMaster DBを指定
–slavesには現在のSlave DBを指定
–logにはmysqlfailoverのログ出力ファイルを指定
–failover-modeにはautoを指定
–candidatesにはMaster DB障害時にMasterに昇格するSlave DBを指定
(Masterに昇格するSlave候補を複数指定できますが意図的にSlave1を指定しています。)
–rpl-userにはレプリケーションで利用しているユーザーを指定
–daemonにはstartを指定してデーモンとして起動するように指定

実行すると以下のようなメッセージが表示され、mysqlfailoverがデーモンとして起動されます。
Starting failover daemon…
次にログを確認してみます。
上記mysqlfailoverコマンドを実行した際の–logオプションに指定したログファイルを確認します。

2017-03-27 14:24:48 PM INFO MySQL Utilities mysqlfailover version 1.6.5.
2017-03-27 14:24:48 PM INFO Server '192.168.33.41:3306' is using MySQL version 5.7.14-log.
2017-03-27 14:24:48 PM INFO Server '192.168.33.42:3306' is using MySQL version 5.7.14-log.
2017-03-27 14:24:48 PM INFO Server '192.168.33.43:3306' is using MySQL version 5.7.14-log.
2017-03-27 14:24:48 PM INFO Checking privileges.
2017-03-27 14:24:48 PM INFO Checking privileges on candidates.
2017-03-27 14:24:48 PM INFO Unregistering existing instances from slaves.
2017-03-27 14:24:48 PM INFO Registering instance on master.
2017-03-27 14:24:48 PM INFO Failover daemon started.
2017-03-27 14:24:48 PM INFO Failover mode = auto.
2017-03-27 14:24:51 PM INFO Master Information
2017-03-27 14:24:51 PM INFO Binary Log File: mysql-bin.000007, Position: 726, Binlog_Do_DB: gmoid, Binlog_Ignore_DB: N/A
2017-03-27 14:24:51 PM INFO GTID Executed Set: 4a144a77-03cb-11e7-9e3c-080027c363c3:1-9
2017-03-27 14:24:51 PM INFO Getting health for master: 192.168.33.41:3306.
2017-03-27 14:24:51 PM INFO Health Status:
2017-03-27 14:24:51 PM INFO host: 192.168.33.41, port: 3306, role: MASTER, state: UP, gtid_mode: ON, health: OK
2017-03-27 14:24:51 PM INFO host: 192.168.33.42, port: 3306, role: SLAVE, state: UP, gtid_mode: ON, health: Binary log and Relay log filters differ.
2017-03-27 14:24:51 PM INFO host: 192.168.33.43, port: 3306, role: SLAVE, state: UP, gtid_mode: ON, health: Binary log and Relay log filters differ.
Failover mode = auto.になっていること確認します。(10行目)
Health Status:を確認し問題なく動いていることが確認できます。(16-18行目)

あえて100000件insertした後にcommitを実行してみるとログには以下のような出力がされました。
2017-03-27 14:53:17 PM INFO Health Status:
2017-03-27 14:53:17 PM INFO host: 192.168.33.41, port: 3306, role: MASTER, state: UP, gtid_mode: ON, health: OK
2017-03-27 14:53:17 PM INFO host: 192.168.33.42, port: 3306, role: SLAVE, state: UP, gtid_mode: ON, health: Slave delay is 9 seconds behind master., No, Slave has 1 transactions behind master.
2017-03-27 14:53:17 PM INFO host: 192.168.33.43, port: 3306, role: SLAVE, state: UP, gtid_mode: ON, health: Slave delay is 9 seconds behind master., No, Slave has 1 transactions behind master.
SLAVEサーバーのhealthの項目を見ると

Slave delay is 9 seconds behind master., No, Slave has 1 transactions behind master.

との記載がありレプリケーションの遅延状況もログを見れば確認できそうです。

■失敗時のメモとして
何回かmysqlfailoverコマンドを試したりしているとmysqlfailoverコマンドのオプションに–failover-mode=autoと記載してもログにはFailover mode = fail.と記載されFailover modeがautoで起動されない場合がありました。
原因の究明まではしておりませんが、起動したmysqlfailoverデーモンを停止して、Master DBにrootユーザーでアクセスし以下のSQLを実施した後、再度mysqlfailoverコマンドを実施したらFailover modeがautoで起動しました。

mysql> use mysql
mysql> select * from failover_console;
mysql> delete from failover_console;
mysql> commit;

4.mysqlfailoverの挙動確認

Masterを停止してmysqlfailoverコマンドのcandidatesオプションに指定したSlave1(192.168.33.42)がマスターに昇格するか確認します。
(以下は/etc/init.d/mysqld stopにてMasterを停止したケースになります。)
Masterを停止するとmysqlfailoverのログファイルには以下のようなログが出力されました。
2017-03-27 15:00:38 PM INFO Master may be down. Waiting for 3 seconds.
2017-03-27 15:00:53 PM INFO Failed to reconnect to the master after 3 attempts.
2017-03-27 15:00:53 PM CRITICAL Master is confirmed to be down or unreachable.
2017-03-27 15:00:53 PM INFO Failover starting in 'auto' mode...
2017-03-27 15:00:53 PM INFO Candidate slave 192.168.33.42:3306 will become the new master.
2017-03-27 15:00:53 PM INFO Checking slaves status (before failover).
2017-03-27 15:00:53 PM INFO Preparing candidate for failover.
2017-03-27 15:00:53 PM INFO Creating replication user if it does not exist.
2017-03-27 15:00:54 PM INFO Stopping slaves.
2017-03-27 15:00:54 PM INFO Performing STOP on all slaves.
2017-03-27 15:00:54 PM INFO Switching slaves to new master.
2017-03-27 15:00:54 PM INFO Disconnecting new master as slave.
2017-03-27 15:00:54 PM INFO Starting slaves.
2017-03-27 15:00:54 PM INFO Performing START on all slaves.
2017-03-27 15:00:54 PM INFO Checking slaves for errors.
2017-03-27 15:00:54 PM INFO Failover complete.
2017-03-27 15:00:59 PM INFO Unregistering existing instances from slaves.
2017-03-27 15:00:59 PM INFO Registering instance on new master 192.168.33.42:3306.
2017-03-27 15:00:59 PM INFO Master Information
2017-03-27 15:00:59 PM INFO Binary Log File: mysql-bin.000001, Position: 715, Binlog_Do_DB: gmoid, Binlog_Ignore_DB: N/A
2017-03-27 15:00:59 PM INFO GTID Executed Set: 47b31649-03cb-11e7-9f79-080027c363c3:1-2[...]
2017-03-27 15:00:59 PM INFO Getting health for master: 192.168.33.42:3306.
2017-03-27 15:00:59 PM INFO Health Status:
2017-03-27 15:00:59 PM INFO host: 192.168.33.42, port: 3306, role: MASTER, state: UP, gtid_mode: ON, health: OK
2017-03-27 15:00:59 PM INFO host: 192.168.33.43, port: 3306, role: SLAVE, state: UP, gtid_mode: ON, health: Binary log and Relay log filters differ.
2017-03-27 15:01:17 PM INFO Master Information
2017-03-27 15:01:17 PM INFO Binary Log File: mysql-bin.000001, Position: 715, Binlog_Do_DB: gmoid, Binlog_Ignore_DB: N/A
2017-03-27 15:01:17 PM INFO GTID Executed Set: 47b31649-03cb-11e7-9f79-080027c363c3:1-2[...]
2017-03-27 15:01:17 PM INFO Getting health for master: 192.168.33.42:3306.
2017-03-27 15:01:17 PM INFO Health Status:
2017-03-27 15:01:17 PM INFO host: 192.168.33.42, port: 3306, role: MASTER, state: UP, gtid_mode: ON, health: OK
2017-03-27 15:01:17 PM INFO host: 192.168.33.43, port: 3306, role: SLAVE, state: UP, gtid_mode: ON, health: Binary log and Relay log filters differ.
16行目にFailover complete.というメッセージが記載されておりフェイルオーバーが実施されたことが分かります。
また31行目を確認するとSlave1(192.168.33.42)がMASTERとなっており、意図したとおりSlave1がMasterに昇格したと分かります。
32行目のSlave2(192.168.33.43)に関してもSLAVEのまま起動していることが分かります。

5.フェイルオーバー後のDBの状態確認

mysqlfailoverのログを見る限りでは問題なく自動でフェイルオーバーされたようなので、実際にMasterに昇格したSlave1にてinsertを実施して問題なくMasterとして稼動し、かつSlave2へのレプリケーションも問題なく動いているか確認します。
mysql> insert into test values(2, 'test');
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
エラーが発生。
残念ながらSlaveがMasterに昇格してもread-onlyモードのままになっており更新処理ができませんでした。
とはいえ、アプリケーションのバグやオペレーションミスでSlaveを更新するようなことが発生すると嫌なのでSlaveはread-onlyにはしておきたい。
どうにかできないかと考えていたところmysqlfailoverコマンドにある–exec-afterというオプションをうまく使えないかと思いつきました。
このオプションはフェイルオーバーが実施された後に指定したスクリプトを実行することができるオプションであり、以下のようなスクリプトを指定すればMasterに昇格したSlaveのread-only設定を解除することができると考えました。
#!/bin/sh
mysql -uroot -p"XXXXX" -h 192.168.33.42 -Ee 'set global read_only = 0;'
上記スクリプトをexec_read_only_off.shとして保存し、mysqlfailoverコマンドのオプションに

–exec-after=exec_read_only_off.sh

を追加する。
この設定をした上でmysqlfailoverを起動し、再度テストした結果、フェイルオーバーが実施された後にMasterに昇格したSlave1のread_onlyが解除され、問題なく更新処理を行うことができ、かつもう一方のSlave2へも伝播されていました。

尚、mysqlfailoverの起動のところで、–candidatesオプションでMasterに昇格するSlave候補を複数指定できるが意図的にSlave1のみを指定したのは、複数指定するとどのSlaveがMasterに昇格するか分からないので、read_onlyモードを解除するスクリプトにてどのSlaveサーバーがMasterに昇格したか判断する処理が必要になります。今回はその判断処理を省略したため意図的にSlave1のみ昇格するSlave候補として指定することにしました。

所感

参照更新あわせて秒間4リクエストぐらいの状態でフェイルオーバーの確認をしましたが、Master DBを/etc/init.d/mysqld stopでキレイにダウンさせたのでうまくフェイルオーバーした可能性もあります。
実際の運用では、高負荷、パニックなどを起こして不安定(サービスとして維持できていない)な状態だけどMaster DB自体はダウンしていない、なので自動で切り替わらないといったケースも考えられます。
また不安定な状態を作り出すのも難しかったりするので、何をもってダウンとみなすのか検討しておくことも重要だと思われます。

最後に

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。

皆さんのご応募をお待ちしています。