Docker Compose搭建Redis一主二从三哨兵高可用集群
相关内容原文地址:
博客园:当我遇上你csy:Docker实战之Redis-Cluster集群
博客园:陈大帅哥Ray:Docker Compose搭建Redis一主二从三哨兵高可用集群
集群方案对比
主从复制
同Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,读写分离。一个Master可以有多个Slaves。
优点:
- 数据备份
- 读写分离,提供服务器性能
缺点: - 不能自动故障恢复,RedisHA系统(需要开发)
- 无法实现动态扩容
哨兵机制
Redis Sentinel是社区版本推出的原生高可用解决方案,其部署架构主要包括两部分:Redis Sentinel集群和Redis数据集群。
其中Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知。Redis Sentinel的节点数量要满足2n+1(n>=1)的奇数个。
优点:
- 自动化故障恢复
缺点: - Redis 数据节点中 slave 节点作为备份节点不提供服务
- 无法实现动态扩容
一、Redis-Cluster
Redis Cluster是社区版推出的Redis分布式集群解决方案,主要解决Redis分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster能起到很好的负载均衡的目的。
Redis Cluster着眼于提高并发量。
群集至少需要3主3从,且每个实例使用不同的配置文件。
在redis-cluster架构中,redis-master节点一般用于接收读写,而redis-slave节点则一般只用于备份, 其与对应的master拥有相同的slot集合,若某个redis-master意外失效,则再将其对应的slave进行升级为临时redis-master。
在redis的官方文档中,对redis-cluster架构上,有这样的说明:在cluster架构下,默认的,一般redis-master用于接收读写,而redis-slave则用于备份,当有请求是在向slave发起时,会直接重定向到对应key所在的master来处理。 但如果不介意读取的是redis-cluster中有可能过期的数据并且对写请求不感兴趣时,则亦可通过readonly命令,将slave设置成可读,然后通过slave获取相关的key,达到读写分离。
-
优点:
- 解决分布式负载均衡的问题。具体解决方案是分片/虚拟槽slot。
- 可实现动态扩容
- P2P模式,无中心化
-
缺点
- 为了性能提升,客户端需要缓存路由表信息
- Slave在集群中充当“冷备”,不能缓解读压力
通过Docker来搭建Redis-Cluster集群,只需要分配6个不同端端口即可。
ip | port |
---|---|
192.168.124.5 | 7001 |
192.168.124.5 | 7002 |
192.168.124.5 | 7003 |
192.168.124.5 | 7004 |
192.168.124.5 | 7005 |
192.168.124.5 | 7006 |
1.1 Redis配置文件
redis-cluster.tmpl:
# redis端口
port ${PORT}
# 关闭保护模式
protected-mode no
# 开启集群
cluster-enabled yes
# 集群节点配置
cluster-config-file nodes.conf
# 超时
cluster-node-timeout 5000
# 集群节点IP host模式为宿主机IP
cluster-announce-ip 192.168.124.5
# 集群节点端口 7001 - 7006
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
# 开启 appendonly 备份模式
appendonly yes
# 每秒钟备份
appendfsync everysec
# 对aof文件进行压缩时,是否执行同步操作
no-appendfsync-on-rewrite no
# 当目前aof文件大小超过上一次重写时的aof文件大小的100%时会再次进行重写
auto-aof-rewrite-percentage 100
# 重写前AOF文件的大小最小值 默认 64mb
auto-aof-rewrite-min-size 64mb
由于节点IP相同,只有端口上的差别,现在通过脚本 redis-cluster-config.sh 批量生成配置文件。
for port in `seq 7001 7006`; do
mkdir -p ./redis-cluster/${port}/conf
&& PORT=${port} envsubst < ./redis-cluster.tmpl > ./redis-cluster/${port}/conf/redis.conf
&& mkdir -p ./redis-cluster/${port}/data;
done
生成的配置文件如下图:
二、Docker Compose编排服务
version: '3.7'
services:
redis7001:
image: 'redis'
container_name: redis7001
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-cluster/7001/conf/redis.conf:/usr/local/etc/redis/redis.conf
- ./redis-cluster/7001/data:/data
ports:
- "7001:7001"
- "17001:17001"
environment:
# 设置时区为上海,否则时间会有问题
- TZ=Asia/Shanghai
redis7002:
image: 'redis'
container_name: redis7002
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-cluster/7002/conf/redis.conf:/usr/local/etc/redis/redis.conf
- ./redis-cluster/7002/data:/data
ports:
- "7002:7002"
- "17002:17002"
environment:
# 设置时区为上海,否则时间会有问题
- TZ=Asia/Shanghai
redis7003:
image: 'redis'
container_name: redis7003
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-cluster/7003/conf/redis.conf:/usr/local/etc/redis/redis.conf
- ./redis-cluster/7003/data:/data
ports:
- "7003:7003"
- "17003:17003"
environment:
# 设置时区为上海,否则时间会有问题
- TZ=Asia/Shanghai
redis7004:
image: 'redis'
container_name: redis7004
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-cluster/7004/conf/redis.conf:/usr/local/etc/redis/redis.conf
- ./redis-cluster/7004/data:/data
ports:
- "7004:7004"
- "17004:17004"
environment:
# 设置时区为上海,否则时间会有问题
- TZ=Asia/Shanghai
redis7005:
image: 'redis'
container_name: redis7005
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-cluster/7005/conf/redis.conf:/usr/local/etc/redis/redis.conf
- ./redis-cluster/7005/data:/data
ports:
- "7005:7005"
- "17005:17005"
environment:
# 设置时区为上海,否则时间会有问题
- TZ=Asia/Shanghai
redis7006:
image: 'redis'
container_name: redis7006
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-cluster/7006/conf/redis.conf:/usr/local/etc/redis/redis.conf
- ./redis-cluster/7006/data:/data
ports:
- "7006:7006"
- "17006:17006"
environment:
# 设置时区为上海,否则时间会有问题
- TZ=Asia/Shanghai
启动结果如图:
三、集群配置
docker exec -it redis7001 redis-cli -p 7001 -a 123456 --cluster create 192.168.124.5:7001 192.168.124.5:7002 192.168.124.5:7003 192.168.124.5:7004 192.168.124.5:7005 192.168.124.5:7006 --cluster-replicas 1
看到如下结果说明集群配置成功:
四、集群测试
4.1 查看集群通信是否正常
redis7001主节点对它的副本节点redis7005进行ping操作。
-h host -p port -a pwd
docker docker exec -it redis7001 redis-cli -h 192.168.124.5 -p 7005 -a 123456 ping
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
PONG
4.2 测试简单存储
redis7001主节点客户端操作redis7003主节点。
docker docker exec -it redis7001 redis-cli -h 192.168.124.5 -p 7003 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.124.5:7003> set name admin
(error) MOVED 5798 192.168.124.5:7002
由于Redis Cluster会根据key进行hash运算,然后将key分散到不同slots,name的hash运算结果在redis7002节点上的slots中。所以我们操作redis7003写操作会自动路由到7002。然而error提示无法路由?没关系,差一个 -c 参数而已。
再次运行查看结果如下:
docker docker exec -it redis7001 redis-cli -h 192.168.124.5 -p 7003 -a 123456 -c
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.124.5:7003> set name admin
-> Redirected to slot [5798] located at 192.168.124.5:7002
OK
192.168.124.5:7002> get name
"admin"
192.168.124.5:7002>
4.3 查看集群状态
4.4 查看slots分片
4.5 查看集群信息
4.6 测试读写分离
在redis cluster中,如果你要在slave读取数据,那么需要带先执行 readonly 指令,然后 get key。
4.7 压测
选项 | 描述 |
---|---|
-t | 指定命令 |
-c | 客户端连接数 |
-n | 总请求数 |
-d | set、get的value大小(单位byte) |
测试如下:
docker docker exec -it redis7001 bash
root@cbc6e76a3ed2:/data# redis-benchmark -h 192.168.124.5 -p 7001 -t set -c 100 -n 50000 -d 20
====== SET ======
50000 requests completed in 10.65 seconds
100 parallel clients
20 bytes payload
keep alive: 1
0.00% <= 2 milliseconds
0.01% <= 3 milliseconds
...
100.00% <= 48 milliseconds
100.00% <= 49 milliseconds
4692.63 requests per second
4.8 容灾测试
杀掉主节点redis7001,看从节点redis7005是否会接替它的位置。
docker stop redis7001
再试着启动7001,它将自动作为slave挂载到7005:
五、SpringBoot配置Redis集群
在SpringBoot2.x版本中,redis默认的连接池已经更换为Lettuce,而不再是jedis。
5.1 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
5.2 配置application.yml
spring:
redis:
timeout: 6000
password: 123456
cluster:
max-redirects: 3 # 获取失败 最大重定向次数
nodes:
- 192.168.124.5:7001
- 192.168.124.5:7002
- 192.168.124.5:7003
- 192.168.124.5:7004
- 192.168.124.5:7005
- 192.168.124.5:7006
lettuce:
pool:
max-active: 1000 #连接池最大连接数(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
cache:
jcache:
config: classpath:ehcache.xml
5.3 redis配置
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
5.4 测试
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Test
public void test() {
redisTemplate.opsForValue().set("name", "admin");
String name = redisTemplate.opsForValue().get("name");
System.out.println(name); //输出admin
}
}
六、Docker Compose搭建Redis主从服务器
主从复制:主节点负责写数据,从节点负责读数据,主节点定期把数据同步到从节点保证数据的一致性。
从性能方面,redis复制功能增加了读的性能,理论上说,每增加一倍的服务器,整个系统的读能力就增加一倍。
选择好路径,通过命令创建文件夹redis,然后进入文件夹创建docker-compose.yml,或者在本地创建,然后通过Xftp工具上传至服务器。docker-compose.yml写入如下内容。
version: '2'
services:
master:
image: redis ## 镜像
container_name: redis-master ##容器别名
command: redis-server --requirepass 123456 ##redis密码
ports:
- "6379:6379" ##暴露端口
networks:
- sentinel-master
slave1:
image: redis
container_name: redis-slave-1
ports:
- "6380:6379"
command: redis-server --slaveof redis-master 6379 --requirepass 123456 --masterauth 123456
depends_on:
- master
networks:
- sentinel-master
slave2:
image: redis
container_name: redis-slave-2
ports:
- "6381:6379"
command: redis-server --slaveof redis-master 6379 --requirepass 123456 --masterauth 123456
depends_on:
- master
networks:
- sentinel-master
启动redis集群:
docker-compose up -d
docker ps查看运行的实例,这里我们需要用到主节点的ip地址,注意不是电脑的ip,是节点的ip地址。
docker inspect 主节点容器id。
七、哨兵sentinel模式搭建
哨兵需要绑定这个主节点的ip。
当主服务器宕机后,需要手动把一台服务器切换成主服务器,这里需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵是redis高可用的解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器以及这些主服务器属下的所有从服务器,它能够在被监视的主服务器下线时,自动将该主服务器属下的某个优先的从服务器升级为新的主服务器,由这个主服务器代替已下线的主服务器继续处理命令请求。
- 哨兵的功能:
- 监控:哨兵会不断地监控检测主节点和从节点是否运作正常。
- 自动故障转移:当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并 让
- 其他从节点改为复制新的主节点。
- 通知:哨兵可以将故障转移的结果发送给客户端。
- 配置提供者:客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
创建sentinel文件夹,然后同样编写docker-compose.yml文件:
version: '2'
services:
sentinel1:
image: redis ## 镜像
container_name: redis-sentinel-1
ports:
- "26379:26379"
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- "./sentinel.conf:/usr/local/etc/redis/sentinel.conf"
sentinel2:
image: redis ## 镜像
container_name: redis-sentinel-2
ports:
- "26380:26379"
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- "./sentinel2.conf:/usr/local/etc/redis/sentinel.conf"
sentinel3:
image: redis ## 镜像
container_name: redis-sentinel-3
ports:
- "26381:26379"
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf
networks:
default:
external:
name: redis_sentinel-master
继续在此目录编写文件,编写sentinel.conf文件:
port 26379
dir /tmp
#172.18.0.3填写自己的主节点ip
sentinel monitor mymaster 172.18.0.3 6379 2
sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 10000
sentinel deny-scripts-reconfig yes
第三行表示Redis监控一个叫做mymaster的运行在172.18.0.3:6379的master,投票达到2则表示master以及挂掉了。
第四行设置主节点的密码。
第五行表示在一段时间范围内sentinel向master发送的心跳PING没有回复则认为master不可用了。
第六行的parallel-syncs表示设置在故障转移之后,同时可以重新配置使用新master的slave的数量。数字越低,更多的时间将会用故障转移完成,但是如果slaves配置为服务旧数据,你可能不希望所有的slave同时重新同步master。因为主从复制对于slave是非阻塞的,当停止从master加载批量数据时有一个片刻延迟。通过设置选项为1,确信每次只有一个slave是不可到达的。
第七行表示10秒内mymaster还没活过来,则认为master宕机了。
创建好文件后,复制好三份。
cp sentinel.conf sentinel1.conf
cp sentinel.conf sentinel2.conf
cp sentinel.conf sentinel3.conf
启动redis哨兵模式:
docker-compose up -d
7.1 故障转移测试
哨兵可以自动转移故障,也就是当主节点宕机的时候,它们能通过选举制度,在从节点中选出一个节点来代替主节点。
故障测试:
进入容器,再进入redis客户端,再输入密码,然后使用info查看节点信息。
可以看到这个节点的身份是master也就是主节点,然后有两个连接着的从节点。
停掉这个节点,看看哨兵是否可以实现故障转移。
docker sotp <containerdId>
然后选择其中一个从节点进入客户端,输入info查看信息。
发现其中一台从节点已经变成主节点了,而且连接的从节点,也变成了一个。