Tech Sketch Bucket of Technical Chips by TIS Inc.

MySQLレプリケーションをMHAとHAProxyでフェイルオーバさせてみた

Pocket

techsketch-banner-OSS+startingblock(700x65).jpg

MySQL には標準でレプリケーション機能が組み込まれており、下図の様なシステム構成とすることで、データの冗長化と参照処理の負荷分散を容易に実現できます。この構成では更新処理はマスタサーバで行うことになりますが、
参照処理は全てのサーバに分散して行えます。

mha01.png

ここでサーバの1台が何らかの障害で停止した場合を考えましょう。スレーブサーバの1台が停止した場合、マスタサーバや他のスレーブサーバで参照処理を継続できるため、停止したサーバへクライアントが参照クエリを送信しないように変更する必要はありますが、システム全体に大きな影響を与えません。

一方、マスタサーバが停止した場合、更新処理が停滞して業務継続に影響します。そのためマスタサーバは手厚く監視する必要があり、もしマスタサーバが停止した場合には業務を継続するため以下の作業が必要です。

  • 最も最新に近い更新内容が反映されているスレーブサーバを選択し、マスタサーバへ昇格
  • 他のスレーブサーバの更新処理の参照先を新マスタサーバに変更
  • クライアントからの更新クエリの送信先を新マスタに変更
  • 停止したサーバへクライアントが参照クエリを送信しないように変更

ただ、MySQLにはマスタサーバ障害時のフェイルオーバ機能が標準では提供されていないため、障害が起こった場合には上記の様な複雑な操作を手動で行わなければなりません。短時間でミスなく障害復旧を行うためにも、切り替え操作を自動化するツールを活用したいところです。

そこで今回はMySQL Master HA (MHA)HAProxyを利用してフェイルオーバを実現してみます。

動作環境を作成する

作成する動作環境の構成とフェイルオーバ後の状態を下図に示します。

mha_haproxy.png

図にあるようにフェイルオーバを実現するための必要な要件は下の通りでした。

  1. マスタサーバの死活監視
  2. 最も最新に近い更新内容が反映されているスレーブサーバを選択し、マスタサーバへ昇格
  3. 他のスレーブサーバの更新処理の参照先を新マスタサーバに変更
  4. クライアントからの更新クエリの送信先を新マスタに変更
  5. 参照クエリを対象サーバへ分散し、サーバ停止が発生した場合は停止したサーバへの参照クエリ送信を止める

このうちMHAは1.~4.を、HAProxyは5.をそれぞれ担当します。4.の実現のために、マスタサーバに固定の仮想IPアドレスを付与し、マスタサーバ停止時は新しいマスタサーバに仮想IPアドレスを付け替えることで、クライアントにマスタサーバの位置を意識させません。

以下では具体的な導入方法を示します。今回利用するソフトウェアは以下の通りです。

  • CentOS 6.2
  • MySQL 5.5
  • MHA 0.53
  • HAProxy 1.4

なお、今回の作業はすべてrootユーザで行いました。

MySQLレプリケーション

今回は新たにMySQL環境を構築します。既存環境へのレプリケーション適用は稿末のリンクを参考にしてください。

MySQLの導入はRemiリポジトリから行います。リポジトリの参照設定を行い、MySQLをインストールします。MySQLのインストール途中で鍵を信用してよいか聞かれますので"yes"と入力してください。

今回は準同期レプリケーションを利用するため、MySQLを起動して必要なモジュールを読み込みます。

レプリケーションに使用するユーザを追加します。

バイナリログ、サーバID、書き込み制限、準同期レプリケーションなどの設定を/etc/my.cnfに入れて再起動します。設定のうち、サーバID(server-id)はサーバごとに一意になるように設定する必要があります。設定の詳細はリンク先のファイルを参照してください。

ここまでの設定を全てのサーバで終えたら、レプリケーションを開始します。そのため、マスタサーバを一時的にロックして、マスタサーバのbinlogの位置を確認します。マスタサーバにて以下のコマンドを実行します。

