理系学生日記

おまえはいつまで学生気分なのか

TerraformによるAzure Database for MySQLのポイントインタイムリストア・Geoリストア検証

Azure Database for MySQLのリストア検証のお時間です。

Azure Database for MySQLのリストア前提

Azure Database for MySQLにおいては、データベースのバックアップは自動的に取得されます。 ユーザとして、このバックアップにはアクセスできず、リストアのみ可能というのが仕様です。

これらのバックアップ ファイルはユーザーに公開されておらず、エクスポートできません。 これらのバックアップは、Azure Database for MySQLの復元操作にのみ使用できます

Azure Database for MySQL でのバックアップと復元

Azure Database for MySQLでは、このバックアップを利用して以下の2種類のリストアが可能です。

  1. ポイントインタイムリストア
  2. Geoリストア

ポイントインタイムリストアは、元のサーバが存在するリージョンに、その名の通り指定した日時のデータベースの状態に復旧できるリストアです。 バックアップ保有期間は7〜35日(ユーザが指定可能)で、そこまでの日時に対してはポイントインタイムリストアが可能です。

一方でGeoリストアは、別のリージョンにデータベースを復旧するリストアです。 Geoリストアの目的はリージョン障害からの復帰であるため、指定日時の状態ではなく、最新バックアップの状態に復旧します。

準備

というわけで、まずはリストア対象のMySQLサーバーを用意します。

まずはリストア対象のテーブルが必要ですので、以下のような雑なテーブルを用意しました。 いずれも、Now()で現在時刻をinsertしています。

mysql> select * from hoge.mydb;
+---------------------+
| dt                  |
+---------------------+
| 2021-07-31 15:33:22 |
| 2021-07-31 15:33:40 |
| 2021-07-31 15:33:46 |
| 2021-07-31 15:39:22 |
| 2021-07-31 15:43:19 |
| 2021-07-31 15:46:56 |
| 2021-07-31 15:49:59 |
| 2021-07-31 15:50:54 |
| 2021-07-31 15:53:20 |
| 2021-07-31 16:52:31 |
| 2021-07-31 16:52:32 |
| 2021-07-31 16:52:33 |
+---------------------+

PointInTimeRestore

7/31 15:50:00をターゲットにポイントインタイムリストアを実施してみましょう。 想定している結果は、リストア後のMySQLサーバーに15:50までのデータが存在しており、15:50以降のデータが存在していないことです。

azurerm_mysql_serverには、リストアに直接関連する設定として以下の3つがあります。

  • create_mode: どのような形でリストアを行うのかを指定する
  • restore_point_in_time: ポイントインタイムリストアの場合、リストアの対象日時を指定する
  • creation_source_server_id: リストア元となる既存のMySQLサーバのIDを指定する

Terraformで指定すると、おおよそ以下のような形になるでしょう。 restore_point_in_timeはRFC 3339形式を解釈するため、タイムゾーンの指定が可能です。

  server_name         = "kiririmode-mysql-restore"
  (snip)
  create_mode               = "PointInTimeRestore"
  restore_point_in_time     = "2021-07-31T15:50:00+09:00"
  creation_source_server_id = module.mysql.id

これでTerraformをapplyすると、バックアップからリストアが実行され、新しいMySQLサーバーが立ち上がります。 この「新しい」というところがポイントで、リストア後に構築されるMySQLサーバーは、既存のMySQLサーバーとは別インスタンスです。 アプリケーションが接続先のデータベースを切り替えるためには、何らかの仕掛けが必要でしょう。

リストア後、実際にmysqlでクエリを投げてみました。以下の通り、15:50までのデータしか入っておらず、 想定通りのポイントインタイムリストアが実行できているようです。

$ mysql -h kiririmode-mysql-restore.mysql.database.azure.com -u 'kiririmode@kiririmode-mysql-restore' -p
mysql> select * from hoge.mydb;
+---------------------+
| dt                  |
+---------------------+
| 2021-07-31 15:33:22 |
| 2021-07-31 15:33:40 |
| 2021-07-31 15:33:46 |
| 2021-07-31 15:39:22 |
| 2021-07-31 15:43:19 |
| 2021-07-31 15:46:56 |
| 2021-07-31 15:49:59 |
+---------------------+
7 rows in set (0.03 sec)

Geoリストア

では、Geoリストアも実施してみましょう。 Geoリストアの場合は、指定日時という概念がなく、最新バックアップからのリストアになります。 このため、指定はPointInTimeRestoreよりはシンプルです。

  server_name         = "kiririmode-mysql-georestore"
  (snip)
  create_mode               = "GeoRestore"
  creation_source_server_id = module.mysql.id

最新のデータまで復元されていました。

$ mysql -h kiririmode-mysql-georestore.mysql.database.azure.com -u 'kiririmode@kiririmode-mysql-georestore' -p
mysql> select * from hoge.mydb;
+---------------------+
| dt                  |
+---------------------+
| 2021-07-31 15:33:22 |
| 2021-07-31 15:33:40 |
| 2021-07-31 15:33:46 |
| 2021-07-31 15:39:22 |
| 2021-07-31 15:43:19 |
| 2021-07-31 15:46:56 |
| 2021-07-31 15:49:59 |
| 2021-07-31 15:50:54 |
| 2021-07-31 15:53:20 |
| 2021-07-31 16:52:31 |
| 2021-07-31 16:52:32 |
| 2021-07-31 16:52:33 |
+---------------------+

注意点

リストアはMySQLサーバー単位で行われます。 MySQLデータベースはMySQLサーバー「内」の概念なので、リストアとともに復元されます。 が、Terraformユーザーへの落とし穴としてMySQLデータベースはTerraform管理下に入りません。

これまでデータベースもTerraformで管理していた場合、リストアの結果として、いわゆる「構成ドリフト」が発生します。

Terraformにおける構成ドリフトの解決策の1つはterraform importです。

mysql_database resourceはimportをサポートしています。 MySQLサーバー内のデータベースとそのIDはaz mysql db listで取得できるので、自分で作ったデータベースをimportしていけば良いでしょう。

$ az mysql db list --resource-group rg_kiririmode_mysql --server-name kiririmode-mysql-restore --output table --query '[].[name,id]'
Column1              Column2
-------------------  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
mysql                /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_kiririmode_mysql/providers/Microsoft.DBforMySQL/servers/kiririmode-mysql-georestore/databases/mysql
information_schema   /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_kiririmode_mysql/providers/Microsoft.DBforMySQL/servers/kiririmode-mysql-georestore/databases/information_schema
performance_schema   /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_kiririmode_mysql/providers/Microsoft.DBforMySQL/servers/kiririmode-mysql-georestore/databases/performance_schema
sys                  /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_kiririmode_mysql/providers/Microsoft.DBforMySQL/servers/kiririmode-mysql-georestore/databases/sys
hoge                 /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_kiririmode_mysql/providers/Microsoft.DBforMySQL/servers/kiririmode-mysql-georestore/databases/hoge
kiririmode-database  /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg_kiririmode_mysql/providers/Microsoft.DBforMySQL/servers/kiririmode-mysql-georestore/databases/kiririmode-database