B/Gデプロイでバージョンを上げたDBを切り戻す際に、DMSのレプリケーションを試してみた
こんにちは!
僕が所属してるチームでは、DBのアップデート対応を行っています。
まだ本番では対応できていないですが、DMSのレプリケーション機能を使った切り戻し方法をリハーサルで使ってみたので、手順や使ってみた所感をまとめていきたいなと思います。
今回アップデートするDBの環境です。
対象 | エンジンバージョン |
---|---|
現状 | Aurora MySQL(5.7)2.11.2 |
アップデート後 | Aurora MySQL 3.08.2(compatible with MySQL 8.0.40) |
背景
データベースをB/Gデプロイでアップグレードした際に、万が一元のバージョンに戻したくなったことを想定して、切り戻しの手順を準備しました。
B/Gデプロイでは、切り戻しの方法が提供されていないので、MySQLのネイティブの機能を使ったレプリケーションを行う想定でした。
しかし、ネイティブの機能を使ったレプリケーションの設定が煩雑であったり、メンテナンスモード中に設定しないといけなかったりと、ダウンタイムが長くなっていました。
B/Gデプロイの直近の変更で、パラメータグループの設定を変更したのですが、その際にbinlog_format=ROW
に変更したのもあり、(以前まではbinlog_format=MIXED
だったため、DMSのレプリケーションできませんでした。)
DMSのレプリケーション機能を使って切り戻しができるかどうかを試してみることにしました。
また以前DMSを使ってデータ移行を試した記事を書いているので、そちらもよかったらご覧ください。
移行手順
大まかな手順ですが、以下の通りです。
- DMSでレプリケーションする用に、クラスターをクローンしておく
- アップデート対象のクラスターに対してB/Gデプロイを行う
- BlueとGreen環境に分かれた後に、スキーマとレコード数をチェック
- メンテナンスモードの実施
- Green環境に切り替え
- テスト
- メンテナンスモード解除
- 一番最初にクローンしたDBをターゲット、B/Gデプロイで切り替えた、Green環境のDBをソースに、DMSでフルロード+CDCを開始
切り戻し手順
万が一5.7のクラスターに戻さないといけない事態になった場合は、アプリケーションと接続があるGreen環境のクラスター(MySQL8.0)とレプリケーション用のクラスター(MySQL5.7)をリネームします。
Green環境のクラスター
- 識別子を
hoge-cluster-old
などに任意の名前にリネームする - 同名エンドポイントを再利用できるようにするために、カスタムエンドポイントを削除
レプリケーション用クラスター
- アプリケーションと接続されてた、Green環境のクラスターと同名の識別子に変更
- カスタムエンドポイントの名前をGreen環境のクラスターと同じものに変更
これで、アプリケーション側に変更を加えることなく、8.0 → 5.7に切り戻せます。
DMSのレプリケーションで良かったこと
ダウンタイムを減らせる
MySQLのネイティブのレプリケーションでは、レプリケーションの設定をメンテナンスモード中に行う必要がありましたが、DMSを使い上記の手順でレプリケーションを行う場合、メンテナンスモードを解除後に同期できるので、ダウンタイムを減らすことができます。
設定が容易
DMSでレプリケーションを行うにあたり以下のものが必要ですが、AWSのコンソール上で完結するので、比較的簡単にレプリケーションの準備が整います。
- レプリケーションインスタンスの作成
- 移行タスクの作成
- DMS用のセキュリティーグループの作成
- DMS用のサブネットグループの作成
- ターゲットのエンドポイント
- ソースのエンドポイント
進行状況がGUIでわかりやすい
移行を実施すると、移行状態が以下のように可視化されるので進捗が分かりやすかったです。
また何かしらエラーがあればCloud Watchと連携されておりそこから詳細に確認できます。
注意点
ターゲットとなるDBにスキーマを事前に用意しないと、インデックス情報が引き継がれない
以前DMSを使った時から起きていましたが、スキーマを用意してからデータ移行を行わないとインデックス情報を引き継いでくれません。
(パラメータグループの設定を色々と変えたので、いい感じにやってくれるのではと淡い期待をしてましたが、ダメでした笑)
とはいえ、今回はレプリケーション対象のクラスターをクローンしているので、スキーマだけリストアすることなく準備できました。
また、DMSの設定で、"TargetTablePrepMode": "TRUNCATE_BEFORE_LOAD"
を設定しました。
フルロードを開始する直前に、ターゲット側のテーブルのデータを全て削除してから、ロードを実行するため、テーブル定義はそのまま残り、データだけが空になってくれます。
こうすることで、DMSを使用する際にターゲットのDBとソースのDBのデータ差分を気にすることなく、レプリケーションできます。
外部キー制約でロード失敗する
RetCode: SQL_ERROR SqlState: HY000 NativeError: 1452 Message: [MySQL][ODBC 8.0(w) Driver][mysqld-5.7.12-log]Cannot add or update a child row: a foreign key constraint fails
子テーブルが親テーブルより先にロードされることで、上記のようなエラーが起きていました。
これは以下のようにターゲットのエンドポイントに、initstmt=SET FOREIGN_KEY_CHECKS=0;
を設定することで、ロードが止まらずに済みました。
最後に
本番対応はまだですが、ダウンタイムを最小限にしつつ、ロールバックパスを確保できたと思います。
検証段階で試してたことですが、フルロード中も、元となるDBに書き込みがあった場合、レプリケーションが機能してくれていたのでメンテナンスモードを挟まなくても同期されるのは便利だなと感じました。
DBアップデートをするにあたり、かなり慎重に進めているなと思いつつも、何かあったときに、元に戻せる状態を作っておくことで、テストでは拾いきれない想定外の障害にも対応できる準備ができたと思っています。
最後に宣伝です!
スペースマーケットでは、一緒にサービスを成長させていく仲間を探しています。
とりあえずどんなことをしているのか聞いてみたいという方も大歓迎です!
ご興味ありましたら是非ご覧ください!

スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion