【OCI クラウド移行ガイド】AWS MySQLonEC2とOCI MySQL on Compute で双方向レプリケーションを設定してみた

# OCIクラウド移行ガイドとは
オンプレミスやAWSなど、複数のプラットフォームからOracle Cloud Infrastructureへの移行プロジェクトに取り組んでいるクラウドエンジニア（@araidon,@kazunishi,@yama6）による、OCI移行手順をまとめたシリーズ記事です。
各回、サンプルワークロードから対象サービスを取り上げ、移行手順をガイドいたします。
まとめ記事は以下になります。

https://qiita.com/yama6/items/b197c0fe3ec75eb02637

# 移行したいサンプルワークロード
日々の業務でよく目にするサービスを中心に、サンプルワークロードとしてまとめてみました。このシリーズでは、主にAWSからの移行を取り上げます。
このワークロードは、ユーザがログインして、Web上で写真を共有するWebサービスをイメージしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2471135/5d657078-030c-a277-544e-eebe031c74cf.png)

# 移行するサービス：Amazon RDS for MySQL
MySQLの移行を実施する際、様々な手法/手段が考えられますが今回はMySQLの機能を利用したレプリケーションを試してみたいと思います。
今回はその中でも双方向レプリケーションを試してみたいと思います。
とはいえ双方向レプリケーションは本番運用で利用するには危険性が高いと筆者は考えています。ご利用する際は十分にご注意ください。

対象とするのはAWS,OracleCloud共に仮想VM上で構築したMySQL同士で試していきたいと思います。

レプリケーション関連はいくつかパターンを実施しています。
過去記事は以下ですので参考になれば。

https://qiita.com/kazunishi/items/2f5abc6af21889f4295a

https://qiita.com/kazunishi/items/92fb241552eaa4273e3e

# 双方向レプリケーションの利用ケースや懸念点
大前提として通常利用は管理が煩雑になりがちです
　→非同期レプリケーションの為、データ不整合になった場合レプリケーションが全て停止する
　　→レプリケーションが停止しても、動作は止まらない為、データベースでデータの枝分かれ(=差異)が発生する
　　　→結果：2つのDB間でのデータ保証が取れない可能性あり

上記より、考えられる利用シーンは以下2つくらいかなと思います。
１）東京・大阪でのDR（基本は東京or大阪の一方通行／障害切り替え後に止めていたReplicationでデータの同期を行う）
２）Version Upやシステム切り替えの際の一時的な利用に限る

# 作業の流れ
イメージとしては「MySQL A(=マスタ)→MySQL B(=スレーブ)のレプリケーション」
「MySQL B(=マスタ)→MySQL A(=スレーブ)のレプリケーション」という方向でそれぞれレプリケーション設定をしていきます
⓪AWSとOCI間でVPN接続した環境の構築
①AWS MySQL on EC2の構築及び、レプリケーションに向けたセットアップ
②OCI MySQL on Computeの構築及び、レプリケーションに向けたセットアップ
③AWS MySQL on EC2→OCI MySQL on Computeへのレプリケーション設定
④OCI MySQL on Compute→AWS MySQL on EC2へのレプリケーション設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2471135/d9f9ba85-b06d-e6ee-edd9-fe4fa2e041eb.png)

# ⓪AWSとOCI間でVPN接続した環境の構築
OCI MDSはパブリックエンドポイントを持つことができないため、AWSとOCIをVPN接続する必要があります。
今回、VPN接続の設定方法は以下記事を参考にしました。

https://qiita.com/yama6/items/c188de191269cb604341

無事にVPN接続ができれば以降の工程に進みます。

# ①AWS MySQL on EC2の構築及び、レプリケーションに向けたセットアップ
今回、EC2の作成手順は割愛させていただき、EC2にssh接続できる状態から作業を開始させていただきます
OSはAmazonLinux2 / t3.midiumを利用しました（t2.microだとインストールに時間がかかったため）
作業については以下のQiita記事を参考にさせていただきました。

https://qiita.com/Hide-Zaemon/items/caa7ff79b3322b6af14a

yumのアップデート
```
sudo yum update
```
デフォルトMariaDBモジュールの無効化
```
sudo yum remove mariadb-libs
```
MySQLチームが提供しているyumの公式リポジトリをセットアップします
リポジトリ追加
```
sudo yum -y install https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
```
リポジトリの有効化
```
sudo yum-config-manager –enable mysql80-community
```
AmazonLinux2の場合、RPM-GPG-KEYのエラーを回避するために以下を実施します
```
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022
```
mysqlのインストール
```
sudo yum install mysql-community-server
```
my.cnfに設定追加を実施します
```
vi /etc/my.cnf

以下を追加
gtid_mode=ON
enforce-gtid-consistency=true
binlog_format=row
local-infile
log-bin=mysql-bin
log-slave-updates
server_id=102
※OCI側とserver_idを異なるように設定する必要があります
```

