こんにちは。技術部プラットフォームグループのharukinです。
この記事では、私たちが提供するネットショップ作成・運用のためのECプラットフォーム「カラーミーショップ」のデータベースを、Amazon RDSのブルー/グリーンデプロイを利用し、MySQLのバージョン5.7.38から8.0.35へアップグレードした経験についてご紹介します。カラーミーショップにおいてはこれが初の試みでした。Amazon RDS固有のファーストタッチレイテンシーの解除方法や、ダウンタイム時間の計測についてもお伝えします。
Amazon RDSのブルー/グリーンデプロイを活用するメリットは、本番環境に準ずるステージング環境を構築し事前検証が可能であることです。ステージング環境は約1分で本番環境に昇格させることができ、昇格時に許容ダウンタイムを超えたり、レプリケーションやインスタンスの問題が生じた場合は、自動的にプロセスが中断される安全性を備えています。
ref. Amazon RDS ブルー/グリーンデプロイの概要 - Amazon Relational Database Service
アップグレード方法の検討
Amazon RDSをアップグレードする際には、インプレースアップグレードとブルー/グリーンデプロイという2つの方法があります。特にブルー/グリーンデプロイは、インプレースアップグレードよりもサービスの停止時間を最小限に抑えることができるため、大きな利点があります。
ブルー/グリーンの両環境を一時的に稼働させることで発生するコスト増は避けられませんが、本番環境と同じ条件での事前検証が可能であること、アップグレード手順が簡素化され、そしてサービスの停止時間が短縮される利点は、それに充分見合う価値があると考えました。
さらに、私が所属する技術部プラットフォームグループ(PFG)は、各事業部の枠を超えた組織として機能しており、各チームの知見やスキルを積極的に共有し合っています。これまでの成功事例の一つとして、minneにてpochyが行った際のAmazon RDSのB/GデプロイによるMySQLアップグレードの事例と遭遇した課題の知見があったことも、カラーミーショップでも同様のアプローチを採用する上での大きな後押しとなりました。
ブルー/グリーンデプロイを試す
まずはじめに、検証環境でブルー/グリーンデプロイを試して、一連の流れを確認しました。
ブルー環境へ影響を与えることなく、グリーン環境を作成・変更できます。たとえば、DBエンジンのアップグレードやデータベースパラメータの変更などがこれに該当します。今回の場合、バージョン5.7.38から8.0.35へのアップグレードを行うため、あらかじめバージョン8.0のパラメータグループを作成しておきました。
※ MySQL8.0からデフォルトの認証プラグインはcaching_sha2_password
に変更になりましたが、AWSのMySQL8.0用パラメーターグループではデフォルトがmysql_native_password
となっています。そのため、今回のDBアップグレードでは「認証プラグインは変更しない」という方針にしました。
% aws rds describe-engine-default-parameters --db-parameter-group-family mysql8.0 --region ap-northeast-1 |grep -A 2 default_authentication_plugin
"ParameterName": "default_authentication_plugin",
"ParameterValue": "mysql_native_password",
"Description": "The default authentication plugin",
1. ブルー/グリーンデプロイの作成
はじめはこのようになっています。
2. ブルー/グリーンデプロイの作成プロセスを観察
AWSマネジメントコンソールから、グリーン環境の作成プロセスを観察していると、バージョン8.0のエンジンとパラメーターグループを選択しましたが、最初にバージョン5.7のデータベースが作成されていました。
その後にバージョン8.0へとアップグレードされるような挙動が確認できました。
3. ブルー/グリーンデプロイの切り替え
ステータスがすべて「利用可能」になると切り替えの準備が完了です。ブルー/グリーンの両環境に接続が可能な状態になっています。
グリーン環境のReplicaLag
が0
になっており、ブルー環境との間でレプリケーション遅延が生じていないことが確認してから切り替えを実行します。
切り替えが完了すると、以下のような状態になります。
ブルー環境のDB識別子はグリーン環境のDB識別子に置き換わり、同時にブルー環境の識別子には-old1
というサフィックスが追加されます。DBのエンドポイントもブルーで利用されていたものがそのままグリーンに適用され、ブルーにはDB識別子と同様に-old1
サフィックスが追加されます。アプリケーション側でのDBのエンドポイントの変更は不要なので非常に便利です。
ここまでが一連の流れでした。
ブルー/グリーンデプロイの切替え前後の検証作業について
Amazon RDSのブルー/グリーンデプロイは、実際の本番環境に非常に近い設定でグリーン環境を構築し、事前の検証を実施することができます。RDS特有のファーストタッチレイテンシー(ファーストタッチペナルティ)に対処するための準備と、AWSの公式ドキュメントに記載されている「通常、ダウンタイムは1分未満」という点についても、私たちのアプリケーションで実際にダウンタイム1分未満で切り替わるのかを確認しました。
ファーストタッチレイテンシーを解除
Amazon RDSでは、EBSデータボリュームに初めてアクセスする際に「ファーストタッチレイテンシー」という現象が起こり、その結果として一時的にIOパフォーマンスが低下します。具体的には、新しいデータベースインスタンス(グリーン環境)に初めてアクセスすると、EBSボリュームにデータがS3からコピーされることが原因です。この問題を回避するには、事前にEBSボリューム全体を読み込んでおく必要があります。
ref. Amazon EBS ボリュームの初期化 - Amazon Elastic Compute Cloud
検証環境のアプリケーションのエンドポイントをグリーン環境のものに変更した際、ファーストタッチレイテンシーによるIOパフォーマンスの低下が観測されました。そのメトリクスは以下の通りです。
@yoku0825さんと@kenchanさんのRDS for MySQLのファーストタッチペナルティを解除する3ライナーを参考にさせていただきました。以下のスクリプトを準備し、実行すると指定されたDBホスト上のすべての主キーとセカンダリキーが読み込まれ、その後すべてのデータが読み込まれます。
#!/bin/bash
set -xe
echo "Start all touch queries"
mysql -h <RDSのエンドポイント> -u<ユーザー名> -p<パスワード> -sse "SELECT distinct table_name, index_name FROM information_schema.statistics WHERE table_schema = '<データベース名>'" | while read table index ; do
mysql -h <RDSのエンドポイント> -u<ユーザー名> -p<パスワード> -sse "SELECT count(*) FROM $table FORCE INDEX($index)" <データベース名> > /dev/null &
done
wait $!
echo "End all touch queries"
グリーン環境を作成し、ブルー環境からグリーン環境への切り替え前にスクリプトを実行しておいたため、切り替えた後にファーストタッチレイテンシーによるIOパフォーマンスの低下はありませんでした。
ブルー/グリーンの切り替え前後でダウンタイムを測定
アプリケーションのダウンタイムを正確に測定するために、ブルー環境からグリーン環境への切り替えおよびアップグレードプロセスが正常に行われているかを確認するため、APIのエンドポイントへcurlコマンドを継続的に実行しました。
$ while true; do date; curl http://example.com/api/path ; date; sleep 1; done | tee -a /tmp/tee.log
ブルー環境からグリーン環境への切り替えは2分で完了し、5xxエラーになるダウンタイムは約1分でした。AWSの公式ドキュメントに記載されている「通常、ダウンタイムは1分未満」という記載を私たちのアプリケーションでも満たしていることが確認できました。
$ cat /tmp/tee.log
Fri Dec 8 18:47:00 JST 2023 ## ブルー環境からグリーン環境への切り替え開始
--- snip ----
Fri Dec 8 18:47:26 JST 2023 ## 5xxエラーが出るようになる
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.17.10</center>
</body>
</html>
Fri Dec 8 18:48:11 JST 2023
Fri Dec 8 18:48:12 JST 2023
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx</center>
</body>
</html>
Fri Dec 8 18:48:12 JST 2023
--- snip ----
Fri Dec 8 18:48:23 JST 2023
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx</center>
</body>
</html>
Fri Dec 8 18:48:23 JST 2023 ## 5xxエラーが出なくなる
--- snip ----
Fri Dec 8 18:49:00 JST 2023 ## ブルー環境からグリーン環境への切り替え完了
minneでの検証中に、ブルー環境からグリーン環境への切り替えが実行できないことがまれにあるという情報を共有されていました。そのため、メンテナンス当日には切り替えに問題が発生する可能性を考慮し、インプレースアップグレードのダウンタイムも測定していました。その結果、インプレースアップグレードでは約6分のダウンタイムが発生しました。このことから、ブルー/グリーンデプロイのダウンタイムはインプレースアップグレードの約1/6になることが明らかとなりました。
アップグレード
これまでの検証の後、Amazon RDS for MySQLのバージョンを5.7.38から8.0.35へアップグレードするための事前準備と当日の手順を決定しました。
事前準備
- グリーン環境を準備し、ステータスがすべて「利用可能」になっていることを確認しておく。
- ファーストタッチレイテンシーを解消するスクリプトを実行しておく。
当日の手順
- ブルー環境のスナップショットを取得する。
- ブルー/グリーンの両環境でエラーログをリアルタイムで監視し、迅速な対応できるようにしておく。
- グリーン環境のデータの整合性が保たれている(
ReplicaLag
が0
である)ことを確認し、切り替えを行う。 - 切り替え完了後、ブルー/グリーンの両環境が「利用可能」であり、エラーログが発生していないことを確認する。
- 最後にAPIのエンドポイントに対しcurlコマンドを実行して、システムの動作検証を行う。
これらの手順でアップグレードを実施しました。
おわりに
ブルー/グリーンデプロイの利点を十分に活用することで、アップグレードプロセスが簡素化され、メンテナンス時間が短縮されました。事前に構築したグリーン環境で十分な検証が可能であることも大きなメリットと感じました。切り替え時に問題が発生した場合を考慮し、インプレースアップグレードで対応するためのメンテナンスプランも準備していましたが、本番環境でもブルー/グリーンデプロイを利用してアップグレードを完了することができました。
最後までお読みいただきありがとうございました!