MySQL互換でマルチマスタ同期レプリケーションが可能なPercona XtraDB Clusterで手軽に冗長構成RDBを構築


# はじめに
みなさんMySQLを使うとき、冗長化はどうしてますか？
Master-Slaveのレプリケーションを頑張って組むか、RDSみたいなフルマネージドサービスを使うか、選択肢は色々あります。

この記事ではシンプルな構成で簡単にMySQL互換の冗長化クラスタを構築する方法を紹介します。
Master-Slaveのように複雑でなく明瞭簡潔なので、構築・運用も楽になります。
コストや自由度の面からRDSはちょっと、という人にもいいかもしれません。

細かくいうと、全ノード書き込み可能なマルチマスタで、同期的にレプリケーションを行うRDBクラスタを構築します。
もちろんトランザクションも処理できますし、障害時にフェイルオーバが発生せずダウンタイムが少ないという特徴もあります。
[Percona XtraDB Cluster](http://www.percona.com/software/percona-xtradb-cluster)というオープンソースのRDBMSを使用して構築します。

# MySQLフォーク
細かいことは省きますがMySQLの開発元がなんだかんだでOracleになってから、MySQLフォークの開発が盛んになっています。
また、多くのLinuxディストリビューションが標準データベースにMySQLではなくMySQLフォークを選択するようになりました。

MySQLフォークとは、ソースコードが公開されているMySQLから派生したRDBMSです。
特に有名なのは、InnoDBの代替ストレージエンジンのXtraDBを使用している[MariaDB](https://mariadb.org/)と[Percona Server](http://www.percona.com/software/percona-server)の2つです。

MariaDBはRed Hat Enterprise Linux 7の標準DBとして採用されたことでも注目されています。

PerconaもMariaDBも、簡単にいうと性能強化されたMySQL互換のRDBMSです。
そのため、MySQLからこれらのDBに乗り換えるだけで性能をあげることも可能です。

MariaDBは独自進化に向かっているのに対して、PerconaはOracleのMySQL Enterpriseに完全互換で強化を施した製品を目指しています。
（MariaDBはそのうちまたMySQL互換に寄せていくといってるらしいですが）

Percona製品はPercona社が開発しています。
Percona社はPercona Server以外にもXtraDB、XtraDB Cluster、XtraBackup、Toolkitなど周辺ツールも開発しています。
MariaDBに比べてPercona Serverは日本ではマイナーですが、XtraBackupやToolkitは使っているという方も多いんじゃないでしょうか。

次の理由から私はPerconaの方が好きなので、この記事でもPerconaで冗長化クラスタを組む方法を紹介したいと思います。

* 強いMySQL互換を目指している
* データベースエンジンや周辺ツールも同社で開発している
* 実際使ってみてPercona Serverの方が完成度が高く感じる
* 名前がかっこいい（XtraDB!!）

# Percona XtraDB Cluster
## Percona Server
MySQLフォークのところでも説明しましたが、Percona ServerはMySQLを元にPercona社が開発しているRDBMSです。
InnoDBの代わりにXtraDBというストレージエンジンを使用していて、

* 性能
* スケーラビリティ
* 信頼性
* 機能
* 管理

などが強化されています。

[ベンチマーク](http://www.percona.com/software/percona-server/benchmarks)を見るととてもわかりやすいです。
（Percona社が出してるものですが、、）

Percona社は、元々MySQLのコンサルで実績のある会社です。
技術者が世界各地に点在していて、世界中で活動しています。

## Percona XtraDB Cluster
Percona XtraDB Clusterは、Percona Serverを元に冗長化クラスタを構築するためのパッケージです。

次のコンポーネントが同梱されています。

* Percona Server
* Galera Library
* wsrep API
* Percona XtraBackup

[Galera Library](http://galeracluster.com/)は[Cordership Oy社](http://galeracluster.com/company/)が開発しているマルチマスタ同期レプリケーションを実現するためのライブラリです。
Galera Libraryを使うことで、簡単に冗長化を行うことができます。

[wsrep API](https://launchpad.net/wsrep)(Write Set Replication API)はマルチマスタ同期レプリケーションのための標準APIです。
レプリケーションプロバイダとアプリケーション間のAPIを定義しています。

## マルチマスタ同期レプリケーション
Percona XtraDB Clusterではアクティブ-アクティブなマルチマスタ同期レプリケーションを実現できます。
つまり、クラスタを構成する全ノードがマスタであり、全ノードで書き込み/読み込みが可能で、全ノード間で同期的にレプリケーションが行われます。
ここが一般的なMySQLの冗長化とは異なる部分です。

MariaDB、MySQLでもGaleraを使用したマルチマスタ同期レプリケーションクラスタが構築可能です。

全ノードがアクティブであるため、障害時にFailoverが発生することもなくダウンタイムがありません。
また、動的にノードの追加・削除が可能なため、無停止での設定変更やバージョンアップを行うことができます。
そしてこれらの操作も非常にシンプルです。

CAP定理でいうと、GaleraはCA、つまり一貫性と可用性を実現しています。
トランザクション処理のためのACID特性も保証されています。

Split Brainも発生しないように設計されており、信頼性の高いシステムが構築できます。

# 作るもの
前置きが長かったですが、ここから実際に構築していきます。

今回はVagrantで複数のVMを立ち上げ、クラスタを構築します。
そして最後にRailsのアプリケーションからクラスタにアクセスします。

## 構成
構成は次の図のようになります。

![20141223 PXC.001.png](https://qiita-image-store.s3.amazonaws.com/0/43208/1f45bcd4-126a-2109-2c84-5ea261b51160.png "20141223 PXC.001.png")


VagrantでVM（CentOS 7）を3台立ち上げ、各VMにPercona XtraDB Cluster（PXC）をインストールします。
その3台でクラスタを構築します。
（3台というのはPerconaが推奨しているクラスタの最小構成数です）

Railsアプリ用にも1台VMを立ち上げ、RailsアプリからはHAProxyを経由してDBにアクセスするようにします。

## バージョン
今回使用したソフトウェアのバージョンは次の通りです。

* Vagrant 1.7.1
* VirtualBox 4.3.18
* CentOS 7.0
* Percona XtraDB Cluster 5.6.21
* Percona XtraBackup 2.2.7
* Galera 3.8

# Percona XtraDB Cluster構築
まずは、PXCを構築します。


## 1. VagrantでCentOS起動
PXC入りCentOS 7を3台立ち上げるVagrantfileをGistに用意したので、そちらを使ってください。
https://gist.github.com/nownabe/27ffdd8fed6a3f1707e8

```bash
$ mkdir pxc_demo
$ cd pxc_demo
$ curl -O https://gist.githubusercontent.com/nownabe/27ffdd8fed6a3f1707e8/raw/62df5d42e00c483f8cbaa3818e7e5eb092836f85/Vagrantfile
$ vagrant up
```

少し時間がかかります。

### (おまけ) Percona XtraDB Clusterのインストール
今回はVagrantfileのprovisionで自動でインストールしましたが、手動でインストールするときは次の手順になります。
（と言ってもVagrantfileに書いてある内容そのままですが）

```bash
$ sudo yum install -y epel-release
$ sudo yum install -y http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm
$ sudo yum install -y Percona-XtraDB-Cluster-56
```

epelは、PXCと依存関係のあるsocatインストールで必要になります。

## 2. my.cnf
次に、PXC用の`/etc/my.cnf`を説明します。
上記のVagrantfileを使用した場合は、すでに設定されているのでここは眺めるだけでOKです。

`wsrep_node_address`のみ各ノードごとに異なります。

```:/etc/my.cnf
[mysqld]

datadir=/var/lib/mysql
user=mysql

# Galera Libraryのパス
wsrep_provider=/usr/lib64/libgalera_smm.so

# クラスタを構成するノードのアドレス
wsrep_cluster_address=gcomm://10.6.3.101,10.6.3.102,10.6.3.103

# Galeraを使うときはROWにする必要があります
binlog_format=ROW

# Galeraが正式にサポートしているのはInnoDBだけです（Perconaは内部的にはXtraDBを使っています）
default_storage_engine=InnoDB

# これもGaleraの要件です
innodb_autoinc_lock_mode=2

# ノードのアドレス
wsrep_node_address=10.6.3.101

# SSTにxtrabackupを使用します。SSTはPXCにおけるノード間データ転送のひとつで、rsync、mysqldumpも使えます。
wsrep_sst_method=xtrabackup-v2

# クラスタ名
wsrep_cluster_name=pxcdemo

# SSTの認証情報
wsrep_sst_auth="sstuser:s3cret"

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
```

## 3. クラスタ起動
### 3.1 クラスタ初回起動について
今回クラスタの起動で2つの特殊な操作を行います。

* 1台目の起動
* SST用ユーザの作成

これらはクラスタの初回起動時に必要な操作になります。

まず、1台目の起動について説明します。

PXCでは普通にサービスを起動すると、`/etc/my.cnf`の`wsrep_cluster_address`を見て、それらのいずれかのノードに接続しに行きます。
しかし、クラスタに1台もノードがいない状態だと接続に失敗し、起動自体にも失敗します。
そのため1台目だけは特別なコマンドで起動を行う必要があります。

次はSST用ユーザの作成について説明します。

2台目以降のPXCは起動すると`wsrep_cluster_address`をみてクラスタに接続します。
そして、SST(Snapshot State Transfer)によりクラスタのいずれかのノードからデータを同期します。
同期が完了するとクラスタのメンバーとして稼働し始めます。

今回の設定だとSSTに認証が必要なため、 接続される側でSST用のユーザを作っておく必要があります。
2台目以降にはユーザ情報も自動で同期されるので、1台目で1度作ればOKです。

### 3.2 1台目起動
では1台目を起動します。

```bash
$ vagrant ssh pxcnode-01 -c 'sudo systemctl start mysql@bootstrap'
```

1台目は`systemctl start mysql@bootstrap`というコマンドで起動します。
CentOS 6の場合は、`service mysql bootstrap-pxc`になります。

SST用のユーザを作成します。

```bash
$ vagrant ssh pxcnode-01 -c 'mysql -uroot'
mysql> create user 'sstuser'@'localhost' identified by 's3cret';
mysql> grant RELOAD, LOCK TABLES, REPLICATION CLIENT on *.* to 'sstuser'@'localhost';
mysql> flush privileges;
mysql> exit
```

### 3.3 2台目、3台目起動
2台目と3台目は普通に起動します。

```bash
$ vagrant ssh pxcnode-02 -c 'sudo systemctl start mysql'
$ vagrant ssh pxcnode-03 -c 'sudo systemctl start mysql'
```

起動すると、自動でpxcnode-01からSSTによるデータ同期が始まります。
今回はほとんどデータがない状態なのですぐ終わります。

## 4. 動作確認
3つのノードで書き込みと確認を行っていきます。

まず、#1でデータベースを作成します。

```bash
$ vagrant ssh pxcnode-01 -c 'mysql -uroot -e "create database demo"'
```

\#2で作成されたデータベースを確認し、テーブルを作成します。

```bash
$ vagrant ssh pxcnode-02 -c 'mysql -uroot -e "show databases"'
+--------------------+
| Database           |
+--------------------+
| information_schema |
| demo               |
| mysql              |
| performance_schema |
| test               |
+--------------------+
$ vagrant ssh pxcnode-02 -c 'mysql -uroot demo -e "create table demotable (name varchar(64))"'
```

\#3でテーブルを確認し、挿入します。

```bash
$ vagrant ssh pxcnode-03 -c 'mysql -uroot demo -e "show tables"'
+----------------+
| Tables_in_demo |
+----------------+
| demotable      |
+----------------+
$ vagrant ssh pxcnode-03 -c 'mysql -uroot demo -e "insert into demotable values (\"from #03\")"'
```

\#1で挿入したデータを確認します。
また、Rais用のユーザとHAProxyのヘルスチェック用のユーザも作成しておきます。

```bash
$ vagrant ssh pxcnode-01 -c 'mysql -uroot demo -e "select * from demotable"'
+----------+
| name     |
+----------+
| from #03 |
+----------+
$ vagrant ssh pxcnode-01 -c "mysql -uroot -e \"grant all privileges on *.* to 'demo'@'%'\""
$ vagrant ssh pxcnode-01 -c "mysql -uroot -e \"grant usage on *.* to 'haproxy'@'%'\""
```

こんな感じで、Percona XtraDB Clusterを使うと手軽に冗長構成RDBを構築することができます。

# アプリケーションからアクセス

## 1. VagrantでCentOS起動
HAProxy + Rails用のVMを立ち上げます。
Rubyをビルドするので、少し時間がかかります。

```bash
$ mkdir pxc_demo_rails
$ cd pxc_demo_rails
$ curl -o Vagrantfile https://gist.githubusercontent.com/nownabe/27ffdd8fed6a3f1707e8/raw/5f66c1301a7f093ec7d99caff40584ed8a4a03ea/Vagrantfile-ruby
$ vagrant up
```

## 2. HAProxy設定
HAProxyの設定ファイルを編集して、サービスを起動します。
これもGistにあるので、ダウンロードして使ってください。

```bash
$ vagrant ssh -c 'sudo curl -o /etc/haproxy/haproxy.cfg https://gist.githubusercontent.com/nownabe/27ffdd8fed6a3f1707e8/raw/14d7f25fbd76c554c451610f2ac033bf809297bf/haproxy.cfg'
$ vagrant ssh -c 'sudo systemctl start haproxy'
```

以下、コンフィグの解説（抜粋）です。

```:haproxy.cfg
# HAProxyのステータスをWebで見れるようにする設定です
listen hastats *:80
    mode http
    maxconn 64
    timeout connect 5000
    timeout client 10000
    timeout server 10000
    stats enable
    stats show-legends
    stats uri /haproxy?hastats
    
    # ステータス画面のユーザ名とパスワードを設定しています
    stats auth admin:p@ssw0rd
    
# MySQLの分散設定です
listen mysql *:3306
    mode tcp
    
    # mysql-checkという専用のヘルスチェック方式で、MySQLサーバに負荷をかけないようにしています
    option mysql-check user haproxy
    
    # ラウンドロビンでPXCの3ノードに分散します
    balance roundrobin
    server pxcnode-01 10.6.3.101 check
    server pxcnode-02 10.6.3.102 check
    server pxcnode-03 10.6.3.103 check
```

Railsアプリを作成する前に、ちゃんと負荷分散できてるか確認してみましょう。
次のコマンドでPXCから接続先のIPが取得できます。

```bash
$ vagrant ssh -c "mysql -udemo -h 127.0.0.1 -e 'show variables like \"wsrep_provider_options\"' | tail -1 | awk '{print \$4}'"
10.6.3.101;
$ vagrant ssh -c "mysql -udemo -h 127.0.0.1 -e 'show variables like \"wsrep_provider_options\"' | tail -1 | awk '{print \$4}'"
10.6.3.102;
$ vagrant ssh -c "mysql -udemo -h 127.0.0.1 -e 'show variables like \"wsrep_provider_options\"' | tail -1 | awk '{print \$4}'"
10.6.3.103;
$ vagrant ssh -c "mysql -udemo -h 127.0.0.1 -e 'show variables like \"wsrep_provider_options\"' | tail -1 | awk '{print \$4}'"
10.6.3.101;
```

ちゃんとラウンドロビンで分散されていることがわかります。

ブラウザで`http://10.6.3.100/haproxy?hastats`にアクセスしてユーザとパスワードを入力すると、HAProxyのステータスが表示されます。
mysqlのSessionsのTotalを見ると、どのPXCノードに何回接続したかを確認できます。

![スクリーンショット 2014-12-25 0.56.27.png](https://qiita-image-store.s3.amazonaws.com/0/43208/b3e6ebb0-bc17-6d95-b342-7d0f77948c0f.png "スクリーンショット 2014-12-25 0.56.27.png")


## 4. Railsアプリ作成
では、Railsのアプリを作ります。
といってもPXCの動作確認が目的なのでScaffoldだけです。

ここからVMにSSHでログインして作業します。

```bash
$ vagrant ssh
```

まずはRailsアプリを作成しましょう。

```bash
$ rails new pxcdemo -d mysql -B
$ cd pxcdemo
$ echo "gem 'therubyracer', platform: :ruby" >> Gemfile
$ bundle install --path vendor/bundle
$ bundle exec rails g scaffold article title:string author:string body:text
```

`config/database.yml`を書き換えます。

```yaml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: demo
  password:
  host: 127.0.0.1

development:
  <<: *default
  database: demo
```

Migrateしてサーバを起動します。

```bash
$ bundle exec rake db:migrate
$ bundle exec rails s -b 0.0.0.0
```


## 5. 動作確認
Railsのサーバが起動できたら、ブラウザで`http://10.6.3.100:3000/articles`にアクセスして、適当にデータ入力してみてください。
![スクリーンショット 2014-12-25 0.53.35.png](https://qiita-image-store.s3.amazonaws.com/0/43208/ea7ca384-65ad-08aa-b080-ec4665690045.png "スクリーンショット 2014-12-25 0.53.35.png")

HAProxyのステータス画面（`http://10.6.3.100/haproxy?hastats`）にアクセスすると、接続数が増えてることがわかります。

こんな感じで、HAProxyを使うことで簡単にアプリからPercona XtraDB Clusterにアクセスすることができます。
HAProxyを各アプリケーションサーバに同居させることで、単一障害点を排除できます。

# おわりに
長くなりましたが、以上です。
Percona XtraDB Clusterの構築方法と、アプリからの使用方法を書いてみました。

PXCを使うと本当に簡単に冗長化・負荷分散が実現できるので超おすすめです。
是非使ってみてください。

# 参考
* [Percona](http://www.percona.com/)
* [Percona Server](http://www.percona.com/software/percona-server)
* [Percona XtraDB Cluster](http://www.percona.com/software/percona-xtradb-cluster)
* [MariaDB](https://mariadb.org/)
* [Linuxディストリビューションから消えてもMySQLが進化するワケ](http://itpro.nikkeibp.co.jp/article/Watcher/20130405/468923/)
* [MySQL を超える](http://www.ibm.com/developerworks/jp/opensource/library/os-beyondmysql/)