MySQLの開始と有効化
```
sudo systemctl start mysqld.service
sudo systemctl enable mysqld.service
```
初期パスワードの確認
```
grep 'temporary password' /var/log/mysqld.log
```
MySQL on EC2のmysqlにログイン
```
mysql -uroot -p
Enter password: <初期パスワードを入力>
```

初期パスワードの変更
```
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '変更後のパスワード';
```

レプリケーション用ユーザの作成
```
mysql> create user usertemp2@'%' identified with mysql_native_password by 'Manager@123';
mysql> grant replication slave on *.* to usertemp2@'%';
mysql> grant all privileges on *.* to usertemp2@'%';
```

GTIDの確認
※gtid_executedの数値をメモしておきます

```
mysql> show global variables like 'GTID%';
+----------------------------------+------------------------------------------+
| Variable_name                    | Value                                    |
+----------------------------------+------------------------------------------+
| gtid_executed                    | 2bb06fc4-895c-11ee-9bd0-06e2971cd3ad:1-4 |
| gtid_executed_compression_period | 0                                        |
| gtid_mode                        | ON                                       |
| gtid_owned                       |                                          |
| gtid_purged                      |                                          |
+----------------------------------+------------------------------------------+
5 rows in set (0.00 sec)
```

ここまで作業が完了すれば次はOCI側の作業に移ります。

# ②OCI MySQL on Computeの構築及び、レプリケーションに向けたセットアップ
今回、Computeの作成手順は割愛させていただき、Computeにssh接続できる状態から作業を開始させていただきます。OSはOracleLinuxを選択しました。

最初にFirewallの透過設定をしておきます
```
sudo firewall-cmd --add-service=mysql --zone=public --permanent
sudo firewall-cmd --reload
```
MySQLチームが提供しているyumの公式リポジトリをセットアップします
レポジトリ追加
```
sudo yum install https://dev.mysql.com/get/mysql80-community-release-el8-4.noarch.rpm
```
RHEL8系のOSの場合、デフォルトで有効になっているMySQLモジュールを無効化する必要があるため、以下のコマンドを実行(デフォルトMySQLモジュールの無効化)
```
sudo yum module disable mysql
```
mysqlのインストール
```
sudo yum install mysql-community-server
```
my.cnfに設定追加を実施します
```
vi /etc/my.cnf

以下を追加
gtid_mode=ON
enforce-gtid-consistency=true
binlog_format=row
local-infile
log-bin=mysql-bin
log-slave-updates
server_id=101
※AWS側とserver_idを異なるように設定する必要があります
```

MySQLの開始と有効化
```
sudo systemctl start mysqld.service
sudo systemctl enable mysqld.service
```
初期パスワードの確認
```
grep 'temporary password' /var/log/mysqld.log
```
MySQL on Computeのmysqlにログイン
```
mysql -uroot -p
Enter password: <初期パスワードを入力>
```

初期パスワードの変更
```
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '変更後のパスワード';
```
レプリケーション用ユーザの作成
```
create user usertemp1@'%' identified with mysql_native_password by 'Manager@123';
grant replication slave on *.* to usertemp1@'%';
grant all privileges on *.* to usertemp1@'%';
```

念のため、通信の確認のために作成したユーザで「AWS側インスタンス→OCI MySQL」「OCI側インスタンス→AWS MySQL」にそれぞれアクセスできるか事前に確認しておきます
AWS側インスタンス→OCI MySQL
```
mysql -u usertemp1 -p -h OCIプライベートIP
```
OCI側インスタンス→AWS MySQL
```
mysql -u usertemp2 -p -h AWSプライベートIP
```

