云计算百科
云计算领域专业知识百科平台

Redis哨兵模式(基于docker,多服务器)

一、下载镜像

采用的是7.4.7的redis镜像

docker pull redis:7.4.7

二、概念与架构图

以下是哨兵模式的架构图

Sentinel1 Sentinel2 Sentinel3
| | |
+— Monitor + Vote —+
|
+—————–+
| Master |
| 6379 |
+—————–+
/ \\
/Replicate \\
v v
+———-+ +———-+
| Slave1 | | Slave2 |
| 6380 | | 6381 |
+———-+ +———-+

节点解释

节点名称数量关键作用数据是否存储
master主节点 1(可能 failover 后变化) 提供写服务,数据来源中心 原始数据持久化
Slave 从节点 2(或者更多) 数据副本,提供读服务,故障接管备用 拷贝主节点数据
Sentinel 哨兵节点 3 个或更多 健康监控、主从切换、服务发现,不存业务数据 只保存状态信息

1、Master(主节点)

职责:

  • 所有写入操作 只会 写在主节点
  • 更新数据会异步同步到所有从节点
  • 当主节点宕机时,它是 failover 的核心对象

特点:

  • 单节点写入性能高
  • 存储的数据是最终来源

2、 Slave(从节点)

职责:

  • 实时复制主节点的数据
  • 支持读操作(读写分离)
  • 主节点宕机时由哨兵提升某个从节点为新主节点(Failover)

特点:

  • 提升读吞吐
  • 数据备份冗余
  • 不参与选主逻辑(由哨兵负责)

主节点恢复后会自动变为新的 Slave —— 哨兵强制一致性规则

3、Sentinel(哨兵节点)

职责:

  • 监控(Monitoring):自动监控主从的状态存活
  • 通知(Notification):当节点异常会通知管理员或客户端
  • 故障迁移(Failover):决定并执行主从切换
  • 服务发现(Service Discovery):向客户端提供新主节点地址

特点:

  • 无数据存储
  • 需要数量至少 3 个(保证多节点投票)
  • 通过多数票判断主节点是否下线(避免误判)

4、Failover(故障转移)机制

主节点宕机后:

1、哨兵投票达成一致:主节点真的挂了(quorum 达成)

2、选出最佳 Slave 提升为 Master

3、通知其他 Slave 复制新的 Master

4、客户端无需修改:通过 Sentinel 获取新主节点地址

三、部署

两台服务器的部署方案

192.168.244.130
192.168.244.131

创建自定义网络

docker network create redis-net

部署的文件目录如下,因为哨兵节点需要将临时文件写入配置文件的所在目录,所以单独存放哨兵节点的配置文件

A服务器的文件

redissentinel/
├─ dockercompose.yml
├─ config
│ ├─ master
│ ├─ redis.conf ← 主节点
│ ├─ server1
│ ├─ redis.conf ← 从节点
│ ├─ sentinel1
│ ├─ sentinel.conf ← 哨兵配置文件
│ ├─ sentinel2
│ ├─ sentinel.conf ← 哨兵配置文件
└─ data
├─ master
├─ slave2
├─ slave3

B服务器的文件目录

redissentinel/
├─ dockercompose.yml
├─ config
│ ├─ server2
│ ├─ redis.conf ← 从节点
│ ├─ sentinel3
│ ├─ sentinel.conf ← 哨兵配置文件
└─ data
├─ slave2

1、A服务器的部署文件

(1)主节点配置文件

主节点配置文件redis.conf

# 绑定到所有网卡,使容器内其他服务可访问
bind 0.0.0.0

# Redis 服务端口(由 docker-compose 覆盖)
port 6379

# 保护模式关闭(允许远程访问,生产环境建议开启密码)
protected-mode no

# Redis 是否后台运行
daemonize no

# 持久化:开启 AOF(实时写磁盘,数据安全性更高)
appendonly yes

# AOF 文件路径(默认存储到挂载的 /data 目录)
appendfilename "appendonly.aof"

# 对外IP必须是宿主机IP地址,否则哨兵将无法获取主节点,端口也是如此(重要)
replica-announce-ip 192.168.244.130
replica-announce-port 6379

# 主从复制时使用 docker-compose 启动参数 –replicaof 自动配置
# replicaof <master-ip> <master-port>
# 在 compose 中已配置,不需在此填写

