Feedforce Developer Blog

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

Aurora(MySQL 5.6 互換)からAurora(MySQL 5.7 互換)へのメジャーバージョンアップをAWS DMSを利用してゼロダウンタイム で行う方法

ソーシャルPLUS 開発チームでインフラエンジニア をやっています id:mayuki123 です。前も同じような記事書きましたが、いつもデータベースの移行作業をやっているわけでなく、Terraform 先生と戯れている時間の方が基本的には長いです。

developer.feedforce.jp

今回はソーシャルPLUSのメッセージ機能で利用しているデータベースをサービスを停止する事なくアップグレードを実施したのでその事についてまとめます。基本的には前述の記事のAWSのDMSを利用した方法を煮詰めたような感じとなります。

※ 2021/1/18 追記
Amazon Aurora で、インプレースアップグレードが出来るようになりました。

AWS DMSとは

AWS DMS はデータベース間のデータ移行を最小限のダウンタイムで実施することの出来るサービスです。MySQL から PostgreSQLといった異なるデータベースエンジンにも移行のサポートをするサービスですが、MySQL同士でも問題なく利用をすることが出来ます。詳細は前回の記事に書いてあるので、そちらを読んでみてください。

本記事でのデータベースのアップグレード方法

新規で Aurora(MySQL 5.7 互換)を作成したあとで、データ同期をしつつ並行稼働状態にしてから、任意のタイミングでエンドポイントを切り替える事でアップグレードをする事にしました。

f:id:mayuki123:20201105085826p:plain

実施した流れの概要としては、下記のようになります。

  1. Aurora(MySQL 5.6 互換) のスナップショットを取得する
  2. 取得したスナップショットからAurora(MySQL 5.7 互換)を復元する
  3. スナップショットを取得後の更新データをDMSを利用してデータ同期する

f:id:mayuki123:20201105091325p:plain

Aurora(MySQL 5.6 互換) に移行する際はデータベーススキーマの移行を mysqldump でDDLのSQLを生成して、データ同期に関しては全てDMSを利用をしていましたが、今回はスナップショットから復元をする事でデータベーススキーマを移行する必要がなくなり、データ移行時のDMSタスクの負荷を抑える事が出来るようになりました。

RDS のスナップショットからリソース作成

まずは、Aurora(MySQL 5.6 互換) のスナップショットを取得して、取得したスナップショットから Aurora(MySQL 5.7 互換)を作成します。

インフラ管理にTerraform を利用している場合は、aws_db_cluster_snapshot リソースのdb_cluster_identifier でAurora(MySQL 5.6 互換)を指定するとスナップショットを作成する事が出来ます。そして、新規で作成する aws_rds_cluster リソース に snapshot_identifier パラメータを付与する事で、Aurora (MySQL 5.7 互換)を作成したスナップショットを元にリソース作成する事が出来ます。

resource "aws_db_cluster_snapshot" "aurora56" {
  db_cluster_identifier          = aws_rds_cluster.aurora56.id
  db_cluster_snapshot_identifier = "aurora56_snapshot_2020XXXX"
}

resource "aws_rds_cluster" "aurora57" {
  snapshot_identifier       = aws_db_cluster_snapshot.aurora56.db_cluster_snapshot_arn
  cluster_identifier        = "new-cluster-aurora57"
  engine                    = "aurora-mysql"
 
  …省略…

}

スナップショットから復元する事で、データベースのユーザ情報やデータベーススキーマ をそのまま移行する事が出来ます。ただし、スナップショット取得を開始してから Aurora(MySQL 5.7 互換)として復元するまでにはデータ量にもよりますが、数時間程度かかる事になります。

データの断面の取得方法

スナップショットから復元した時に、どこまでデータ更新がされた状態なのかは バイナリログ の情報を利用する事になります。 新しいAurora クラスターを スナップショットから復元すると、インスタンス のイベント情報に Binlog position のメッセージが出力されます。

f:id:mayuki123:20201020075553p:plain

この Binlog position を利用する事で、スナップショットから復元後のデータ更新をDMSタスクで実施出来るようになります。

DMS タスクの設定

DMSタスクにバイナリログ を設定する

DMSタスクには変更データキャプチャ (CDC) の開始位置として、バイナリログ の開始位置を設定する事が出来ます。

f:id:mayuki123:20201105100605p:plain

この設定変更は AWSコンソールからは変更する事が出来ず、aws cli から変更する事が出来ました。(2020/10/20 時点)

aws dms modify-replication-task --replication-task-arn <dms-task-arn> --cdc-start-position "mysql-bin-changelog.000071:60167395"

CLIから変更をすると下記のように変更データキャプチャ (CDC) の開始位置として設定されます。こうする事で、スナップショット取得時点以降のデータのみをDMSでレプリケーションさせる事が出来るようになります。

f:id:mayuki123:20201020111459p:plain

