Stanby Tech Blog

求人検索エンジン「スタンバイ」を運営するスタンバイの開発組織やエンジニアリングについて発信するブログです。

Redisのバージョンアップをした話

はじめに

初めまして、株式会社スタンバイのSEOチームの本田です。

スタンバイではElastiCache for Redis (以後 Redis と記載) の バージョン3を長く利用していましたが、 2023年7月31日にバージョン3がEOLを迎えるため、バージョン7へのアップグレードを5月に行いました。

実施したのは半年ほど前ですが、アップグレードに伴い必要だった手順などをご紹介していきます。

期限までにアップグレードしないとどうなるのか

AWSからの案内によると、バージョン3は新規作成ができなくなり、稼働中のものは自動的に6.2以上にアップグレードされてしまうようです。(下記一部メールから抜粋)

2022 年 7 月 31 日から 2023 年 7 月 31 日まで - ElastiCache for Redis バージョン 3 インスタンスの Redis 6.2 以降へのアップグレードはいつでも開始できます [3]。 2023 年 5 月 1 日以降 - AWS コンソールから ElastiCache for Redis バージョン 3.x.x インスタンスを作成することはできません。 2023 年 7 月 31 日以降 - ElastiCache for Redis バージョン 3 は ElastiCache コンソール、CLI、API、または CloudFormation では利用できません。2023 年 7 月 31 日以降のメンテナンス期間内に、すべての Redis 用 ElastiCache 3.x.x クラスターを Redis 6.2 に自動的にアップグレードします。

また、AWS公式ドキュメントによると、5.0.5以前のバージョンからそのままメジャーバージョンのアップグレードを行うとフェールオーバー時のDNS伝搬で最大1分は接続断されてしまいます。

ElastiCache クラスターは 5.0.5 より前のバージョンでアップグレードできます。アップグレードプロセスは同じですが、DNS 伝達中のフェールオーバー時間が長くなる可能性があります (30 秒~1 分)。

今回アップグレード対象となるRedisはほぼ全てのスタンバイのページ表示の際に利用されているので、1分間の瞬断でもあってもユーザーに大きく影響を与えてしまう可能性がありました。

そのため、

Redisをダウンタイムなしでアップグレードする

を、対応方針としました。

アップグレードと切り替え方法

ダウンタイムなしで切り替える場合どのようにするかという話ですが、アップデート対象となるElastiCacheのエンドポイントをAmazon Route 53で以下のようにCNAME設定しており、

redis-service.stanby(例) -> redis.xxxx.ng.0001.xxxx.cache.amazonaws.com

社内の各アプリケーションはCNAMEのホスト名を利用して該当のElastiCacheにアクセスをしています。 そのため、新規でバージョン7のクラスタを構築しCNAME先のエンドポイントを切り替えれば、各アプリケーションのソースコードを変更することなくバージョンアップされたRedisを参照するように切り替えることができます。

また、ElastiCacheはスナップショットから復元する際にエンジンバージョンをアップグレードしてクラスタを作成できます。 稼働中のElastiCacheのスナップショットを取り、そのスナップショットを用いてバージョン7のクラスタを構築することで、データ移行のための設定をすることなくバージョンのアップグレードを行えます。 幸いにもアップグレード対象のRedisに関しては、読み込みはスタンバイのページが表示される度に行いますが、書き込みは非同期になっており一時的に停止可能であるため、Redisのアップグレード作業中は書き込みを停止して新しいクラスタを構築できました。

以上からアップグレードと切り替え方法は下記のようなフローになりました。

手順が確認できたので、次は実際に行ったことをまとめていきます。

アップグレードするために行ったこと

アップグレードするために行った手順は下記になります。

  1. Redisのアップグレード内容の確認
  2. バージョンアップしたRedisの立ち上げ
  3. 負荷試験
  4. リリース手順書を書いてリハーサル
  5. 本番反映

Redisのアップグレード内容の確認

今回はRedisのメジャーバージョンが3から7へと一気に4つも上がるのでアップグレードの内容をよく確認する必要がありました。 主に確認したのは大きく2つ、

  • パラメータグループ
  • コマンド

パラメータグループに関しては地道にAWS公式ドキュメントを読んでいきました。

今回のRedisのアップグレードにおいてはアプリケーションの変更は伴わないため、追加されたパラメータの確認よりも、変更、削除されたパラメータについて重点的に確認をしました。 今回アップグレード対象のRedisはクラスター設定をしておらず、かつ利用しているコマンドもGET, SET, DELしかないシンプルなものであったため、パラメータグループの変更に影響されるものはありませんでした。 もしクラスタ設定されているのであれば、Redis6での変更点で

