【Azure】Azure Database for MariaDBがサ終するくせにマイグレーションが用意されていない

Azureにはマネージドデータベースとして、[MySQL](https://azure.microsoft.com/ja-jp/products/mysql)・[MariaDB](https://azure.microsoft.com/ja-jp/products/mariadb)・[PostgreSQL](https://azure.microsoft.com/ja-jp/products/postgresql/)・[CosmosDB](https://azure.microsoft.com/ja-jp/products/cosmos-db/)・[SQL Server](https://azure.microsoft.com/ja-jp/products/azure-sql/)など様々な選択肢が用意されています。

管理画面からちょこっと操作するだけで簡単にDBを作ることができ、手動でやるとたいへん面倒なバックアップ・レプリケーション・スケーリングなどの設定もいとも簡単に行うことができます。
私はこれを使い始めてから、手動でデータベースを作ることがなくなりました。


ところがMicrosoftは先日、Azure Database for MariaDBの[提供をやめると発表](https://learn.microsoft.com/ja-jp/azure/mariadb/whats-happening-to-mariadb)しました。
2025/09/19に終了します。
サポートはしないけど一応動くとかではなく、完全に動かなくなるみたいです。
従って、MariaDBを使っている場合はそれまでにサービスを閉じるか別のデータベースに移行しなければなりません。

移行先としては[ドキュメント](https://learn.microsoft.com/ja-jp/azure/mariadb/whats-happening-to-mariadb)でもお勧めされている[Azure Database for MySQL フレキシブルサーバー](https://learn.microsoft.com/ja-jp/azure/mysql/flexible-server/overview)が最適でしょう。
そもそもMariaDBはMySQLのforkだから互換性が高く、というかこれまで互換性で引っかかったことがないです。
専門家に言わせれば違うのでしょうが、個人的には全く同じ使い方で支障が出たことはないですね。
だからきっと移行しても安心です。


# Azure Database Migration Service

Azureには[Database Migration Service](https://azure.microsoft.com/ja-jp/products/database-migration)というサービスが存在します。
これを使えばデータベースAからデータベースBへとデータを簡単に移行したり、さらには異なる種類のDB間でもレプリケーションすら行えてしまうという優れもののサービスです。
そんなわけで、さっそくこれを使って移行しようとしたわけですよ。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/cb63192e-fc1f-ba69-89a7-61d35c86ad6d.png)


は？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？？

お前の都合で移行するんだからマイグレーションくらい用意しとけ！！！！！！！！

# 手動でマイグレーションする

[Azure Database for MariaDBのドキュメント](https://learn.microsoft.com/ja-jp/azure/mariadb/whats-happening-to-mariadb)において、[移行方法の説明](https://aka.ms/AzureMariaDBtoAzureMySQL)へのリンクがはってあります。
ところがこの[マイグレーションガイド](https://aka.ms/AzureMariaDBtoAzureMySQL)、全面英語なうえに全てコマンドラインの手動マイグレーションであり、さらにはそのまま打つと動かないところがあったりして色々苦労したので記録しておきます。


# マイグレーションを実行するサーバを用意

移行元のMariaDBでも移行先のMySQLでもなく、それ以外にもう一台マイグレーション用のサーバが必要です。
[MySQL Shell](https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysqlsh.html)というクライアントを使うので、[そいつをインストールする](https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysql-shell-install-linux-quick.html)ためのサーバを用意します。
まあなんか適当に[VM](https://azure.microsoft.com/ja-jp/products/virtual-machines)でも立てればいいよ。

もしくは[MySQL ShellはWindows用もある](https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysql-shell-install-windows-quick.html)ので、ローカルマシンで動かしてかまわない規模のサービスであればこっちのほうが手っ取り早いかもしれません。

もちろんサーバからは、MariaDBとMySQL両方に対して3306ポートが通っている必要があります。

MySQL Shellのインストール手順。

```bash:マイグレーションサーバ
su -

# mysql-community-releaseの存在確認
yum repolist all | grep mysql
# mysql-community-releaseが既にインストールされていた場合は削除しないといけないらしい
yum remove mysql-community-release

# リポジトリを追加
wget https://dev.mysql.com/get/mysql80-community-release-el8-8.noarch.rpm
yum install mysql80-community-release-el8-8.noarch.rpm

# MySQL Shellをインストール
yum install mysql-shell

# インストール確認
mysqlsh --version
  # mysqlsh Ver 8.0.34 for Linux on x86_64
```

suするな？
うるせえ知るか。


# MariaDBのバイナリログが有効か確認する

とりあえずMariaDBで[バイナリログ](https://dev.mysql.com/doc/refman/8.0/ja/replication-options-binary-log.html)が有効になっているかを確認しましょう。

```sql:MariaDB
SHOW VARIABLES LIKE 'log_bin';
```

これがオフだった場合、MariaDBはバイナリログを保存していません。
バイナリログはINSERTとかUPDATEとかALTERとかのSQLを保存しているログです。
従ってこれがないとレプリケーションすることができないため、まずは有効にする必要があります。

でもAzure管理画面では、直接これを有効にする方法が何故かないんですよね。
『レプリケーション』 → 『レプリカの追加』を実行するとバイナリログが有効になります。
有効にさえしてしまえばレプリケーション自体は不要なのですぐに停止・削除してかまいません。

あとついでに、『接続のセキュリティ』の項目から『SSL接続を強制する』を無効にしておくと後で楽になります。


# MariaDBからバックアップ

現在のデータを、MariaDBからマイグレーション用サーバにバックアップします。


```bash:マイグレーションサーバ
# バックアップ用ディレクトリを用意
mkdir /path/to/tmp
cd /path/to/tmp

# MariaDBにログイン
mysqlsh --uri [ユーザ名]@[サーバ]:3306

# バックアップコマンド
util.dumpInstance("/path/to/tmp/mariadbbackup", {threads: 16, showProgress: true, users:false})
```

実際のログインコマンドは`mysqlsh --uri username@sample.mariadb.database.azure.com:3306`みたいなかんじになります。
これでMariaDBのデータがマイグレーションサーバの`/path/to/tmp/mariadbbackup`ディレクトリに保存されました。

なおユーザ名に`@`が入っていた場合は`%40`に書き替えましょう。
でないとユーザ名とサーバの区切りと誤認識されて動きません。

またmysqlsh上では、コマンドの最後に半角スペース等があるとエラーになります。
大抵の言語は末尾スペースとか無視してくれるのですが、mysqlshはそんな融通を利かせてはくれないようです。


`util.dumpInstance`は第二引数で[様々なパラメータに対応している](https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysql-shell-utilities-dump-instance-schema.html)のですが、今回は[ドキュメント](https://aka.ms/AzureMariaDBtoAzureMySQL)に従っただけなのでthreadsが何なのか、どうしてusersはfalseにするのか、などはよくわかりません。

またバックアップ対象のスキーマを指定していないのですが、自分で作ったスキーマだけがバックアップされました。
`sys`とか`information_schema`とかのシステム用は自動的に対象外にされるようです。
特定のスキーマだけをバックアップする[コマンド](https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysql-shell-utilities-dump-instance-schema.html)とかもありますが、今回は不要だったので調べていません。


# バックアップ位置を確認する

せっかく保存したバックアップファイルですが、どこまでのデータが入っているかわからないと、あとでレプリケーションするときに困ります。
`@.json`というファイルに、その情報が記録されています。

```bash:マイグレーションサーバ
cat /path/to/tmp/mariadbbackup/@.json

    "binlogFile": "mysql-bin.999999",
    "binlogPosition": 12345,
```

`binlogFile`と`binlogPosition`の値をメモしておきましょう。


# MySQLにリストア

今度は移行先のMySQLで作業します。

```bash:マイグレーションサーバ
# MySQLにログイン
mysqlsh --uri [ユーザ名]@[サーバ]:3306

# リストアコマンド
util.loadDump("/path/to/tmp/mariadbbackup", {threads: 16, showProgress: true, ignoreVersion: true})
```

[ドキュメント](https://aka.ms/AzureMariaDBtoAzureMySQL)には`ignoreVersion`がありませんが、これを指定しないとエラーになりました。

:::note alert
ERROR: Destination MySQL version is newer than the one where the dump was created.
Loading dumps from different major MySQL versions is not fully supported and may not work.
Enable the 'ignoreVersion' option to load anyway.
Util.loadDump: MySQL version mismatch (MYSQLSH 53011)
:::

移行先のメジャーバージョンが移行元より新しいのでエラーになったということでした。
でも移行元はMariaDB10.2で、移行先はMySQL8.0だったのですが、これでも移行先のほうが新しいと判定するんですね。

`ignoreVersion`はバージョンの違いを無視してリストアするオプションです。
つまりこれを指定すると、バージョンの違いによって互換性の問題が発生する可能性があるということです。
ということでバージョン違いによる差があるか調査してみたところ、移行前にはあった`INT(1)`とかの`(1)`が飛んで`INT`になっていました。
これは[MySQLの仕様](https://dev.mysql.com/doc/refman/8.0/ja/numeric-type-syntax.html)なので回避方法はありませんし、そもそもあれ桁数ではないのでほぼ無意味な項目であり、消えた方がすっきりですね。


# レプリケーション実行

単純にバックアップした時点のDBをリストアするだけでよいならここで終わりですが、毎秒アクセスが来るようなDBであれば、限りなく最新の状態を保ったまま移行する必要があります。
ということでレプリケーションの出番です。

```bash:マイグレーションサーバ
# MySQLにログイン
mysqlsh --uri [ユーザ名]@[サーバ]:3306

# 使用言語をSQLにする
\sql

# レプリケーション設定
CALL mysql.az_replication_change_master('MariaDBのサーバ', 'ユーザ名', 'パスワード', 3306, 'mysql-bin.999999', 12345, '');

# レプリケーション開始
CALL mysql.az_replication_start;

# ステータス確認
SHOW SLAVE STATUS;
```

レプリケーション設定の第5引数は`binlogFile`、第6引数は`binlogPosition`で、どこの時点からレプリケーションを始めるかの設定です。
従って、ここには先ほどメモした値を入力します。
誤った値を入れるとデータが正しくならないので注意しましょう。

`mysql.az_replication_start`後、レプリケーションがうまくいかなかったら`error connecting to master`みたいなエラーが発生します。
メッセージに従って対処する必要があります。
私はSSLでひっかかりました。

第7引数の''は、MariaDBへの接続にSSLが必須である場合に証明書を指定します。
のはずなんだけど、なんかフルパスで指定しても`SSL connection error: SSL_CTX_set_default_verify_paths failed`とか言われて解決できなかったんですよね。
よくわからないし面倒だったので、単にAzure管理画面からMariaDBへのアクセスにSSLが必須のチェックを外しました。
どうせ移行が終わったらDB自体すぐ止めるからまあ別にいいでしょ。

全てのエラーが解消されたのち、レプリケーションコマンドを打つと`Slave_IO_State`が`Waiting for source to send event`になります。
これでもうレプリケーションが始まっているはずなので、MariaDBになんか突っ込んでMySQL側に反映されるか確認するとよいでしょう。
ただレプリケーション開始直後はデータが追い付いていないので、同期が終わるまでしばらく待機する必要があります。
具体的には`Seconds_Behind_Master`の値が0になるまで待ちます。


# アプリケーションのDB向き先変更

最後にアプリケーションをデプロイします。
これは単にアプリケーションのDSNをMariaDBからMySQLに変更するだけです。

そういえばユーザは移行しなかったので、別途作成する必要があります。
適当にGRANTしておきましょう。


# レプリケーション停止

MySQL側にインサートとかを行ってレプリケーションに矛盾が生じたら、その時点でレプリケーションは勝手に止まるみたいです。
ただ判定がいまいちわからないので、手動で停止したほうがより安全だと思います。
これもマイグレーションガイドに載ってないんだよな。

```bash:マイグレーションサーバ
# MySQLにログインして実行
CALL mysql.az_replication_stop;
```

これでレプリケーションが停止しました。

最後にMariaDBとマイグレーションサーバを削除して、移行は完了です。


# 感想

Azure側都合で対応を取りやめるんだからマイグレーションくらい用意してくれよ。

[Azure Database Migration Service](https://azure.microsoft.com/ja-jp/products/database-migration)は、このあたりのコマンドをAzure管理画面上でできるようにしてあるだけだと思うんですよね。
しかしこれがもう驚くほど便利であり、3TBの高負荷MySQLを手動マイグレーションして死にかけていたのが画面上でぽちぽちするだけでできるようになったときはもう本当に感動感謝感激雨霰でしたよ。
だからもっと対応データベース増やしてくれ。