Feedforce Developer Blog

フィードフォース開発者ブログ

RDS for MySQL5.6 から Aurora(MySQL 5.6 互換)へ移行しました

ソーシャルPLUS 開発チームでインフラエンジニア をやっています id:mayuki123 です。気づけば入社から10ヶ月ほど経っていました。

ソーシャルPLUSのメッセージ機能については1年半ほど前にAurora移行を実施していますが、2019年2月中旬にメインのソーシャルログイン機能で利用している データベースをRDS for MySQL5.6からAmazon Aurora(MySQL 5.6 互換)に移行することが出来ました。

developer.feedforce.jp

Auroraに移行する利点等は多くの方がまとめているかと思うので、今回はデータベース移行で実施した手順に着目して書こうと思います。

準備期間と移行作業の概要

ソーシャルPLUSの利用者もかなり増えてきている状況になってきていた事もあり、昨年の10月くらいから本腰を入れてAurora移行作業の準備をし始めました。私のデータベース知識に関しては前職でOracleを少し触った事がある程度で、MySQLに関しては入社してからプロダクトで触り始めたので、3,4ヶ月ほどの時間をかけて調査/検証作業を実施してから、移行作業を完遂する事が出来ました。

最終的な移行作業自体は下記の流れになりまして、1~3が事前作業で9割くらいを占めていました。当日作業はエンドポイントの切り替えのみを実施したので、サービス停止時間も数分ほどで済みました。今回実施した移行手順の詳細を書いていこうと思います。

  1. 新しいAurora 環境の構築
  2. データベーススキーマの移行
  3. DMSを利用したデータ移行
  4. RDS for MySQL5.6 から Amazon Auroraへのエンドポイント変更

データベース移行方法の選択

データベースをAuroraに移行する方法はAWSのドキュメントにまとめられています。

docs.aws.amazon.com

RDS for MySQL5.6を利用している場合はAuroraのリードレプリカを作成してから昇格させる方法が一般的かとは思います。

今回の移行作業では、大量のレコードがあるテーブルのスキーマ構造を変更したい要望があり、RailsからDBのマイグレーションをしようとするとテーブルロックが発生してサービス影響が出てしまう状況でしたので、異なるスキーマ構造のAmazon Aurora(MySQL 5.6 互換) を事前に作成しておいて、内部データに関してはAWSのDMSを利用して移行をする方法をとる事で、データベースエンジンの移行に合わせてテーブルのスキーマ変更も実施する事にしました。

AWSのDMSとは

AWS DMS はデータベース間のデータ移行を最小限のダウンタイムで実施することの出来るサービスです。MySQL から postgresqlといった異なるデータベースエンジンにも移行のサポートをするサービスですが、MySQL同士でも問題なく利用をすることが出来ます。 RDS for MySQL5.6 と Auroraの間にDMSインスタンスを経由してデータ移行をする形になります。

f:id:tech-feedforce:20190328160817p:plain

AWSのDMSのリソースについて

AWSのDMSを利用するためには下記のリソースの設定をする必要があります。

f:id:mayuki123:20190401172513p:plain
DMSのリソース

  • DMSのレプリケーションインスタンス
  • データベースのエンドポイント (Source/Target)
    • ソースとターゲットでそれぞれ設定が必要がある
  • タスク設定
    • 特定カラムはデータ移行を行わない事などのフィルター設定が出来る。
    • 移行したデータ内容に差異がないか検証する設定もある。

DMSのインスタンスのリソース不足がネックになる事が多かったので、タスク設定のチューニングがわりと大事でした。

データロードの種類

DMSのタスクのデータロードには下記の三種類があります。

  1. 既存のデータ移行する
  2. 既存のデータを移行して、継続的な変更をレプリケートする
  3. データ変更のみをレプリケートする

3種類のデータロードの該当箇所は下記のようなイメージになります。

f:id:mayuki123:20190401172730p:plain

今回の移行作業では 2,3 を利用しています。2種類利用している理由は後述します。

新しいAurora環境の構築

データベースインスタンスの作成

既存のRDS for MySQL5.6 と同等の設定でAuroraのデータベースインスタンスの作成を行いました。 弊社ではTerraformを利用したコード管理をしているので、複製は比較的しやすい状況でした。

データベースのユーザ アカウントの移行