(2)从节点配置文件

从节点(server1)的配置文件redis.conf

# 绑定到所有网卡,使容器内其他服务可访问
bind 0.0.0.0

# Redis 服务端口(由 docker-compose 覆盖)
port 6380

# 保护模式关闭(允许远程访问,生产环境建议开启密码)
protected-mode no

# Redis 是否后台运行
daemonize no

# 持久化:开启 AOF(实时写磁盘,数据安全性更高)
appendonly yes

# AOF 文件路径(默认存储到挂载的 /data 目录)
appendfilename "appendonly.aof"

# 对外IP必须是宿主机IP地址,否则哨兵将无法获取主节点,端口也是如此(重要)
replica-announce-ip 192.168.244.130
replica-announce-port 6380

# 主从复制时使用 docker-compose 启动参数 –replicaof 自动配置
# replicaof <master-ip> <master-port>
# 在 compose 中已配置,不需在此填写

(3)哨兵节点配置文件

1号哨兵节点(sentinel1)配置sentinel.conf

port 26379
daemonize no
protected-mode no

# 监控主节点,IP地址为主节点对应的宿主机IP地址(重要)
sentinel monitor mymaster 192.168.244.130 6379 2

# 主节点超时时间
sentinel down-after-milliseconds mymaster 5000

# 故障转移最大允许时间
sentinel failover-timeout mymaster 10000

# 绑定到所有网卡
bind 0.0.0.0

# (如有密码)
# sentinel auth-pass mymaster yourpassword
# 指定哨兵对外的IP地址,通过这IP地址让其他服务找到该哨兵节点(重要)
sentinel announce-ip "192.168.244.130"
sentinel announce-port 26379

2号哨兵节点(sentinel2)配置sentinel.conf

port 26380
daemonize no
protected-mode no

# 监控主节点,IP地址为主节点对应的宿主机IP地址(重要)
sentinel monitor mymaster 192.168.244.130 6379 2

# 主节点超时时间
sentinel down-after-milliseconds mymaster 5000

# 故障转移最大允许时间
sentinel failover-timeout mymaster 10000

# 绑定到所有网卡
bind 0.0.0.0

# (如有密码)
# sentinel auth-pass mymaster yourpassword
# 指定哨兵对外的IP地址,通过这IP地址让其他服务找到该哨兵节点(重要)
sentinel announce-ip "192.168.244.130"
sentinel announce-port 26380

(4)docker-compose文件

services:
redis-master:
image: redis:7.4.7
container_name: redismaster
restart: always
ports:
"6379:6379"
volumes:
./config/master/redis.conf:/usr/local/etc/redis/redis.conf
./data/master:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
networks:
redisnet
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 5s
retries: 5
start_period: 3s

redis-slave1:
image: redis:7.4.7
container_name: redisslave1
restart: always
ports:
"6380:6380"
depends_on:
redis-master:
condition: service_healthy
volumes:
./config/slave1/redis.conf:/usr/local/etc/redis/redis.conf
./data/slave1:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf", "–replicaof", "192.168.244.130", "6379"]
networks:
redisnet

redis-sentinel1:
image: redis:7.4.7
container_name: redissentinel1
depends_on:
redis-master:
condition: service_healthy
ports:
"26379:26379"
volumes:
./config/sentinel1:/usr/local/etc/redis
./data/sentinel1:/data
command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"]
networks:
redisnet

redis-sentinel2:
image: redis:7.4.7
container_name: redissentinel2
depends_on:
redis-master:
condition: service_healthy
ports:
"26380:26380"
volumes:
./config/sentinel2:/usr/local/etc/redis
./data/sentinel2:/data
command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"]
networks:
redisnet

networks:
redis-net:
external: true
name: redisnet

docker-compose命令(新版本的docker-compose)

# 启动
docker compose up -d
# 删除
docker compose down -v # 取消挂载

2、B服务器的部署文件

(1)从节点配置文件

从节点(server2)的配置文件redis.conf

# 绑定到所有网卡,使容器内其他服务可访问
bind 0.0.0.0

# Redis 服务端口(由 docker-compose 覆盖)
port 6379

# 保护模式关闭(允许远程访问,生产环境建议开启密码)
protected-mode no

