一、docker-compose介绍
在上一讲中,我们已经介绍了docker的基本操作。这就是为后面的redis集群和mysql集群做准备。但在生产环境中,我们通常使用更规范的docker-compose。
1.1 docker-compose的介绍
Docker Compose是Docker官方提供的一个工具,它可以用于定义和运行多个Docker容器应用程序。使用Docker Compose,你可以通过一个YAML文件来定义你的应用程序所需要的服务、网络和存储等资源,然后使用一个命令来启动、停止和重启你的应用程序。可以帮助开发人员更加轻松地管理和运行多个Docker容器应用程序。它提供了简单的配置、多容器管理、方便的操作和环境隔离等特点,可以大大提高开发和部署的效率。
1.2 docker-compose的安装
sudo apt-get update
sudo apt-get install docker-compose-plugin
1.3 docker-compose的基础语法
对于基础语法的学习,那一定是先去官网
我们常用的docker-compose配置主要是3个代码段,services,volumes,networks
1.3.1 version 代码段
该代码段定义Docker Compose文件版本号。关于docker-compose的各版本。关于各版本的信息,可以看官网。
1.3.2 services 代码段
定义应用程序的各个服务,每个服务都可以包含以下几个参数:
- build:构建镜像的上下文路径和Dockerfile路径。
- image:使用的镜像名称。
- volumes:定义容器与主机之间的共享目录。
- networks: 定于服务的网络
该字段相当于docker-cli中的docker connect 网络名:容器名 - ports:定义容器的端口映射。
- environment:定义容器中的环境变量。
- depends_on:定义容器之间的依赖关系。
1.3.3 networks 代码段
定义容器之间的网络,每个网络可以包含以下几个参数:
- driver:指定网络驱动程序。
- name:指定网络的名称。
- external:指定网络是否是外部网络。
1.3.4 volumes代码段
定义数据卷,用于在容器之间共享数据。
1.3.5 restart代码段
定义容器的重启策略。
1.3.6 environment代码段
定义全局环境变量。
1.3.7 secrets代码段
定义使用Docker Secrets机制的密钥和证书文件。
二、redis主从配置
2.1 redis主从读写分离的介绍
随着访问量增加,redis可能会变慢。但大多数情况下,redis缓存是查询量高,更新量低。那么,读写分离的方案就呼之欲出。
所谓读写分离,也叫redis的主从模式,一个主节点,只负责插入更新,然后再自动更新到从服务器,从服务器只负责查找。
redis主从复制逻辑大概是这样的:
- 从节点连接主节点并发送SYNC命令,请求同步数据。
- 主节点接收到SYNC命令后,执行BGSAVE命令生成RDB快照文件,并将RDB文件传输给从节点。
- 主节点将写操作记录到内存中的数据库,并将这些操作缓存到内存中的复制缓冲区中。
- 主节点将复制缓冲区中的写操作发送给从节点,并等待从节点的ACK确认。
- 从节点接收到主节点发送的复制缓冲区中的写操作后进行执行,并返回ACK确认。
- 当从节点接收到RDB文件时,首先会清空当前的数据,然后将RDB文件中的数据加载到自己的数据库中。
- 从节点将主节点发送的复制缓冲区中的写操作和RDB文件中的数据进行合并,并将合并后的数据持久化到磁盘中的AOF文件中。
8.从节点通过与主节点保持长连接,接收主节点发送的复制缓冲区中的写操作。
总结起来,就是连接时全量复制,之后主节点每次写入命令,都做增量执行。
2.2 文件夹准备
cd ~/DOCUMENTS/docker/redis
mkdir compose-master-slave
cd compose-master-slave
mkdir master/config master/data s1/config s1/data s2/config s2/data s3/config s3/data
2.3 配置文件修改
和上一期讲的一样,先去官网下载redis的配置文件
然后做如下修改:
logfile "/data/redis-log.log" #log文件存放位置
dir "/data/redis" #指定dump.rdb路径
dbfilename dump.rdb
requirepass 密码
对于从服务器,还需要做如下修改(主服务器不需要改)
replicaof master 6379
masterauth 密码
replica-read-only yes
2.4 docker-compose文件
version: "3.9"
name: "redis-master-slave"
services:
master:
image: redis:7-alpine
ports:
- 6370:6379
command: redis-server /etc/redis/redis.conf #容器启动后,运行的命令
volumes:
- ./master/data:/data
- ./master/config/redis.conf:/etc/redis/redis.conf
networks:
redis-master-slave:
aliases:
- master
#restart: always
slaver-1:
image: redis:7-alpine
ports:
- 6378:6379
command: redis-server /etc/redis/redis.conf --slaveof master 6379 #容器启动后,运行的命令
volumes:
- ./s1/data:/data
- ./s1/config/redis.conf:/etc/redis/redis.conf
networks:
redis-master-slave:
aliases:
- s1
#restart: always
depends_on:
- master
slaver-2:
image: redis:7-alpine
ports:
- 6377:6379
command: redis-server /etc/redis/redis.conf --slaveof master 6379 #容器启动后,运行的命令
volumes:
- ./s2/data:/data
- ./s2/config/redis.conf:/etc/redis/redis.conf
networks:
redis-master-slave:
aliases:
- s2
#restart: always
depends_on:
- master
slaver-3:
image: redis:7-alpine
ports:
- 6376:6379
command: redis-server /etc/redis/redis.conf --slaveof master 6379 #容器启动后,运行的命令
volumes:
- ./s3/data:/data
- ./s3/config/redis.conf:/etc/redis/redis.conf
networks:
redis-master-slave:
aliases:
- s3
#restart: always
depends_on:
- master
networks:
redis-master-slave:
2.5 运行容器
cd ~/DOCUMENTS/docker/redis/compose-master-slave
sudo docker compose -f redis-cpmpose.yml up -d
2.6 redis的可执行文件位置
在docker-compose文件中,我们看到有这样一段代码:
command: redis-server /etc/redis/redis.conf #容器启动后,运行的命令
那redis-server 放在容器的哪里呢,还有哪些其他的命令呢?
sudo docker exec -it 1c263f059068 sh
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd /usr/local/bin
ls
docker-entrypoint.sh redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
我们可以看到,/usr/local/bin是实际redis-server的存放路径。其中还有一个重要的东西,就是redis-sentinel
三、redis哨兵配置
3.1 多主多从的需求
看过上面的介绍,我们自然会想到,有1主多从来解决查询能力的横向拓展,那我们能不能把主服务器也做个集群。一方面,主服务器做集群意味着我们可以存更多的内容;另一方面,这也提高了可用性,一台主服务器挂了,其他主服务器还能运行。
于是问题就来了,主服务器集群,我如何决定一个key到底存在哪个服务器上呢?
3.2 Redis Cluster(集群)
3.2.1 文件夹和文件准备
cd ~/DOCUMENTS/docker/redis
mkdir compose-cluster
cd compose-cluster/
mkdir node1 node2 node3 node4 node5 node6 node1/data node1/config node2/data node2/config node3/data node3/config
node4/data node4/config node5/data node5/config node6/data node6/config
redis配置文件如下:
bind 0.0.0.0
port 6379
dir /data
logfile ./redis.log
requirepass ilv0404@1314
masterauth ilv0404@1314
protected-mode yes
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.149.132
cluster-announce-port 6001
cluster-announce-bus-port 16001
注意:cluster-announce-ip,cluster-announce-port,cluster-announce-bus-port是申明的公网的地址、端口和bus端口。在这里就是wsl的eth0。
而后和2.3一样,把redis的配置文件拷到对应位置,并做相应修改
3.2.2 docker-compose
version: '3.9'
name: "redis-cluster"
services:
node1:
image: redis:7-alpine
ports:
- 6001:6379
- 16001:16379
command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
volumes:
- ./node1/data:/data
- ./node1/config/redis.conf:/etc/redis/redis.conf
networks:
redis-cluster:
ipv4_address: 172.20.0.31
aliases:
- node1
node2:
image: redis:7-alpine
ports:
- 6002:6379
- 16002:16379
command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
volumes:
- ./node2/data:/data
- ./node2/config/redis.conf:/etc/redis/redis.conf
networks:
redis-cluster:
ipv4_address: 172.20.0.32
aliases:
- node2
node3:
image: redis:7-alpine
ports:
- 6003:6379
- 16003:16379
command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
volumes:
- ./node3/data:/data
- ./node3/config/redis.conf:/etc/redis/redis.conf
networks:
redis-cluster:
ipv4_address: 172.20.0.33
aliases:
- node3
node4:
image: redis:7-alpine
ports:
- 6004:6379
- 16004:16379
command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
volumes:
- ./node4/data:/data
- ./node4/config/redis.conf:/etc/redis/redis.conf
networks:
redis-cluster:
ipv4_address: 172.20.0.34
aliases:
- node4
node5:
image: redis:7-alpine
ports:
- 6005:6379
- 16005:16379
command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
volumes:
- ./node5/data:/data
- ./node5/config/redis.conf:/etc/redis/redis.conf
networks:
redis-cluster:
ipv4_address: 172.20.0.35
aliases:
- node5
node6:
image: redis:7-alpine
ports:
- 6006:6379
- 16006:16379
command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
volumes:
- ./node6/data:/data
- ./node6/config/redis.conf:/etc/redis/redis.conf
networks:
redis-cluster:
ipv4_address: 172.20.0.36
aliases:
- node6
redis-cluster-cli:
image: redis:7-alpine
command: redis-cli -a ilv0404@1314 --cluster create 192.168.149.132:6001 192.168.149.132:6002 192.168.149.132:6003 192.168.149.132:6004 192.168.149.132:6005 192.168.149.132:6006 --cluster-replicas 1 --cluster-yes
networks:
redis-cluster:
depends_on:
- node1
- node2
- node3
- node4
- node5
- node6
networks:
redis-cluster:
#driver: host
ipam:
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.1
注意,这里有一个坑。关于cluster-bus 的端口映射,如果redis本身端口设置的是6379的话,bus实际上就是16379。
还有一个坑,redis-cluster-cli中,不得用虚拟地址如node1:6379,不然集群无法创建。具体原因我还不知道。
3.2.3 启动并创建集群
首先先把data文件夹清空
sudo rm -rf node1/data/* node2/data/* node3/data/* node4/data/* node5/data/* node6/data/*
启动docker-compose
sudo docker compose -f redis-cluster-cpmpose.yml up -d
3.2.4 测试
redis-cli -c -p 6001 -a 密码
127.0.0.1:6001> set pig 1
-> Redirected to slot [13819] located at 172.20.0.33:6379
OK
172.20.0.33:6379> set cat 2
-> Redirected to slot [1075] located at 172.20.0.31:6379
OK
172.20.0.31:6379> set dog 3
OK
四、springboot的接入
其实关于SpringBoot的接入十分简单,只需要把yml中redis的相关配置改下就行了。
redis:
host: localhost
port: 6001
password: ilv0404@1314
# 连接超时时间(记得添加单位,Duration)
timeout: 10000ms
lettuce:
shutdown-timeout: 100 # 关闭超时时间
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
min-idle: 0 # 连接池中的最小空闲连接
cluster:
max-redirects: 3
nodes: localhost:6001,localhost:6002,localhost:6003,localhost:6004,localhost:6005,localhost:6006
注意,我这里没有用虚拟机,linux机是windows的wsl,所以写的localhost
4.1 redisson连接redis-cluster时的jar包异常
这里还有个大坑,我查了好半天才解决。
在Idea中运行,redis的操作毫无问题,但jar包部署时,则会报一个类无法找到的BUG
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/zset/Tuple
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1082)
......
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/zset/Tuple
at org.redisson.spring.data.connection.RedissonConnection.<clinit>(RedissonConnection.java:2369)
at org.redisson.spring.data.connection.RedissonConnectionFactory.getConnection(RedissonConnectionFactory.java:11
1)
at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:193)
......
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:236)
at indi.zhifa.recipe.bailan.framework.redis.util.RedisUtil.set(RedisUtil.java:146)
{
"code": 500,
"data": null,
"message": "发生未知错误 org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.redisson.spring.data.connection.RedissonClusterConnection"
}
解决方法呢,按如下配置pom
<!-- *****************redisson*************************-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-30</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 我的SpringBoot用的是2.4。redisson.version是3.20.0-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-24</artifactId>
<version>${redisson.version}</version>
</dependency>
<!-- 这个引用是关键中的关键,redisson高版本把这个包搞掉了,还得再引用回来-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>