🐳

【RDS Blue/Greenデプロイ】Green環境でスキーマ変更や書き込みをする方法と注意点

2024/04/23に公開

はじめに

RDS Blue/Greenデプロイを使用すると、インスタンスの変更やDBエンジンのバージョンアップ、ファイルシステム設定のアップグレード、パラメータの変更等をダウンタイムを最小限に抑えて実行することができ、とても便利ですよね。

今回、データベースのカラム型変更(string型text型)をしたく、Blue/GreenデプロイのGreen環境のMySQLでALTER TABLEレコード追加を行ったのですが、無条件でなんでもGreen環境に変更を加えられるわけではないということを学びました。(よく考えればそりゃそうだ)
Green環境で書き込みする方法と、その際の注意点を記載します。

Green環境の大前提

Green環境はBlue環境からレプリケーションされている

-- AWSドキュメント
グリーン環境はステージング環境です。ステージング環境は、論理レプリケーションを使用して現在の本稼働環境と同期したままになります。
ブルー/グリーンデプロイを使用してスキーマの変更を実装する場合は、レプリケーション互換の変更のみを行ってください。

つまり、レプリケーション互換のない変更だと、Blue/Greenの入れ替えが失敗してしまいます。Green環境の変更を行う際は、まずレプリケーション互換のある変更かどうかを確認しましょう。

レプリケーション互換のある変更とない変更について、AWSドキュメントに記載がありました。例えば、カラム名の変更テーブルの名の変更はレプリケーション互換がないようです。
確かにBlue環境とGreen環境でカラム名が異なっていたら、レプリケーションできないですね。

-- AWSドキュメント
ブルーデプロイからグリーンデプロイへのレプリケーションを中断することなく、テーブルの最後に新しい列を追加する、インデックスを作成する、またはインデックスを削除することができます。ただし、列名の変更やテーブル名の変更などのスキーマの変更は、グリーンデプロイへのレプリケーションを中断させます。

詳しくはこちらをご覧ください。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/blue-green-deployments-overview.html#blue-green-deployments-best-practices

Greenはデフォルトで読み取り専用(ReadOnly)である

-- AWSドキュメント
ブルー/グリーンデプロイを作成すると、グリーン環境の DB インスタンスはデフォルトで読み取り専用になります。

デフォルト(読み取り専用)のままALTER TABLEをすると下記のようなエラーになりました。

ALTER TABLE `reports` CHANGE `target_id` `target_id` text;
→ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement

システム変数も読み取り専用になっています。

SHOW VARIABLES LIKE '%read%';
read_only                               | ON   

基本的にGreen環境は読み取り専用で保つことが推奨されているようです。

-- AWSドキュメント
グリーン環境のデータベースは読み取り専用のまま維持してください。グリーン環境ではレプリケーションの競合が発生する可能性があるため、書き込み操作の有効化には注意することをお勧めします。また、スイッチオーバー後に本稼働データベースに意図しないデータが発生する可能性もあります。

テスト中は、グリーン環境のデータベースを読み取り専用に保つことをお勧めします。

Green環境で書き込みをできるようにする方法

とはいえ、今回のようにGreen環境でテーブルに対し書き込みをしたい場合もあります。
AWS公式ドキュメントに書き込み可能にする方法が記載されていました。

-- AWSドキュメント
RDS for MySQL の書き込み操作を有効にするには、read_only パラメータを 0 に設定し、DB インスタンスを再起動します。RDS for PostgreSQL の場合、セッションレベルで default_transaction_read_only パラメータを off に設定します。

要するに先ほどのread_onlyシステム変数をOFFにすれば良いようです。
今回は、RDSのパラメータグループを使って変更しました。

パラメータグループを新たに作成し、read_onlyの値を{TrueIfReplica}から0に変更します。

Green環境の「変更」から、パラメータグループを上記で作成したパラメータグループに変更し「今すぐ適用」します。

1分ほどでパラメータグループのステータスが「再起動を保留中」になりました。
AWSドキュメントによると、新しい DB パラメータグループの関連付け – 新しい DB パラメータグループを DB インスタンスに関連付ける場合、RDS は、DB インスタンスが再起動された後にのみ、変更された静的および動的パラメータを適用しますとあるので、再起動を行います。

2分ほどでステータスが「同期中」になりました。
Green環境でシステム変数を確認すると、read_only=OFFに変更され、書き込みが可能になりました。

