昔からMySQLでレプリカ(複製)が作成できる機能の存在は知っていたけれど、大きなスケールのサイトで、ロードバランスに使用される読み込み専門のレプリカとして使われるだろうな、くらいに思っていました。しかし、最近はこれがほぼリアルタイムに近いバックアップとしても使える可能性を知り、早速取り組んだ次第です。

AWS RDS

MySQLのデータベースのレプリカを作成するには、もちろんレプリカのソースとなるマスターのMySQLサーバーがあります。私のケースでは、LAMPなのでマスターはAWSのEC2のマシンに常駐サービスの1つとして存在しますが、さてレプリカはどこに置いたら良いでしょう?

レプリカと言ってもこれもまたMySQLサーバーが必要なので、新規のEC2のマシンを用意して、そこにレプリカを置くことになります。しかし単にレプリカのためだけの目的で、もう1つマシンを用意するのはちょっとです。将来においてマシンのOS更新とかバックアップの設定とかメンテが面倒です。そこで注目したのは、AWSのRDSです。今時流行りのSAAS(Software AS Service)です。

今まで使ったことがないAWSのサービスなので親交を深める良い機会です。と思って取り組みましたが、はっきり言って、手順が複雑です。数日かけて何回も失敗して、レプリカのDBを何回も作成し直して。。。ということで、忘れないように貴重な(少なくとも私には)手順の作成となりました。

1.レプリカのDBインスタンスの作成(AWS)

まずは、AWS RDSのページへ行き、レプリカを保存するDBインスタンス(マシン)の作成です。以下へのアクセスは、AWSへのアカウントへのログインが必要です。

https://ap-northeast-1.console.aws.amazon.com/rds/home?region=ap-northeast-1#launch-dbinstance:

その後以下のステップを経て、DBインスタンスの作成です。AWSでは「データベースの作成」という言葉が使用されていますが、実際は、DBインスタンス、つまりマシンの作成です。1つのDBインスタンスで、いくつものデータベースを作成が可能ですので。混乱しないように注意してください。

ステップ1 エンジンの選択

ここでは、「エンジン」にはMySQLを選択します。また、最初のテスト段階では必ず「無料バージョン」を選択するのを忘れずに。

ステップ2 DB詳細の指定

ここでは、とくに「DBエンジンのバージョン」に注意してください。なるべくマスターと同じMySQLのバージョンの使用を薦めます。すでに「無料利用枠」を選択しているなら、「DBインスタンスのクラス」は、一番小さいマシンのt2.microしか選択できません。他のオプションも選択不可となっています。下の画面には表示されていませんが、「DBインスタンの識別子」や「マスターユーザーの名前」と「マスターパスワード」の入力も必要です。

ステップ3 詳細設定の指定

この画面での注意としては、「パブリックアクセシビリティ」では「いいえ」を選択すること。いろいろなケースがありますが、今回はマスターとのコミュニケーションをプライベートのIPアドレスを通して行うゆえにです。さらに、「アベイラビリティーゾーン」では、必ずマスターとは違うゾーンを選ぶことです。ゾーンは物理的に独立した場所にあるデータセンターなので、片方で火事などが起こってデータを失ってもも片方ではOKという仮定です。ここでは触れませんが、このためにVPCのサブネットを前もって作成する必要があるかもしれません。

完了!

これでDBインスタンス作成の設定が完了です。これから数分間でDBインスタンスへのアクセスが可能となります。

2.マスターでの設定と作業

レプリカのDBインスタンスを作成したところで、今度はマスターでの設定です。いくつかの作業があります。

レプリカのログの作成

マスターのMySQLサーバーにおいて、レプリカのためのログの作成が必要です。このログは、マスターのDBで実行されるすべてのSQL文を皆バイナリのファイルに入れたようなものです。それをレプリカが定期的にのMySQLサーバーが取りに行き、レプリカでそれらを実行してマスターと同期するわけです。

ログのファイルは以下のように番号が付いたファイルとして作成されます。最新のものほど大きい番号となります。

$ ls -1
mysql-bin.000001
mysql-bin.000002
mysql-bin.000003
mysql-bin.index

このログ作成の設定は、マスターのマシンの/etc/my.cnfで行います。

...
[mysqld]
...
server-id=1	
binlog-do-db = test1_db
binlog-do-db = test2_db
log-bin=/var/lib/mysql/logs/mysql-bin.log	
...

server-idはユニークな数字のIDでレプリカのIDとは重複しないようにしてください。私の設定ではAWS RDSのレプリカのserver-idは非常に大きな数字(例:2092607505)なので小さい数字ならとりあえずOKです。binlog-do-dbではレプリカを作成したいDB名を指定します。複数のときは例のように複数行で指定します。log-binは、ログファイルの場所とそのファイル名のフォーマットを指定します。

設定後に、MySQLサーバーをリスタートします。もちろんその前にLaravelのアプリなら、

