ヘヴィメタル・エンジニアリング

AWS特化型エンジニアのほのぼのヘヴィメタルブログ

ヘヴィメタル・エンジニアリング

クラウド特化型ヘヴィメタルエンジニアのほのぼのブログ

AWSアカウント間でRDS Auroraを移行する方法 ~ダウンタイム・データ欠損無し~

よくAWSに触れるものです。

先日こんな事がありました。

...

???「このAWSアカウントにサービス固まり過ぎじゃない?アカウント分けようか。あ、各サービスのRDSも移行してね。もちろんデータ欠損とダウンタイムはなしだからね。」

あたし「(え、厳しくない??)わかりました!できます!」

...

よくある無茶振りというやつですね。

アカウント超えるんでしょ?結構難しくないか?

とか思いますが、こういうときはとりあえずできると言って検証してみましょう。

検証

まず今回の環境は以下です。
Amazon RDS Aurora
Mysql version: 5.7.2 ・DBクラスターはKMSキーで暗号化済み

そして、今回の条件は以下
1. RDSの移行によるデータ欠損はなくす
2. ダウンタイムなし

1.に対するアプローチはRDSインスタンスレプリケーションが有効です。
レプリケーションとはmasterDBとslaveDBを同期させることを言います。

例えばAuroraクラスターを立てて、そのクラスターのリードレプリカを作成するとすると、そのリードレプリカはmasterDBとレプリケーションされていることになります。

そしてレプリケーションする元のDB(新アカウントの方)は旧アカウントのDBのスナップショットから復元します。
これによってデータ欠損は避けれるでしょう。


※ただ復元したスナップショットからレプリケーションを有効にする段階で工夫をしないとデータ欠損する可能性があるので、そちらに関しては後述します。

2.に対しては、とても難しいです。
今回はあくまでRDSの移行の際でのダウンタイムを想定しますが、結論から言うとバイナリーログが有効であれば実現可能です。
バイナリーログとはデータベース変更のイベントをまとめたログです。

このバイナリーログのFormatを有効にするように、パラメーターグループで設定していればダウンタイムなく実現できます。

パラメーターグループのbinlog_formatをOFF以外にすればよいのですが、今回の環境ではOFFになっていたので、値をROW, MIXED, STATEMENTのどれかにするためにDBを再起動する必要があります。

その再起動で30秒~50秒ほどのダウンタイムが生じます。

※もとからバイナリーログのFormatを有効にしておけば設定の必要はないのでダウンタイムは生じません

このダウンタイムは許容する他ありません。

以下では実際の移行手順を記載していきます。



計画

以下の図が今回することの全容です。

f:id:xkenshirou:20200705104544p:plain

手順としては、

  1. AアカウントとBアカウント間でVPCピアリング接続を確立する

[Aアカウント]
2. Auroraクラスターパラメーターグループでbinlog_formatをMIXEDにする
3. バイナリーログ保持期間を設定
4. バイナリーログのポジションとファイル名を確認、スナップショットを取得
5. スナップショットを暗号化するKMSキーをBアカウントとの共有を有効
6. スナップショットを共有

[Bアカウント]
7. 共有されたスナップショットからAuroraを復元

[Aアカウント]
8. レプリケーションユーザーを作成

[Bアカウント]
9. レプリケーション設定
10. レプリケーションスタート

細かく解説していきます。



手順

1. AアカウントとBアカウント間でVPCピアリング接続を確立する

今回はアカウント間、及びVPC間を超えてAurora同士の接続をするためVPCピアリング接続を確立させる必要があります。

VPCピアリングに関しては以前記事を書きましたので、こちらを参考にしてください。

xkenshirou.hatenablog.com

2. Auroraクラスターパラメーターグループでbinlog_formatをMIXEDにする

まずはクラスターパラメーターグループでbinlog_formatの値を有効にしましょう。

f:id:xkenshirou:20200704121552p:plain

値は特別なことがない限りMIXEDでいいでしょう。
あとはAuroraクラスターを再起動する必要があります。

ここでダウンタイム30秒~50秒です。

再起動が完了したら実際に値が有効になっているか確認しましょう。

f:id:xkenshirou:20200705021434p:plain

f:id:xkenshirou:20200705021511p:plain

ここで注意なのが、実はmysql内でのbinlog_formatの値はパラメーターグループのbinlog_formatの値を変えずともMIXED, ROW, STATEMENTのいずれかの値になっています。(OFFにはなりません)
ではパラメーターグループのbinlog_formatの値を変えるとどのvariablesが変わるかと言うとlog_binbinlog_formatです。
log_binがONになり、binlog_formatの値が指定のものになる、それによってようやくバイナリーログが有効になります。

3. バイナリーログ保持期間を設定

バイナリーログの保持期間を設定します。Auroraだと最大で90日間まで設定できます。

以下のストアドプロシージャを用いましょう。
mysql.rds_show_configuration
mysql.rds_set_configuration

ここでの注意点としては2点あります。
1点目は、当たり前ですが保持期間を伸ばせば伸ばすほどDBのパフォーマンスは落ちます。
2点目は、Auroraインスタンスタイプがt2.smallの場合は最大で7日間までしか保持期間を設定できないみたいです。(ドキュメントには書いてありませんが検証しました)
t2.smallインスタンスはAuroraで用いられる場合は開発、検証環境のみでの使用が推奨されているので、それと関連がありそうです。

f:id:xkenshirou:20200705022710p:plain

4. バイナリーログのポジションとファイル名を確認、スナップショットを取得