# ③AWS MySQL on EC2→OCI MySQL on Computeへのレプリケーション設定
## ③-1レプリケーション設定
AWS側のMySQLで確認したGTIDをOCI側のMySQLにpurgedします(=OCI MySQLでの作業)
MySQL on Computeのmysqlにログインします
```
RESET MASTER;
SET @@GLOBAL.gtid_purged = "AWS側で確認した数値";
SET @@GLOBAL.gtid_purged = "2bb06fc4-895c-11ee-9bd0-06e2971cd3ad:1-4";
```
レプリケーション設定をします
```
change REPLICATION SOURCE to source_host='AWSプライベートIP',source_user='usertemp2', source_password='Manager@123', source_auto_position=1 for channel 'channel2';


change REPLICATION SOURCE to source_host='10.1.141.29',source_user='usertemp2', source_password='Manager@123', source_auto_position=1 for channel 'channel2';
```
レプリケーションを開始します
```
start replica for channel 'channel2';
```
レプリケーションのステータスを確認します
```
SHOW REPLICA STATUS\G
```
エラーが出ていないことを確認します
何かしらエラーが出ているときは以下でレプリケーションを停止して原因の究明/対処を実施します
```
stop replica for channel 'channel2';
```
## ③-2動作確認
AWS側MySQLをマスタとしたレプリケーションが設定できたのでAWS側MySQLにアクセスします

以下更新作業を実施します
```
CREATE DATABASE rpl;
CREATE TABLE rpl.test(id int AUTO_INCREMENT, col1 CHAR(10), PRIMARY KEY(id));
INSERT INTO rpl.test VALUES(1, "TEST");
SELECT * FROM rpl.test;
```

OCIのMySQLでもrplDBがレプリケーションされていることを確認するため、OCIのMySQLにアクセスします
AWS側での更新作業が反映されていることを確認します
```
SHOW DATABASES;	
	mysql> SHOW DATABASES;
	+--------------------+
	| Database           |
	+--------------------+
	| information_schema |
	| mysql              |
	| performance_schema |
	| rpl                |
	| sys                |
	+--------------------+
	5 rows in set (0.00 sec)
	
SELECT * FROM rpl.test;	
	mysql> SELECT * FROM rpl.test;
	+----+------+
	| id | col1 |
	+----+------+
	|  1 | TEST |
	+----+------+
	1 row in set (0.01 sec)
```
これにより、現時点で以下のようなAWS MySQLをマスタ、OCI MySQLをスレーブとしたレプリケーションが設定できました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2471135/077bc658-8568-78cb-f6a7-5c8b88232a5c.png)

# ④OCI MySQL on Compute→AWS MySQL on EC2へのレプリケーション設定
続いて、逆方向のOCI MySQLをマスタ、AWS MySQLをスレーブとしたレプリケーションが設定していきます

## ④-1レプリケーション設定
AWS側のMySQLにアクセスします

レプリケーション設定をします
※OCIで設定したchannel名とは異なるものを設定します
```
change REPLICATION SOURCE to source_host='10.2.1.197',source_user='usertemp1', source_password='Manager@123', source_auto_position=1 for channel 'channel1';
```
レプリケーションを開始します
```
start replica for channel 'channel1';
```
レプリケーションのステータスを確認します
```
SHOW REPLICA STATUS\G
```
エラーが出ていないことを確認します
何かしらエラーが出ているときは以下でレプリケーションを停止して原因の究明/対処を実施します
```
stop replica for channel 'channel1';
```

OCI側MySQLをマスタとしたレプリケーションが設定できたのでOCI側MySQLにアクセスし、更新作業を実施します
```
CREATE DATABASE rpl2;
CREATE TABLE rpl2.test(id int AUTO_INCREMENT, col1 CHAR(10), PRIMARY KEY(id));
INSERT INTO rpl2.test VALUES(1, "TEST");
SELECT * FROM rpl2.test;
```

AWSのMySQLでもrplDBがレプリケーションされていることを確認します。
```
SHOW DATABASES;	
	mysql> SHOW DATABASES;
	+--------------------+
	| Database           |
	+--------------------+
	| information_schema |
	| mysql              |
	| performance_schema |
	| rpl                |
	| rpl2               |
	| sys                |
	+--------------------+
	6 rows in set (0.00 sec)
	
	
SELECT * FROM rpl2.test;	
	mysql> SELECT * FROM rpl2.test;
	+----+------+
	| id | col1 |
	+----+------+
	|  1 | TEST |
	+----+------+
	1 row in set (0.00 sec)
```

これでOCI MySQLをマスタ、AWS MySQLをスレーブとしたレプリケーションが設定ができました
それにより以下のような双方向レプリケーションが実現できました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2471135/ee28673e-ef3f-4b2c-9f75-b3d17eaec341.png)

使う場面は限定されそうですが、双方向レプリケーションを実施する方への一助になれば幸いです。
今回はIaaS上での検証を実施しましたが、今後はRDS⇔MDS、RDS⇔PaaS、MDS⇔MDS等のパターンも実施できればと思います。