前言
在开发环境中,大家应该都用的是redis单点吧,但是在生产环境中我相信没有人敢用单点的,应该都是用的cluster,因为万一单点挂掉的话,我们的应用也就自然而然的ConnectionException。那么,接下来我就记录一下我自己用docker搭建redis-cluster吧!
基本概念
每个Redis集群中的节点都需要打开两个TCP连接。一个连接用于正常的给Client提供服务,比如6379,还有一个额外的端口(通过在这个端口号上加10000)作为数据端口,比如16379。第二个端口(本例中就是16379)用于集群总线,这是一个用二进制协议的点对点通信信道。这个集群总线(Cluster bus)用于节点的失败侦测、配置更新、故障转移授权,等等。客户端从来都不应该尝试和这些集群总线端口通信,它们只应该和正常的Redis命令端口进行通信。注意,确保在你的防火墙中开放着两个端口,否则,Redis集群节点之间将无法通信。命令端口和集群总线端口的偏移量总是10000。
Redis集群不同一致性哈希,它用一种不同的分片形式,在这种形式中,每个key都是一个概念性(hash slot)的一部分。Redis集群中的每个节点负责一部分hash slots,并允许添加和删除集群节点。比如,如果你想增加一个新的节点D,那么久需要从A、B、C节点上删除一些hash slot给到D。同样地,如果你想从集群中删除节点A,那么会将A上面的hash slots移动到B和C,当节点A上是空的时候就可以将其从集群中完全删除。因为将hash slots从一个节点移动到另一个节点并不需要停止其它的操作,添加、删除节点以及更改节点所维护的hash slots的百分比都不需要任何停机时间。也就是说,移动hash slots是并行的,移动hash slots不会影响其它操作。
为了保持可用,Redis集群用一个master-slave模式,这样的话每个hash slot就有1到N个副本。而redis-cluster规定,至少需要3个master和3个slave,即3个master-slave对。当我们给每个master节点添加一个slave节点以后,我们的集群最终会变成由A、B、C三个master节点和A1、B1、C1三个slave节点组成,这个时候如果B失败了,系统仍然可用。节点B1是B的副本,如果B失败了,集群会将B1提升为新的master,从而继续提供服务。然而,如果B和B1同时失败了,那么整个集群将不可用。
Redis集群不能保证强一致性。换句话说,Redis集群可能会丢失一些写操作,原因是因为它用异步复制。为了使用redis-cluster,需要配置以下几个参数:
- cluster-enabled <yes/no>: 如果是yes,表示启用集群,否则以单例模式启动
- cluster-config-file <filename>: 可选,这不是一个用户可编辑的配置文件,这个文件是Redis集群节点自动持久化每次配置的改变,为了在启动的时候重新读取它。
- cluster-node-timeout <milliseconds>: 超时时间,集群节点不可用的最大时间。如果一个master节点不可到达超过了指定时间,则认为它失败了。注意,每一个在指定时间内不能到达大多数master节点的节点将停止接受查询请求。
- cluster-slave-validity-factor <factor>: 如果设置为0,则一个slave将总是尝试故障转移一个master。如果设置为一个正数,那么最大失去连接的时间是node timeout乘以这个factor。
- cluster-migration-barrier <count>: 一个master和slave保持连接的最小数量(即:最少与多少个slave保持连接),也就是说至少与其它多少slave保持连接的slave才有资格成为master。
- cluster-require-full-coverage <yes/no>: 如果设置为yes,这也是默认值,如果key space没有达到百分之多少时停止接受写请求。如果设置为no,将仍然接受查询请求,即使它只是请求部分key。
准备工具
一、安装docker
二、在docker库获取镜像:redis,ruby;下载redis-trib.rb(命令:wget http://download.redis.io/redis-stable/src/redis-trib.rb)
三、找到一份原始的redis.conf文件,将其重命名为:redis-cluster.tmpl,并配置如下几个参数,此文件的目的是生成每一个redis实例的redis.conf:
# bind 127.0.0.1
protected-mode no
port ${PORT}
daemonize no
dir /data/redis
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
四、利用ruby镜像构建一个redis-trib镜像,因为搭建redis-cluster的时候需要用到redis-trib工具。redis-trib镜像的Dockerfile如下(tips:我下载的ruby版本是:2.5-slim):
FROM ruby:2.5-slim MAINTAINER dongsilin<dslzc@foxmail.com> RUN gem install redis RUN mkdir /redis WORKDIR /redis ADD ./redis-trib.rb /redis/redis-trib.rb
通过命令(docker build -t redis-trib .),即可构建出redis-trib镜像,后续搭建redis-cluster的时候需要用到。
搭建
这里我准备的是2套环境:所有redis实例运行在同一台宿主机上;redis实例运行在不同的宿主机上。相信大家在生产环境中都应该是部署在不同的机器上,下面我将分别讲述:
一、所有redis实例运行在同一台宿主机上
- 由于此处所有redis实例运行在同一台宿主机,而redis-cluster之间需要通信,所以需要创建docker network
# 创建docker内部网络 docker network create redis-cluster-net
- 创建 master 和 slave 文件夹并生成配置文件,用于存放配置文件redis.conf以及redis数据
# 创建 master 和 slave 文件夹 for port in `seq 7000 7005`; do ms="master" if [ $port -ge 7003 ]; then ms="slave" fi mkdir -p ./$ms/$port/ && mkdir -p ./$ms/$port/data \ && PORT=$port envsubst < ./redis-cluster.tmpl > ./$ms/$port/redis.conf; done
- 运行docker redis 的 master 和 slave 实例
# 运行docker redis 的 master 和 slave 实例 for port in `seq 7000 7005`; do ms="master" if [ $port -ge 7003 ]; then ms="slave" fi docker run -d -p $port:$port -p 1$port:1$port \ -v $PWD/$ms/$port/redis.conf:/data/redis.conf \ -v $PWD/$ms/$port/data:/data/redis \ --restart always --name redis-$ms-$port --net redis-cluster-net \ redis redis-server /data/redis.conf; done
- 组装masters : slaves 节点参数
# 组装masters : slaves 节点参数 matches="" for port in `seq 7000 7005`; do ms="master" if [ $port -ge 7003 ]; then ms="slave" fi matches=$matches$(docker inspect --format '{{(index .NetworkSettings.Networks "redis-cluster-net").IPAddress}}' "redis-$ms-${port}"):${port}" "; done
- 创建docker-cluster,这里就用到了上面的redis-trib镜像
# 创建docker-cluster docker run -it --rm --net redis-cluster-net redis-trib ruby redis-trib.rb create --replicas 1 $matches
- 执行第5步的命令后,需要在接下来的console中输入“yes”,即可完成docker-cluster的搭建,如下图:
二、redis实例运行在不同的宿主机上
这里我将3个master实例运行在一台机(10.82.12.95)上,3个slave实例运行在另一台机器(10.82.12.98)上
- 在两台机器上分别创建文件夹
# 创建文件夹 for port in `seq 7000 7002`; do mkdir -p ./$port/ && mkdir -p ./$port/data \ && PORT=$port envsubst < ./redis-cluster.tmpl > ./$port/redis.conf; done
- 在两台机器上分别运行docker redis 实例,注意这里就没有使用docker network了,直接使用的是宿主机的host,原因是要在不同机器的docker容器中通信是很麻烦的,有兴趣的朋友可以看下相关文章
# 运行docker redis 实例 for port in `seq 7000 7002`; do docker run -d \ -v $PWD/$port/redis.conf:/data/redis.conf \ -v $PWD/$port/data:/data/redis \ --restart always --name redis-$port --net host \ redis redis-server /data/redis.conf; done
- 在任意一台机器上创建docker-cluster
# 创建docker-cluster docker run -it --rm redis-trib ruby redis-trib.rb create --replicas 1 10.82.12.95:7000 10.82.12.95:7001 10.82.12.95:7002 10.82.12.98:7000 10.82.12.98:7001 10.82.12.98:7002
- 执行第3步的命令后,需要在接下来的console中输入“yes”,即可完成docker-cluster的搭建
测试
执行命令:docker exec -it redis-7000 redis-cli -c -h 10.82.12.95 -p 7000,就进入了redi-cli界面,可以进行任何骚操作,比如:
注意Redirected to slot [xxxx] located at xxxx,证明了每个节点负责一部分hash slots。
结语
这篇文章需要有一定的linux、redis和docker基础的朋友才能看懂,不然看起来有些概念懵懵懂懂。现在docker越来越流行,相信不少的朋友都有涉足吧!我在搭建的工程中,开始使用的是RedisDesktopManager-v0.8.8.384,连接和get命令没问题,就是不能执行set命令,升级到0.9.3.817后倒是呢能够执行get和set命令,但是出现可以重复的现象,懵逼了好久。
经过测试,用程序代码操作是没有任何问题的,估计是RedisDesktopManager-0.9.3.817有bug,而RedisDesktopManager-v0.8.8.384不支持redis-cluster。最后,我想求一款好用的支持redis-cluster的redis操作工具,不知道朋友们有推荐吗?另外,文章有任何不恰当的地方,欢迎各位留言指正
参考:
Redis集群
docker redis4.0 集群(cluster)搭建