# Redis 是否后台运行
daemonize no

# 持久化:开启 AOF(实时写磁盘,数据安全性更高)
appendonly yes

# AOF 文件路径(默认存储到挂载的 /data 目录)
appendfilename "appendonly.aof"

# 对外IP必须是宿主机IP地址,否则哨兵将无法获取主节点,端口也是如此(重要)
replica-announce-ip 192.168.244.131
replica-announce-port 6379

# 主从复制时使用 docker-compose 启动参数 –replicaof 自动配置
# replicaof <master-ip> <master-port>
# 在 compose 中已配置,不需在此填写

(2)哨兵节点配置文件

3号哨兵节点(sentinel3)配置sentinel.conf

port 26379
daemonize no

# 启用主机名解析
# sentinel resolve-hostnames yes
protected-mode no
# dir "/tmp"
# 监控主节点(使用服务名,不要硬编码 IP)
sentinel monitor mymaster 192.168.244.130 6379 2

# 主节点超时时间
sentinel down-after-milliseconds mymaster 5000

# 故障转移最大允许时间
sentinel failover-timeout mymaster 10000

# 绑定到所有网卡
bind 0.0.0.0

# (如有密码)
# sentinel auth-pass mymaster yourpassword
# 指定哨兵对外的IP地址,通过这IP地址让其他服务找到该哨兵节点(重要)
sentinel announce-ip "192.168.244.131"
sentinel announce-port 26379

(3)docker-compose文件

services:
redis-slave2:
image: redis:7.4.7
container_name: redisslave2
restart: always
ports:
"6379:6379"
volumes:
./config/slave2/redis.conf:/usr/local/etc/redis/redis.conf
./data/slave2:/data
command: ["redis-server", "/usr/local/etc/redis/redis.conf", "–replicaof", "192.168.244.130", "6379"]
networks:
redisnet

redis-sentinel3:
image: redis:7.4.7
container_name: redissentinel3
ports:
"26381:26381"
volumes:
./config/sentinel3:/usr/local/etc/redis
./data/sentinel3:/data
command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"]
networks:
redisnet

networks:
redis-net:
external: true
name: redisnet

docker compose命令(新版本的docker-compose)

# 启动
docker compose up -d
# 删除
docker compose down -v # 取消挂载

3、注意点

(1)容器启动时会出现容器因为权限问题无法读取配置文件的情况,需要给配置文件赋予权限

# 赋予目录权限
chmod 777 sentinel
# 赋予文件权限
chmod 666 sentinel.conf

(2)在主节点未启动完成之前其他节点暂时不要启动

四、验证

使用命令查看哨兵节点的日志(以redis-sentinel1为例)

docker logs -f redis-sentinel1

日志如下则成功启动,并且已经连接到了主节点

1:X 09 Dec 2025 03:51:45.194 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:X 09 Dec 2025 03:51:45.197 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:X 09 Dec 2025 03:51:45.197 * Redis version=7.4.7, bits=64, commit=00000000, modified=0, pid=1, just started
1:X 09 Dec 2025 03:51:45.197 * Configuration loaded
1:X 09 Dec 2025 03:51:45.197 * monotonic clock: POSIX clock_gettime
1:X 09 Dec 2025 03:51:45.198 * Running mode=sentinel, port=26379.
1:X 09 Dec 2025 03:51:45.199 * Sentinel ID is 8bb480d5b1d1fff329534943c19571eb7aad38c7
1:X 09 Dec 2025 03:51:45.199 # +monitor master mymaster 192.168.244.130 6379 quorum 2
1:X 09 Dec 2025 03:51:55.216 * +convert-to-slave slave 172.21.0.1:6379 172.21.0.1 6379 @ mymaster 192.168.244.130 6379
1:X 09 Dec 2025 03:52:01.507 * +fix-slave-config slave 172.21.0.6:6379 172.21.0.6 6379 @ mymaster 192.168.244.130 6379
1:X 09 Dec 2025 03:52:01.507 * +fix-slave-config slave 172.21.0.7:6379 172.21.0.7 6379 @ mymaster 192.168.244.130 6379

通过 redis-cli 客户端检查各个节点的情况

1、基本命令

(1)查看哨兵监控的主节点信息

docker exec redis-sentinel1 redis-cli -p 26379 SENTINEL masters

