Amazon AuroraでBlue/Greenデプロイを検証する。

# はじめに
2022年の`AWS re:Invent`にて発表となった`RDS`での**Blue/Greenデプロイ**が、すぐにでも導入したい内容だったので、実験がてら検証してみたいと思います。

以下公式の発表と参考サイト

- [Announcing Amazon RDS Blue/Green Deployments for safer, simpler, and faster updates](https://aws.amazon.com/jp/about-aws/whats-new/2022/11/amazon-rds-blue-green-deployments-safer-simpler-faster-updates/)
- [New – Fully Managed Blue/Green Deployments in Amazon Aurora and Amazon RDS](https://aws.amazon.com/jp/blogs/aws/new-fully-managed-blue-green-deployments-in-amazon-aurora-and-amazon-rds/)
- [Using Amazon RDS Blue/Green Deployments for database updates](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/blue-green-deployments.html)
- [【衝撃】AWSのRDSがデータを失わないBlue/Greenデプロイに対応しました #reinvent](https://dev.classmethod.jp/articles/rds-bg-deploy/)

# 今回の発表について
今回、新機能として紹介されましたが、2021年8月に以下の記事が公開されている通り、以前から同様のソリューションは実現可能だったようです。

- [Automate the Amazon Aurora MySQL blue/green deployment process](https://aws.amazon.com/jp/blogs/database/automate-the-amazon-aurora-mysql-blue-green-deployment-process/)

今回のアップデートでマネジメントコンソール上から数ステップで**Blue/Greenデプロイ**の構成が実現できるようになったことが今回新機能として紹介された理由とのこと。

また、以下の制限事項があるため、制限事項に当てはまる場合は残念ながら使用できません。

- Blue/Greenデプロイ未サポート機能
    - Amazon RDSプロキシ
    - カスケードリードレプリカ
    - クロスリージョンリードレプリカ
    - Multi-AZ DB Cluster
    - AWS CloudFormation

- Blue/Greenデプロイ制限事項
    - 暗号化されていないDBインスタンスから暗号化されたDBインスタンスへの変更不可
    - 暗号化されたDBインスタンスから暗号化されていないDBインスタンスへの変更不可
    - Blue環境のDBインスタンスより低いエンジンバージョンへの変更不可
    - Blue環境とGreen環境のリソースは同一AWSアカウント

# AuroraでのBlue/Greenデプロイ構成構築
1から説明すると長くなるので、今回は東京リージョンでVPC等の準備は完了している前提で進めます。

- [事前準備](#事前準備)
    - [VPCサブネットの作成](#vpcサブネットの作成)
    - [サブネットグループの作成](#サブネットグループの作成)
- [パラメータグループの作成](#パラメータグループの作成)
- [パラメータの変更](#パラメータの変更)
- [Aurora Clusterの作成](#aurora-clusterの作成)
- [Blue/Greenデプロイの作成](#bluegreenデプロイの作成)

## 事前準備
:::note info
事前準備で紹介する以下2つの手順ですが、後述する`Aurora`の設定で「**EC2コンピューティングリソースに接続**」を選択する場合は実施不要です。

後述する手順では　検証で使うEC2インスタンスと接続するために「**EC2コンピューティングリソースに接続**」の手順で紹介していますが、「**EC2コンピューティングリソースに接続しない**」を選択する場合は適宜読み替えてください。
:::

### VPCサブネットの作成
もし`VPC`の**サブネット**の設定で、3つの`Availability Zone`（以下AZ）が作られていない場合は、以下の例のように3つ準備しておきます。

|IPv4 CIDR|Availability Zone|
|:--|:--|
|10.1.0.0/24|ap-northeast-1a|
|10.1.1.0/24|ap-northeast-1c|
|10.1.2.0/24|ap-northeast-1d|

### サブネットグループの作成
`3AZ`のサブネットグループが作成されていない場合は`RDS`のダッシュボードの「**サブネットグループ**」より作成しておきます。

|項目|設定|
|:--|:--|
|名前|任意（今回は`blue-green-subnet`）|
|説明|任意（今回は`blue-green-subnet`）|
|VPC|Auroraを所属させるVPC|
|アベイラビリティーゾーン|ap-northeast-1a<br>ap-northeast-1c<br>ap-northeast-1d|

## パラメータグループの作成
`RDS/Aurora`の**Blue/Greenデプロイ**を実施するためには**DBクラスタ用のパラメータグループ**の`binlog_format`を`MIXED`に変更する必要があります。

`binlog_format`のデフォルトは`OFF`なので、`Aurora`作成時に差し替えられるよう前もって`binlog_format`を`MIXED`に変更したパラメータグループを作成しておきます。

尚、デフォルトのパラメータグループは**パラメータの変更ができないため**新規で作成してパラメータ変更を行う必要があります。

今回の場合、**DBクラスタ用のパラメータグループ**にしか`binlog_format`のパラメータが存在しないため、**DBインスタンス用のパラメータグループ**は新たに作成する必要はありませんが、前述の通り、デフォルトのパラメータグループはパラメータ変更ができないため、ついでに**DBインスタンス用のパラメータグループ**も今後を考えて作成しておきます。

以下、`RDS`のダッシュボードの「**パラメータグループ**」から新規で作成する際の各種設定。

- DBクラスタ用パラメータグループ

|項目|設定|
|:--|:--|
|パラメータグループファミリー|aurora-mysql5.7|
|タイプ|DB Cluster Parameter Group|
|グループ名|任意（今回は`blue-green-cl-param`)|
|説明|任意（今回は`blue-green-cl-param`)|

- DBインスタンス用パラメータグループ

|項目|設定|
|:--|:--|
|パラメータグループファミリー|aurora-mysql5.7|
|タイプ|DB Parameter Group|
|グループ名|任意（今回は`blue-green-param`)|
|説明|任意（今回は`blue-green-param`)|

## パラメータの変更
作成した**DBクラスタ用のパラメータグループ**を編集して、`binlog_format`を`MIXED`に変更します。

`RDS`のダッシュボードから「**パラメータグループ**」を選択して、作成した`blue-green-cl-param`にチェックをしてから「**編集**」を選択します。

![Monosnap_20221210_105708.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/4624faa6-2556-8432-d814-1958db965c8b.png)

変更可能なパラメータが大量に表示されるため、フィルタ欄に`binlog_format`と入力して「**値**」のリストボックスから「**MIXED**」を選択して、「**変更の保存**」で保存して終了です。

::::note info
2024/07/29現在、導入当初のドキュメントの記載から更新されているようで、AWSのドキュメント上は「**ROW**」が推奨されておりますので、「**MIXED**」ではなく、「**ROW**」の使用を推奨します。
::::

![Monosnap_20221210_110307.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/e4c511c0-fbc6-a22f-cfc7-f8c209a9ee8e.png)

## Aurora Clusterの作成
今回はテストなので必要最低限の設定のみ行うようにします。

項目が多いので設定した内容を以下表で記載。

|項目|設定|備考|
|:--|:--|:--|
|データベース作成方法を選択|標準作成||
|エンジンのタイプ|Amazon Aurora||
|エディション|Amazon Aurora MySQL互換エディション|まだPostgreSQLは対応していないためMySQLを選択|
|グローバルデータベース機能をサポートするバージョンを表示|無効||
|並列クエリ機能をサポートするバージョンを表示|無効||
|Serverless v2をサポートするバージョンを表示|無効||
|利用可能なバージョン|Aurora (MySQL 5.7) 2.10.2|デフォルトで選択済みのバージョンを指定|
|テンプレート|開発/テスト|テスト目的なので今回は開発/テストを選択|
|DBクラスター識別子|任意（今回は`blue-green-deploy-db`）||
|マスターユーザー名|admin|デフォルトのまま|
|マスターパスワード|任意|適当なパスワードを指定|
|DBインスタンスクラス|メモリ最適化クラス(r クラスを含む)||
|インスタンスタイプ|db.r6g.large|最低限のスペックに変更|
|マルチAZ配置|別のAZでAuroraレプリカ/リーダーノードを作成する(可用性のスケーリングに推奨)||
|コンピューティングリソース|EC2コンピューティングリソースに接続|検証でEC2を使用したいため指定|
|EC2インスタンス|(検証用EC2)||
|Virtual Private Cloud(VPC)|AuroraとEC2が所属するVPC|選択不可のため、デフォルトのまま<br>EC2を接続しない場合は指定|
|DBサブネットグループ|`rds-ec2-db-subnet-group-1`|選択不可のため、デフォルトのまま<br>EC2を接続しない場合は作成した`blue-green-subnet`を指定|
|パブリックアクセス|なし||
|VPCセキュリティグループ(ファイアウォール)|新規作成||
|新しいVPCセキュリティグループ名|任意（今回は`Blue-Green-SG`）||
|データベースポート|3306||
|データベース認証オプション|パスワード認証||
|Performance Insightsをオンにする|無効|今回はテストなので未設定|
|拡張モニタリングの有効課|無効|今回はテストなので未設定|
|最初のデータベース名|任意（今回は`bluegreendb`）||
|DBクラスターのパラメータグループ|blue-green-cl-param|先ほど作成したものを指定|
|DBパラメータグループ|blue-green-param|先ほど作成したものを指定|
|フェイルオーバー優先順位|指定なし||
|バックアップ保持期間|1日間|デフォルトのまま|
|スナップショットにタグをコピー|有効|デフォルトのまま|
|暗号を有効化|無効||
|バックトラックを有効にする|無効||
|ログのエクスポート|すべて無効||
|IAMロール|RDSサービスにリンクされたロール|選択不可のためデフォルトのまま|
|マイナーバージョン自動アップグレードの有効化|無効||
|メンテナンスウィンドウ|設定なし||
|削除保護の有効化|無効||

## Blue/Greenデプロイの作成
いよいよ**Blue/Greenデプロイ**の構成を作成していきます。

DBクラスタが**Blue/Greenデプロイ**のサポートがされている構成・バージョンであれば、以下のようにクラスタを選択して「**アクション**」を見ると「**ブルー/グリーンデプロイの作成 - ベータ**」という項目が表示されます。
※正式リリースされているはずなんですがまだベータ・・・

![Monosnap_20221210_140253.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/f07dcb65-ef21-823b-22f8-1b0d833bf5c9.png)

設定項目としては4つしか無いので、先程作成したDBと同様の設定を行っておきます。

以下設定は切り替え先となるDBクラスタの設定となるため、基のDBと合わせる必要はなく、行う作業によってエンジンバージョンやパラメータグループの指定を行います。

DB識別子も基のDBと合わせる必要は無く、基のDBと同じにしたとしても、**ブルー/グリーンデプロイ**で作成されたことがわかる識別子が末尾に付与されるので、何でも良いです。（今回の例では基のDBと同じ識別子を指定）

![Monosnap_20221210_140617.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/178549a9-f6e7-b4dc-3dd3-2ade7c94f087.png)

以下表でも設定した内容を記載しておきます。

|項目|設定|備考|
|:--|:--|:--|
|Blue-Green環境識別子|blue-green-deploy-db|今回は基のDBと同じに設定|
|Green DBインスタンスのエンジンバージョンを選択します。|5.7.mysql_aurora.2.10.2 - current|今回は基のDBと同じに設定|
|Choose the DB cluster parameter group for green databases.|brue-green-cl-param|今回は基のDBと同じに設定|
|Green DBインスタンスのDBパラメータグループを選択します。|brue-green-param|今回は基のDBと同じに設定|

無事作成が完了すると以下の図のように`Blue`（基のDB）と`Green`（切り替え用のDB）の2クラスタが表示されます。

私の環境ではだいたい50分程度で作成完了しましたが、最初の5分程度でクラスタやインスタンスの作成が開始されていない場合は失敗しているので、後述する内容等を確認して頂き、再度実施してください。

![Monosnap_20221210_202906.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/1f4a8731-66c1-18e6-f832-f28c1fa142f2.png)

# Blue/Greenデプロイの検証
無事切替用のGreen環境が構築できたので、検証を行いながら**Blue/Greenデプロイ**を試してみようと思います。

今回は検証用のEC2を使って以下の流れでDB更新した際にGreen環境のDBが更新されるのか、切替時のダウンタイムがどの程度かを検証してみます。

- [Blue/Greenデプロイ切り替え確認用EC2インスタンスの準備](#bluegreenデプロイ切り替え確認用ec2インスタンスの準備)
- [DB更新の確認](#db更新の確認)
    - [Blue側DB更新時のGreen側確認](#blue側db更新時のgreen側確認)
    - [Green側DB更新時のBlue側確認](#green側db更新時のblue側確認)
- [Blue/Greenデプロイによる切替の実施](#bluegreenデプロイによる切替の実施)
    - [読み込み・書き込み確認用テーブルの作成](#読み込み書き込み確認用テーブルの作成)
    - [読み込み・書き込み確認用スクリプトの作成](#読み込み書き込み確認用スクリプトの作成)
    - [Blue/Greenデプロイによる切替](#bluegreenデプロイによる切替)
    - [切替時間の確認](#切替時間の確認)
- [検証結果まとめ](#検証結果まとめ)

尚、私と同じように検証を行いたい方は、事前に適当なEC2インスタンスを準備しておいてください。

## Blue/Greenデプロイ切り替え確認用EC2インスタンスの準備
切替時のダウンタイムを確認するため、確認用のEC2インスタンスを準備しておきます。

検証で使用する`mysql`のクライアントは、`amazon-linux-extras`で`mariadb`をインストールすると一緒にインストールされるので、`mariadb`をインストールします。

```bash:mysqlクライアントインストール
sudo su -
amazon-linux-extras install mariadb10.5
```

## DB更新の確認
作成されたDBのエンドポイントを確認すると、基クラスタとなるBlueクラスタと新しく作成されたGreenクラスタのエンドポイント、AWSのドキュメントに変更されると記載されているリソースIDが以下のように設定されています。

- Blueクラスタ

|エンドポイント名|タイプ|
|:--|:--|
|blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|ライターインスタンス|
|blue-green-deploy-db.cluster-ro-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|リーダーインスタンス|

|DB識別子|ロール|リソースID|
|:--|:--|:--|
|blue-green-deploy-db|リージョン別クラスター|cluster-BCJ27RJM2AZSTPMAN6RCBKAOOU|
|blue-green-deploy-db-instance-1|ライターインスタンス|db-OQ37HBCGSYL3ZEB7KLGK3HG2AY|
|blue-green-deploy-db-instance-1-ap-northeast-1a|リーダーインスタンス|db-E46PXHUHMAMLOKK22X2CTRVCQY|

- Greenクラスタ

|エンドポイント名|タイプ|
|:--|:--|
|blue-green-deploy-db-green-93roku.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|ライターインスタンス|
|blue-green-deploy-db-green-93roku.cluster-ro-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|リーダーインスタンス|

|DB識別子|ロール|リソースID|
|:--|:--|:--|
|blue-green-deploy-db-green-93roku|リージョン別クラスター|cluster-3QDYASSLHLPAKMZ7RXR3PEDE6I|
|blue-green-deploy-db-instance-1-green-a8u1gc|ライターインスタンス|db-4T63D3MUEHIXZ4PZCU7JFTCAY4|
|blue-green-deploy-db-instance-1-ap-northeast-1a-green-yrwmwr|リーダーインスタンス|db-NH7KV6KN6MLRZLKIF5WVHP72C4|

`RDS`の**Blue/Greenデプロイ**では、Blue - Green間で同期が行われており、Green作成後にBlue側の更新が入った場合でも、Green側に同期される動作となるそうです。

そのため、実際にGreen側も更新されているのか、また、Green側を更新した場合にBlue側はどうなるかを確認してみようと思います。

### Blue側DB更新時のGreen側確認
更新確認のため、Blue側のライターインスタンス、Green側のライターインスタンス両方に接続を行っておきます。

`-p[パスワード]`の箇所はスペース無しでパスワードを入力。

```bash:Blue側ライターインスタンスへの接続
mysql -h blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com -u admin -p[パスワード]
```

```bash:Green側ライターインスタンスへの接続
mysql -h blue-green-deploy-db-green-93roku.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com -u admin -p[パスワード]
```

まだテーブルが作成されていないため、Blue、Greenどちらも`Empty`で表示されるはずです。

```sql:bluegreendbのテーブル確認
MySQL [(none)]> use bluegreendb;
MySQL [bluegreendb]> show tables;
Empty set (0.002 sec)
```

この状態でBlue側に適当なテーブルを作成してみます。

```sql:Blue側テーブルの作成
MySQL [bluegreendb]> create table bgtest(
    -> id int,
    -> bluegreen varchar(255)
    -> );
Query OK, 0 rows affected (0.027 sec)
```

```sql:Blue側テーブルへのインサート
MySQL [bluegreendb]> insert into bgtest(id, bluegreen) values(1,'blue');
Query OK, 1 row affected (0.015 sec)
```

作成後、Blue側、Green側どちらもコマンドで確認してみるとGreen側にデータが同期されていることが確認できるかと思います。

```sql:Blue側テーブル作成後のGreen側テーブル確認
MySQL [bluegreendb]> show tables;
+-----------------------+
| Tables_in_bluegreendb |
+-----------------------+
| bgtest                |
+-----------------------+
1 row in set (0.002 sec)
```

```sql:Blue側テーブルデータインサート後のGreen側テーブルデータ確認
MySQL [bluegreendb]> select * from bgtest;
+------+-----------+
| id   | bluegreen |
+------+-----------+
|    1 | blue      |
+------+-----------+
1 row in set (0.002 sec)
```

### Green側DB更新時のBlue側確認
Blue側が更新された場合でもGreen側に同期されることが確認できたため、次はGreen側を更新した場合にBlue側に同期されるかを確認してみます。

先程のテーブルに今度はGreen側からデータをインサートしてみます。

```sql:Green側テーブルへのインサート
MySQL [bluegreendb]> insert into bgtest(id, bluegreen) values(2,'green');
Query OK, 1 row affected (0.009 sec)
```

作成後、Blue側、Green側どちらもコマンドで確認してみるとGreen側のデータは同期されていないことが確認できるかと思います。

```sql:Green側テーブルデータインサート後のBlue側テーブルデータ確認
MySQL [bluegreendb]> select * from bgtest;
+------+-----------+
| id   | bluegreen |
+------+-----------+
|    1 | blue      |
+------+-----------+
1 row in set (0.002 sec)
```

```sql:Green側テーブルデータインサート後のGreen側テーブルデータ確認
MySQL [bluegreendb]> select * from bgtest;
+------+-----------+
| id   | bluegreen |
+------+-----------+
|    1 | blue      |
|    2 | green     |
+------+-----------+
2 rows in set (0.004 sec)
```

**Blue/Greenデプロイ**の仕組みとして、切替先となるGreen側の動作に問題ないことが確認できたら**Blue/Greenデプロイ**で切り替えて、DBデータに影響がないようにする仕組みとなるので、テスト中のGreenのデータ更新がBlue側に同期されてしまったら不整合が生じることから、Green側のデータは同期されないようになっているため、上記のような結果となります。

ちなみにこの状態でBlue側に`id2`のデータを挿入すると、以下のようになります。

```sql:Blue側テーブルデータインサート後のBlue側テーブルデータ確認
MySQL [bluegreendb]> select * from bgtest;
+------+-----------+
| id   | bluegreen |
+------+-----------+
|    1 | blue      |
|    2 | blue      |
+------+-----------+
2 rows in set (0.002 sec)
```

```sql:Blue側テーブルデータインサート後のGreen側テーブルデータ確認
MySQL [bluegreendb]> select * from bgtest;
+------+-----------+
| id   | bluegreen |
+------+-----------+
|    1 | blue      |
|    2 | green     |
|    2 | blue      |
+------+-----------+
3 rows in set (0.002 sec)
```

## Blue/Greenデプロイによる切替の実施
切替前の時点ではBlue→Green方向の同期のみ行われることが確認できたため、今回の主題となる**Blue/Greenデプロイ**による切替を行ってみようと思います。

公式ページを見ると、Blue側とGreen側の切替を行う際、データ保護を行った上で切替が行われるため、1分程度のダウンタイムが発生すると記載されていることから、実際にどの程度の時間がかかるか、確認用スクリプトを作成してダウンタイムを計測してみます。

### 読み込み・書き込み確認用テーブルの作成
時間を記録したいので、先程とは別の`bgcheck`という名前のテーブルを作成します。

```sql:bgcheckテーブルの作成
MySQL [bluegreendb]> create table bgcheck(
    -> id int,
    -> date datetime
    -> );
Query OK, 0 rows affected (0.055 sec)
```

```sql:bgcheckテーブルへのインサート
MySQL [bluegreendb]> insert into bgcheck(id, date) values(1,cast(now() as datetime));
Query OK, 1 row affected (0.005 sec)
```

以下テーブルデータの結果。

```sql:bgcheckテーブルデータ確認
MySQL [bluegreendb]> select * from bgcheck;
+------+---------------------+
| id   | date                |
+------+---------------------+
|    1 | 2022-12-10 11:38:44 |
+------+---------------------+
1 row in set (0.002 sec)
```

### 読み込み・書き込み確認用スクリプトの作成
**Blue/Greenデプロイ**の仕組みとして、切替を行うと、元々Blue側に設定されているエンドポイントがGreen側に自動で切り替わることで、クライアント側からは同一のエンドポイントで通信ができる仕組みとなっています。

そのため、読み込み確認用スクリプトと書き込み確認用スクリプトを作成して、検証用EC2インスタンスからBlue側のライターインスタンスに対する読み込み・書き込み確認とBlue側のリーダーインスタンスに対する読み込み確認を行います。

雑なシェルですが、以下で作成。

```bash:dbread.sh（読み込み確認用スクリプト（Writerインスタンス用））
#!/bin/bash

SLEEPTIME=1
ENDPOINT="blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com"
DBUSER="admin"
PASSWORD="[パスワード]"
DBNAME="bluegreendb"
TABLENAME="bgcheck"
COLUMN="date"
ID="1"

while true
do
	echo "SELECT実行時間 $(mysql -h ${ENDPOINT} -u ${DBUSER} -p${PASSWORD} ${DBNAME} -ss -e "select ${COLUMN} from ${TABLENAME} WHERE id=${ID}")"
	sleep ${SLEEPTIME}
done
```

```bash:dbread_reader.sh（読み込み確認用スクリプト（Readerインスタンス用））
#!/bin/bash

SLEEPTIME=1
ENDPOINT="blue-green-deploy-db.cluster-ro-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com"
DBUSER="admin"
PASSWORD="[パスワード]"
DBNAME="bluegreendb"
TABLENAME="bgcheck"
COLUMN="date"
ID="1"

while true
do
	echo "SELECT実行時間 $(mysql -h ${ENDPOINT} -u ${DBUSER} -p${PASSWORD} ${DBNAME} -ss -e "select ${COLUMN} from ${TABLENAME} WHERE id=${ID}")"
	sleep ${SLEEPTIME}
done
```

```bash:dbwrite.sh（書き込み確認用スクリプト）
#!/bin/bash

SLEEPTIME=1
ENDPOINT="blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com"
DBUSER="admin"
PASSWORD="[パスワード]"
DBNAME="bluegreendb"
TABLENAME="bgcheck"
COLUMN="date"
ID="1"

while true
do
	echo "UPDATE実行時間 $(date "+%Y-%m-%d %H:%M:%S")"
	mysql -h ${ENDPOINT} -u ${DBUSER} -p${PASSWORD} ${DBNAME} -ss -e "update ${TABLENAME} set ${COLUMN}=cast(now() as datetime) where id=${ID};"
	sleep ${SLEEPTIME}
done
```

`dbwrite.sh`（書き込み確認用スクリプト）で1秒ごとに`ID=1`の時間を更新し、`dbread.sh`（読み込み確認用スクリプト）で1秒ごとに`ID=1`の時間を読み取るようにして、読み込み、書き込みのダウンタイムを計測します。

### Blue/Greenデプロイによる切替
実際に切替を行うためには「**ブルー/グリーンデプロイ**」ロールを選択して、「**アクション**」→「**切り替え**」を行います。

![Monosnap_20221210_211125.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/51f8220c-3f2c-280f-7550-e67b75e72557.png)

切替確認と、切替失敗とみなすタイムアウトの設定を行い、「**切り替え**」を選択することで切替が開始されます。

![Monosnap_20221210_211329.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/6a23bbfc-3881-0775-e132-7c9393e6639a.png)

無事成功すると元々BlueだったクラスタとDBインスタンスは識別子の末尾に`-old1`という名前が付与され、元々GreenだったクラスタとDBインスタンスは元々Blueだった識別子を継承するような動作となります。

![Monosnap_20221210_213240.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/6d7ef707-1b87-7a37-9c5a-9c5f6757d499.png)

また、エンドポイントについて、Blueクラスタ側は切替前と同様で、Greenクラスタ側は元々のBlueクラスタのエンドポイント名に`-old1`が付与された名前となります。

リソースIDについては、Blue/Greenデプロイによりリソースが切り替わったことから、Blue側のリソースIDは元々Green側だったリソースIDに変化、Green側のリソースIDは元々Blue側だったリソースIDに変化しています。

- 切替後のBlueクラスタ

|エンドポイント名|タイプ|
|:--|:--|
|blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|ライターインスタンス|
|blue-green-deploy-db.cluster-ro-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|リーダーインスタンス|

|DB識別子|ロール|リソースID|
|:--|:--|:--|
|blue-green-deploy-db|リージョン別クラスター|cluster-3QDYASSLHLPAKMZ7RXR3PEDE6I|
|blue-green-deploy-db-instance-1|ライターインスタンス|db-4T63D3MUEHIXZ4PZCU7JFTCAY4|
|blue-green-deploy-db-instance-1-ap-northeast-1a|リーダーインスタンス|db-NH7KV6KN6MLRZLKIF5WVHP72C4|

- 切替後のGreenクラスタ

|エンドポイント名|タイプ|
|:--|:--|
|blue-green-deploy-db-old1.cluster-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|ライターインスタンス|
|blue-green-deploy-db-old1.cluster-ro-cs5yre5qdxto.ap-northeast-1.rds.amazonaws.com|リーダーインスタンス|

|DB識別子|ロール|リソースID|
|:--|:--|:--|
|blue-green-deploy-db-old1|リージョン別クラスター|cluster-BCJ27RJM2AZSTPMAN6RCBKAOOU|
|blue-green-deploy-db-instance-1-old1|ライターインスタンス|db-OQ37HBCGSYL3ZEB7KLGK3HG2AY|
|blue-green-deploy-db-instance-1-ap-northeast-1a-old1|リーダーインスタンス|db-E46PXHUHMAMLOKK22X2CTRVCQY|

Blue→Greenの同期については、切替のタイミングでBlue→Green方向の同期が停止され、Green側は以下のようにライターインスタンスに接続しても更新できない状態となるようです。

```sql:Green側ライターインスタンスでデータ更新した場合
MySQL [bluegreendb]> insert into bgtest(id, bluegreen) values(3,'green');
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
```

### 切替時間の確認
Blue/Green切替時に実行していた3つのスクリプトの結果を確認します。

結論から言うと、私の検証方法ではライターインスタンスに対しては2分程度のダウンタイムが発生しましたが、リーダーインスタンスに対してはダウンタイムは発生しませんでした。

以下それぞれのスクリプト結果の抜粋。

```bash:dbread.sh（読み込み確認用スクリプト（Writerインスタンス用））の結果抜粋
SELECT実行時間 2022-12-10 12:24:51
SELECT実行時間 2022-12-10 12:24:52
SELECT実行時間 2022-12-10 12:24:52

ERROR 2002 (HY000): Can't connect to MySQL server on 'blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.ama' (115)
SELECT実行時間
SELECT実行時間 2022-12-10 12:24:52
SELECT実行時間 2022-12-10 12:27:07
SELECT実行時間 2022-12-10 12:27:08
```

```bash:dbread_reader.sh（読み込み確認用スクリプト（Readerインスタンス用））の結果抜粋
SELECT実行時間 2022-12-10 12:24:52
SELECT実行時間 2022-12-10 12:24:52
SELECT実行時間 2022-12-10 12:24:52
SELECT実行時間 2022-12-10 12:27:07
SELECT実行時間 2022-12-10 12:27:08
SELECT実行時間 2022-12-10 12:27:09
```

```bash:dbwrite.sh（書き込み確認用スクリプト）の結果抜粋
UPDATE実行時間 2022-12-10 12:24:52
UPDATE実行時間 2022-12-10 12:24:53
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement
UPDATE実行時間 2022-12-10 12:24:54
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement
UPDATE実行時間 2022-12-10 12:24:55

ERROR 2002 (HY000): Can't connect to MySQL server on 'blue-green-deploy-db.cluster-cs5yre5qdxto.ap-northeast-1.rds.ama' (115)
UPDATE実行時間 2022-12-10 12:27:07
UPDATE実行時間 2022-12-10 12:27:08
```

スクリプトの動作としては、`dbread.sh`と`dbwrite.sh`は切替後、12:24:52〜12:27:07の間、エラー出力＆結果出力停止、`dbread_reader.sh`は切替後も結果が止まることなく出力＆復旧タイミングで時間更新されました。

上記検証結果より、Blue/Green切替時、ライターインスタンスに対しては`Read`も`Write`も行えなくなり、リーダーインスタンスに対してはダウンタイムなく`Read`が行えるようです。

尚、元々Blue側だったクラスタとDBインスタンスは自動で削除されず、残ったままだと再度**Blue/Greenデプロイ**を作成することができないので、不要となったら手動で削除しましょう。

切替自体は問題なくできましたが、唯一気になったのは、2022年12月現在、**Blue/Greenの切り替えはできますが、切り戻しは行えません。**

そのため、Blue/Green切替後に切り戻すことになった場合はスナップショットから復元したりする必要があるということは覚えておきましょう。

## 検証結果まとめ
検証結果が長くなったため、以下まとめ。

- Blue/Green切替前の段階ではBlue→Greenへデータ同期が行われる
- Blue/Green切替時、リーダーインスタンスへの読み込みはダウンタイムなく可能
- Blue/Green切替時、ライターインスタンスへの読み込み・書き込みは2分程度のダウンタイムがある
- Blue/Green切替後、旧クラスタ・インスタンスは読み込み専用となる
- Blue/Green切替後、旧クラスタ・インスタンスは自動で削除されない
- Blue/Green切替後、旧クラスタ・インスタンスが残っていると再度Blue/Greenデプロイはできない
- Blue/Green切替後、旧クラスタ・インスタンスに切り戻しはできない

# 旧クラスタ・インスタンスの削除方法
不要となった旧クラスタ・インスタンスを削除するには、最初に「**ブルー/グリーンデプロイ**」ロールを削除してから旧DBインスタンスを削除することで削除できます。

この方法で削除しないと、後述のように「**ブルー/グリーンデプロイ**」ロールが削除できなくなるようなので注意しましょう。

![Monosnap_20221210_220946.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/59e70a7d-928c-b0f7-0223-a939f3295e74.png)

![Monosnap_20221210_221240.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/f579d348-60ec-c7b0-eeeb-664e1a16b8ce.png)

「**ブルー/グリーンデプロイ**」ロールが削除されれば、通常のDBクラスタ削除と同様、クラスタに所属するDBインスタンスを全て削除することで旧クラスタを削除できます。

![Monosnap_20221210_224414.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/bd6387d3-7942-ba00-f199-e346faaaa910.png)

# 手順を実施していて詰まった部分と補足
記事的にはすんなり進んでいるように書いていますが、あまり`RDS`や`DB`自体に詳しくないこともあり、ちょこちょこと詰まったので以下で詰まった点を書いておきます。

- [デフォルトのパラメータグループの値を変更できない](#デフォルトのパラメータグループの値を変更できない)
- [パラメータグループを変更したら再起動が必要](#パラメータグループを変更したら再起動が必要)
- [サブネットグループを3AZで定義していないとBlue/Greenの作成に失敗する](#サブネットグループを3azで定義していないとbluegreenの作成に失敗する)
- [旧クラスタ・インスタンスを削除する際にBlue/Greenデプロイのロールから削除しないと消せなくなる](#旧クラスタインスタンスを削除する際にbluegreenデプロイのロールから削除しないと消せなくなる)

## デフォルトのパラメータグループの値を変更できない
今回の**Blue/Greenデプロイ**の問題というより、パラメータグループの仕様を私が知らなかっただけですが、デフォルトで作成されるパラメータグループは値を編集することができません。

`binlog_format`の値を変更するためには「[事前準備](#事前準備)」で実施している通り、新規でパラメータグループを作成して編集しておく必要がありました。

## パラメータグループを変更したら再起動が必要
パラメータグループのパラメータのうち、「**適用タイプ**」が「**static**」のパラメータはインスタンスの再起動を行わないと適用されません。
※「**dynamic**」のパラメータは再起動無しで適用可能

`binlog_format`は適用タイプが「**static**」のため、再起動が必要となるので、本番環境へ適用する際には気をつけましょう。

## サブネットグループを3AZで定義していないとBlue/Greenの作成に失敗する
今回これが一番ハマりました。。。

`RDS`で**Blue/Greenデプロイ**を実施する場合、元々の構成を`2AZ`で構成していたとしても、サブネットグループを`3AZ`にしておかないと、**Blue/Greenデプロイ**の作成は開始されますが、インスタンスが作成されず、プロビジョン状態から進まない状態となります。

今回紹介した手順では検証用のEC2インスタンスと接続するために「**EC2コンピューティングリソースに接続**」を指定したためサブネットグループは指定できず勝手にサブネットグループが作られましたが、EC2インスタンスと接続しないようにすれば既存のサブネットグループを選択することができます。

最初試したときには動作確認をするだけの目的だったため、元々`2AZ`で作っていたサブネットグループを選択してそのまま使用していたことから、何度やってもプロビジョン状態から進まず、途方に暮れていました。

たまたま`3AZ`であれば成功したという話を聞いて解決しましたが、その話を聞かなければ、まだまだお金と時間が無駄に消えていっていたでしょう。。。

## 旧クラスタ・インスタンスを削除する際にBlue/Greenデプロイのロールから削除しないと消せなくなる
不要となった旧クラスタ・インスタンス削除する際、インスタンスを先に削除すると「**ブルー/グリーンデプロイ**」ロールの項目だけ残ってしまい、消せなくなりました。（Blue/Green両方のインスタンスを全て消したら消えました）

もしかしたらしばらく待っていたら消えたかもしれませんが、「**ブルー/グリーンデプロイ**」ロールの項目だけ残ってしまっていると、再度**Blue/Greenデプロイ**を行おうとしても、「**アクション**」に「**ブルー/グリーンデプロイの作成 - ベータ**」の項目が出てこないため、必ず「**ブルー/グリーンデプロイ**」ロールから消すようにしましょう。

# おわりに
検証した結果、気にしないといけない箇所はありますが、要点さえ抑えておけば運用で大活躍できる機能だと感じました。

この記事が少しでも皆様の日々の運用の参考になれば幸いです。