cluster-allow-reads-when-down: プライマリが落ちたときにレプリケーションの読み込みを許可するか(デフォルトは許可しない)

は一度確認したほうが良いと考えられます。 他にもRedis4の変更点でメモリが溢れたときに取る挙動のパラメータmaxmemory-policyに選択肢が増えていたりと、パラメータの変更を見つつアプリケーションのあるべき姿と照らし合わせる必要があります。

次にコマンドですが、これはRedisコマンドの公式ドキュメントと、アプリケーションで用いているコマンドを照らし合わせる必要があります。例えば、SETコマンドはページの下部にHistoryがあり、取りうるオプションが増えているなどの変更点があります。

Terraformでスナップショットからバージョンあげて復元

Terraformを用いてスナップショットからバージョンを上げて新規にクラスタを作成する方法ですが resourceのクラスタの設定でsnapshot_name属性があるので、そこに引数としてセットしてapplyするだけで新規に作られます。

resource "aws_elasticache_parameter_group" "default" {
  name        = "cache-params"
  family      = "redis7"
}

# クラスタ設定はエンジンバージョンに最新バージョンを指定
# snapshot_nameに復元するスナップショット名を指定
resource "aws_elasticache_cluster" "example" {
  replication_group_id = "cluster-example"
  num_cache_clusters   = 2
  node_type            = "cache.r6g.4xlarge"
  port                 = 6379
  engine               = "redis"
  engine_version       = "7.0"
  parameter_group_name = "default.redis7"
  snapshot_name        = "snapshot_name"
  ....
  other
}

設定中にハマった点は、Terraformプロバイダーのバージョンが古く

Error: engine_version: Redis versions must match <major>.x when using version 6 or higher, or <major>.<minor>.<bug-fix>

apply時に上記のエラーが出ましたが、v5.3.0PRにて解消されるので、上記のエラーに遭遇したらTerraformプロバイダーのバージョンを上げましょう。

負荷試験

アップグレード対象のRedisは求人検索結果の表示など非常に重要な部分で使われているため、本番反映前に負荷試験を行いパフォーマンス低下や不具合が起きないか確認する必要がありました。 スタンバイでは求人検索機能に関するSLOを設けているため、それを基準に負荷試験を実施しました。

リリース手順書を書いてリハーサル

本番作業をスムーズに問題なく実施できるように、検証環境でリハーサルを実施してから本番作業をすることにしました。 リリース手順書を作成し、チーム内レビューを通して検証環境でのリハーサルに挑みました。 実際に検証環境でCNAME切り替えると想定外の事が起きたので、リハーサルをやってよかったと言えます。実はRedisのCNAMEを切り替えただけだとうまく切り替わってくれないという事象が発生しました。 理由はRedisのClient Timeoutsはデフォルトで無制限(つまりコネクションを閉じない)だからでした。

By default recent versions of Redis don't close the connection with the client if the client is idle for many seconds: the connection will remain open forever.

解決方法はRedisに接続しているアプリケーションをデプロイし直すことで新たにコネクションを接続しに行くので解決しました。

本番反映

あとは、本番前日にスナップショットからバージョンをあげたRedisを立ち上げて当日に切り替え作業するだけでした。しっかりとリリース手順書を書いたのでスムーズに切り替えも終わり無事アップグレードできました。

よかったこと

アップグレードの対応を通して個人的によかったことを列挙します。

  • Redis5.0.6からGravitonが対応になっており、同スペックで低コスト運用が可能になったので少しコスト削減に貢献できました。
  • Redis5.0.6以降は、アップグレードに伴うダウンタイムが最小限に抑えられるので、今回のような大掛かりの作業をしなくてもすむ可能性が出てきました。(参考: アップグレードにと関する考慮事項)
  • Redis自体は長く使用はしていたものの、どんな機能があるのか、どんな設定ができるのかなど詳細をあまり意識していませんでしたが、今回のアップグレードの変更内容を一通り読むことで曖昧だった部分について理解が深まりました。

最後に

以上、Redis3をRedis7にアップグレードした話でした。 今回のアップグレードでは、ダウンタイムなしで切り替えを行いました。

今回のアップグレード対応は個人的にRedisについてもっと知りたいと思える良い機会になりました。

参考文献

スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com