表示されたFileとPositionの値はスレーブサーバへの設定に必要になります。スレーブサーバにマスタサーバへの接続に必要な情報とBinlogのFileとPositionを入力してレプリケーションを開始します。全てのスレーブサーバで以下のコマンドを実行します。

スレーブサーバとして動作しているか確認するには以下のコマンドを実行します。

内容が次のようになっていれば正しく動作しています。

マスタサーバのロックと書き込み制限の解除を行います。マスタサーバにて以下のコマンドを実行します。

これでレプリケーション環境が整いました。レプリケーションの確認のため、マスタサーバにテスト用のデータベースとユーザとテーブルを作成し、同じ物がスレーブサーバにも作成されたことを確認してください。

MHA

MHAはPerlで記述されたMySQLのフェイルオーバツールで、作者の 松信嘉範氏 が勤める ソーシャルゲーム企業でも利用 されているそうです。

MHAは以下の機能を提供します。

  • マスタサーバの死活監視
  • 最も最新に近い更新内容が反映されているスレーブサーバを選択し、マスタサーバへ昇格
  • 他のスレーブサーバの更新処理の参照先を新マスタサーバに変更

非同期レプリケーションでマスタサーバが停止した場合、更新された最新のデータがスレーブサーバへレプリケーションされないことがあるため、取得可能である場合にはサービス再開前にスレーブサーバに同期する必要があります。また準同期レプリケーションを採用していたとしてもスレーブ間でデータのずれが発生する可能性があり、サービス再開前にスレーブ間のデータの整合性をとる必要があります。MHAにはこうした問題を解決する機能が用意されています。

MHAは、MHAマネージャとMHAノードで構成されます。MHAマネージャはマスタサーバの死活監視とフェイルオーバ全体の制御を行うプロセスで、各MySQLサーバとは別のサーバで稼動させます。一方、MHAノードは各MySQLサーバに1つづつ配置され、フェイルオーバが発生した場合にスレーブサーバの更新状態の調査、更新状態が最新のスレーブサーバをマスタサーバへ昇格、他のスレーブサーバの更新処理参照先の新マスタサーバへの変更を行います。

なお、MHAを導入した段階では、マスタサーバ停止時に新しいマスタサーバに仮想IPアドレスを付け替える機能は用意されていませんので、ソースコードに付属しているサンプルスクリプトを各自改変して使用することになります。

まずMHAノードの導入します。全てのMySQLサーバで以下のコマンドを実行してインストールを行います。

次にMHAマネージャを導入します。MHAマネージャサーバにて以下のコマンドを実行してください。MHAマネージャのインストールにはMHAノードも必要になります。また標準のリポジトリではパッケージが足りないのでEPELリポジトリも導入します。

MHAは動作のディレクトリが必要なので全てのMySQLサーバとMHAマネージャサーバで用意します。このディレクトリのパスを後ほど設定ファイル記入します。

仮想IP付け替えのスクリプトを用意します。MHAのソースコードに添付されたサンプルを独自に改変します。このスクリプト、master_ip_failoverを/usr/binに配置して、実行権限を755に変更してください。

MHAの動作設定をMHAマネージャサーバに行います。MHAの設定ファイルmha.cnfには監視対象のサーバ群やmysqlのレプリケーション用ユーザとパスワード、フェイルオーバのスクリプトなどのパスを記述します。詳細はリンク先のファイルを確認ください。設定ファイルは/etcに設置してください。

MHAマネージャとMHAノードの通信は公開鍵認証のSSHを利用します。そのためMHAマネージャサーバにて鍵を作成します。

作成した公開鍵/root/.ssh/rsa_id.pubの内容を全てのMySQLサーバの/root/.ssh/authorized_keysに追記してください。またMHAマネージャサーバの/root/.ssh/known_hostsにMySQLサーバの内容を記述するために以下のコマンドをMHAマネージャサーバで実行します。

仮想IPを割り付けを行うためMySQLマスタサーバで以下のコマンドを実行ます。

