TL;DR
- Redisへのセッション情報はapplication.yml に
spring.session.store-type: redis
、spring.redis.cluster.node: 接続先
を設定するのみ - 上記のみの場合、クラスターのスケールアップ・スケールアウトに対応できない
- スケールアップ・スケールアウトに対応するためにはapplication.ymlに
spring.redis.lettuce.cluster.refresh.apaptive: true
の設定が必要
背景・モチベーション
EC2 複数台で稼働しているSpringBootアプリケーションのセッションを各EC2上のTomcatで管理している。
各ユーザのセッション保持はALBのスティッキーセッションをONにして維持している。
頻回のデプロイのタイミングで各EC2がALBから切り離されるため、デプロイごとに全ユーザのセッションが途切れてしまう。
セッションを個別KVS(Redis)化することでユーザの利便性向上を図る
アーキテクト
※ あとで図を書く
- AWS上で稼働しているサービス
- いわゆる三層アプリケーション
- CDN → WAF → ALB → EC2 → DB
- apache httpd/Tomcat/SpringBoot
KVSについて
AWS上で稼働しているので、Elasticache for Redisを採用。
DynamoDB/Elasticache for Memcachedもありだが、レスポンスなどを優先してRedisとした。バックアップも取れるし。
スケールアップ・スケールアウトを含めて、冗長化・可用性のためにクラスター構成。
作成方法は公式ドキュメントを参照のこと
Redisにつないでみる
application.ymlに下記を設定するだけで、セッション情報をElasticache for Redisに移行可能。
spring
session:
store-type: redis
redis:
configure-action: none
redis:
cluster:
nodes: xxxxxx.yyyyyy.clustercfg.apne1.cache.amazonaws.com:6379 # AWS構築したElasticache for Redis Clusterのエンドポイント。portは6379でよいはず
負荷を掛けてみる
検証のために高負荷を掛けてみる。特定ノードの負荷が上がり、一部ユーザがログインできない状況になる。
ここで、シャード追加。あれ?効果がない。。
ならば、インスタンスタイプを変更して、スケールアウト。あれ?あれれ???レスポンスが帰ってこない????
画面真っ白??
アプリケーション完全接続不可..!!
Tomcat再起動.. なんとかサービス復旧.. 検証環境でよかった..
シャード追加後、Tomcatで1/シャード数くらいの確率で下記Eceptionが発生していた。
Caused by: io.lettuce.core.RedisException: java.io.IOException:
インスタンスタイプ変更後は一切Redisに接続できていなかった。
解決方法
どうやら、最初の設定ではRedisクラスターへの接続は可能だが、シャード追加・インスタンスタイプ変更によるクラスター内部の構成変更時にエンドポイントの後ろの実際の接続先への再接続が行われない模様。
SpringSessionでRedis設定を行うとデフォルトでlettuceというクライアントが利用される。
lettuceではクラスターへのrefresh設定がデフォルトでOFFになっていた。
applicatin.ymlに下記設定を追加することによる、lettuceのrefresh設定が必要だった。
spring
session:
lettuce:
cluster:
refresh:
adaptive: true
period: 60s # 60sでとりあえずよさそう。要チューニング
現状
上記設定により、インスタンスタイプ変更、シャード追加、シャード削除を実施後もTomcat再起動なしでアプリケーション側でのエラー発生しなくなった。
現在、本番切換えに向けて追加の負荷試験中。