とあるAuroraクラスタの、完全なデータコピーかつ最新データも元クラスタからレプリケーションで反映し続けるクラスタって作れるのかなーと調べてみると、公式のユーザーガイドにも一般情報にも見つからないしで、裏技臭がしたので書き留めておきます。
実はわりと常識レベルだったりしたらゴメンナサイって感じですが・・・おそらく仕様変更など入らず、ずっと使い続けられるテクニックだと推測できる内容であります。それでは、Auroraクラスタの完全コピーを作りたくなった理由も含めて、まとめていきたいと思います。
Auroraの基本的なレプリケーション
Auroraの復習ですが、Writer(=Master) と Reader(≒Slave) は共通のディスクを利用することで、極小さな遅延(10-20ms)でのデータ共通化を実現しています。なので、Writer/Reader間は従来のbinlogを用いたMySQLレプリケーションではありません。もし、従来のレプリケーションをしたいのであれば、こちら
クラスタの最新コピーを作りたい
適切な単語を知らないだけかもですが、ここでいう最新のコピーってのは、運用中のWriterと全く同じデータを保持し続けるWriterのつもりで書いています。図にするとこんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 |
Cluster01 ┏━━━━━┓ 共有Disk ┏━━━━┓ ┃Writer 01 ┠─────┨ Readers┃ ┗━━┯━━┛ ┗━━━━┛ | |レプリケーション Cluster02 | ┏━━┷━━┓ ┏━━━━┓ ┃Writer 02 ┠─────┨ Readers┃ ┗━━━━━┛ 共有Disk ┗━━━━┛ ↑ └─ こいつを作りたい |
作りたい理由
コピーを作りたい理由は主に2つあります。1つは、稼働中の Writer01 に変更を加える計画がある時に、その変更によって悪影響が出ないか確認することです。例えば、オンラインALTER TABLE や、pt-online-schema-change によるものです。Writer02 に参照はこないものの、十分な更新量の中で検証実行できるので、Writer01 におけるオンライン実行の安全性を確認しやすくなります。ついでに、アプリケーションから発行された更新クエリが、スキーマ変更後もエラーにならないか、といった確認もできます。
もう1つは、クラスタの切り替えです。通常はオンラインではしづらい変更── 例えばデータの分割や、変更時間が長く影響がでるスキーマ変更です。Writer02 を作成して、事前にデータの編集を済ませ、Blue-Green Deployment手法などで瞬時に切り替えることで、重い変更もオンラインでできる場合があります。
ボツネタ集
まずは、そんな願望を砕いてくれたパッと思いつく系の手法を並べておきます。スナップショット復元
RDSは自動または手動によって、スナップショットを取得することができるので、それを復元して不足分をレプリケーションで持ってくればえぇやん☆というものです。
1 2 3 4 5 6 7 8 9 10 |
Cluster01 ┏━━━━━┓ 共有Disk ┏━━━━┓ ┃Writer 01 ┠─────┨ Readers┃ ┗━━━━━┛ ┗━━━━┛ ↑途中からレプリケーションするための明確な情報がない Cluster02 | ┏━━┷━━┓ ┃Writer 02 ┃← スナップショットから復元 ┗━━━━━┛ |
これは悲しいことに、目につく基本情報では、元Writerの binlog の File/Position が不明なため、レプリケーションを始めることができません。スナップショットとbinlogは全くの別機能であるため、Writerが binlog_format を有効にしているからといって、スナップショットの詳細情報に binlog 情報を記録してくれるというわけではないのです。
まぁAWS大先生なら、これくらいならいつか実装してくれそうな気がしますが・・・
最初からレプリケーションしておく
Cluster01 を作成する時に、同時に Cluster02 も作成しておき、レプリケーションしておくという方法です。
1 2 3 4 5 6 7 8 9 10 |
Cluster01 ┏━━━━━┓ 共有Disk ┏━━━━┓ ┃Writer 01 ┠─────┨ Readers┃ ┗━━┯━━┛ ┗━━━━┛ | |レプリケーションしておく Cluster02 | ┏━━┷━━┓ ┃Writer 02 ┃ ┗━━━━━┛ |
これは、Writer02 を常時稼働させる分が費用的に無駄ですし、検証終了後などに作り直したい場合にアウトなので、お話になりません。
漢は黙ってmysqldump!!
–master-data くっつけて採取して、Writer02 にリカバリや!
1 2 3 4 5 6 7 8 9 10 |
Cluster01 ┏━━━━━┓ 共有Disk ┏━━━━┓ ┃Writer 01 ┠─────┨ Readers┃ ← 1.mysqldump取得して ┗━━┯━━┛ ┗━━━━┛ | |3.レプリケーションまで(笑) Cluster02 | ┏━━┷━━┓ ┃Writer 02 ┃← 2.リカバリして ┗━━━━━┛ |
……なんて爺臭いものをAurora上でやってたら、死にたくなりそうですね。あらゆる意味で却下。
特定時点への復元……からのレプリケーション
特定の時点への DB インスタンスの復元 の機能を使うと、そのインスタンスの数分以上前の時点のデータを保持した、新規インスタンスを起動することができます。データの内容は、データベースやテーブルはもちろん、実はログも保持されているため、復元したWriter02 のログ状態を確認したら、Writer01 へレプリケーションをつなぐことが可能とわかりました。例えばこんな感じで・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# Writer01 でINSERT用テーブルを作成 writer01> create table numbers (`num` int auto_increment, index(num)); # Writer01 に対してINSERTを垂れ流す $ while : do echo "insert into numbers values();" | \ mysql -h gedow-01.cluster-abcdefg.ap-northeast-1.rds.amazonaws.com -u root -pPassWord gedow done # Writer01 から"特定時点への復元"で Writer02 を作成 # 作成が完了したらINSERTを止めておく # Writer01 のログを確認 writer01> SHOW BINARY LOGS; +----------------------------+-----------+ | Log_name | File_size | +----------------------------+-----------+ | mysql-bin-changelog.000001 | 120 | | mysql-bin-changelog.000002 | 1050 | | mysql-bin-changelog.000003 | 20829497 | +----------------------------+-----------+ # Writer02 のログを確認 # どうやら1, 2番のログも保持され続けていることがわかります # 3番のログはスナップショットの瞬間の最終地点と予想 # 4番以降は何度かの再起動時に切り替わっているものと予想 writer02> SHOW BINARY LOGS; +----------------------------+-----------+ | Log_name | File_size | +----------------------------+-----------+ | mysql-bin-changelog.000001 | 120 | | mysql-bin-changelog.000002 | 1050 | | mysql-bin-changelog.000003 | 2607945 | | mysql-bin-changelog.000004 | 120 | | mysql-bin-changelog.000005 | 120 | | mysql-bin-changelog.000006 | 120 | +----------------------------+-----------+ # 念のため双方の mysql-bin-changelog.000002 を比較し、同内容であることを確認 $ mysqlbinlog -u root -p -h gedow-01.cluster-abcdefg.ap-northeast-1.rds.amazonaws.com \ --read-from-remote-server --raw mysql-bin-changelog.000002 $ mysqlbinlog mysql-bin-changelog.000002 > gedow-01.log $ mysqlbinlog -u root -p -h gedow-02.cluster-abcdefg.ap-northeast-1.rds.amazonaws.com \ --read-from-remote-server --raw mysql-bin-changelog.000002 $ mysqlbinlog mysql-bin-changelog.000002 > gedow-02.log $ diff -u gedow-01.log gedow-02.log # Writer02 から Writer01 へレプリケーション接続してみます # まずは Writer01 でレプリケーションユーザーの準備と最大値の確認 writer01> GRANT REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'repl'@'10.%' IDENTIFIED BY 'password'; writer01> select max(num) from numbers; +----------+ | max(num) | +----------+ | 83988 | +----------+ # Writer02 でレプリケーションを開始 # Writer02 の最終地点と同じ場所から始めれば、スナップショット後の更新を全部反映できるはず writer02> select max(num) from numbers; +----------+ | max(num) | +----------+ | 10514 | +----------+ writer02> CALL mysql.rds_set_external_master ('gedow-01.cluster-abcdefg.ap-northeast-1.rds.amazonaws.com', 3306, 'repl', 'password', 'mysql-bin-changelog.000003', 2607945, 0); writer02> CALL mysql.rds_start_replication; writer02> show slave status \G Slave_IO_Running: Yes Slave_SQL_Running: Yes writer02> select max(num) from numbers; +----------+ | max(num) | +----------+ | 83988 | +----------+ |
ということで、無事、Writer01 の復元からレプリケーションで最新状態まで追いつくことができました。
再度スナップショットからの復元で深掘り
では、さきほど(公式情報だけで)諦めたスナップショットの復元も、実はbinlogが残っていてできるのではないか、と思い直して試したところ・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# Writer01 のスナップショットを取得し、 # Writer01 に少々の更新クエリを発行したあと、 # スナップショット復元から Writer02 を作成 # Writer01 のログ状態は writer01> SHOW BINARY LOGS; +----------------------------+-----------+ | Log_name | File_size | +----------------------------+-----------+ | mysql-bin-changelog.000001 | 120 | | mysql-bin-changelog.000002 | 1050 | | mysql-bin-changelog.000003 | 999 | +----------------------------+-----------+ # Writer02 のログ状態は writer02> SHOW BINARY LOGS; +----------------------------+-----------+ | Log_name | File_size | +----------------------------+-----------+ | mysql-bin-changelog.000001 | 120 | | mysql-bin-changelog.000002 | 1050 | | mysql-bin-changelog.000003 | 751 | | mysql-bin-changelog.000004 | 120 | | mysql-bin-changelog.000005 | 120 | +----------------------------+-----------+ # さきほどと同様に、Writer02 から Writer01 へレプリケーションを開始し、 # 正常に動作することを確認 |
ということで、スナップショットも、特定時点への復元も、おそらく /rdsdbdata/ ディレクトリを丸っと採っているようなバックアップデータを利用しているのではないか、と予想できます。(/rdsdbdata/tmp/ は知らんけど:-)
その他 運用など
公式手法ではない
今のところ、復元されたインスタンスから元のインスタンスに対して、残されたbinlog Positionを継続する形でレプリケーションをつなぐ、という方法は、公式ユーザーガイドやよくある質問には載っていなさそうです。そのため、ドヤって推奨できるものではないのですが、ログだけ後から削ったり、ログ部分だけスナップショットとして綿密に機能していましぇん、なんてことはないと思うので大丈夫でしょう(自己責任)復元後のインスタンス設定
今回は、「特定時点への復元」でも「スナップショットから復元」でも、元Writerからレプリケーションできることは確認できたわけですが、ではどちらがよいかというと、「特定時点への復元」が良いと考えています。理由は、「スナップショットから復元」だと、「DBクラスターのパラメータグループ」と「パラメータグループ」、「セキュリティグループ」がデフォルト指定になってしまうためです。binlog_format などを有効にするためには、起動後に「DBクラスターのパラメータグループ」を再指定して、再起動までしなくてはいけませんし、「セキュリティグループ」も当然変更しないと 3306 に接続できません。
「特定時点への復元」だと、「パラメータグループ」はデフォ開始なものの、「DBクラスターのパラメータグループ」は強制的に元Writerと同じになりますし、「セキュリティグループ」は復元時に選択済みになっています。また、自動スナップショットよりも binlog の位置が数分前とかなり近いため、最新状態になるまでの時間が短いだろうという理由もあります。
log_output との関係
log_output を TABLE にするか FILE にするかで保存先が変わりますが、結論を言うとどちらで運用しようと、途中で変更しようと、binlog の状態に変更はないことを確認しています。例えば、Writer01 => FILE の状態から復元をすると、復元したインスタンスの「パラメータグループ」はデフォ値になって TABLE として起動されますが、それでも binlog は元のスナップショット取得時の状態を保ってくれます。また、TABLE から FILE に変更したとしても、同様に状態を保ってくれます。
こういった仕様をみると、非常に気を使って設計したんだろうな、という印象を受けますね。
RDS MySQLの場合
いまさらですが……RDS MySQLの「特定時点への復元」を利用すると、こんな感じになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Writer01 を「自動バックアップ」を有効 = バイナリログが残る にして起動 # 少々の更新クエリを発行後にログを確認 writer01> show binary logs; +----------------------------+-----------+ | Log_name | File_size | +----------------------------+-----------+ | mysql-bin-changelog.000006 | 479 | | mysql-bin-changelog.000007 | 422 | +----------------------------+-----------+ # Writer02 をWriter01の「特定時点への復元」から起動 # 起動後にすぐログを確認 writer02> show binary logs; +----------------------------+-----------+ | Log_name | File_size | +----------------------------+-----------+ | mysql-bin-changelog.000002 | 414 | | mysql-bin-changelog.000003 | 638 | +----------------------------+-----------+ |
ということで、バイナリログは保持されないので、途中からレプリケーションすることはできません。また、スナップショットからも試みましたが、同じく取得時点の File/Position がわかる内容ではありませんでした。
この検証では、log_output=TABLE のままで行っているので、DBデータディレクトリを丸っと取得するようなものではない、と推測できます。
とりあえずやりたいことを実現できてホッとしたところですが、
ブラックボックスに手を突っ込んで探し出すのは、楽しくもあり、虚しくもあり、、
ゲーム攻略感覚みたいなものですね、ホント:-)