ロールバックのための双方向レプリケーションの設定

前回のAurora 移行記事にも記載していますが、もしもという時のためのロールバック戦略を考えておく必要があります。

以前はメンテナンス時間(DBが一切更新されない状態)にして逆方向のDMSタスクを作成するようにしていました。この方法だとサービスを停止してから逆方向のDMSタスクを起動するという一手間が必要ではありました。

今回はDMSタスクの双方向レプリケーションという機能を活用する事で、ゼロダウンタイム でのデータベースの切り替え(アップグレード)を実現しました。

この方法はDMSタスクを2種類(旧→新 , 新→旧)を設定して、双方のDMSタスクを稼働させた状態にする事でどちらのデータベースに対して更新がかかったとしてもレプリケーションをすることが可能になります。今回の作業の中で言えば DMSタスク(新→旧) へのレプリケーションは何かあった時のロールバックの為だけに利用するために設定しました。

f:id:mayuki123:20201105095819p:plain

同時に二つのDMSタスクを指定する事で、データが逆流してしまうのではと懸念されるかと思いますが、ループバックを防止する機能があります。

下記の設定をDMSタスクに対して設定をすればループバックを防止する事が出来ます。

{
. . .

  "LoopbackPreventionSettings": {
    "EnableLoopbackPrevention": Boolean,
    "SourceSchema": "source_database",
    "TargetSchema":"target_database"
  },

. . .
}

この設定をしておく事で、数時間後に問題が発覚したとしてもエンドポイントを戻せば元のデータベースにデータの欠落なく戻す事ができます。

DMSを利用する場合の注意事項

実際にアクセス量の少ないデータベースに関しては本記事の手法でメンテナンス時間を設けずにメジャーバージョンアップを実施して問題なく移行をする事ができましたが、ソーシャルPLUSの認証機能で利用をしているデータベースは毎秒ソーシャルログインの認証アクセスが来る事もあり、安全重視でメンテナンス時間を設けて実施をしています。

また、この記事で紹介した方法も万能ではなく、いくつかの注意事項もありますので、書き残しておきます。

本番データベースへの負荷

Aurora (MySQL 互換) のデータベースを DMSのエンドポイントのソースとして指定する場合のみ気をつける必要があります。

RDS for MySQL5.6 から Aurora(MySQL 5.6 互換)に移行した時には問題にはなりませんでしたが、Aurora データベースでバイナリログ の設定を有効化しても、リードレプリカのインスタンスではバイナリログ は無効化された状態となっています。そのため、DMS にエンドポイントのソースとして Writer インスタンス を指定する必要があり、DMSタスクを実行してレプリケーションをしている最中にはCPU負荷が高くなるといった事象が発生します。本番データベースが一定のCPU負荷を超えている場合には、サービスへの影響が発生する可能性があります。

参考記事

双方向レプリケーションの制限事項

DMS タスクの双方向レプリケーションに関しても制限事項があります。データの不整合の検出ができないことや、一括変更のコミットはサポートされていないといった点があります。エンドポイントの切り替えタイミングでのデータ更新に関しては意識しておく必要があるため、アクセスの多い部分で利用しているデータベースで実施する際は、新旧のデータベースに同時にデータ更新がされないように数分のメンテナンスタイムを設けるようにしました。

DMSタスクのデータ検証が失敗する

DMSタスクにはデータ検証という機能があり、レコードの中身を比較して差異がない事を確認出来る機能となっています。 本番環境のワークロードでDMSタスクを実行した時のみデータ検証に失敗するという事象が発生しました。

f:id:mayuki123:20201027174919p:plain

データ検証に失敗したレコードは ターゲットデータベースに awsdms_control データベース がDMSによって作成されていて、awsdms_validation_failures_v1 テーブルに書き込まれます。データ検証に失敗したレコードの中身を確認していると、データ更新がされていた事でソースとターゲットのレコードが不一致となっていました。データ検証に失敗した割合も件数も多くはなかったので、目視で問題なさそうである事を確認しました。更新の多いテーブルはデータ検証に失敗する可能性があるので、確認方法は事前に準備しておきましょう。

おわりに

この記事ではDMSの機能を活用してゼロダウンタイム でデータベースのメジャーバージョンアップを実施する手法についてまとめました。過去にもDMSを利用してAurora データベースへの移行を実施をしていたのでそれなりに知見は持ち合わせていましたが、それでも検証からメジャーバージョンアップの実施までは数ヶ月かけています。手順や手法が複雑になればなるほど、より多くの検証すべき箇所や注意すべき事が増えるので、場合によってはサービスを停止してでもシンプルな方法で実施する方が良いとは思っています。

サービスの規模やフェーズ、アプリケーションのワークロード、また組織体制によっても適切な手法は変わると思いますが、数多くあるデータベースのメジャーアップデートの一手法として目に止まればよいなと思っています。