データベースのユーザ アカウントの移行するために、percona-toolkitに含まれている pt-show-grantsというツールを利用しました。

pt-show-grantsを利用するとデータベースに存在するユーザ と権限情報をがSQLとして取得出来ます。作成されたSQL文をデータベースに対して実行をすればユーザ 移行をする事が出来ます。下記は実行時の一例になります。

-- Grants dumped by pt-show-grants
-- Dumped from server <RDSのエンドポイント> via TCP/IP, MySQL 5.6.39-log at 2018-12-12 02:11:48
-- Grants for '<ユーザ 名>'@'%'
GRANT USAGE ON *.* TO '<ユーザ 名>'@'%' IDENTIFIED BY PASSWORD <secret>;
GRANT ALL PRIVILEGES ON `<データベース名>`.* TO '<ユーザ 名>'@'%';

データベースのスキーマ移行

データベースのスキーマ移行に関しては mysqldumpを利用しました。オプションに --nodata と付与すると、定義情報のみをダンプする事が出来ます。

mysqldump -h <ホスト名> --no-data -u <ユーザ 名> -p <データベース名>

mysqldumを利用してスキーマ移行した後にAurora側で変更したいテーブルに対してALTER TABLEを実行する事で、indexの追加やカラムの削除などのスキーマの変更を実施しました。

スキーマの移行に関してはAWS SCTというAWSの公式ツールもありますので、データベースエンジンが異なる場合にはこちらを利用するのが良さそうです。

上記ツールを利用した場合でも、元のデータベースと新しく作ったデータベースのスキーマが一致しているかは確認した方がよいです。MySQL Utilitiesに含まれている mysqldiffコマンドで簡単に確認が出来ます。今回の移行作業ではパラメータグループの設定でコード化されていない箇所があった事に気づけたので、ツールでの比較は便利です。

RDS for MySQL5.6とAuroraでは mysql.userテーブルの構造が異なっていた事も分かりやすかったです。

$ mysqldiff --server1 mysql --server2 aurora mysql.user:mysql.user --skip-table-options --force
# server1 on localhost: ... connected.
# server2 on localhost: ... connected.
# Comparing mysql.user to mysql.user                               [FAIL]
# Object definitions differ. (--changes-for=server1)
#

--- mysql.user
+++ mysql.user
@@ -39,8 +39,11 @@
   `max_updates` int(11) unsigned NOT NULL DEFAULT '0',
   `max_connections` int(11) unsigned NOT NULL DEFAULT '0',
   `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0',
-  `plugin` char(64) COLLATE utf8_bin DEFAULT 'mysql_native_password',
+  `plugin` char(64) COLLATE utf8_bin DEFAULT '',
   `authentication_string` text COLLATE utf8_bin,
   `password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
+  `Load_from_S3_priv` enum('N','Y') COLLATE utf8_bin NOT NULL DEFAULT 'N',
+  `Select_into_S3_priv` enum('N','Y') COLLATE utf8_bin NOT NULL DEFAULT 'N',
+  `Invoke_lambda_priv` enum('N','Y') COLLATE utf8_bin NOT NULL DEFAULT 'N',
   PRIMARY KEY (`Host`,`User`)
 )
# Compare failed. One or more differences found.

DMSを利用したデータ移行

今までの手順でデータが入っていないスキーマ構造の異なるAuroraが出来上がったので、DMSを利用してデータの移行を行います。

データロードの種類でDMSのタスク設定で 既存のデータを移行して、継続的な変更をレプリケートするを利用する事で、既存データベースのデータを一括ロードする事に加えて、サービス停止をせずとも継続的にデータ同期を実施してくれます。合わせてフィルタリングも出来るので、特定テーブルの一部カラムのデータだけは同期しない事も出来たので、スキーマ変更をしていても問題は発生しなかったです。

また、DMSのタスクにはデータ検証の機能があるので、失敗したレコードに差分がないかの確認をすることも出来ます。今回データベースを移行したソーシャルPLUSの環境では特定レコードでデータ検証に失敗する事もありましたので、事前にどのレコードでデータ同期に失敗しているかを確認して問題を潰していくといった事を実施しました。

データ量が少ない場合には MySQL Utilitiesに含まれている mysqldbcompareコマンドで、二つのデータベースのスキーマ/データ内容が一致しているかを確認することが出来ます。データ量が多いと時間がかかって現実的ではないので、今回はDMSのデータ検証機能を信頼することにしました。

