さくらインターネット研究所さんの「MariaDB Galera Clusterを試す」という記事を読んで居ても立ってもいられなくなり、さっそく AWS で構築してみました。
上記の記事によれば
簡単にまとめると次のようになります。
- Galera Replicationが複数のRDBMをレプリケーションするwsrep APIを提供し、同期をとります
- 完全同期型であるため、すべてのノードがアクティブかつマスターとなります
- クラスターノードのどれに対してもリード/ライトが可能です
- ノードの追加/削除は自動で行えます
- クライアント接続は通常のMySQLとなんら変わりなく使えます
おー!スレーブ/マスター形式のレプリケーションよりも、断然使いやすそうやんか!
ってわけで 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.cnf
に wsrep_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 を作っておくとノード追加が楽になりますよ。
さて、ELB の HealthCheck は以下のように設定します。
- Ping Protocol: HTTP
- Ping Port: 80
- Ping Path: /mysql-heartbeat
他の値は任意で変更してください。
ELB のエンドポイントに対して MySQL で接続してみてください。接続が確認できると思います。
1台 MySQL サーバを落としても、ちゃんと接続できます。試してみてください。
これで MySQL サーバの冗長化が完成しました。
ノードを追加したい場合は、同じようにインスタンスを作って ELB の配下に入れてあげれば良いです。
簡単ですねー。
はじめまして。私も評価中です。
さくらさんで設定書いてる内蔵? (セキュアらしい)rsync でのDB内容ずれ自動同期設定だと、
ポート 4444 の開放も必要ですよね。閉じてる場合、ログをちゃんと見るとエラーが出てますが。
あとデーモン起動失敗が確定するのにちょっと時間がかかるので、サービス起動成功と出てもつながらないことが。
galeraの接続失敗は起動後しばらくしてから確定するようで、少したってしらっとプロセス消えてる感じになります。