为什么用 Sentinel
普通主从复制只有一个 master:master 挂掉后,需要运维手工把某个 slave 升成 master,再让其他 slave 切换指向新 master。Sentinel 就是为了自动化这件事:
- 监控:持续检查 master / slave 是否健康
- 通知:通过 API / pub-sub 让应用知道状态变化
- 自动故障转移:master 失效时,从 slave 中投票选新 master
- 配置提供者:客户端连接 Sentinel,自动获取当前 master 地址
一个 Sentinel 集群至少 3 个节点,避免单点。quorum 通常设为 2(多数派)。
节点拓扑
角色 容器名 IP client / sentinel
master redisMaster 172.21.0.2 6668
slave 1 redisSlaves01 172.21.0.3 6669
slave 2 redisSlaves02 172.21.0.4 6670
slave 3 redisSlaves03 172.21.0.5 6671
sentinel 1 redisSentinel01 172.21.0.6 26668
sentinel 2 redisSentinel02 172.21.0.7 26669
sentinel 3 redisSentinel03 172.21.0.8 26670
配置文件
三份配置分别放 ./conf/:
master-redis.conf
bind 0.0.0.0
port 6379
protected-mode no
# 持久化
appendonly yes
appendfsync everysec
# 认证
requirepass <REPLACE_WITH_STRONG_PASSWORD>
masterauth <REPLACE_WITH_STRONG_PASSWORD>
slave-redis.conf
bind 0.0.0.0
port 6379
protected-mode no
appendonly yes
appendfsync everysec
requirepass <REPLACE_WITH_STRONG_PASSWORD>
masterauth <REPLACE_WITH_STRONG_PASSWORD>
# Redis 5+ 用 replicaof(slaveof 仍兼容)
replicaof 172.21.0.2 6379
replica-serve-stale-data yes
replica-read-only yes
master-sentinel.conf
bind 0.0.0.0
port 26379
daemonize no
# 监控 master(最后那个 2 是 quorum)
sentinel monitor mymaster 172.21.0.2 6379 2
# master 30 秒没回应视为下线
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时
sentinel failover-timeout mymaster 60000
# 同时同步新 master 的 slave 数量
sentinel parallel-syncs mymaster 1
# 认证
sentinel auth-pass mymaster <REPLACE_WITH_STRONG_PASSWORD>
Sentinel 配置里监控的 IP 要用 master 的实际可达地址,不要写
127.0.0.1——容器间靠 172.21.0.0/16 互通。docker-compose 编写
version: "3.8"
services:
master:
image: redis:7.2-alpine
container_name: redisMaster
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
- ./conf/master-redis.conf:/usr/local/etc/redis/redis.conf
ports: [ "6668:6379" ]
networks:
cluster_net: { ipv4_address: 172.21.0.2 }
slaves01:
image: redis:7.2-alpine
container_name: redisSlaves01
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
- ./conf/slave-redis.conf:/usr/local/etc/redis/redis.conf
ports: [ "6669:6379" ]
networks: { cluster_net: { ipv4_address: 172.21.0.3 } }
slaves02:
image: redis:7.2-alpine
container_name: redisSlaves02
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
- ./conf/slave-redis.conf:/usr/local/etc/redis/redis.conf
ports: [ "6670:6379" ]
networks: { cluster_net: { ipv4_address: 172.21.0.4 } }
slaves03:
image: redis:7.2-alpine
container_name: redisSlaves03
command: redis-server /usr/local/etc/redis/redis.conf
volumes:
- ./conf/slave-redis.conf:/usr/local/etc/redis/redis.conf
ports: [ "6671:6379" ]
networks: { cluster_net: { ipv4_address: 172.21.0.5 } }
sentinel01:
image: redis:7.2-alpine
container_name: redisSentinel01
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./conf/master-sentinel.conf:/usr/local/etc/redis/sentinel.conf
ports: [ "26668:26379" ]
networks: { cluster_net: { ipv4_address: 172.21.0.6 } }
sentinel02:
image: redis:7.2-alpine
container_name: redisSentinel02
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./conf/master-sentinel.conf:/usr/local/etc/redis/sentinel.conf
ports: [ "26669:26379" ]
networks: { cluster_net: { ipv4_address: 172.21.0.7 } }
sentinel03:
image: redis:7.2-alpine
container_name: redisSentinel03
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./conf/master-sentinel.conf:/usr/local/etc/redis/sentinel.conf
ports: [ "26670:26379" ]
networks: { cluster_net: { ipv4_address: 172.21.0.8 } }
networks:
cluster_net:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/16
启动与验证
docker compose up -d
# 看 master 视角的副本
docker exec redisMaster redis-cli -a <PASSWORD> info replication
# role:master
# connected_slaves:3
# 看 Sentinel 视角
docker exec redisSentinel01 redis-cli -p 26379 sentinel masters
# 1) "name"
# 2) "mymaster"
# 3) "ip"
# 4) "172.21.0.2"
# 5) "port"
# 6) "6379"
# ...
# 14) "num-slaves"
# 15) (integer) 3
# 16) "num-other-sentinels"
# 17) (integer) 2
故障转移演示
手动杀掉 master:
docker stop redisMaster
等 30 秒(down-after-milliseconds 默认值),Sentinel 会判定 master 不可达。再次查看:
docker exec redisSentinel01 redis-cli -p 26379 sentinel masters
输出里 ip 应已变成某个 slave 的 IP(如 172.21.0.3),且 role-reported 为 master。
把老 master 启动回来:
docker start redisMaster
它会以 slave 身份重新加入(这是 Sentinel 的标准行为——避免脑裂)。
客户端接入
应用不应该硬编码 master IP,而是连 Sentinel 自动发现当前 master:
# Python redis-py
from redis.sentinel import Sentinel
sentinel = Sentinel(
[("172.21.0.6", 26379),
("172.21.0.7", 26379),
("172.21.0.8", 26379)],
sentinel_kwargs={"password": "<PASSWORD>"},
password="<PASSWORD>",
)
master = sentinel.master_for("mymaster")
master.set("foo", "bar")
// Java Lettuce
RedisURI sentinelUri = RedisURI.create("redis://:<PASSWORD>@172.21.0.6:26379");
RedisClient client = RedisClient.create(sentinelUri);
StatefulRedisMasterConnection master =
MasterClient.connect(client,
ImmutableList.of("172.21.0.6", "172.21.0.7", "172.21.0.8"),
"mymaster");
使用准则
- Sentinel 至少 3 节点,quorum 多数派。2 节点不行(脑裂风险)。
- Sentinel 自身也要持久化(
dir),重启后才知道 master 是谁。 down-after-milliseconds别设太小,否则网络抖动会误触发切换。- 应用必须用支持 Sentinel 的客户端,否则故障切换后连不上。