深夜2時に起きたこと
3年ほど前、深夜2時にアラートが飛んできた。ECシステムのカート機能とセッションが全て落ちていた。原因はOOM killerによるRedisマスターの死亡で、自動復旧の仕組みが何もなかった。開発チームがSSHで入り、手動でレプリカをマスターに昇格させ、アプリの設定を更新してサービスを再起動するまで、約40分のダウンタイムが発生した。
その障害をきっかけに、Redis Sentinelを本格的に調べ始めた。この記事はドキュメントのコピペではなく、実際の運用経験をもとに書いたものだ。
なぜスタンドアロンRedisは本番に不十分なのか
Redisレプリケーション(マスター・レプリカ構成)は読み取りのスケールアウトとバックアップの問題を解決できるが、フェイルオーバーは自動で行われない。マスターが落ちると:
- レプリカはマスターのダウンを検知するが、自動で昇格しない
- アプリケーションは古いIPへの接続を続け、大量エラーが発生する
- 手動対応が必要:レプリカの昇格、接続文字列の更新
比較すると、MongoDBはレプリカセットでプライマリを自動選出し、MySQL 8はGroup Replicationを持ち、PostgreSQLはPatroniやRepmgrを使う。Redisにも同様の仕組みが必要で、Sentinelがその答えだ。
Redis Clusterも選択肢だが、データシャーディングが必要で構成がかなり複雑になる。多くのシステム(セッション、キャッシュ、pub/sub)では、Sentinelで十分であり、障害時のデバッグもはるかに簡単だ。
Redis Sentinelの仕組み
SentinelはRedisサーバーとは別の独立したプロセスで、Redisの隣で動作する。通常は監視するだけで何も干渉しない。複数のSentinelを同時に起動すると、分散した意思決定によるフェイルオーバーのためのクォーラムが形成される。
マスター障害時の流れ:
- 各Sentinelが1秒ごとにマスターにpingを送り続ける
- 1つのSentinelが応答を受け取れなくなると、マスターを
SDOWN(主観的ダウン)としてマークする - そのSentinelが他のSentinelに確認し、クォーラム数が同意すると
ODOWN(客観的ダウン)に昇格する - Sentinel群の中でリーダーが選出される
- Sentinelリーダーが最適なレプリカを選び、新マスターに昇格させる
- 残りのレプリカの設定を更新し、新マスターからレプリケーションさせる
- pub/subチャンネル経由でクライアントに通知する
クォーラムには奇数台のSentinelが必要で、通常は3台。2台でネットワーク分断が発生すると、どちらも単独でクォーラムを達成できずデッドロックとなり、フェイルオーバーが実行できない。
環境準備:3サーバー、1 Sentinelクラスター
実際に本番で使っている構成:
192.168.1.10— Redisマスター + Sentinel 1192.168.1.11— Redisレプリカ1 + Sentinel 2192.168.1.12— Redisレプリカ2 + Sentinel 3
3台全てにRedisをインストール(Ubuntu 22.04):
sudo apt update && sudo apt install -y redis-server
sudo systemctl enable redis-server
RedisマスターとレプリカのConfiguration
マスターサーバー(192.168.1.10)の/etc/redis/redis.confを編集:
bind 0.0.0.0
protected-mode no
port 6379
requirepass "your_strong_password"
masterauth "your_strong_password"
2台のレプリカサーバー(192.168.1.11と192.168.1.12)にレプリケーション設定を追加:
bind 0.0.0.0
protected-mode no
port 6379
replicaof 192.168.1.10 6379
requirepass "your_strong_password"
masterauth "your_strong_password"
3台全てでRedisを再起動:
sudo systemctl restart redis-server
# レプリケーションが正常に動作しているか確認
redis-cli -a your_strong_password info replication
マスターの出力にはrole:masterとconnected_slaves:2が表示されるはずだ。
Redis SentinelのConfiguration
3台全てのサーバーにSentinel設定ファイルを作成する。重要な注意点:Sentinelはフェイルオーバー時にこのファイルに自動書き込みするため、プロセスにファイルへの書き込み権限が必要だ:
sudo nano /etc/redis/sentinel.conf
設定ファイルの内容は3台とも同じで、sentinel announce-ipのみ各サーバーのIPに変更する:
port 26379
daemonize yes
logfile /var/log/redis/sentinel.log
pidfile /var/run/redis/redis-sentinel.pid
# クラスター名はプロジェクト名にすると分かりやすい
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel auth-pass mymaster your_strong_password
# 何ms応答がなければダウンとみなすか
sentinel down-after-milliseconds mymaster 5000
# フェイルオーバー後に同時に再同期するレプリカ数(1推奨)
sentinel parallel-syncs mymaster 1
# フェイルオーバーのタイムアウト(ms)
sentinel failover-timeout mymaster 10000
# このサーバーの実際のIPを宣言 — NAT/Docker環境では必須
sentinel announce-ip 192.168.1.10
sentinel announce-port 26379
Sentinelを起動:
sudo redis-sentinel /etc/redis/sentinel.conf
# またはsystemdを使用(本番環境推奨)
sudo nano /etc/systemd/system/redis-sentinel.service
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/redis-sentinel /etc/redis/sentinel.conf
PIDFile=/var/run/redis/redis-sentinel.pid
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now redis-sentinel
Sentinelの動作確認
# Sentinelに接続
redis-cli -p 26379
# クラスター情報を確認
127.0.0.1:26379> SENTINEL masters
127.0.0.1:26379> SENTINEL replicas mymaster
127.0.0.1:26379> SENTINEL sentinels mymaster
# 現在のマスターアドレスを取得(アプリケーションはこのコマンドを使う)
127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster
実際のフェイルオーバーテスト
セットアップ完了後、必ずこの手順を実施する。フェイルオーバーを一度もテストせずに本番投入することは絶対にしない:
# マスターサーバーでRedisを停止
sudo systemctl stop redis-server
# 別のサーバーでSentinelのログを監視
tail -f /var/log/redis/sentinel.log
# 約10〜15秒後、新しいマスターを確認
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
正常に動作していれば、Sentinelのログに選出と昇格のプロセス全体が記録される。新しいマスターは、その時点でレプリケーションオフセットが高い方に応じて192.168.1.11または192.168.1.12になる。
Sentinel経由でアプリケーションを接続する
ここは多くの人が誤解するポイントだ。アプリケーションはRedisマスターに直接接続しない。クライアントがSentinelに「現在のマスターはどこか?」と問い合わせてから接続する。フェイルオーバーが発生すると、クライアントが自動的に新しいマスターを検出するため、アプリケーションの再起動は不要だ。
redis-pyを使ったPythonの例:
from redis.sentinel import Sentinel
sentinel = Sentinel(
[
('192.168.1.10', 26379),
('192.168.1.11', 26379),
('192.168.1.12', 26379),
],
socket_timeout=0.5,
password='your_strong_password'
)
# マスター接続を取得(書き込み用)
master = sentinel.master_for('mymaster', socket_timeout=0.5)
# レプリカ接続を取得(読み取り用)
replica = sentinel.slave_for('mymaster', socket_timeout=0.5)
master.set('key', 'value')
print(replica.get('key'))
ioredisを使ったNode.jsの例:
const Redis = require('ioredis');
const redis = new Redis({
sentinels: [
{ host: '192.168.1.10', port: 26379 },
{ host: '192.168.1.11', port: 26379 },
{ host: '192.168.1.12', port: 26379 },
],
name: 'mymaster',
password: 'your_strong_password',
sentinelPassword: 'your_strong_password',
});
実際に遭遇したトラブル
Sentinelを運用する中で経験した実際の落とし穴:
masterauthの設定忘れ:フェイルオーバー時、新しいマスターに昇格したレプリカに他のレプリカが認証できずレプリケーションが止まる。よく見られる症状:レプリケーションラグの急増、ノード間のデータ乖離。全ノードでrequirepassとmasterauthを必ず設定すること。- SentinelのIPアナウンス未設定:NATやDocker環境では、Sentinelが誤った内部IPを自己報告するため、フェイルオーバー後にクライアントが接続できなくなる。クライアントが到達できる実際のIPで
sentinel announce-ipを必ず設定すること。 - sentinel.confのパーミッション問題:Sentinelはフェイルオーバー時に設定ファイルへの書き込みが必要だ。ファイルがrootの所有でSentinelが
redisユーザーとして動作している場合、フェイルオーバーがサイレントに失敗する。明確なエラーが出ず、タイムアウト後に自己回復するだけだ。 - down-after-millisecondsの設定値が低すぎる:クラウド環境では、ネットワークレイテンシが500ms〜1sにスパイクすることがある。1000msの設定値は誤検知フェイルオーバーを引き起こす可能性がある。本番環境では5000msに設定することが多い。
本番環境でのSentinel監視
redis_exporter経由でPrometheusと連携できる。Sentinelモードに対応済みだ。Prometheusスタックがまだない場合は、定期実行のbashスクリプトで十分対応できる:
#!/bin/bash
MASTER=$(redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster | head -1)
echo "現在のマスター: $MASTER"
# マスターが取得できない場合はアラート
if [ -z "$MASTER" ]; then
echo "アラート: Redisマスターを特定できません!" | mail -s "Redis Sentinel Alert" [email protected]
fi
Redis Sentinelは全ての問題を解決するわけではない。大規模データで本格的なシャーディングが必要になればRedis Clusterが次のステップだ。しかし、一般的なユースケースの90%(セッション、キャッシュ、キュー)では、Sentinelは十分に強力で、深夜の障害時でもデバッグしやすく、そして何より重要なのは、深夜2時に電話で叩き起こされることがなくなることだ。