実際にBアカウントのAuroraでレプリケーションを有効にする場合、バイナリーログのファイル名とどの時点からレプリケーションをスタートさせるかのポジションを指定する必要があります。
そのためにAアカウントのAurora内でshow master statusコマンドを叩きます。

f:id:xkenshirou:20200705023903p:plain

上の画像では、ファイル名がmysql-bin-changelog.000002、ポジションが3799であることがわかり、この値は後ほど使います。

そして対象のDBのスナップショットを取得してここでの手順は完了です。

ちなみにこのポジションがとても重要で、少しでも間違えるとレプリケーションの際にデータ欠損やデータ重複が生まれます。 一つでもinsertやupdateイベントがあると更新されるので、何回かshow master statusを叩いてポジションがしばらく変化しないタイミングでスナップショットを取得し、その後もしばらくコマンドを叩いてスナップショット取得期間でデータが更新されていないことを確認しましょう。
スナップショットはスナップショット取得開始した時点までのデータを反映します。

※ちなみに公式ドキュメントではこの間DBをLOCKすることを推奨していますが、今回の要件としてLOCKはできなかったので上記の様なやり方になりました。

5. スナップショットを暗号化するKMSキーをBアカウントとの共有を有効

Aアカウントで取得したスナップショットを暗号化しているKMSキーをBアカウントとの共有を許可しましょう。

KMSのCMK(customer master key)でないとアカウント間共有できないので注意してください。
もしCMKで暗号化していない場合はスナップショットのアクションから"スナップショットのコピー"を選択して暗号化キーを変えてください。

KMSの指定のCMKを選択し、別のAWSアカウントとの共有を有効にしましょう。

f:id:xkenshirou:20200705110739p:plain

f:id:xkenshirou:20200705114723p:plain

6. スナップショットを共有

共有するスナップショットを選択し、スナップショットの共有からBアカウントへの共有を許可しましょう。

一旦Aアカウントでの作業は終了です。

f:id:xkenshirou:20200705111323p:plain

7. 共有されたスナップショットからAuroraを復元

次はBアカウントに移りましょう。

共有されたスナップショットはスナップショットページの"自分と共有"の項目に表示されるので対象のスナップショットを選択しましょう。
そして、その選択したスナップショットを元のAuroraと同じ設定値にして、復元して完了です。

この際、復元した後にセキュリティーグループの設定はお忘れなく。

f:id:xkenshirou:20200705111722p:plain

8. レプリケーションユーザーを作成

またAアカウントに戻ってきます。

Aアカウントの対象DBにログインをしてレプリケーション専用ユーザーを作成しましょう。

以下のようなコマンドを実行してください。

・専用ユーザーの作成

mysql> create user ‘repl_user'@’%' identified by '<password>';

レプリケーション権限の付与

mysql> GRANT REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'repl_user'@'%';

・権限を確認

mysql> show grants for ‘repl_user'@’%';



9. レプリケーション設定

Bアカウントでレプリケーションの設定をします。
以下のコマンドを実行します。

mysql> CALL mysql.rds_set_external_master ('<旧クラスターホスト>', 3306, 'repl_user', '<password>', '<binlog file name>', <position>, 0);

先程、Aアカウントで作成したユーザーを指定して、レプリケーションをスタートするバイナリーログファイル名とポジションをここで指定します。
今回で行ったらファイル名がmysql-bin-changelog.000002、ポジションが3799でしたね。

10. レプリケーションスタート

これで終わりです。

以下のコマンドでレプリケーションをスタートしましょう。

mysql> CALL mysql.rds_start_replication;

レプリケーションをスタートしたら必ず、レプリケーション状態を確認しましょう。

mysql> show slave status\G;

以下が実行結果例です。

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: xxx.ap-northeast-1.rds.amazonaws.com
                  Master_User: repl_user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: xxx
          Read_Master_Log_Pos:xxx
               Relay_Log_File: xxx
                Relay_Log_Pos:xxx
        Relay_Master_Log_File: xxx
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table: mysql.rds_replication_status,mysql.rds_monitor,mysql.rds_sysinfo,mysql.rds_configuration,mysql.rds_history
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos:xxx
              Relay_Log_Space:xxx
              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: xxx
                  Master_UUID: xxx
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:

※ xxxとしているところは意図的に変更しています。

レプリケーションスレーブの状態が確認できます。
重要なのは以下

・Slave_IO_State
このパラメーターを見ればレプリケーションに成功しているかがわかります。Waiting for master to send eventとなっていれば成功しています。

・Seconds_Behind_Master
masterとの差分が見れます。0になっていれば差分なくレプリケーションできています。

・Slave_SQL_Running_State
レプリケーションにエラーが出た場合はこちらに表示されます。



以上で移行完了です!お疲れ様でした。



まとめ

結構無理難題でしたが結果的にはできましたね。

アカウント間での移行なのでVPCピアリング張ったりと結構難易度高めでした。

公式のドキュメントにある方法をいろいろと駆使したり、公式のサポートにも助言を仰いだりしてなんとか実現できた形なので、結構オリジナルではありますが、かなり正確に移行できると思います。

今回の重要な点は確認です。

レプリケーションが有効になっているかの確認を怠ってアプリケーションのRDSへの向き先を変えると大変なことになるので注意しましょう。

※参考
レプリケーションについての公式ドキュメント アカウント間でのやり方や、結構情報が足りないので参考程度に。

docs.aws.amazon.com

・パラメーターグループ一覧

docs.aws.amazon.com