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 の配下に入れてあげれば良いです。
簡単ですねー。

1 thought on “MariaDB Galera Cluster による DB サーバの冗長化

  1. cat.y

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

    返信

cat.y にコメントする コメントをキャンセル

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

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください