MariaDB Galera Cluster による DB サーバの冗長化

さくらインターネット研究所さんの「MariaDB Galera Clusterを試す」という記事を読んで居ても立ってもいられなくなり、さっそく AWS で構築してみました。
上記の記事によれば

簡単にまとめると次のようになります。

  • Galera Replicationが複数のRDBMをレプリケーションするwsrep APIを提供し、同期をとります
  • 完全同期型であるため、すべてのノードがアクティブかつマスターとなります
  • クラスターノードのどれに対してもリード/ライトが可能です
  • ノードの追加/削除は自動で行えます
  • クライアント接続は通常のMySQLとなんら変わりなく使えます

via. MariaDB Galera Clusterを試す (1) « さくらインターネット研究所

おー!スレーブ/マスター形式のレプリケーションよりも、断然使いやすそうやんか!
ってわけで AWS の ELB 配下に複数台 MariaDB Galera Cluster を配置する方法を解説します。

MariaDB Galera Cluster のインストール

まずは CentOS 6 にインストールする方法。
MariaDB の公式サイトで yum レポジトリを用意してくれてるので、インストールは簡単です。
リポジトリジェネレータから、CentOS 6 用のリポジトリを生成して /etc/yum.repos.d/MariaDB.repo という名前で保存しましょう。
その後、以下のコマンドを実行してインストールします。

# yum install MariaDB-Galera-server MariaDB-client galera

インストールできたら /etc/my.cnf.d/server.cnf を修正します。
僕は、こんな感じにしました。

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
character-set-server = utf8
skip-name-resolve
default-storage-engine=InnoDB
binlog_format=ROW

wsrep_cluster_name=DBCLUSTER
wsrep_cluster_address=gcomm://10.0.0.2
wsrep_node_address=10.0.0.1
wsrep_provider='/usr/lib64/galera/libgalera_smm.so'
wsrep_sst_method=rsync
wsrep_slave_threads=4

innodb_strict_mode
innodb_file_per_table
innodb_additional_mem_pool_size = 16M
innodb_buffer_pool_size=64M
innodb_write_io_threads = 4
innodb_read_io_threads = 4
innodb_thread_concurrency = 16
innodb_log_file_size=16M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method=O_DIRECT
innodb_autoinc_lock_mode=2
innodb_locks_unsafe_for_binlog=1

query_cache_size=64M
query_cache_limit=2M
query_cache_min_res_unit=4k
query_cache_type=1

tmp_table_size=64M
max_heap_table_size=64M

table_open_cache=1024
max_allowed_packet=1M
sort_buffer=512K
read_buffer_size=256K
read_rnd_buffer_size=256K
join_buffer_size=256K
key_buffer_size=16M

wsrep_* が Galera Cluster 用の設定ですね。
12 – 14行目が重要です。
12行目(wsrep_cluster_name)は参加するクラスタの名前です。
13行目(wsrep_cluster_address)は参加したい MariaDB Galera Cluster にすでに参加している他のノードの IP アドレスです。
14行目(wsrep_node_address)は自分のノードの IP アドレスです。未指定ならば、一つ目の NIC に設定されているアドレスが自動的に採用されます。

ここまで準備できたら、初期ノードを起動します。
このとき、他のノードは起動していないので my.cnfwsrep_cluster_address が設定されていると隣のノードを探しに行ってエラーになってしまいます。
以下のようにして起動すれば、他のノードを探しに行かずに MariaDB Galera Cluster が起動します。

# service mysql start --wsrep_cluster_address=gcomm://

初期ノードが起動したら mysql -u root でログインして localhost とリモートサーバからログインできるようにユーザ権限を設定します。

MariaDB [(none)]> grant all privileges on *.* to root@'%' identified by 'DB_PASS' with grant option;
MariaDB [(none)]> grant all privileges on *.* to root@localhost identified by 'DB_PASS' with grant option;

既存のクラスタに参加する

2台目のノードを先ほど立ち上げたクラスタに参加させましょう。
と言っても、やることはほとんどありません。
初期ノードをインストールしたときと同様に MariaDB Galera Cluster をインストールします。
メンドくさいので、初期ノードをセットアップした EC2 から AMI を作成して、そこからローンチすれば良いでしょう。
その後 /etc/my.cnf.d/server.cnf の 13, 14 行目を修正してあげましょう。
wsrep_cluster_address は、初期ノードの IP アドレス、wsrep_node_address は自分の IP アドレスに変更します。
あとは # service mysql start で MySQL サーバを起動してやれば良いだけです。

この状態で mysql -u root -p でログインして、以下の sql 文を実行すると参加しているクラスタの情報が取得できます。

