Redis集群选举算法决定了一个Redis集群至少需要3个master,同时每个master节点至少需要一个slave节点做备份,因此一个Redis集群至少需要6个节点。本文也正是在一台宿主机上使用docker构建一个有6个节点的Redis集群。
Redis使用槽(slot)指派的方式来进行数据在节点上的分配。一个Redis集群总共有16384(2^14)个槽,假设有3个master,平均每个master负责16384/3=5461个槽。key分配的算法是:对key进行CRC16算法,然后跟16384取模,得到的就是落在哪个槽位,然后根据槽在节点上的分配关系,最终得到这个key落在哪个节点的哪个槽上。槽公式如下:slot = CRC16(key) & 16383。
宿主机(本文操作系统为MacOS 11.6,不同系统上docker安装和使用会有不同)
git
Homebrew
envsubst
docker
Mac安装git:
参考文章:Mac安装Git_rockvine的博客-CSDN博客_mac安装git
建议直接去Git官网下载官方安装包installer进行安装,比较方便。
Mac安装Homebrew:
参考文章:Mac安装brew_@单空的博客-CSDN博客_mac 安装brew
Mac安装envsubst:
参考文章:Mac 下安装 envsubst 命令 | 言曌博客
两行命令即可:
brew install gettext
brew link --force gettext
Mac安装docker
参考文章:MacOS Docker 安装 | 菜鸟教程
笔者选择的是手动下载安装Install Docker Desktop on Mac。
这里贴一下Redis的docker hub地址,可以看到Redis各个版本的镜像:Docker Hub
先找出本机的IP:192.168.31.230,后面会用到。
由于我在本机配置了6个服务,所以需要6份配置文件,为了提高效率,可以采用批量创建模版redis-cluster.tmpl
port ${PORT}
requirepass 1234
masterauth 1234
protected-mode no
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-announce-ip 192.168.31.230
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
执行脚本文件redis-cluster.sh
#!/bin/bash
for port in `seq 6371 6376`; do \
mkdir -p ${port}/conf \
&& PORT=${port} envsubst < redis-cluster.tmpl > ${port}/conf/redis.conf \
&& mkdir -p ${port}/data;\
done
port:节点端口;
requirepass:添加访问认证;
masterauth:如果主节点开启了访问认证,从节点访问主节点需要认证;
protected-mode:保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
daemonize:是否以守护线程的方式启动(后台启动),默认 no;
appendonly:是否开启 AOF 持久化模式,默认 no;
cluster-enabled:是否开启集群模式,默认 no;
cluster-config-file:集群节点信息文件;
cluster-node-timeout:集群节点连接超时时间;
cluster-announce-ip:集群节点 IP,填写宿主机的 IP;
cluster-announce-port:集群节点映射端口;
cluster-announce-bus-port:集群节点总线端口。
每个Redis节点都需要建立两个TCP连接,一个用于为客户端提供服务的正常Redis TCP端口,例如 6379。还有一个基于6379端口加10000的端口,比如 16379。 第二个端口用于集群总线,节点使用集群总线进行故障检测、配置更新、故障转移授权等等。客户端一般只需要与Redis命令端口6379通信即可,但是请确保防火墙中的这两个端口都已经打开,否则Redis集群节点将无法通信。
这里我们使用docker compose来编排服务。
version: '3'
services:
redis-cluster-01:
image: redis:7.0.0
container_name: redis-cluster-01
environment:
TZ: Asia/Shanghai
networks:
- default
ports:
- 6371:6371
- 16371:16371
volumes:
- /Users/mucheng/Documents/docker/redis-docker/6371/conf:/etc/redis
- /Users/mucheng/Documents/docker/redis-docker/6371/data:/data
command: ["redis-server","/etc/redis/redis.conf"]
redis-cluster-02:
image: redis:7.0.0
container_name: redis-cluster-02
environment:
TZ: Asia/Shanghai
networks:
- default
ports:
- 6372:6372
- 16372:16372
volumes:
- /Users/mucheng/Documents/docker/redis-docker/6372/conf:/etc/redis
- /Users/mucheng/Documents/docker/redis-docker/6372/data:/data
command: ["redis-server","/etc/redis/redis.conf"]
redis-cluster-03:
image: redis:7.0.0
container_name: redis-cluster-03
environment:
TZ: Asia/Shanghai
networks:
- default
ports:
- 6373:6373
- 16373:16373
volumes:
- /Users/mucheng/Documents/docker/redis-docker/6373/conf:/etc/redis
- /Users/mucheng/Documents/docker/redis-docker/6373/data:/data
command: ["redis-server","/etc/redis/redis.conf"]
redis-cluster-04:
image: redis:7.0.0
container_name: redis-cluster-04
environment:
TZ: Asia/Shanghai
networks:
- default
ports:
- 6374:6374
- 16374:16374
volumes:
- /Users/mucheng/Documents/docker/redis-docker/6374/conf:/etc/redis
- /Users/mucheng/Documents/docker/redis-docker/6374/data:/data
command: ["redis-server","/etc/redis/redis.conf"]
redis-cluster-05:
image: redis:7.0.0
container_name: redis-cluster-05
environment:
TZ: Asia/Shanghai
networks:
- default
ports:
- 6375:6375
- 16375:16375
volumes:
- /Users/mucheng/Documents/docker/redis-docker/6375/conf:/etc/redis
- /Users/mucheng/Documents/docker/redis-docker/6375/data:/data
command: ["redis-server","/etc/redis/redis.conf"]
redis-cluster-06:
image: redis:7.0.0
container_name: redis-cluster-06
environment:
TZ: Asia/Shanghai
networks:
- default
ports:
- 6376:6376
- 16376:16376
volumes:
- /Users/mucheng/Documents/docker/redis-docker/6376/conf:/etc/redis
- /Users/mucheng/Documents/docker/redis-docker/6376/data:/data
command: ["redis-server","/etc/redis/redis.conf"]
networks:
default:
external:
name: mynet
这里对compose脚本中几个比较重要的内容做个说明:
image:
docker镜像名,这里使用标签为7.0.0版本的镜像;
networks:
docker容器网络,跟宿主机网络做映射。在某些场景下可用于容器互联,和跨宿主机访问。
这里我们提前创建了一个名为mynet的自定义网络。
关于建立docker网络,参考这篇文章:如何创建Docker中的网络_Hemi Fate的博客-CSDN博客_docker 创建网络
ports:
宿主机端口和容器端口的映射,格式为<宿主机端口>:<容器端口>
command:
启动容器时执行的命令。
volumes:
数据卷,用于宿主机目录或文件跟容器目录做映射。Redis的配置文件就是放在宿主机目录上,然后在容器启动时加载到容器目录中。格式为<宿主机目录绝对路径>:<容器目录>,这里最好配置宿主机目录的绝对路径,不然容易出问题。
最终我的文件目录如下:
切换到redis-docker目录下,执行docker-compose up -d
这里如果执行失败,需要检查一下:
需要在Preferences里把这个勾去掉(是去掉,不是勾上),否则可能会出现宿主机容器卷里的配置文件到了容器里变成了目录。
具体原因参考这篇文章:文件系统共享(osxfs) - Docker 中文文档 - 文江博客
到目前为止,6个容器都已经启动了,可以在docker桌面上查看:
但这个时候这些容器都是相互独立的,没有构成集群。
构造集群分三步:
节点之间通过握手建立连接;
进行槽分配;
指定节点主从关系;
构造集群的方式有两种:
一种是自动创建法,另一种是手动创建法。简便起见,本文采用自动创建法。
# 选择一个容器节点进入
docker exec -it redis-cluster-01 /bin/sh
输入下面命令创建集群(由于我们只有6个节点,所以3主3从,每个主节点只有一个副本)
redis-cli -a 1234 --cluster create 192.168.31.230:6371 192.168.31.230:6372 192.168.31.230:6373 192.168.31.230:6374 192.168.31.230:6375 192.168.31.230:6376 --cluster-replicas 1
这里可能会创建失败:
原因是本地容器卷没有建在一个共享目录下,容器启动时可能无权限加载容器卷中的配置文件redis.conf。
按照提示,找到当前可以挂载的目录:
可以选择当前已经设置共享的目录,也可以自己添加共享目录。这里选择/Users,把之前的redis-docker整体复制到新目录下。
改完之后记得修改一下数据卷的绝对路径为新路径。
然后重新创建集群,出现如下信息,说明集群创建成功。
这里可以看到槽分配过程以及节点主从关系。
使用刚刚的节点登录到集群:
redis-cli -c -a 1234 -h 192.168.31.230 -p 6371
查看集群状态
集群状态:cluster info;集群节点查询:cluster nodes;
做个简单的测试:
【欢迎关注我的公众号】