k8s_redis_cluster_java_app_2
クバネティス(Kubernetes) 環境で Redis-Cluster Java App 構築 ②
Failover テスト
🖧 Redis-Cluster ポッド(Pod) ダイアグラム
1. Failover(障害対応)テスト開始
SSH ターミナルを2つ開き、Terminal-1 では App ポッド(Pod) 生成、Redis ポッド(Pod) ダウンなどを実行し、 Terminal-2 で App ログを見ます。
1) Java(Spring) App ポッド(Pod) 生成
サーバ (Terminal-1 [~/kube/app/cluster/]) で実行
$ kubectl apply -f app-deployment.yaml -> ポッド(Pod) 生成
💻 App ログ
サーバ (Terminal-2 [~/kube/app/cluster]) で実行
$ kubectl logs -f -l app=spring-redis-cluster-app
1秒あたり10件ずつ入力([SUCCESS])されることを確認できます。
2) Redis(Redis-0) 💥 ポッド(Pod) ダウン(delete)
Terminal-1) $ date; kubectl delete pod redis-cluster-0 -> 時刻: 11:04:49
💻 Redis-0 ログ
① 49秒 Redis ダウン (+0秒)
② 50秒 Redis 再起動 (+1秒)
③ 54秒 マスター -> 複製へ転換 (+5秒)
Redis-0 全ログ
💻 Redis-4 ログ
① 49秒 マスターとの接続切断 (+0秒)
② 54秒 複製 -> マスターへ転換(昇格) (+5秒)
Redis-4 全ログ
💻 App ログ
Terminal-2) $ kubectl logs -f -l app=spring-redis-sentinel-app
① 49秒 Redis 接続切断 -> 再接続試行 (+0秒)
② 50秒 Redis-1,2 にはデータ入力される (+1秒)
③ 53秒~54秒 Redis-0 へ4件入力失敗 (+4秒~+5秒) の後、Redis-4 がマスターへ転換(昇格)し正常入力されます
④ 59秒 Redis-0 接続成功 -> 入力成功 (+10秒)
App 全ログ
🖧 Redis-Cluster ポッド(Pod) ダイアグラム: Failover 状態 (Redis-4 Master-1)
2. Failback(原状回復)テスト
1) Redis(Redis-4) ポッド(Pod) 💥 ダウン(delete)
💻 Redis-4 ログ
① 49秒 Redis ダウン (+0秒)
② 52秒 Redis 再起動 (+3秒)
③ 55秒 マスター -> 複製へ転換 (+6秒)
Redis-4 全ログ
💻 Redis-0 ログ
① 50秒 マスターとの接続切断 (+1秒)
② 55秒 複製 -> マスターへ転換 (+5秒)
Redis-0 全ログ
💻 App ログ
① 50秒 Redis-4 接続切断 (+1秒)
② 54秒~56秒 5件入力失敗 (+4秒~+6秒) の後、Redis-0 がマスターへ転換(昇格)し正常入力されます
③ 59秒 Redis-4 接続成功 (+10秒)
App 全ログ
🖧 Redis-Cluster ポッド(Pod) ダイアグラム: 原状回復状態 (Redis-0 Master-1)
🧑💻 テスト検討と核心まとめ
今回の障害対応(Failover/Failback)テストで、クバネティス(Kubernetes) 環境の Redis-Cluster と Java(Spring Boot / Lettuce) アプリケーション間の実際の障害対応動作とクラスタトポロジー再構成過程を検証しました。
⏱️ 障害対応タイムライン (Timeline)
| 区分 | 時刻 (相対時間) | Redis Cluster ノード(Node) 動作 | Java (Spring) App 動作 |
|---|---|---|---|
| 障害発生 | 11:04:49 (+0秒) | Master(Redis-0) ポッド(Pod) 終了 (delete) | Redis-0 接続切断とバックグラウンド再接続試行。 📍正常ノード(Node)(Redis-1, 2)向けデータは Async 処理によりブロッキングなく入力成功し続ける (11:04:50~) |
| 再試行上限到達 | 11:04:53~54 (+4~5秒) | Replica(Redis-4) が障害検知後、昇格投票(Election) 進行中 | Redis-0 スロット向けの一部リクエスト(4件)がバックグラウンド 5回(5秒)再試行しきい値を超えて最終失敗(FAIL)処理 |
| Failover 完了 | 11:04:54 (+5秒) | 投票完了後 Replica(Redis-4) を新規 Master へ昇格 | クラスタトポロジー更新試行 (ClusterTopologyRefresh) |
| 完全正常化 | 11:04:55 (+6秒) | 新規 Master(Redis-4) が該当スロット(0~5460)の書き込み/読み取り開始 | 変更トポロジーが適用され すべてのスロットへのデータが再び100%正常(SUCCESS)書き込み再開 |
| 旧マスター再起動 | 11:04:59 (+10秒) | 再起動した旧マスター(Redis-0)が新規マスター(Redis-4)の Replica ノード(Node)へ再構成 | Redis-0 ノード(Node) 接続再確立(Reconnected) 完了 |
💡 実務適用のための核心示唆 (Key Takeaways)
-
1. Redis-0 マスターがダウンしても、生きている Redis-1,2 には正常に入力されます
-
2. Cluster ノード(Node)間の障害検知と昇格時間 (約4~5秒):
Master ノード(Node)がダウンすると、残りのノード(Node)がcluster-node-timeoutの間状態を 確認(FAIL 判定)したあと、Replica ノード(Node)が自動昇格するまで 約4~5秒のダウンタイムが発生します。 -
3. Spring / Lettuce クライアントの Dynamic Topology Refresh(トポロジー自動更新)の重要性:
Redis Cluster 環境では Master ノード(Node)が変わると、そのノード(Node)が担当していたハッシュスロット(Hash Slot)の 所有権が移ります。 Spring App(Lettuce)の クラスタトポロジー自動更新オプションが動き、変わった Master IP とスロット情報をリアルタイムで検知したため、別途のサービス再起動なしで連携が維持されました。 -
4. Client-Side Retry(5秒)の効果と限界:
1回目のテスト(Redis-0 ダウン)では App の Retry(5秒)範囲内である5秒時点で Failover が 完了し、成功率100%(消失0件)でした。 一方、2回目のテスト(Redis-4 ダウン)ではトポロジー再調整時間の差で1件の失敗 リクエスト(keyA-00000607)が発生しました。 したがって実務では App Retry だけでなく、非同期キュー処理と再試行メカニズムを合わせて設計しなければ 完全な無停止は保証できません。 -
5. Cluster の自動ノード(Node)役割再構成(Re-configuration):
復旧した旧 Master(Redis-0) ノード(Node)が再び起動すると、クラスタバス経由で変わったノード(Node)状態を 受け取り、自動で新規 Master(Redis-4)の Replica ノード(Node)へ転換します。 したがって管理者の手動介入なしでも、クラスタ全体の HA(高可用性)ターゲット数が復旧します。 -
6. ユーザ体験(UX)とタイムアウトの考慮事項:
クライアント観点では、障害発生時に約4~5秒の応答遅延(Latency)が発生します。 Web/App フロントエンド API 呼び出し時にユーザ画面が止まらないよう、Front-end Timeout と Circuit Breaker(Resilience4j など)を適切に組み合わせることを推奨します。
🛡️ Front-end Timeout と Circuit Breaker の説明
🛡️ Redis 障害時にシステム麻痺を防ぐ安全装置
Sentinel の障害対応(Failover)中に発生する 4~5秒の応答遅延(Latency) は、バックエンドサーバと ユーザ画面へ障害が伝わる連鎖障害(Cascading Failure)を起こし得ます。 これを防ぐため、実務では次の2つの安全装置を必ず組み合わせて構築します。
1. Front-end Timeout (フロントエンドタイムアウト)
ユーザのブラウザやモバイルアプリがバックエンド API を呼んだあと、「応答が来ないときに無限待ちせず、リクエストを諦める最大時間」です。
- 問題状況 (Timeout 未設定時): Redis 障害中に4秒応答が止まると、焦ったユーザが '再読み込み' や 'ボタン' を連打します。これによりバックエンド Tomcat/Spring スレッド(Thread)が枯渇し、Web サーバ全体がダウンします。
- 解決策 (2~3秒 Timeout 適用): 2秒以内に応答がなければ直ちにリクエストを止め、「一時的なネットワーク遅延です。しばらくして再試行してください」 という案内を出してバックエンド暴走を遮断します。
2. Circuit Breaker (サーキットブレーカ - Resilience4j)
電気盤の '遮断器' のようにバックエンド(Spring) アプリケーション内部に置き、Redis 障害検知時に 遮断器を開いて(Open) Redis への接続試行を直ちに止めるライブラリです。
- 🟢 CLOSED (正常): すべてのキャッシュ/データリクエストを Redis へ正常に渡します。
- 🔴 OPEN (遮断): Redis 遅延/エラー率が指定値を超えると遮断器を開き、 Redis 呼び出しを試みず、あらかじめ準備した代替ロジック(Fallback: 例: Main RDB 直接照会)を 直ちに実行します。
- 🟡 HALF-OPEN (検証): Redis が復旧したか確認するため少数のリクエストだけ送り、 正常動作が確認できれば再び 🟢 CLOSED 状態へ戻します。
🔄 障害発生時の2技術の連携プロセス
- 0秒 (Redis Master ダウン): Sentinel が Failover を始め、応答遅延が発生
- 1~2秒 (Circuit Breaker 作動): Spring App の Resilience4j がエラー/遅延を検知し、遮断器を 🔴 OPEN へ転換 ➔ Redis の代わりに RDB 照会またはデフォルト値返却(Fallback)
- 2秒 (Front-end Timeout 作動): フロントエンドがリクエストを安全に切り、無限ローディングとユーザ連打による WAS 暴走を防止
- 4~5秒 (Failover 完了 & 自動復旧): Sentinel が新規 Master 昇格を完了すると、Resilience4j がそれを検知して 🟢 CLOSED に戻し、キャッシュサービスを自動正常化
| << K8s Redis-Cluster App ① | K8s Redis-Sentinel 準備 ① >> |
|---|
