CUBE SUGAR CONTAINER

技術系のこと書きます。

MariaDB Galera Cluster を CentOS7 で使ってみる

MySQL (MariaDB) の冗長化には Master-Slave 構成を組んだり MySQL Cluster を使ったりと色々ある。 その中で Galera Cluster は同期レプリケーションを用いたマルチマスタ型のクラスタが組むやり方だ。

今回はこの MariaDB Galera Cluster を CentOS7 で組んでみることにする。 尚、Galera Cluster でクラスタを組むには最小構成でノードが 3 台必要となる。 また、各ノード間でレプリケーションのために IP の疎通が必要だ。 今回は各ノードに 192.168.33.0/24 のネットワークから .11 .12 .13 を付与する。

尚、今回はタイトルにある通り CentOS7 を使用する。

$ cat /etc/redhat-release 
CentOS Linux release 7.1.1503 (Core) 
$ uname -r
3.10.0-229.11.1.el7.x86_64

MariaDB Galera Cluster をインストールする

CentOS7 では RDB が MySQL から MySQL フォークの MariaDB に変更されている。 ただし、残念ながら Base リポジトリでは Galera Cluster に必要な RPM が用意されていない。 そのため、まずは MariaDB が提供しているリポジトリをノードの yum パッケージマネージャに登録する必要がある。

MariaDB の安定版リリースには 5.5 系と 10.0 系があるが、今回はより新しい 10.0 系を使うことにする。

$ cat << EOF | sudo tee /etc/yum.repos.d/MariaDB.repo > /dev/null
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.0/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1
EOF

リポジトリを登録したら一旦 yum をクリーンした上で必要な RPM パッケージをインストールする。

$ sudo yum clean all
$ sudo yum -y install MariaDB-Galera-server MariaDB-client galera

次に MariaDB の設定を行う。 ここに記述しているのはほとんどが Galera Cluster において必須のもの。 wsrep_cluster_address にはクラスタに参加する IP アドレスをカンマ区切りで羅列するので、使用する IP アドレスが違う場合は編集する必要がある。 また、wsrep_node_address には自身の IP アドレスを指定する。 何かあったときのエラーログについては /var/lib/mysq/error.log に出力される。

$ cat << EOF | sudo tee /etc/my.cnf.d/server.cnf > /dev/null
[mariadb]
wsrep_provider='/usr/lib64/galera/libgalera_smm.so'
wsrep_cluster_address=gcomm://192.168.33.11,192.168.33.12,192.168.33.13
wsrep_node_address=$(ip -f inet -o addr show | grep 192.168.33. | cut -d "/" -f 1 | awk '{print $NF}')
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
innodb_doublewrite=1
wsrep_on=ON

log-error = error.log
EOF

次に必要なポートを空ける。 3306 は MySQL クライアントとの接続に使う。 それ以外についてはレプリケーションなどでノード間の通信に必要となる。

$ for i in 3306 4444 4567 4568; do sudo firewall-cmd --permanent --add-port=$i/tcp; done;
$ sudo firewall-cmd --reload

あとは各ノードで MariaDB のサービスを開始する。 クラスタの最初のノードでは --wsrep-new-cluster オプションが必要。

$ sudo service mysql start --wsrep-new-cluster

ここは本来であれば CentOS7 だから systemctl コマンドを使うのでは?という感じなんだけど、公式ドキュメントの通りにコマンドを実行すると以下のようなエラーになってしまった。

$ systemctl start mysql --wsrep-new-cluster
systemctl: オプション '--wsrep-new-cluster' を認識できません

SysVinit の Script も用意してあるみたいだから、まあいいかな。

$ ls /etc/init.d/ | grep mysql
mysql

2 台目以降のノードについてはオプションを付ける必要はない。

$ sudo service mysql start

以上の操作をノード 3 台分繰り返す。

Vagrant で構築を自動化する

とはいえ、単調な作業をノード 3 台分繰り返すのはつらいので自動化するために Vagrantfile を用意した。

まずは Vagrant が使える状態にしておく。

$ vagrant --version
Vagrant 1.7.4

Vagrant のインストール方法については別のサイトに解説をゆずるけど、OSX であれば Homebrew と Homebrew Cask を使うのが楽ちん。

$ brew cask install virtualbox
$ brew install vagrant

あとはこちらで用意した Gist 上にある Vagrantfile をチェックアウトして実行するだけ。

$ git clone https://gist.github.com/b9f4575f840f4d33c671.git mariadb-galera-centos7
$ cd mariadb-galera-centos7
$ vagrant up

