Redis Sentinelによる高可用性設計:机上の空論を排した実戦的アーキテクチャ論
こんにちは。テックリードの私だ。
今日のコードレビューで「Redisを導入するので、高可用性のためにSentinelを2台立てました」という設計書を見かけた。私は即座にペンを置き、赤入れをした。
――「甘い。その構成では、スプリットブレイン(脳味噌分裂)を防げず、障害時にデータが消失するリスクがある」
クラウドネイティブな時代であっても、マネージドなRedis(AWS ElastiCacheやGCP Memorystoreなど)の裏側で何が起きているのか、あるいはオンプレミスやKubernetes上で自前で堅牢な高可用性クラスターを組み上げる場合、Redis Sentinelの挙動を完全に理解していなければ、本番環境の障害時にシステムを崩壊させる引き金を引くことになる。
今回は、教科書的なマニュアルの引き写しではない。極限の負荷に耐え、泥臭いネットワーク分断(分断脳)をも生き抜く、プロフェッショナルのためのRedis Sentinel設計論を授けよう。
—
1. Sentinelの本質:単なる監視役ではなく「合意形成の調停者」
まず大前提として、Redis Sentinelはデータキャッシュを保持しない。
Sentinelの本質は、マスターとレプリカの「状態監視」「自動フェイルオーバー」、そしてクライアントへの「構成情報の配信(Service Discovery)」を行う分散コーディネーターである。
+————-+ +————-+
| Client | | Client |
+——+——+ +——+——+
| (Sentinelへ問い合わせてマスターを特定)
v v
+————————————-+
| Redis Sentinel |
| [S1] <---- Raftベースの合意 ----> [S2] |
| \ / |
| +———- [S3] ————+ |
+——————+——————+
| 監視・死活判定
+———v———+
| Master (Write) |
+———+———+
| 複製 (Replication)
+———v———+
| Replica (Read) |
+——————-+
勘違いしやすいが、Sentinelはマスターのデータそのものを守るわけではない。「今、誰が真のマスターであるか」をネットワーク全体で合意形成(Consensus)するためのシステムなのだ。
—
2. 自動フェイルオーバーの裏側と「クォーラム(定足数)」の数学的真実
マスターが沈黙したとき、自動フェイルオーバーが走る。このプロセスは以下のステップでアトミックに実行される。
1. SDOWN(Subjectively Down / 主観的ダウン): ひとつのSentinelから見て、マスターが `down-after-milliseconds` の間応答しない状態。
2. ODOWN(Objectively Down / 客観的ダウン): クォーラム(定足数)で設定された数のSentinelが「マスターが落ちている」と判断した状態。ここで初めてフェイルオーバーの合意が形成される。
3. リーダー選出: Sentinel群の中で、Raftアルゴリズムに類似したプロセスにより、フェイルオーバーを主導する「リーダーSentinel」が選ばれる。
4. 新マスター昇格: リーダーが、最適なレプリカ(オフセットが進んでいる、優先度が高いなど)を選び、`SLAVEOF NO ONE`(Redis 5以降は `REPLICAOF NO ONE`)を発行してマスターに昇格させる。
⚠️ 致命的な設計ミス:クォーラムとSentinelの「奇数台ルール」
ここで実務上の極めて重要な設計原則を伝える。
Sentinelの数は、常に「3台以上」かつ「奇数」でなければならない。
よくあるアンチパターンがこれだ:
- 「コスト削減のためにSentinelを2台にしました」
- 「クォーラムを `2` にしました」
これの何が問題か?
Sentinelが2台の場合、ネットワークが分断されて1台ずつ孤立したとする。どちらのSentinelも「自分から見える側」で過半数(2票中2票)の合意を取ろうとするが、どちらも他方の生存を確認できないため、どちらもリーダーを選出できず、フェイルオーバーが永遠に始まらない(可用性の喪失)。
逆にクォーラムを `1` にすると、一時的な高負荷による応答遅延(False Positive)をマスターの死と誤認し、不必要なフェイルオーバー(フラッピング)が頻発してシステムが崩壊する。
正しい設定例(`sentinel.conf`)
マスターの監視定義
監視名, マスターIP, ポート, クォーラム数
sentinel monitor mymaster 192.168.1.100 6379 2
マスターがダウンしているとみなす時間(ミリ秒)
sentinel down-after-milliseconds mymaster 5000
フェイルオーバーのタイムアウト(ミリ秒)。これを超えると次のレプリカへ
sentinel failover-timeout mymaster 60000
同時に新しいマスターの同期を始めるレプリカの数
sentinel parallel-syncs mymaster 1
※ここでクォーラムを `2` に設定する場合、Sentinel自体の物理/仮想インスタンスは最低 3台 展開していることが絶対条件となる。
—
3. クライアントサイドの設計:スマートクライアントの必須要件
「マスターが切り替わったら、アプリケーションはどうやってそれを知るのか?」
ここで多くのジュニアエンジニアが、アプリケーション側で定期的にIPアドレスをポーリングするような泥臭いコードを書き始める。今すぐやめてほしい。
Redis Sentinel環境では、クライアントは「Sentinel対応(Sentinel-aware)」のライブラリを使用しなければならない。
正しいクライアント接続の概念(Python / `redis-py` の例)
import redis
from redis.sentinel import Sentinel
1. Sentinelのノードリストを初期化時に渡す(最低3台ののエンドポイントを指定)
sentinel = Sentinel(
[
(“sentinel-1.internal”, 26379),
(“sentinel-2.internal”, 26379),
(“sentinel-3.internal”, 26379),
],
socket_timeout=0.5,
)
2. Sentinelに「現在のマスターはどこか?」を問い合わせて接続を取得
ハードコードされたIPアドレスは一切使わない
master = sentinel.master_for(
“mymaster”, socket_timeout=0.1, decode_responses=True
)
書き込み処理
master.set(“user:1001:status”, “active”)
3. 読み込み専用のレプリカに接続したい場合(負荷分散)
slave = sentinel.slave_for(
“mymaster”, socket_timeout=0.1, decode_responses=True
)
print(slave.get(“user:1001:status”))
このコードが優れている理由
1. エンドポイントの動的解決: アプリケーションは固定のマスターIPを知らない。起動時およびフェイルオーバー検知時にSentinelへ問い合わせ、最新のマスター/レプリカのIPを動的に取得・キャッシュする。
2. リダイ렉ション透過性: フェイルオーバーが発生した瞬間、古いマスターへの書き込みはエラー(`READONLY` や接続断)になるが、スマートクライアントは瞬時にSentinelと再対話し、新しいマスターへ接続を張り直す。
—
4. プロが踏む「地雷」:スプリットブレインとネットワークパーティション
可用性設計において、最も恐ろしいのは「ネットワークの分断(Split-Brain)」だ。
[ゾーンA] [ゾーンB]
Master (Write可能) Sentinel (過半数側)
Sentinel (少数派) X Replica -> 新Masterに昇格!
Client (こちらに書き込む)
もし、ネットワークの不具合により、旧マスターが孤立したとする。
旧マスターは「自分がまだマスターだ」と思い込み、クライアントからの書き込みを受け付け続ける。しかし、ネットワークの向こう側(過半数のSentinelが存在する側)では、別のレプリカが新しいマスターに昇格している。
結果、両方のノードが「私はマスターだ」と主張し、書き込みが分岐する(データの不整合)。
Redis Sentinelにおける対策と限界
Redis Sentinelは、Raftやetcdのような厳密なCPシステム(Consensus / Partition Tolerance)ではない。Raftと異なり、旧マスターに対して「お前はもうマスターではない」と強制的に降格させるシグナル(フェンス・トークンなど)を完全に同期保証するわけではないため、極端なネットワーク分断時には一時的な二重書き込みのリスクが理論上ゼロにはならない。
チーフアーキテクトからの実践的アドバイス:
1. クライアントのタイムアウトとリトライを厳格に絞る: 旧マスターに取り残されたクライアントが古いセッションで書き込み続けられないよう、TCPキープアライブやコネクションの有効期限を短く設定する。
2. データの重要度を見極める: 金融トランザクションや絶対に欠損・分岐が許されないデータストアとしてRedisを使う場合、Sentinel構成ではなく、Redis Cluster や、強整合性を持つ別のストレージ(RDB等)との組み合わせを再検討すべきだ。Redis Sentinelは「数秒のダウンタイムと直近の数秒のデータロストを許容できるが、高い可用性を必要とするキャッシュやセッションストア」に最適化されている。
—
5. 監視と運用のベストプラクティス
最後に、本番運用で絶対に組み込むべき監視項目を提示する。Sentinelを導入して「ハイ、終わり」ではない。監視して初めて高可用性は成立する。
1. `sentinel_masters` メトリックの監視: 監視対象のマスターが正常に認識されているか。
2. `connected_replicas` の数: レプリカの接続数が期待値(例: 2台)を下回っていないか。レプリカが1台落ちた状態でのマスター障害は、フェイルオーバー先の喪失(完全停止)を意味する。
3. `link-down-since` の検知: マスターとレプリカ間のレプリケーション遅延や切断が起きていないか。
—
まとめ
Redis Sentinelによる高可用性設計の要点を振り返る。
- 奇数台の厳守: Sentinelは必ず3台以上、奇数でデプロイする(クォーラムの適切な設定)。
- スマートクライアントの徹底: アプリケーション側でIPをハードコードせず、Sentinel-awareなライブラリで動的にマスターを解決する。
- 特性の理解: Sentinelは万能の分散合意システムではなく、CAP定理におけるAvailabilityとPartition Toleranceに寄った仕組みであることを理解し、データロストの可能性を設計に織り込む。
アーキテクチャに「なんとなく動く」という妥協は許されない。すべての挙動には理由があり、すべての設定値には根拠が必要だ。
次の設計レビューでは、これらの要件を満たした美しい構成図を持ってきてもらうことを期待している。
コメント