MHAマネージャを起動するため、MHAマネージャサーバで以下のコマンドを実行します。

これでMHAの準備ができました。

HAProxy

HAProxyはソフトウェアロードバランサの1つでMySQLのクエリを分散することができます。またHAProxyはある負荷分散対象のサーバが停止した場合、そのサーバへのリクエスト送信を止めることができ、次の要件を満たしています。

  • 参照クエリを対象サーバへ分散し、サーバ停止が発生した場合は停止したサーバへの参照クエリ送信を止める

HAProxyの導入方法を示します。以下の作業はクライアントで行います。リポジトリを追加してHAproxyをインストールします。

Haproxyに想定通りの動作をさせるため/etc/haproxy/haproxy.cfgを変更します。クライアントでは参照クエリはlocalhost:3306に向けて送信します。詳細はリンク先のファイルを参照ください。なお、HAProxyのログはsyslogに出力されるので、ファイルへの書き込みには別途設定が必要となりますが、今回は説明を割愛します。

HAProxyからMySQLへの死活監視はSQLを送信するのでMySQLに専用のユーザを作ります。レプリケーションされている状態でMySQLマスタサーバにて以下のSQLを実行してください。

HAProxyを起動します。

これでHAProxyの準備ができました。

フェイルオーバの動作検証

フェイルオーバの動作を確認します。動作検証にはVMware ESXi 5.0で仮想化された環境を用います。検証に用意した仮想マシンはクライアント(JMeter + HAProxy)=1台、MySQLサーバ=3台、MHAマネージャサーバ=1台で、仮想マシン1台に与えたリソースはCPU1コア(Intel Xeon CPU X5570 2.93GHz)、メモリ4GB、HDD8GBです。


今回はJMeterを用いて、参照更新比率 = 100:1、同時接続数 = 30、Think Time = 正規分布(平均20ms、偏差1ms)の負荷を掛けました。そして電源断を模して、仮想マシンの強制停止させました。またタイムアウトを15秒に設定しています。

結果は次のグラフのようになります。

mha_graph.png

このグラフは横軸:経過時間(ミリ秒)、縦軸:レスポンスに掛かった時間(ミリ秒)です。またプロットされている点は横軸に区間の代表値(秒単位)を、縦軸にJMeterで観測されたレスポンス時間の1秒間当たりの平均時間を表示しています。また色に応じて参照更新とレスポンスの正常・異常を表しており、赤:参照正常・緑:更新正常・青:更新異常です。

グラフからは次のことが読み取れます。

  • MHAマネージャは障害発生後5秒程度で障害を検知する
  • MHAマネージャの障害検知開始後9秒程度でマスタサーバが停止していることを確定、フェイルオーバを開始する
  • フェイルオーバに掛かった時間は9秒程度
  • 参照クエリは異常レスポンスが無いことから、HAProxyの停止サーバ切り離しはごく短い時間で行われると考えられる
  • フェイルオーバ開始までに発行された更新クエリはタイムアウトまで待ち、異常レスポンスで終了する
  • 開始後30000ミリ秒から40000ミリ秒に掛けてクエリを送信していないが、これはJMeterの全てのスレッドが障害の発生したサーバへ更新クエリを送信、タイムアウト待ちになっており、クエリの送信ができないためと考えられる
  • フェイルオーバ開始から終了までの更新クエリは異常レスポンスとなるがレスポンス時間はごく短い

このようにMHAとHAProxyとの組合せでMySQLレプリケーションのフェイルオーバできることが分かりました。

最後に注意点です。MHAは一度フェイルオーバしてしまうとプロセスが終了してしまい、継続的な監視を行いません。MHAを作り変えるなどで継続的な監視することは可能だと思われますが、ある程度の運用対処を行うのが現実的です。Zabbix等の監視ツールを用いてMHAマスタプロセスと各種ログの監視を行い、フェイルオーバの発生時には次の障害が起こる前にMHAの機能を復帰させるのが良いと考えられます。

参考

エンジニア採用中!私たちと一緒に働いてみませんか?