これでノード 3 台構成のクラスタが出来上がる。

$ vagrant ssh
Last login: Fri Aug 21 17:45:17 2015 from 10.0.2.2
Welcome to your Vagrant-built virtual machine.
$ mysql -u root
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.0.21-MariaDB-wsrep MariaDB Server, wsrep_25.10.r4144

Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> SHOW GLOBAL STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+
1 row in set (0.00 sec)

動作を確認する

ここからは作成した Galera Cluster の挙動を実際に触って確かめていこう。

Galera Cluster では各ノードはすべてマスタとして読み書きできる。 ここからは各ノードで mysql コマンドを使って localhost にログインして動作を確認しよう。

$ mysql -u root
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.0.21-MariaDB-wsrep MariaDB Server, wsrep_25.10.r4144

Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

各ノードが識別できるように、それぞれのノードではプロンプトの表示を変えておく。

MariaDB [(none)]> prompt node1 > ;
PROMPT set to 'node1 > '
node1 > 

まずは node1 でデータベースと、その中にテーブルを作る。

node1 > create database sample;
Query OK, 1 row affected (0.01 sec)

node1 > use sample;
Database changed

node1 > create table users (id integer primary key auto_increment, nickname text not null);
Query OK, 0 rows affected (0.13 sec)

すると、同じ内容がレプリケーションされて node2 でも見られるようになる。

node2 > show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sample             |
| test               |
+--------------------+
5 rows in set (0.01 sec)

node2 > use sample;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

node2 > desc users;
+----------+---------+------+-----+---------+----------------+
| Field    | Type    | Null | Key | Default | Extra          |
+----------+---------+------+-----+---------+----------------+
| id       | int(11) | NO   | PRI | NULL    | auto_increment |
| nickname | text    | NO   |     | NULL    |                |
+----------+---------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

また、Galera Cluster では MySQL Cluster と異なりトランザクション分離レベルにスタンドアロン構成と同様の REPEATABLE READ が使用できるため、ファジーリードやファントムリードを心配することなくトランザクションを使用できる。 以下では node1 で追加したレコードが node2 にもレプリケーションされることを示すと同時に、ダーティーリードが起きないことも確認している。

node1 > begin;
Query OK, 0 rows affected (0.00 sec)

node2 > begin;
Query OK, 0 rows affected (0.00 sec)

node1 > insert into users (nickname) values ("foo");
Query OK, 1 row affected (0.00 sec)

node2 > select * from users;
Empty set (0.00 sec)

node1 > commit;
Query OK, 0 rows affected (0.01 sec)

node2 > select * from users;
Empty set (0.00 sec)

node2 > commit;
Query OK, 0 rows affected (0.00 sec)

node2 > select * from users;
+----+----------+
| id | nickname |
+----+----------+
|  1 | foo      |
+----+----------+
1 row in set (0.01 sec)

一時的な障害が起きた際の挙動も確認しておこう。 まずは node2 の MariaDB サービスを停止する。

$ sudo service mysql stop
Shutting down MySQL.... SUCCESS! 

この状態で node1 に新しいレコードを追加する。

node1 > insert into users (nickname) values ("bar");
Query OK, 1 row affected (0.01 sec)

node1 > select * from users;
+----+----------+
| id | nickname |
+----+----------+
|  1 | foo      |
|  5 | bar      |
+----+----------+
2 rows in set (0.00 sec)

そして node2 の MariaDB サービスを復帰させる。

$ sudo service mysql start
Starting MySQL...SST in progress, setting sleep higher. SUCCESS! 

復帰後の node2 でテーブルの状態を確認してみると、停止中に追加されたレコードがちゃんと見える。 これは、サービス開始時のステートトランスファーで既存のクラスタとの同期を完了させているためだろう。 つまり、一時的な障害であればノードは自動的に復旧してくれる。

node2 > select * from users;
+----+----------+
| id | nickname |
+----+----------+
|  1 | foo      |
|  5 | bar      |
+----+----------+
2 rows in set (0.00 sec)

べんりっ

まとめ

今回は CentOS7 を使って MariaDB Galera Cluster を組んでみた。 既存の冗長化手法と Galera Cluster を比べると、自分で冗長化のためのスクリプトなどを書く手間がなかったり、トランザクション分離レベルが落ちるといったこともないので使い勝手がいい。 とはいえ、もちろん Galera Cluster も完全無欠の存在ではないので幾つかの制限事項はあるみたい。 それについてはまた次の機会に書くことにする。