show variables like '%read%';
read_only                               | OFF   

Green環境でALTER TABLE書き込み処理をやってみた

カラムの型変更 → ⭕️

レプリケーションも問題なく、Blue/Greenの切り替えも成功しました。

ALTER TABLE `reports` CHANGE `target_id` `target_id` text;

インデックスの削除・追加 → ⭕️

レプリケーションも問題なく、Blue/Greenの切り替えも成功しました。

DROP INDEX index_reports_on_target_id ON reports;
CREATE INDEX index_reports_on_target_id ON reports (target_id(255));

書き込み(レコードの追加) → ⭕️だがID重複は❌

Green環境でレコードの追加をした場合、「Green環境のテーブルの最後に新しい列を追加する」場合は問題がありません。

-- AWSドキュメント
例えば、ブルーデプロイからグリーンデプロイへのレプリケーションを中断することなく、テーブルの最後に新しい列を追加する、インデックスを作成する、またはインデックスを削除することができます。

ただし、Green環境で作られたレコードのIDとBlue環境で作られたレコードのIDが重複する場合はレプリケーションに失敗し、Blue/Greenデプロイもうまくいきませんでした。

RDSのログとイベント > 最近のイベントを見てみると、このようなエラーが。レプリケーションに失敗していることがわかります。

Read Replica Replication Error - SQLError: 1062, reason: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'ANONYMOUS' at source log mysql-bin-changelog.028833, end_log_pos 64316. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.

さらにその下のログとイベント > ログのうち、最近の更新のあるmysql-error-running.logmysql-error.logの中身を見てみると、'Duplicate entry '10219' for key 'books.PRIMARY'というログが。

2024-04-10T13:18:09.323975Z 1442 [ERROR] [MY-010584] [Repl] Replica SQL for channel '': Worker 1 failed executing transaction 'ANONYMOUS' at source log mysql-bin-changelog.028833, end_log_pos 64316; Error 'Duplicate entry '10219' for key 'books.PRIMARY'' on query. Default database: 'database_production'. Query: 'INSERT INTO `books` (`client_id`, `name`, ..., Error_code: MY-001062
2024-04-10T13:18:09.324450Z 1441 [ERROR] [MY-010586] [Repl] Error running query, replica SQL thread aborted. Fix the problem, and restart the replica SQL thread with "START REPLICA". We stopped at log 'mysql-bin-changelog.028833' position 62567
2024-04-10T13:18:09.325665Z 1440 [System] [MY-014001] [Repl] Replica receiver thread for channel '': connected to source 'rdsrepladmin@x.x.x.x:3306' with server_uuid=xxxxxxxxx, server_id=xxxxxxxxx. Starting replication from file 'mysql-bin-changelog.028842', position '65363'.

今回、Green環境で動作確認をするためにアプリケーションをGreen環境のエンドポイントに接続し、ブラウザからレコード登録などの操作をした後、エンドポイントをBlue環境に戻しました。同じbooksテーブルにBlue環境とGreen環境でそれぞれ同じid: 10219のレコードができてしまっていました。

こちらの記事にあるような状況が発生していました。
https://server-setting.info/centos/mysql_replication_1062_error.html

今回は再びGreen環境に接続し、重複していたid: 10219のレコードを削除しました。

DELETE FROM books where id >= 10219;

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

その後、再度Blue/Greenデプロイを行い成功しました!

まとめ

💡 Green環境はデフォルトで読み取り専用だが、書き込み可能に変更できる。
💡 Green環境はレプリカなので、無条件でなんでもGreen環境に変更を加えられるわけではない。Green環境でデータベースの変更を加える際は、まず「レプリケーション互換があるか」を確認する。
  • レプリケーション互換がある変更
    • テーブルを新規作成する
    • インデックスを作成・削除する
    • テーブルの最後に新しい列を追加する
  • レプリケーション互換がない変更
    • 列名の変更やテーブル名の変更などのスキーマの変更
    • Green環境で作られたレコードのIDとBlue環境で作られたレコードのIDが重複する場合

参考にさせていた記事

https://qiita.com/falken/items/9bc7683e936e126c1433#ステージング環境の書き込みを有効化にする
https://dev.classmethod.jp/articles/rds-bg-deploy/

株式会社リンクエッジ

Discussion