MariaDB [(none)]> show status like 'wsrep_%';
+----------------------------+---------------------------------------+
| Variable_name              | Value                                 |
+----------------------------+---------------------------------------+
| wsrep_local_state_uuid     | 792cf51c-7b29-11e2-0800-cf3c53b78e4d  |
| wsrep_protocol_version     | 4                                     |
| wsrep_last_committed       | 440                                   |
| wsrep_replicated           | 0                                     |
| wsrep_replicated_bytes     | 0                                     |
| wsrep_received             | 7                                     |
| wsrep_received_bytes       | 1220                                  |
| wsrep_local_commits        | 0                                     |
| wsrep_local_cert_failures  | 0                                     |
| wsrep_local_bf_aborts      | 0                                     |
| wsrep_local_replays        | 0                                     |
| wsrep_local_send_queue     | 0                                     |
| wsrep_local_send_queue_avg | 0.000000                              |
| wsrep_local_recv_queue     | 0                                     |
| wsrep_local_recv_queue_avg | 0.000000                              |
| wsrep_flow_control_paused  | 0.000000                              |
| wsrep_flow_control_sent    | 0                                     |
| wsrep_flow_control_recv    | 0                                     |
| wsrep_cert_deps_distance   | 0.000000                              |
| wsrep_apply_oooe           | 0.000000                              |
| wsrep_apply_oool           | 0.000000                              |
| wsrep_apply_window         | 0.000000                              |
| wsrep_commit_oooe          | 0.000000                              |
| wsrep_commit_oool          | 0.000000                              |
| wsrep_commit_window        | 0.000000                              |
| wsrep_local_state          | 4                                     |
| wsrep_local_state_comment  | Synced                                |
| wsrep_cert_index_size      | 0                                     |
| wsrep_causal_reads         | 0                                     |
| wsrep_incoming_addresses   | 10.0.0.1:3306,10.0.0.2:3306           |
| wsrep_cluster_conf_id      | 18                                    |
| wsrep_cluster_size         | 2                                     |
| wsrep_cluster_state_uuid   | 792cf51c-7b29-11e2-0800-cf3c53b78e4d  |
| wsrep_cluster_status       | Primary                               |
| wsrep_connected            | ON                                    |
| wsrep_local_index          | 1                                     |
| wsrep_provider_name        | Galera                                |
| wsrep_provider_vendor      | Codership Oy <info@codership.com>     |
| wsrep_provider_version     | 23.2.2(r137)                          |
| wsrep_ready                | ON                                    |
+----------------------------+---------------------------------------+
40 rows in set (0.00 sec)

wsrep_incoming_addresses で、参加しているノードの IP アドレスが確認できると思います。

ELB を前段に置いてロードバランスする

さて、このままだと処理を分散できないので、MariaDB Galera Cluster 達の前に ELB を置いてあげましょう。
ELB の設定とかは割愛します。
MySQL のポート 3306 を通すようにしてあげれば良いです。

ただし ELB の Health Check で 3306 を確認するようにすると、MySQL で以下のようなエラーが発生するようです。
(参考: Elastic Load BalancerでMySQLサーバーを冗長化しようとするとエラーが出る – kazu0620の日記)

Host 'hostname' is blocked because of many connection errors. 
Unblock with 'mysqladmin flush-hosts' 

そこで Perl モジュール付きの Nginx をインストールして、Perl で簡単な healthcheck プログラムを書いてみました。
php とかで healthcheck プログラムを作っても良かったんですが、これだけのためにわざわざ php-fpm 起動するのはもったいないですしね。
Nginx 公式サイトのリポジトリから配布されている Nginx サーバには perl モジュールは入っていないので、perl モジュール付きの Nginx を配布している epel リポジトリからインストールするか、Nginx をコンパイルする時に configure オプションとして -with-http_perl_module を付加してビルドしてください。

今回作成した mysql-heartbeat.pm は、以下のような感じです。
MySQL にログインできるかどうかチェックしてログインできたら heartbeat-mysql って表示するだけです。

package MySQLHeartBeat;
use nginx;
use DBI;
our $monitor_host = 'localhost';
our $monitor_port = '3306';
our $monitor_user = 'username';
our $monitor_pass = 'password';

sub handler {
   my $r = shift;
   if ( DBI->connect("dbi:mysql:mysql:$monitor_host:$monitor_port", $monitor_user, $monitor_pass) ) {
       $r->send_http_header('Content-Type', 'text/plain; charset=utf-8');
       $r->print("heartbeat-mysql\n");
   }
   return OK;
}

1;

$monitor_user, $monitor_pass は、ご自分の環境に合わせて修正してください。
これを /etc/nginx/modules/perl/mysql-heartbeat.pm として保存しておきます。

続いて /etc/nginx/nginx.conf を修正します。

http {
   :

  perl_modules /etc/nginx/modules/perl;
  perl_require mysql-heartbeat.pm;

  server {
    listen      80 default;
    server_name _;
    location /mysql-heartbeat {
      perl MySQLHeartBeat::handler;
    }
  }
}

これで http://localhost/mysql-heartbeat にアクセスすると MySQL に接続できる場合は heartbeat-mysql と返します。
MySQL に接続できない場合は無応答となるので、これを ELB から叩いてやれば healthcheck ができますね。
ここまでできたら、この状態で AMI を作っておくとノード追加が楽になりますよ。

Configure Health Checkさて、ELB の HealthCheck は以下のように設定します。

  • Ping Protocol: HTTP
  • Ping Port: 80
  • Ping Path: /mysql-heartbeat

他の値は任意で変更してください。

ELB のエンドポイントに対して MySQL で接続してみてください。接続が確認できると思います。
1台 MySQL サーバを落としても、ちゃんと接続できます。試してみてください。

これで MySQL サーバの冗長化が完成しました。
ノードを追加したい場合は、同じようにインスタンスを作って ELB の配下に入れてあげれば良いです。
簡単ですねー。

MariaDB Galera Cluster による DB サーバの冗長化」への1件のフィードバック

  1. cat.y

    はじめまして。私も評価中です。
    さくらさんで設定書いてる内蔵? (セキュアらしい)rsync でのDB内容ずれ自動同期設定だと、
    ポート 4444 の開放も必要ですよね。閉じてる場合、ログをちゃんと見るとエラーが出てますが。
    あとデーモン起動失敗が確定するのにちょっと時間がかかるので、サービス起動成功と出てもつながらないことが。
    galeraの接続失敗は起動後しばらくしてから確定するようで、少したってしらっとプロセス消えてる感じになります。

    返信

コメントを残す

メールアドレスが公開されることはありません。