[re:Invent 2022] ゲームバックエンドによく使われるRDSのBlue/Greenデプロイ新機能を使ってみた

こちらは [AWS for Games Advent Calendar 2022](https://qiita.com/advent-calendar/2022/aws-for-games) の12月6日分のエントリーです。

# はじめに
ゲームのバックエンドにプレイヤーやゲームのデータなどを保存するためにSQL系データベースはよく使われてますね。しかし、ローンチ後、ゲームに追加要素を加えるとデータの構成も当然変わります。基本的にゲームのアップデートを行う時、メンテナンス作業が必要になって、プレイヤーが遊べない時間が出てしまいます。

その問題を解決するには、つい先日のre:Invent 2022で発表された「RDSのBlue/Greenデプロイ」という新しい機能です。

簡単に説明すると、
1. 本番環境にあるデータベース（Blue）をレプリケーションすることで、開発環境・ステージング（Green）が作成される
1. 本番環境のデータ変更（INSERT・UPDATEなど）が開発環境に **リアルタイムで同期される**
1. 開発環境に **テーブルのスキーマを変更するのは可能** （※ 一部不可能の変更もある）
1. 開発環境の変更作業が終わったら、ほぼゼロダウンタイムで **数分に本番環境にデプロイ** が可能

AWS [ドキュメンテーションからの引用](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html)ですが、構成図がこんな感じです。
![rds-blue-green.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/29623968-6599-d503-1fdf-16801d3f8503.jpeg)
しかも、アプリケーション側には切り替えなどの変更は必要ありません。デプロイ後、本番環境で使ってたサーバー名はそのまま継続で使い続けられます。

:::note warn
実際切り替え中にDBサーバーへの接続は切断され、 **数秒間** 接続不可にはなりますが、アプリケーション側での接続リトライ機能があればほとんど影響は感じません。切り替え中には書き込みが一時停止されるので、データを失うことはありません。
:::

このとても便利な機能を実際使って、スキーマ変更も試してみたので、必要な設定とかちょっと引っかかったところなどを紹介したいと思います。

# 対応データベース種類

AWSからの発表記事では、Blue/GreenデプロイはGA（一般公開）となってます。一応日本語版のRDSコンソールでは「ブルー/グリーンデプロイの作成 - ベータ」と表示されるからベータ版かと思われるかもですが、英語版のコンソールでは「Create Blue/Green Deployment - new」と表示されます。

2022年12月の現時点では、対応データベースの種類はMySQL互換性（MariaDBを含む）のRDSかAuroraのみとなります：

1. RDS for MySQL
1. RDS for MariaDB
1. Amazon Aurora with MySQL

:::note warn
Amazon Auroraでは、レプリケーションを正常に行うためにはパラメータグループ設定の `binlog_format` を `OFF` → `MIXED` に変更する必要があります。（RDS/MySQLは `MIXED` がデフォルトとなってるので、RDSの場合はこちらの設定は変更する必要ありません。）
:::

ちなみに、上の構成図のようにマルチAZのDBインスタンスだけではなく、単一のDBインスタンスでもBlue/Greenデプロイは可能です。DBのインスタンスタイプはバースト可能クラス（db.t4g.microなど）でも問題ありません。

# 実際使ってみた

今回試したいのはスキーマ変更なので、例えばゲームアプリケーションの追加要素で既存の `users` テーブルに列を増やす必要があるとします。列を増やしたら、本番環境へのデプロイまでやります。

## 検証のための事前準備

実際スキーマの変更を試すには既存データが必要のため、事前準備としてDBにシンプルに `users` テーブルを作成します。

```sql
mysql> CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.09 sec)

mysql> DESC users;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int         | NO   | PRI | NULL    | auto_increment |
| name  | varchar(64) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.02 sec)
```

そしてユーザーのレコードも少し追加します。

```sql
mysql> INSERT INTO users ( name ) VALUES ( 'fred' );
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO users ( name ) VALUES ( 'betty' );
Query OK, 1 row affected (0.02 sec)

mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
+----+-------+
2 rows in set (0.02 sec)
```

これで本番環境にデータが存在するので、事前準備は完了です。

## Blue/Greenデプロイを作成

RDSコンソールからBlue/Greenデプロイを作成したい本番環境のDBを選択し、右上の「アクション」から「ブルー/グリーンデプロイの作成」を選びます。「Blue-Green環境識別子」に好きな名前を入力し、右下の「ステージング環境の作成」をクリックします。

:::note
ステージング環境のDBエンジンバージョンは本番環境より新しく設定できるので、DBエンジンのアップグレードは本番にデプロイする前にテストを行うのも可能です。
:::

![create-staging-dialog.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/3b3e446d-10bd-d653-7b9a-903084e6a438.jpeg)
これでステージング環境のDBは新しく作成され、本番環境からのレプリケーションが設定されます。
![staging-create-progress.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/d156e883-7e8f-a57d-19c3-99a6f39c12ac.png)
ステージング環境を作成するには約15分程度かかりますが、この間は本番環境に影響はまったくありません。

## 開発・ステージング（Green）環境を見てみよう

![bgtest-info.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/c69e8b82-5cb1-4615-a2dd-b5e7dfd7aae4.jpeg)
ステージング環境が「利用可能」になったので、まずは接続して `users` テーブルを見てみましょう。

```sql:ステージング環境
$ mysql -u admin -h bgtest-green-bzh9ry.cXXXXXXXXXX9.ap-northeast-1.rds.amazonaws.com -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 16
Server version: 8.0.31 Source distribution

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use bgtest
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+------------------+
| Tables_in_bgtest |
+------------------+
| users            |
+------------------+
1 row in set (0.02 sec)

mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
+----+-------+
2 rows in set (0.02 sec)
```

本番環境からのデータは問題なくコピーされましたね。

## 本番環境からのレプリケーション同期を確認

ステージング環境のデータコピーができたので、リアルタイム同期のレプリケーションが正常に動いてることを確認しましょう。まずは本番環境で新規ユーザーが作成されるとします。

```sql:本番環境
mysql> INSERT INTO users ( name ) VALUES ( 'max' );
Query OK, 1 row affected (0.02 sec)

mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
+----+-------+
3 rows in set (0.02 sec)
```

ステージング環境で同じSELECTだけを行うと、

```sql:ステージング環境
mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
+----+-------+
3 rows in set (0.02 sec)
```

本番環境で行ったINSERTが正しくレプリケーションされてるの確認が取れました。

## ステージング環境のスキーマを変更する（その１）

:::note warn
レプリケーション中にスキーマを変更する場合はいくつか制限があります。基本的にステージング環境での変更は可能なのは以下の通りです。
1. テーブルの列を増やす（DEFAULT必須・テーブルの最後に追加のみ、列の順番・名前の変更や削除は不可）
1. テーブルの新規作成
1. インデックスの新規作成
1. インデックスの削除

詳しく知りたい方はMySQLの[ソースとレプリカで異なるテーブル定義を使用したレプリケーション](https://dev.mysql.com/doc/refman/8.0/ja/replication-features-differing-tables.html)をご参考にしてください。
:::

いよいよ、ステージング環境の `users` テーブルのスキーマを変更してみましょう。今回のゲームアプリケーションの追加要素では、ユーザーの勝ち負けのゲーム数を記録する必要になったので `users` テーブルに2つの列を追加します。

```sql:ステージング環境
mysql> ALTER TABLE users ADD (wins INT DEFAULT 0, losses INT DEFAULT 0);
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
mysql> 
```

って、あれ...？

## ステージング環境の書き込みを有効化にする

見逃しやすいのですが、[ブルー/グリーンデプロイのドキュメンテーション](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html#blue-green-deployments-major-steps)にはちゃんと書いてあって、レプリケーションされたステージング環境（Green）はデフォルトで **読み取り専用** になってます。
![green-read-only.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/66aa95d7-f257-b718-9d2b-6885c0ce5ba5.jpeg)
そして、この読み取り専用の状態を **書き込み可能** に変更するのは可能です。

:::note
Blue/Greenデプロイを作成する際にはパラメータグループを指定することが可能なので、予め書き込み可能のパラメータグループを作成しておいてからBlue/Greenデプロイの作成を行うと適用・再起動の必要はなくなります。
:::

### 新規パラメータグループを作成

書き込み可能に変更するにはDBインスタンスのパラメータグループを変更する必要があります。ただ、作成した時点のデフォルトで使った `default.mysql8.0` というパラメータグループは編集できないので、新規作成する必要があります。
![parameter-group-create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/d09494e3-b3a9-da73-954c-1e0a0719793f.png)
パラメータグループファミリーの正しいDBエンジンのバージョンを選択して、グループ名と説明を入力し作成します。新規作成したパラメータグループを選択し、パラメータ編集画面が開きます。

フィルタパラメータのフィルドに `read_only` を入力すると、すぐに変更する必要であるパラメータは表示されます。
![parameter-change.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/f542ea6b-cebc-4053-9aad-66996f8620dd.png)
この `read_only` パラメータ値が `{TrueIfReplica}` になってるので、レプリケーションされたDBインスタンスは `True (1)` となって、読み取り専用になります。

この値を `0` に変更して、右上の **変更の保存** ボタンをクリックするを忘れずに。

### ステージング環境に新しいパラメータグループを適用する

RDSコンソールに戻って、ステージング環境のDBインスタンス（Green）を選択し、右上の変更ボタンをクリックします。
![rds-modify-instance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/617a172c-4fcc-7b3b-34e1-8cfb3995f2ee.png)
「追加設定」までスクロール（だいぶ下の方）し、 **DBパラメータグループ** を `default.mysql8.0` から先ほど新規作成したパラメータグループに変更し、ページの右下の続行ボタンをクリックします。
![rds-parameter-group-edit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/d600c185-7d79-89ff-00c4-1d0ccf01c8fd.png)
変更される前には変更される項目の確認画面が出てきます。
![rds-modify-check.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/289af664-f8fe-2475-a952-bef7700a74d1.png)
確認するのは、
1. DBインスタンスがステージング環境（Green）のインスタンス名になってる
1. DBパラメータグループが新しく作成した書き込み可能のグループ名になってる

の2つだけです。「すぐに適用」を選択し、DBインスタンスを変更のボタンをクリックします。ステージング環境はすぐ「変更中」状態に変わりますが、まだ続きがあります。
![rds-instance-reboot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/48e30a79-2ee5-18a2-ec49-dbb0865a4f6c.png)
数分が経ったらステージング環境は「利用可能」状態に戻りますが、設定タブを見ると **再起動を保留中** が表示されてますので、画面の一番右上の「アクション」ボタンから「再起動」を選択し、再起動をさせましょう。

## ステージング環境のスキーマを変更する（その２）

改めて、ステージング環境の `users` テーブルのスキーマを変更してみましょう。

```sql:ステージング環境
mysql> SELECT * FROM users;
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
Connection id:    16
Current database: bgtest

+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
+----+-------+
3 rows in set (0.23 sec)

mysql> ALTER TABLE users ADD (wins INT DEFAULT 0, losses INT DEFAULT 0);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM users;
+----+-------+------+--------+
| id | name  | wins | losses |
+----+-------+------+--------+
|  1 | fred  |    0 |      0 |
|  2 | betty |    0 |      0 |
|  3 | max   |    0 |      0 |
+----+-------+------+--------+
3 rows in set (0.01 sec)
```

これでステージング環境でスキーマ変更ができたので、本番環境も見てみましょう。

```sql:本番環境
mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
+----+-------+
3 rows in set (0.01 sec)
```

本番環境は以前のスキーマのままです。

## ステージング環境の変更したスキーマとレプリケーション同期を確認

さって、スキーマが変更されたステージング環境ですが、この状態で本番環境に新規ユーザーが作成されるとどうなりますか？やってみましょう。

```sql:本番環境
mysql> INSERT INTO users ( name ) VALUES ( 'david' );
Query OK, 1 row affected (0.02 sec)

mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
|  4 | david |
+----+-------+
4 rows in set (0.02 sec)
```

そしてステージング環境で同じSELECTだけを行うと、

```sql:ステージング環境
mysql> SELECT * FROM users;
+----+-------+------+--------+
| id | name  | wins | losses |
+----+-------+------+--------+
|  1 | fred  |    0 |      0 |
|  2 | betty |    0 |      0 |
|  3 | max   |    0 |      0 |
|  4 | david |    0 |      0 |
+----+-------+------+--------+
4 rows in set (0.02 sec)
```

スキーマが変更されたステージング環境でも、本番環境での新しいユーザーのレコードは正常にレプリケーションされますね。実際の検証画面がこちらです。
![schema-compare.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/6a2ef150-097c-3b9c-626b-1c1a88ca5ac4.png)

:::note
繰り返しにはなりますが、この時点までは本番環境には一切影響はありません。途中で何か失敗した場合はステージング環境とBlue/Greenデプロイを削除し、やり直してもまったく問題ありません。
:::

## 本番環境へのデプロイ

ステージング環境のスキーマ変更作業が終わったので、切り替えをすることで変更された内容が本番環境にすぐに反映されることができます。RDSコンソールからブルー/グリーンデプロイを選択し、右上の「アクション」ボタンから「切り替え」を選択します。
![blue-green-switch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/5c9e5962-ef5b-a1c6-3939-89a67b6d4bdf.png)
本番環境はBlueからGreenに切り替えたいので、そのまま「切り替え」ボタンをクリックします。

切り替えを待ってる間は本番環境に連続で同じSELECTを実行してみましょう。

```sql:本番環境
mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
|  4 | david |
+----+-------+
4 rows in set (0.02 sec)

mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | fred  |
|  2 | betty |
|  3 | max   |
|  4 | david |
+----+-------+
4 rows in set (0.02 sec)

mysql> SELECT * FROM users;
【＊＊＊　ここで9秒間の待機時間が発生　＊＊＊】
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
Connection id:    67
Current database: bgtest

+----+-------+------+--------+
| id | name  | wins | losses |
+----+-------+------+--------+
|  1 | fred  |    0 |      0 |
|  2 | betty |    0 |      0 |
|  3 | max   |    0 |      0 |
|  4 | david |    0 |      0 |
+----+-------+------+--------+
4 rows in set (9.06 sec)
```

できました！「切り替え」ボタンを押してから1分以内で、たった9秒間の待機時間がありましたが、本番環境には新しく変更したスキーマが無事にデプロイされました。

## 切り替えのあと

切り替えが終わったら、RDSコンソールはこんな感じになります。
![rds-after-switch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2282314/c67ab580-6abf-1f3a-ae0b-2991ec1b3352.png)
注目すべきなのが、
1. 切り替え前のステージング環境の「DB識別子」が切り替え前の本番環境に変更された
1. 切り替え前の本番環境が **「〇〇-old」** に変更された
1. ブルー/グリーンデプロイが「切り替え完了」状態になった

以前の本番環境が名前変更され、残っているということです。必要がなくなったら、ブルー/グリーンデプロイと一緒に削除しましょう。

# まとめ

読み返してみると結構な量になってしまいましたが、必要な設定さえ準備しておけば、非常にシンプルなシステムになってます。

1. Blue/Greenデプロイを作成
1. Greenのスキーマを変更
1. 作業が終わったら切り替え

デプロイを作成した際に書き込みを有効にするパラメータグループを設定するを忘れた場合は、
1. Greenにパラメータグループを適用
1. Greenを再起動

もしくは、Blue/Greenデプロイを削除し、新しく作り直すのも可能です。

これよりもっと詳しく知りたい方はこちらの記事もぜひ。

https://dev.classmethod.jp/articles/rds-bg-deploy/

https://aws.amazon.com/blogs/aws/new-fully-managed-blue-green-deployments-in-amazon-aurora-and-amazon-rds/

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html

# 宣伝

JAWS-UG GameTech専門支部はゲームに関する情報交換、技術紹介、Tips、テクノロジーアップデート紹介を通じてGameTech界隈を盛り上げていくことを目的としたコミュニティです。コミュニティスペースとして Discord を活用していますので、ゲームとAWSの交流したい方はぜひご参加ください。

https://discord.gg/hyCdc2jVUM

12月8日(木)は 第3回: re:Invent 2022 re:Cap のオンライン勉強会も行いますので、都合がよかったらご参加ください。

https://jawsug-gametech.connpass.com/event/264379/