みなさんこんにちは、研究所の鷲北です。前回に引き続き、MariaDB Galera Clusterを試していきたいと思います。
前回は、MariaDB Galera Clusterを使ってデータベース・サーバのクラスタを作りました。複数のノード間で動的にデータが共有される様子を確認しましたが、問題も残っていました。データ冗長はとれたものの、DBサーバとして負荷分散されたわけでもサービス冗長されたわけでもないということです。WordPressのようなアプリケーションがDBを利用するとき、サーバを1つ指定したいわけですが、複数のノードから1つを選んで指定しても何もメリットはありません。分散処理や冗長処理は、MariaDB Galera Clusterの仕組みを使って実現する必要があります。今回は、LVSを使って分散・冗長処理を行う方法について説明します。
LVSと分散処理
LVS (Linux Virtual Server)は、レイヤ4スイッチングを利用してネットワークサービスの負荷分散を可能にしたものです。ここでは、データベースへのアクセスを受け付け、DBクラスタ上のノードへ分散する処理をLVSに任せることを考えてみます。
LVSの負荷分散機能を担うサーバをダイレクター(Director)と呼びます。ここでは、lvsという名前のサーバにしましょう。ダイレクターにはサーバとしてのIPアドレスの他に、負荷分散を行うネットワークサービスに使うための仮想IPアドレス(VIP)を定義します。
LVSを使った環境では、データベースアクセスは次のようになります。
- アプリケーション(たとえばWordPressが動作しているWebサーバ)はダイレクターにクエリを発行します
- ダイレクターは分散アルゴリズムに従いクラスタのDBサーバに処理を転送します
- DBサーバは処理を実行し、結果をWebサーバに返します
なお、今回は構成上無理のないダイレクト・ルーティング形式にしています。
LVSは負荷分散の他に、いくつもの重要な処理を行ってくれます。
- 死活監視。クラスタを構成するDBサーバがダウンしたり応答しなくなった場合、分散対象から自動的に外す。これにより障害サーバに処理を回してしまうことを防ぐ
- 負荷状況の確認と分散。リクエストを順番に分配するのではなく、コネクション数がもっとも少ないノードへ処理を回すことができる。ノードごとにweight値を設定でき、さまざまなアルゴリズムで分散処理をコントロールできる
- ダイレクター自身の冗長化。2台のダイレクターをクラスタ化し、障害が発生しても処理が停止しないようにできる
このようなLVSダイレクターとMariaDB Galera Clusterを組み合わせれば、負荷分散かつ冗長構成のとれたデータベースシステムが完成するというわけです。LVSの原理や詳細な解説は参考リンクをご覧いただくとして、以下に設定手順を示していきたいと思います。
Webサーバの準備
まず最初にアプリケーションサーバあるいは負荷発生元として、Webサーバを作成します。最初に掲げた目標の通りWordPressサーバとしたいので、Apacheが動作し、PHPがインストールされ、MariaDB-clientが動いている環境を準備します。ちなみにサーバの互換性は十分取れているので、MySQLクライアントでも問題なく動作します。お好みに合わせて準備してください。
Webサーバの起動
さくらのクラウドをご利用であれば、新しいサーバを起動してセットアップします。回線は不要で、NICを既存のローカルスイッチに接続します。モニタから起動して、設定ファイルを書き換えて再起動します。
- /etc/sysconfig/network
NETWORKING=yes NETWORKING_IPV6=no HOSTNAME=web GATEWAY=192.168.0.1
- /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0 BOOTPROTO="static" ONBOOT="yes" TYPE="Ethernet" HWADDR=9c:a3:ba:xx:xx:xx NETMASK=255.255.255.0 BROADCAST=192.168.0.255 IPADDR=192.168.0.11 NETWORK=192.168.0.0
ネットワークに繋がったら、必要なアプリケーションをインストールします。MariaDBをインストールするときはリポジトリを用意するのを忘れないようにしてください。
[root@web ~]# yum install httpd php php-mysql MariaDB-client
NATの設定とWebアクセスの確認
WebサーバはNATルータの背後にありますので、このままでは外部からWebサーバにアクセスできません。NATサーバに設定を入れて、Webサーバにアクセスできるようにします。具体的にはNATサーバのグローバルIPアドレス80番ポートへのアクセスは、WebサーバのローカルIPアドレス192.168.0.11へ転送するようにします。これはNATサーバのiptablesの設定を書き換えます。
- NATサーバの /etc/sysconfig/iptables
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE
-A PREROUTING -d xxx.xxx.xxx.xxx -p tcp --dport 80 -j DNAT --to 192.168.0.11:80
COMMIT
#
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
赤く示した行が追加する行になります。「xxx.xxx.xxx.xxx」になっている部分は、NATサーバのグローバルIPアドレスに置き換えてください。なお、もしfilterセクションで80番ポートに制限を入れている場合はそちらにも設定を入れないと転送されませんので注意してください。
NATサーバの設定ができたら、httpdを起動してWebサーバにアクセスできるか試してみます。Apacheを使用していれば、デフォルトページが見えるはずです。
WordPress用DBの設定
次にMariaDBにWordPress用のデータベースを作成します。DBサーバはまだ負荷分散には対応していませんが、ノード単体であれば使えますので、WordPressも問題なく動作します。動作を確認するため、WordPressのインストールまで終わらせておきます。
DBへの設定はdb1、すなわち192.168.0.21サーバで行います。WordPress用に専用のアカウントとパスワード、データベースを作成するので、それぞれ名前とパスワードを考えておきます。手順はWordPressの公式サイトのインストールガイドにもあるので参考にしてください。以下の設定手順は例ですので、DB_NAME、DB_USER、DB_PASSWORDは適宜書き換えてください。
[root@db1 ~]# mysql -u root -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 4046 Server version: 5.5.28a-MariaDB MariaDB Server, wsrep_23.7rc1.rXXXX Copyright (c) 2000, 2012, Oracle, Monty Program Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> create database DB_NAME; Query OK, 1 row affected (0.00 sec) MariaDB [(none)]> grant all privileges on DB_NAME.* to DB_USER@'%' identified by 'DB_PASSWORD' with grant option; Query OK, 0 rows affected (0.02 sec) MariaDB [(none)]> flush privileges; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> \q
WordPressのインストール
続いて、WordPressをWebサーバにインストールします。WordPressは公式サイトから最新版をダウンロードしてください。以下のようにwgetを使えば簡単にダウンロードできます。tarコマンドで展開した後、Apacheの設定に合わせてオーナーシップを変更しておきます。最後にwordpress以下のファイルをすべてDocumentRootにコピーします。
[root@web ~]# wget http://ja.wordpress.org/latest-ja.tar.gz [root@web ~]# tar zxf latest-ja.tar.gz [root@web ~]# chown -R apache:apache wordpress [root@web ~]# cp -rp wordpress/* /var/www/html
コピーが完了したら、WordPressの設定を行います。この設定についてもWordPress公式サイトのインストールガイドをお読みいただきたいのですが、wp_config.phpの変更点だけ書きだしておきます。
- MariaDBに設定した通りにDB_NAME、DB_USER、DB_PASSWORD を記述してください
- DB_HOSTはMariaDB Galera Clusterのノードのうち、1つを指定してください。ここでは192.168.0.21とします
設定が済んだら、http://xxx.xxx.xxx.xxx/wp-admin/install.php にアクセスします。アドレス部分はNATサーバのグローバルIPアドレスに置き換えてください。すべてが問題なければ、WordPressの設定画面が起動するはずです。
ここまでのまとめ
話が複雑になってしまいましたので、ここまでの構成を図にしてみましょう。今のところ、MariaDB Galera ClusterとWebサーバが起動していて、WordPressが動作している状態になっています。MariaDBはクラスタになっていますが分散処理されておらず、見た目は単一のDBサーバとして動作しています。
LVSサーバの準備
次にLVSサーバを準備します。DBサーバをクラスタによって冗長構成にしたので、LVSも2台の冗長構成にしてみたいと思います。
まずlvs1という名前で新規にサーバを作成します。ロードバランサーなのですが、NICは1つでOKです。これまで作成してきたサーバと同様に、CentOS 6.3をインストールしupdateを済ませます。lvs1のIPアドレスは192.168.0.2とします。
LVSの機能は、2つのソフトウェアによって制御します。ipvsadmとkeepalivedです。ipvsadmがレイヤ4スイッチングをサービスし、keepalivedが死活監視を行ってipvsadmをスーパーバイズし、設定を切り替える役割を果たします。したがってLVSの設定はすべてkeepalived.confに記述することになります。
もうひとつ、keepalivedはMariaDBの死活監視のためにmysqladminを必要とします。db1やdb2にpingしたり、ssh接続してリモートでmysqlを起動するのもよいのですが、LVSサーバでmysqladminのpingコマンドを実行した方がよさそうなので、mysqlクライアントをインストールして利用することにします。
[root@lvs1 ~]# yum install ipvsadm keepalived MariaDB-client
インストールが完了すると /etc/keepalived/keepalived.conf というサンプル・ファイルが置かれますが、これは適当にリネームして、必要最小限の新しい設定ファイルを作り直します。keepalived.confの記述については、マニュアル等を参考にしてください。ここではだいたい動く設定を示すだけにとどめておきます。
- /etc/keepalived/keepalived.conf
# VRRP設定とVIPの定義 vrrp_instance VI { state BACKUP nopreempt interface eth0 lvs_sync_daemon_interface eth0 virtual_router_id 1 priority 100 advert_int 1 authentication { auth_type PASS auth_pass PASSWORD } virtual_ipaddress { 192.168.0.20/24 dev eth0 } } # サーバグループの定義 virtual_server_group GALERA { 192.168.1.20 3306 } # サーバグループ GALERA の設定 virtual_server group GALERA { delay_loop 6 lb_algo wlc lb_kind DR protocol TCP # db1 real_server 192.168.0.21 3306 { weight 1 inhibit_on_failure MISC_CHECK { # 死活監視: リターンコードが0ならばOK misc_path "mysqladmin -u root -pDB_PASS --connect_timeout=5 --host=192.168.0.21 ping" misc_timeout 10 } } # db2 real_server 192.168.1.22 3306 { weight 1 inhibit_on_failure MISC_CHECK { # 死活監視: リターンコードが0ならばOK misc_path "mysqladmin -u root -pDB_PASS --connect_timeout=5 --host=192.168.1.22 ping" misc_timeout 10 } # db3 real_server 192.168.1.23 3306 { weight 1 inhibit_on_failure MISC_CHECK { # 死活監視: リターンコードが0ならばOK misc_path "mysqladmin -u root -pDB_PASS --connect_timeout=5 --host=192.168.1.23 ping" misc_timeout 10 } } }
設定ファイル中、vrrp_interfaceセクションのPASSWORDはVRRPセッションのパスワードです。任意のパスワードに設定してください。DB_PASSは前回データベースに設定したパスワードになりますので、そちらを記述するようにしてください。これらが正しく設定されないと、データベースがダウンしていると認識され、正しくスイッチングされなくなってしまいます。
もうひとつ、LVSサーバで行っておくべきことがIPフォワーディングを有効にすることです。/etc/sysctl.confファイルにオプション指定部分があって、通常は0になっているので、これを1に変更します。
- /etc/sysctl.conf
# Controls IP packet forwarding
net.ipv4.ip_forward = 1
準備が済んだら、設定をリロードします。
[root@db1 ~]# sysctl -p
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
:
:
DBサーバのループバックアドレスの設定
keepalivedの設定の次は、VIPアドレスをDBサーバのループバックアドレスに設定します。これはLVSによって転送されてきたパケットを受信するために必要な設定です。
まずスクリプトファイルを用意します。
- /etc/sysconfig/network-scripts/ifcfg-lo_0
DEVICE=lo:0 IPADDR=192.168.0.20 NETMASK=255.255.255.255 ONBOOT=yes
次に、ちょっとおまじないをします。LinuxのARPリクエストの処理は、デフォルトではどのインターフェースにきても応答してしまうという設定になっています。このままだとループバックに付与したVIPにも反応してしまうので、インターフェースのIPアドレスに対するリクエストにのみ応答するように設定する必要があります。このためにsysctl.confに以下の2行を追加します。
- /etc/sysctl.conf
net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2
準備が済んだら、設定をリロードし、ループバックアドレスの設定を行います。
[root@db1 ~]# sysctl -p net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 : : net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 [root@db1 ~]# service network restart : :
この設定を、すべてのDBサーバで行っておきます。
LVSの起動
ようやく準備が整いましたのでLVSを起動してみましょう! LVSサーバが1台しかありませんが、1台でも問題なく動きますので気にせずに行きましょう。
lvs1で、keepalivedを起動します。状態の確認はipvsadmコマンドを使います。
[root@lvs1 ~]# service keepalived start
Starting keepalived: [ OK ]
うまく起動できれば、VIPがeth0に付与されたはずです。これをコマンドで確かめます。
[root@wp-db-lvs1 ~]# ip addr show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 9c:a3:ba:xx:xx:xx brd ff:ff:ff:ff:ff:ff inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0 inet 192.168.0.20/24 scope global secondary eth0 valid_lft forever preferred_lft forever
赤く示したようにsecondary IPとして付与されていればOKです。また、ロードバランスが設定できたかどうかは以下のようにして確かめます。
[root@lvs1 ~]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.0.20:3306 wlc
-> 192.168.0.21:3306 Route 1 0 0
-> 192.168.0.22:3306 Route 1 0 0
-> 192.168.0.23:3306 Route 1 0 0
問題なく起動すれば、Weight欄に1が入っているはずです。もしMariaDBがダウンするとWeight値は0になり、フォワーディングされなくなります。
ActiveConnは現在のコネクション数を表します。コネクション数が増えると、この数字も変化していきます。DBクラスタがどれぐらいビジーか、この値を確認することでチェックできます。
WordPressをLVSに向ける
さて、現在WordPressはDBサーバのうちの1つ、db1を直接参照していました。これをLVSに向けなおして、負荷分散・冗長化された状態にしてみましょう。設定はごく簡単で、wp-config.phpファイルを編集するだけです。
- wp-config.php
/** MySQL のホスト名 */
define('DB_HOST', '192.168.0.20');
変更できたら、WordPressサイトにアクセスしてみてください。問題なく表示できれば、すべてがうまく設定できたということです。
LVSサーバを2台に増やす
LVSサーバはまだ1台でしたので、ここで2台に増やして完全に冗長構成にしておきましょう。
実はLVSサーバの設定も、DBサーバと同じように完全にコピーすることで簡単に作ることができます。異なるのはインターフェースについているIPアドレスぐらいです。もちろん、手順通りにやり直していただいても結構ですが、さくらのクラウドをご利用の場合は
- いったんlvs1を停止
- lvs1をクローン、ディスクはコピーを選択
- lvs1は起動しておく
- lvs2を起動、モニタ接続でifcfg-eth0ファイルを書き換え(MACアドレスとIPアドレスの設定)
- reboot
で完了です。実はkeepalived.confはまったく同じ内容で動くので、これでLVSの冗長化は完了しているはずです。
障害発生時の動作
今回構築したシステムで、サーバのダウンや回線断が発生するとどうなるでしょうか。いくつかのケースを順に試してみましょう。
ノードのダウン
Galera Clusterのノードがダウンすると、何が起こるでしょうか。実際に確かめてみましょう。
webサーバから、VIPを介してDBクラスタに接続してみます。
[root@web ~]# mysql -u root -p --host=192.168.0.20
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 43
Server version: 5.5.28a-MariaDB MariaDB Server, wsrep_23.7rc1.rXXXX
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>
この状態ではクラスタのどのノードが応答したのか分かりませんので、ホスト名を引いてみます。
mysql> show variables like 'hostname';
+---------------+--------+
| Variable_name | Value |
+---------------+--------+
| hostname | db3 |
+---------------+--------+
1 row in set (0.05 sec)
mysql>
きちんとホスト名を設定していれば、これで現在どのノードに接続しているかが分かります。この例ではdb3に接続しています。そこでdb3のMariaDBを落としてみましょう。サーバの電源を落としてもいいのですが、ここでは簡単にサービスを停止するだけにしておきます。
[root@db3 ~]# service mysql stop
Shutting down MySQL... SUCCESS!
もういちどmysqlのコンソールに戻って、ホスト名を引いてみます。
mysql> show variables like 'hostname';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 5087
Current database: *** NONE ***
+---------------+--------+
| Variable_name | Value |
+---------------+--------+
| hostname | db2 |
+---------------+--------+
1 row in set (0.07 sec)
mysql>
このように、いったん接続エラーになり、リトライして別のノードに接続しなおしているのが分かります。LVSサーバではどのようになっているでしょうか。ipvsadmコマンドで確かめてみましょう。
[root@lvs1 ~]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP galera:mysql wlc
-> db1:mysql Route 1 0 0
-> db2:mysql Route 1 1 0
-> db3:mysql Route 0 0 0
db3のweight値が0になり、コネクション数も0になっているのが分かります。これらの動作をまとめると以下のようになります。
- サービスを監視しているkeepalivedが、ノードのダウンを検出して対象ノードのweight値を0にします。これによりスイッチングが停止し、故障ノードに処理が振り分けられるのを防止します
- Galera Clusterはノードのダウンを検出すると同期グループから自動的に外します。今後ノードが復帰してくるまでずっと外されたままです
- ノードがダウンした瞬間に処理していたトランザクションは失敗します。実は私はきちんと検証していませんが、故障ノードが他のノードと行おうとしている同期処理は、Galera Replicationがよろしく行ってくれるはずです(ここで不具合が発生するとデータの同期が壊れて大変なことになります)
- アプリケーションはセッションダウンに備えなければいけません。エラー処理を適切に行えば、エンドユーザに不愉快な思いはさせずに済むようアプリケーションを作れます
- ノードダウンから異常検出、切り離しまでは5秒~10秒程度かかります。この辺の数値はkeepalivedの設定によります。短くしようとするほどコスト(ヘルスチェックの頻度が上がるためにいろいろなものが犠牲になります)が大きくなりますが、切り替え時間は短くなります
WordPressの場合、せいぜいブログの記事やコメントの書き込みに失敗する程度ですし、切り替え時間からみて問題が生じる可能性は非常に低いと考えられます。もっとアクセス頻度が高く、特に書き換えが多いサービスでは厳密なチェックが必要でしょう。
LVSサーバのダウン
LVSサーバがダウンした場合は次のようになります。
- LVSサーバはマスター/バックアップ構成になっています。バックアップは定期的にマスターをチェックしています
- バックアップのkeepalivedがマスターのダウンを検出すると、VIPは無効になったと解釈して自らにVIPを設定します
- バックアップは自分を昇格させてマスターになります
このあとのDBへのアクセスは、バックアップのLVSが処理してくれるようになります。
Webサーバの冗長化
ここまでくると、実はWebサーバも冗長化が簡単だと気づかれる読者も多いと思いますが、さすがにこれを解説し始めると脱線が過ぎるのでここでは割愛させていただきます。興味のある方はぜひ自分でチャレンジしてください。
おわりに
今回はMariaDB Galera ClusterとLVSを組み合わせて、DBサーバとして利用可能なシステムを構築してみました。ちょっと試すだけならLVSの冗長化とkeepalivedの設定は不要ですが、クラスタDBを使う大きな理由に冗長化があると思われるため、ついでに実装しています。不明瞭な点は、Webにも優良な文献がたくさんありますので、ぜひそちらをご覧ください。
次回はMariaDB Galera Clusterのベンチマークを行ってみたいと思います。