$ php artisan down

や、クロンジョブなどのサービスをダウンさせること忘れないでください。

リスタート後に、MySQLのコマンドで、マスターのステータスを見ることができます。

		
mysql> show master status;

+------------------+----------+----------------------------------+------------------+
| File             | Position | Binlog_Do_DB                     | Binlog_Ignore_DB |
+------------------+----------+----------------------------------+------------------+
| mysql-bin.000001 |      107 | test1_db, test2_db               |                  |
+------------------+----------+----------------------------------+------------------+
1 row in set (0.00 sec)

この時点では、Laravelのアプリもクロンジョブも再開してOKです。

レプリカのためのDBユーザーの作成

今度は、レプリカのMySQLがマスターのログからデータを取得するために、マスターにおいてDBユーザーを作成します。

$ mysql -u root -p
...
mysql> GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'%' IDENTIFIED BY 'testtest';
mysql> FLUSH PRIVILEGES;

最初の行は、slave_userがtesttestのパスワードでログインして複製の情報を取得することをOKしています。ここではどこのホストからでもマスターのどのデータベースの複製がOKという設定です。

レプリカからマスターへのアクセス制限

ここではインスタンス(マシン)レベルで、レプリカがマスターにアクセスできる設定をします。
まず、レプリカのDBインスタンスのIPアドレスが必要です。DBインスタンスの情報画面で、まず「エンドポイント」のホスト名を取得します。

それを、シェルでpingします。

$ ping db1.xxxxxx.us-west-2.rds.amazonaws.com
PING db1.xxxxxx.us-west-2.rds.amazonaws.com (10.0.1.32) 56(84) bytes of data.

そのIPアドレスを今度は、AWSのコンソールの「セキュリティグループ」で以下のように、ポート3306に対してオープンします。このセキュリティグループはマスターで使用されているものです。

レプリカの設定

もうゴールは近しです。マスターのデータをレプリカにコピーしてレプリカの開始です。

マスターからレプリカへのアクセスの設定

先ほどとは逆に、今度はマスターからレプリカへアクセスできるための設定です。AWSコンソールで先と同様な作業ですが、今度は、レプリカの「ステップ3 詳細設定の指定」の画面で作成したあるいは指定したセキュリティグループにおいて、マスターからのアクセスを許します。

画面での、10.0.0.137は、マスターのプライベートのIPアドレスです。

マスターのデータをレプリカにコピーして複製を開始

まず、マスターで対象のデータベースのデータをエクスポートします。

$ mysqldump -u root test1_db --master-data -p > test1_db.sql

次には、レプリカにアクセスしてそのデータをレプリカにインポートします。

$ mysql -u root -h db1.xxxxxx.us-west-2.rds.amazonaws.com -p
...
mysql> source test1_db.sql
...

引き続いて、レプリカのMySQLコマンドでマスターを設定して、複製を開始します。

mysql> CALL mysql.rds_set_external_master ('10.0.0.137', 3306, 'slave_user', 'testtest', 'mysql-bin.000001', 107, 0);		
mysql> CALL mysql.rds_start_replication;		

最初の行の、‘mysql-bin.000001’, 107の数字は、test_db1.sqlのファイルの最初のページから情報が得られます(以下)。

--
-- Position to start replication or point-in-time recovery from
--

CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=107;

エクスポートの時点でのどのログファイル、どの位置かがこれでわかるため、エクスポートからインポートの間にどんなに時間が開いても、どんな変更があっても、そこの場所から複製を開始してマスターと同期できるわけです。

最後に、レプリカの複製の状況を見たいときは、コマンドshow slave statusを実行します。ここで問題があれば、エラーとして表示されますので注意をしてください。

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.0.0.137
                  Master_User: slave_user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 8148599
               Relay_Log_File: relaylog.002965
                Relay_Log_Pos: 658
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: mysql.rds_sysinfo,mysql.rds_history,mysql.rds_replication_status
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 8148599
              Relay_Log_Space: 1357
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1
1 row in set (0.00 sec)

マスター側では、レプリカのRDSからログを取得に来ているのが以下でわかります。

$ mysql -u root -p
...
mysql> show processlist;
+--------+------------+-----------------------------------------------+------+-------------+--------+-----------------------------------------------------------------------+------------------+
| Id     | User       | Host                                          | db   | Command     | Time   | State                                                                 | Info             |
+--------+------------+-----------------------------------------------+------+-------------+--------+-----------------------------------------------------------------------+------------------+
|  54532 | slave_user | ip-10-0-1-32.us-west-2.compute.internal:47756 | NULL | Binlog Dump | 440946 | Master has sent all binlog to slave; waiting for binlog to be updated | NULL             |
| 102446 | root       | localhost                                     | NULL | Query       |      0 | NULL                                                                  | show processlist |
+--------+------------+-----------------------------------------------+------+-------------+--------+-----------------------------------------------------------------------+------------------

By khino