(2)查看指定主节点下的从节点列表

docker exec redis-sentinel1 redis-cli -p 26379 SENTINEL slaves <master-name>

(3)查看当前监控该主节点的 Sentinel 节点列表(同一个主节点可能被多个 Sentinel 监控)

docker exec redis-sentinel1 redis-cli -p 26379 SENTINEL sentinels <master-name>

(4)查看哨兵提供的主节点对外的IP地址和端口

docker exec <sentinel-name> redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
docker exec redis-sentinel1 redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

返回的是主节点的对外IP和端口,例如下面这样才是正确的,这样才能让其他服务找到主节点

192.168.244.130
6379

返回的错误示例如下,IP是docker网络的IP,则将会导致其他服务器上的服务找不到主节点,无法解析这个IP

172.20.0.3
6379

注意点:这里返回的IP必须是宿主机的IP地址,这样开发环境才能正确的获取到主节点,否则开发环境将会无法连接到主节点上,其他服务器也是同理

(6)查询节点的信息

# 通过redis-cli客户端进行查询
redis-cli -h 192.168.244.130 -p 6379 info replication

# 主节点节点信息
role:master # 节点角色(主节点)
connected_slaves:2 # 从节点数量
slave0:ip=172.20.0.3,port=6379,state=online,offset=25203,lag=0
slave1:ip=172.20.0.4,port=6379,state=online,offset=24933,lag=0
master_failover_state:no-failover
master_replid:e0164bb431139668db4329b89535050599d46ae4
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25338
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25338

# 从节点信息
role:slave
master_host:redis-master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:41650
slave_repl_offset:41650
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:e0164bb431139668db4329b89535050599d46ae4
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:41650
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:713
repl_backlog_histlen:40938

2、测试

测试哨兵模式是否生效,关闭现在的主节点,通过命令查看主节点是否有被切换,如果被切换说明没有问题

docker stop redis-master

3、踩坑

(1)不在同一个docker网络下的服务访问不到哨兵节点

检查哨兵节点的配置文件sentinel.conf,其中的IP设置是否正确

(2)本地开发环境无法访问到哨兵节点返回的主节节点,例如如下报错

Unable to connect to 172.20.0.5/<unresolved>:6379

这种情况是因为 配置文件 redis.conf没有配置如下参数,或者参数配置错误

# 让哨兵通过这个IP和端口找到宿主机并且返回给其他需要redis的服务
replica-announce-ip 192.168.244.130
replica-announce-port 6379

(3)更新了配置文件却还是不生效,出现以上两个问题

这是由于sentinel.conf 有残留的缓存引发的,因为哨兵节点在运行时会更改这个配置文件,会有自动生成的配置,需要将这个配置文件恢复为最初的样子并保留自己的配置,并将 data 目录下产生的数据文件全部清理,再重新启动集群就可以解决

五、整合springboot

使用springboot的版本为3.5.7

1、pom.xml

pom文件依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、application.yml

在配置文件中添加如下配置,更多配置请自行查询

spring:
data:
redis:
sentinel:
# 主节点名称
master: mymaster
# 哨兵节点
nodes:
192.168.244.130:26379
192.168.244.130:26380
192.168.244.131:26379

3、反序列化配置

RedisConfig.java

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

StringRedisSerializer stringSerializer = new StringRedisSerializer();

// 推荐写法:直接传入 ObjectMapper
ObjectMapper mapper = new ObjectMapper();
mapper.findAndRegisterModules();
mapper.activateDefaultTyping(
mapper.getPolymorphicTypeValidator(),
ObjectMapper.DefaultTyping.NON_FINAL
);

GenericJackson2JsonRedisSerializer jsonSerializer =
new GenericJackson2JsonRedisSerializer(mapper);

// 设置 key & value 序列化方式
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);

template.afterPropertiesSet();
return template;
}
}

4、测试

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class RedisBootDemoApplicationTests {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Test
void contextLoads() {

}

@Test
void setValue() {
redisTemplate.opsForValue().set("test01", "hello redis-sentinel");
}

@Test
void getValue() {
String test01 = (String) redisTemplate.opsForValue().get("test01");
System.out.println("获取值: " + test01);
}
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » Redis哨兵模式(基于docker,多服务器)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!