Amazon Auroraへの移行

以下は2016-04-27に会社(Fablic, inc.)の技術ブログinFablicに投稿した記事(http://in.fablic.co.jp/entry/2016/04/27/182453)になります。
inFablicが閉鎖され閲覧ができなくなってしまったため、会社の了承を取った上で転載しております。

元記事にリンクを貼っていただいていた方に対しては大変お手数ですが、こちらにリンクし直していただければ幸いです。


こんにちは、エンジニアの@yutadayoです。 Fablic が運営するフリルでは先日インフラをEC2-Classic環境からVPC環境に全面移行し、同時にリリース当時から使っていた Amazon RDS for MySQL から Amazon Aurora へと移行しました。

まず、移行して2ヶ月程度Auroraを運用した上での、良かった点と改善が必要な点を紹介します。

良かった点

レプリケーション遅延の解消

触れ込みの通り、Amazon Aurora レプリカはプライマリと同じデータボリュームを共有しているため、リプリケーションの遅延はほとんどありません、今まで遅延に悩まされていた課題はこれで解決しました。

コストカット

MySQLでは Master に対してリードレプリカ2台の構成にしていたのですが、運用後パフォーマンスをみて、AuroraではReaderを1台減らすことにしました。 これにより、インスタンス1台分のコスト削減につながりました。

パフォーマンスの改善

各指標で MySQL と比べてパフォーマンスの改善がみられました。
db.r3.2xlarge で比較しています。

パフォーマンスが改善したお陰でアプリケーションの応答性能が改善し、移行前に比べて 50ms ほどフリルが使用しているAPIのレスポンスタイムが早くなりました。

改善が必要な点

良い面もありましたが、改善が必要な点もいくつか見受けられました、やはり銀の弾丸は無いので、Auroraを選択する際は下記の点に注意を払う必要があります。

突発的な再起動

負荷が比較的高い状態や、DBのスキーマ変更を行った場合に、突発的にリードレプリカが再起動する現象に何度か見舞われました。ほとんど負荷が無い状態でも発生したケースもありますので、アプリケーション側でDBの接続を切るなど、再起動時に何らかの対応が必要かと思います。

定期的なメンテナンス

移行後に2回ほど Aurora 自体のAWSの定期メンテナンスが行われました、メンテナンス後にパフォーマンスが上がったケースもありますが、まだまだ不安定な部分もあるようです。特にメンテナンスが行われる際は、数分ですがダウンタイムが発生しますので、DBを稼働させ続けないといけないアプリケーションにとっては悩みのタネになるでしょう。

Auroraへの移行の経緯

ここからは、移行への経緯と実施した作業の詳細を紹介したいと思います。 フリルではAmazon RDS for MySQLをサービスリリース当初から使っておりましたが、サービスが成長するにつれて、下記の問題点がでてきました。

  • リードレプリカのレプリケーション遅延
  • Push通知などの高負荷状態におけるパフォーマンス低下

Auroraのセールスポイントとして、公式にもあるように下記の点で魅力を感じていました。

高い可用性とレプリケーション

Amazon Aurora レプリカはプライマリと同じデータボリュームを共有しているため、実質的にレプリケーションラグはありません。 ラグは通常数十ミリ秒です。MySQL リードレプリカの場合、レプリケーションラグは変更率または適用率、およびネットワーク通信の遅延に応じて無制限に増大する可能性があります。ただし、通常の状況では数分のレプリケーションラグが一般的です。

パフォーマンス

SysBench による r3.8xlarge インスタンスのテストの結果、Amazon Aurora は 500,000 セレクト/秒および 100,000 アップデート/秒のパフォーマンスを示しました。これは同じハードウェアで実行する MySQL の 5 倍のパフォーマンスです。

AWSの進化は早く、上記のようなメリットも十分にあったため、EC2-Classic 環境から VPC環境に移行するタイミングでDBもAuroraに移行することを決断しました。

移行作業

移行前の環境は下記のような構成になっていました。

f:id:infablic:20160424192036p:plain

EC2-Classic 環境に構築されていた Master / Slave 構成のRDS for MySQLVPC環境化にAuroraとして移行するのがゴールです。

f:id:infablic:20160424191725p:plain

移行手順は色々検討したのですが、公式サイトにも載っている下記のページを参考に、スナップショットとレプリケーションを使用して移行することにしました。

RDS for MySQL(以下 MySQLと表記します) のスナップショットから Aurora インスタンスを作成します。その後、MySQL をマスター Aurora をスレーブとしたレプリケーションを構築し、稼働中の MySQL へ書き込まれていくデータを Aurora にも反映させます。レプリケーションが完了したら、サービスを停止し、アプリケーションが接続するデータベースを切り替えます。

  • データ量にもよりますが、それなりの大きさだとスナップショットから Auroraインスタンスを作成するには数時間かかります。
  • サービスを止めて、移行できるのであれば、単純にスナップショットからAuroraインスタンスを作成するだけで良いかと思いますが、サービスの稼働を止められない場合はレプリケーションを行うことで、サービスのダウンタイムを極力短くすることができます。

手順

レプリケーション設定を行う準備段階と、サービスを止めて、切り替えを行うメンテナンス当日の作業に分けて説明したいと思います。

大まかな流れは下記のようになります。

1: レプリケーションマスターのバイナリログ記録を有効にする
2: 不要になるまでレプリケーションマスターのバイナリログを保持する
3: レプリケーションマスターのスナップショットからAuroraインスタンスを作成する
4: レプリケーションを有効にする
5: アプリケーションの接続先をAuroraに切り替える

MySQL と Aurora のレプリケーション設定

1. レプリケーションマスターのバイナリログ記録を有効にする

リードレプリカを作成することで、バイナリログ記録を有効にすることができます。

新しくリードレプリカインスタンスを起動します。

f:id:infablic:20160424171940p:plain

2. 不要になるまでレプリケーションマスターのバイナリログを保持する

Amazon RDSでは、通常、バイナリログは一定期間経つと消去されてしまいます。バイナリログが削除されるとレプリケーションをうまく行うことができないので、これを回避する必要があります。

公式ドキュメント にもあるようにレプリケーションマスターのバイナリログファイルは、変更がレプリカに適用されるまで保持する必要があります。

上記で作成したリードレプリカのレプリケーションを停止することでバイナリログが削除されるのを避けることができます。

f:id:infablic:20160424172408p:plain

レプリケーションを停止する

レプリケーションの停止は作成されたリードレプリカに対して下記の mysql.rds_stop_replication プロシージャを呼び出します。

mysql> call mysql.rds_stop_replication;
+---------------------------+
| Message |
+---------------------------+
| Slave is down or disabled |
+---------------------------+
1 row in set (1.01 sec)

3. レプリケーションマスターのスナップショットからAuroraインスタンスを作成する

作成する Aurora にレプリケーションを設定する際に、バイナリログのポジションが必要になってきますので、公式ドキュメント にあるように SHOW SLAVE STATUS コマンドを発行し、Master_Log_File フィールドから現在のバイナリログファイルの名前を、Exec_Master_Log_Pos フィールドからそのログファイルの場所を確認しておきます。

レプリケーションの位置を記録する

レプリケーションStop 状態なのを確認してから行います。

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: 
                (中略)
              Master_Log_File: mysql-bin-changelog.000038
               (中略)
             Slave_IO_Running: No
            Slave_SQL_Running: No
                (中略)
          Exec_Master_Log_Pos: 120

RDS MySQL スナップショットの Aurora への移行 にもあるように、レプリケーションが停止した状態のリードレプリカから手動でスナップショットを作成し、マネージメントコンソールの スナップショットの移行 機能を使ってAuroraインスタンスを作成します。

データ量にもよりますが、スナップショットの移行には数時間かかります。このタイミングでパラメータグループやセキュリティグループも設定しました。また、スナップショット作成後は不要になるリードレプリカは削除します。

EC2-Classic から VPC環境へ移行の場合の注意点

EC2-Classic から VPC環境への移行の場合、レプリケーションを正常に行うためにAuroraインスタンスがグローバルにアクセスできないといけません、ですのでインスタンス作成時に忘れずに パブリックアクセス可能 オプションをONにしておく必要があります。 パブリックアクセス可能 にしておくと作成時にインスタンスグローバルIPが振られるのが分かります。

また、 Aurora が属するサブネットのルートテーブルやセキュリティグループを適切に設定し、MySQLからアクセスできるようにする必要があります。

MySQL から Aurora へレプリケーションを行う際に Aurora インスタンスグローバルIPが必要になってきますので、予め確認しておきます。

➜  ~  nslookup XXXXXXXXXX.ap-northeast-1.rds.amazonaws.com
(中略)

Non-authoritative answer:
XXXXXXXXXX.ap-northeast-1.rds.amazonaws.com

(中略)

Name:   ec2-[グローバルIP].ap-northeast-1.compute.amazonaws.com
Address: グローバルIP

弊社のVPC環境では、本来はパブリックアクセスの必要は無いので、移行完了後に、パブリックアクセス可能 のオプションはOFFにしました。

Aurora インスタンスの作成完了後に Readerノードも立ち上げてクラスタ構成にしておきます。

f:id:infablic:20160425113456p:plain

4. レプリケーションを有効にする

MySQLレプリケーション用のユーザーを作成しておきます。その際、接続元のIPに Aurora のグローバルIPを指定しておきます。

GRANT REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'レプリケーションユーザ名'@'グローバルIP' IDENTIFIED BY 'パスワード';

ほとんどの方がセキュリティーグループの設定で、MySQLへのグローバル環境からの接続を禁止していると思いますので、Aurora インスタンスレプリケーションを行えるように MySQLのセキュリティグループのCIDRブロックに Aurora のグローバルIPを追加し、アクセスができるようにしておきます。 この設定がないとVPC と EC2-Classic 環境間で通信が行えません。

f:id:infablic:20160424175508p:plain

レプリケーションを有効にする にある通り Aurora 上で mysql.rds_set_external_master コマンドを発行し MySQL をマスターに設定します。
また、この際に事前に記録しておいた、MASTER_LOG_FILEMASTER_LOG_POS の値を指定します。

mysql> call mysql.rds_set_external_master(
  -> 'MySQL マスターのエンドポイント'
  -> , '3306'
  -> , 'レプリケーションユーザ名'
  -> , 'パスワード'
  -> , 'MASTER_LOG_FILE'
  -> , MASTER_LOG_POS
  -> , 0
  -> );
Query OK, 0 rows affected (0.03 sec)

mysql.rds_start_replication コマンドを実行して、レプリケーションを開始します。

mysql> call mysql.rds_start_replication;

+-------------------------+
| Message                 |
+-------------------------+
| Slave running normally. |
+-------------------------+
1 row in set (1.02 sec)

Slave_IO_Running Slave_SQL_RunningYes になっていればレプリケーションは成功です。
Seconds_Behind_Master パラメータが 0 になったら、同期は完了しています。

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
         (中略)
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

f:id:infablic:20160425114042p:plain

データベースの切り替え

ここからは、提供するサービスをメンテナンス状態にし、利用するデータベースを MySQL から Auroraに切り替えます。メンテナンス状態中はEC2-Classic環境へアクセスが来ないことを確認します。

フリルではメンテナンス機能があり、設定を有効にすることでiOSAndroidアプリがアプリケーションサーバへ通信することを制御できます。 メンテナンス機能を有効にすると下記のような状態になり、一部機能は使えない状態となります。

f:id:infablic:20160424183701p:plain:w350

VPC環境に予め構築していたサーバ群へ順次 DNSやデータベースの接続先を変更していき、すべてのサーバの移行を完了させます。 アプリケーションからのDBアクセスがすべて Aurora に移行したタイミングでレプリケーションを解除しました。

公式ドキュメント にある通り mysql.rds_stop_replication mysql.rds_reset_external_master コマンドを発行して、レプリケーションを停止させます。

mysql> call mysql.rds_stop_replication;
+---------------------------+
| Message                   |
+---------------------------+
| Slave is down or disabled |
+---------------------------+
1 row in set (1.01 sec)

レプリケーション設定の解除

mysql> call mysql.rds_reset_external_master;
+----------------------+
| message              |
+----------------------+
| Slave has been reset |
+----------------------+
1 row in set (0.02 sec)

f:id:infablic:20160424191640p:plain

後日、すべてのサービスが正常に稼働しているのを確認し、MySQLインスタンスを停止して、インスタンスを削除しました。事前にテスト環境での検証、リハーサルなども充分に行っていたので、当日は大きなトラブルもなく、無事に移行を行うことができました。

まとめ

MySQLのスナップショットとレプリケーション機能を使うことで、短いダウンタイムでデータベースをAuroraに切り替えることができました。

課題はいくつかあるものの、総じて見ると、パフォーマンスは改善し、システム全体のスケーラビリティ、アベイラビリティが高まりましたので、移行して良かったと考えております。

この記事が、Auroraへの移行を検討している皆様の参考になれば幸いです。