事前に本番環境同等の容量のデータベースを要してしてDMSを利用してデータ移行を試しておいた事もあり、大体どのくらいの時間があればデータ移行が出来るのかを確認しました。データ量が多いとDMSのインスタンスのリソース不足ではまる事が多かったので、大きめのインスタンスタイプで試してみるのが良いのかなと思います。

実際に本番データを移行させる際には、データ検証が失敗しても確認出来る余裕を作るためにも、一週間ほど前からDMSタスクを実行してレプリケーションさせ続ける事にしました。事前に確認していたレコード以外に新たにデータ検証に失敗する部分がなくてよかったです。

今回の移行作業の中ではDMSのデータ移行に問題がないかの検証作業に一番時間をかけて確認をしたと思います。

エンドポイントの切り替え作業

今までの作業は事前作業であり、実際のメンテナンス時間に実施したのはこの作業だけになります。 実作業としてはRDS for MySQL5.6に接続しているものをAuroraにエンドポイントを切り替えるだけになります。

f:id:mayuki123:20190401175439p:plain

AWS DMSでは多少のレプリケーションラグが発生するので、 即座にエンドポイントの切り替えを行うと、データに不整合性が発生する可能性があります。ソーシャルPLUSはアプリケーション側でRailsのrackを利用してDBに接続出来ない状態にするメンテナンスモードを準備出来ていたのはよかったです。

メンテナンス時間に入った段階で RDS for MySQL5.6に対してデータ挿入をして、Auroraにそのデータが反映されたら、最後のトランザクションが更新された証明になるので、確認後にエンドポイントを切り替えるという流れをとってます。この方法はAWS社のスライドでも推奨しているようです。

今回、移行作業自体はエンドポイントの切り替えだけでしたので、サービス停止も数分程度で済みました。 ここまで当日作業が少ないのは深夜作業だったとしても、心に余裕を持って作業をする事が出来ました。

データベース移行に失敗した場合

念入りに準備したとしても上手くいくとはかぎらないので、切り戻しの準備をしておくと当日に慌てずに済みます。今回のケースで障害のタイミングで考えられるのは下記のパターンを想定してました。

(1) エンドポイントをAuroraに切り替えた直後に障害が判明した場合

DBに接続することが出来ずにアプリケーションの動作がしなかった際は、Auroraに切り替えたエンドポイントを元のRDS for MySQL5.6に戻すだけで切り戻しができます。切り戻しも楽に出来るので、慌てずに済みます。

(2) エンドポイントをAuroraに切り替えた数日後に障害が判明した場合

数日後に戻すとなった場合、1の手段のようにエンドポイントを戻すだけでは切り替えタイミング ~ 切り戻しを実施までのデータ更新分が欠落します。そのため、Aurora移行用とは別にロールバック用のDMSタスクを準備しておきました。

f:id:mayuki123:20190401175949p:plain

ロールバック用のDMSのタスク設定として、 3. データ変更のみをレプリケートするとして、DMSのタスク開始時間をメンテナンス時間に設定をすれば、データベースのエンドポイントを切り替えたタイミング以降のデータについても AuroraからRDS for MySQL5.6へのデータ更新が出来るようになり、何か会った時にアプリケーションからデータベースへのエンドポイントを切り戻ししたとしてもデータの欠落なく戻すことが出来ます。

今回は何事もなく移行は出来たとはいえ、何かあった時のための準備もしっかりとしておく事が大事かとは思います。

最後に

サービス開始からのデータが蓄積続けてるデータベース移行という作業のために、万全を期すためには多くの検証しないといけないことがあるなと実感しました。検証作業の多くが何か問題があった時のため予防策の検証をしていましたが、最終的は特に何事もなくAurora移行作業を完了させることが出来たので良かったです。事前準備の数だけ本番作業も落ち着いて実施する事が出来る気がしました。 私自身はこの数ヶ月でAurora移行に関する検証作業に集中しすぎてしまった感はありますが、チームメンバが他の作業を進めてくれていたのでとても良い環境だなと思っています。

今回の方法をより突き詰めれば、メンテナンス時間をより短く数十秒くらいでデータベースの切り替え作業を行う事が出来るんじゃないかなと思っています。より安定感のあるサービス提供をするためにも、今後も縁の下の方でインフラ環境の整備を続けていきます。