文章目录
写在前面
免责声明:自己也是docker新人,主要是学习和记录
是真的脑抽才会用Docker搭redis集群,homebrew安装的redis配多个配置文件应该也可以直接搭建,时间全花在搞Docker上了,后续有时间了搞一下另一种搭建方式再写一篇博客。
真心建议不要用MacOS搭服务,很痛苦,有时候觉得还不如Windows。开发的话还可以
一、环境准备
1.系统版本
本文演示的系统版本是MacOS 13。
2.确认Docker已安装
并且,这里本人用的不是Docker Desktop,而是colima,因此使用Docker之前记得colima start
启动Docker。
3.拉取Redis镜像
➜ ~ docker pull redis
Using default tag: latest
latest: Pulling from library/redis
d981f2c20c93: Pull complete
5b8f51f5c4bb: Pull complete
2d3d8bc9388e: Pull complete
849e9b1b24f1: Pull complete
6e2590bc72d8: Pull complete
bdd261b6469d: Pull complete
Digest: sha256:f9724694a0b97288d2255ff2b69642dfba7f34c8e41aaf0a59d33d10d8a42687
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
二、创建Docker容器网络
注意:这里有一点需要说清楚,这也是在这篇博客快要结束的时候才发现的。
如果你要搭建的Redis集群需要对外提供服务,那么可以进行这一步。而如果你只是需要搭建一个本地的Redis集群,供自己本地开发使用,即使用127.0.0.1来访问,那么就不需要这一步了。在下文中,会有多处描述这种区别,会用“需要对外提供服务”和“不需要对外提供服务”的简述来区分上述两种情况,读者根据情况做不同的配置即可。
如果 不需要对外提供服务, 本章节不需要配置。
1.创建虚拟网卡
➜ ~ docker network create myredis
d4bece55cbc015be462605755377faa4a15694d815eb39f9fe2f973f5d8154e1
2.查看Docker网卡信息
➜ ~ docker network ls
NETWORK ID NAME DRIVER SCOPE
3831cf1496c6 bridge bridge local
01f8f8a01a74 host host local
d4bece55cbc0 myredis bridge local
1fc0198954f8 none null local
3.查看Docker网络详细信息
4.补充(删除网卡信息、帮助命令)
docker network rm myredis #删除网卡命令 多个中间 空格隔开
docker network --help #显示可带参数等
三、编写配置文件
这里用一个脚本来生成多个配置文件,部分内容根据实际情况做更改
如果 需要对外提供服务,使用如下配置:
for port in $(seq 6379 6384);
do
mkdir -p /[你想设置的目录]/redis/node-${port}/conf
touch /[你想设置的目录]/redis/node-${port}/conf/redis.conf
docker volume create redis_${port};
cat << EOF > /[你想设置的目录]/redis/node-${port}/conf/redis.conf
port ${port}
requirepass 1234
bind 0.0.0.0
protected-mode no
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip [你的宿主机IP]
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
EOF
done
如果 不需要对外提供服务,可使用如下配置:
for port in $(seq 6379 6384);
do
mkdir -p /[你想设置的目录]/redis/node-${port}/conf
touch /[你想设置的目录]/redis/node-${port}/conf/redis.conf
docker volume create redis_${port};
cat << EOF > /[你想设置的目录]/redis/node-${port}/conf/redis.conf
port ${port}
requirepass 1234
bind 0.0.0.0
protected-mode no
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 127.0.0.1
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
EOF
done
将脚本写成脚本文件后执行,通过tree查看目录结构,目录结构如图
如果没有tree命令可以先安装tree(brew install tree
)
命令解释:
port:节点端口;
requirepass:设置密码,访问时需要验证
protected-mode:保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
daemonize:是否以守护线程的方式启动(后台启动),默认 no;
appendonly:是否开启 AOF 持久化模式,默认 no;
cluster-enabled:是否开启集群模式,默认 no;
cluster-config-file:集群节点信息文件;
cluster-node-timeout:集群节点连接超时时间;
cluster-announce-ip:集群节点 IP
注意: 如果你想要你的redis集群可以供外网访问,这里直接填 服务器的IP 地址即可
如若为了安全,只是在服务器内部进行访问,这里还需要做一些修改。
cluster-announce-port:集群节点映射端口;
cluster-announce-bus-port:集群节点总线端口。
坑一:redis 在官网上有说明为什么需要映射两个端口 :
四、创建并启动容器
1.启动Redis容器
使用如下shell脚本,一次性启动6个Redis容器:
如果 需要对外提供服务,且配置了docker虚拟网卡,使用如下配置:
for port in $(seq 6379 6384); do \
docker run -it -d -p ${port}:${port} -p 1${port}:1${port} \
--privileged=true -v /Users/sqs/Dev/redis/node-${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \
--privileged=true -v redis_${port}:/data \
--restart always --name redis-${port} --net myredis --sysctl net.core.somaxconn=1024 \
redis redis-server /usr/local/etc/redis/redis.conf
done
如果 不需要对外提供服务,可使用如下配置:
for port in $(seq 6379 6384); do \
docker run -it -d \
--privileged=true -v /Users/sqs/Dev/redis/node-${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \
--privileged=true -v redis_${port}:/data \
--restart always --name redis-${port} --net host \
redis redis-server /usr/local/etc/redis/redis.conf
done
这两个脚本的主要区别在于,第一脚本使用的端口映射,使用的网络模式是桥接模式;第二个脚本没有使用端口映射,使用的网络模式是host模式。
补充
如果使用的–net host网络模式,docker run命令中还做了端口映射的话,端口映射会被docker忽略,执行结果中会有相关提示,上面给出的脚本中已经去掉了端口映射的内容。
参数解释
-it:交互
-d:后台运行,容器启动完成后打印容器
–privileged:是否让docker 应用容器 获取宿主机root权限(特殊权限-)
-p :端口映射
-v:文件挂载
–sysctl参数来设置系统参数,通过这些参数来调整系统性能
–restart always:在容器退出时总是重启容器
–name :给容器取名
–net myredis :使用我们创建的虚拟网卡 (想详细了解,可以去看看Docker 网络方面知识)
如果 用的第一个脚本创建的容器,docker run成功后结果如下:
如果 用的第二个脚本创建的容器,docker run成功后结果如下:
坑二:关于脚本中为什么要创建Docker volume
在步骤三“编写配置文件”中可以看到,脚本中创建了docker volum,本质的原因就是在第四部创建并启动容器中,如果将宿主机的本地目录挂载到容器的/data
目录,会出现如下的问题,所以干脆又创建了docker volume使得/data
目录可以正常挂载,且能保证数据的持久化。
如果挂载的是宿主机本地的目录,执行脚本后出现如下错误:
查看了一下发现容器根本就没有起来
删除容器,重新执行脚本,发现容器没有正常运行:
容器的状态都是Restarting,这里真的是让人很郁闷了。
接下来使用docker logs -f [container id]
来查看容器启动日志,发现如下错误:
从报错信息大致来看是因为没有权限,至于具体是哪里没有权限,还真的是让我一头雾水。
网上大概查了一下,主要就是因为容器没有权限操作宿主机的目录。如果是Linux环境,好像是关闭SELINUX即可,但是我这里是Mac环境,无法验证这种说法。至于MacOS环境下,有博主说是因为SIP的原因,说是需要关闭SIP,但是MacOS中SIP还是很不建议关闭的,因此这个说法也就不去实际操作验证了,所以还需要另想它法。
因此,就选择了使用docker volume来做映射的方法,命令已经写在了步骤三的脚本中,同样的也是需要创建6个。
docker volume create redis_${port};
问题:volume中的数据如何访问?
创建了volume之后,首先通过如下命令可以查看其挂载点
docker volume inspect redis_6379
如图所示,可以看到其挂载的目录是在/var/lib/docker/volumes
下,但是在宿主机上,可以发现这个目录是不存在的。那么容器关闭后需要持久化的数据怎么办??
其实这里不用担心,虽然看不到这个目录,但是Redis的/data
目录中的数据确实是持久化到我们创建的volume中的。想要访问这个目录,可以使用如下命令:
docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
这里也是启动一个容器并进入其命令行(第一次执行需要下载debian的镜像),在其中就可以访问volume所在的目录了,并且也可以方便的其中的文件拷贝到宿主机的本地目录里。
参数解释:
-即使未连接也保持标准输入打开 + 分配伪 TTY
--privileged “给出了所有功能的容器。允许特殊情况下,像跑步泊坞窗”。
--pid定义使用主机 VM 命名空间。
debian要使用的实际映像。
nsenter一个debian 的工具来在不同的命名空间中运行程序
-t是目标PID
-m挂载提供的 PID 命名空间。
-u输入 Unix 时间共享 (UTS) 命名空间。
-n输入提供的 PID 网络命名空间。
-i输入提供的 PID IPC 命名空间。
2.创建Redis Cluster集群
如果 需要对外提供服务,且配置了docker虚拟网卡,使用如下步骤创建:
可以随意选择其中一个节点进入,创建Redis集群。这里以6379节点为例:
1.进入redis-6379容器
docker exec -it redis-6379 /bin/bash

2.创建集群
进入6379容器后,在命令行执行如下命令将所有节点组建成集群:
其中的IP地址就是在redis.conf
文件中的cluster-announce-ip
的IP地址,也是前面最开始创建的docker虚拟网卡的网关地址。
redis-cli -a <你设置的redis密码> --cluster create [宿主机IP]:6379 [宿主机IP]:6380 [宿主机IP]:6381 [宿主机IP]:6382 [宿主机IP]:6383 [宿主机IP]:6384 --cluster-replicas 1
执行上述命令后,显示如下内容,需要输入一个yes
确认配置
最终出现这几行内容,就说明集群创建完成。
如果 不需要对外提供服务,直接使用如下步骤创建:
因为docker网络模式用的host模式,创建集群时可以直接使用IP 127.0.0.1
redis-cli -a 1234 --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1
同样的,出现如下内容代表创建完成。
3.集群创建验证
可以继续在容器6379中执行:
redis-cli -c -a 1234
连接上redis后,执行cluster info
,可以看到共6个节点
再执行cluster nodes
,可以看到共六个节点,三个master,三个slave,验证没问题。
如果使用的是--net host
网络模式及上述配置,验证截图如下:
五、集群测试
1.本地测试
我们就不在容器内测试了,直接在宿主机测试。
外网是无法测试的,因为目前是本地环境,没有公网IP。局域网也不测了先,没有那么多电脑。
主要测试逻辑就是:使用端口A set值,然后通过端口B get值,如果可以get到说明集群模式没有问题。
1.首先我们通过6380端口连接Redis集群,进行set操作,可以看到已经set成功
2.然后我们通过另一个master节点6381进行get操作
可以看到因为name所在的slot在6380节点上,这里会自动重定向到6380,且重定向成功
坑三:cluster-announce-ip如果设置的为docker内网关IP,会导致容器外部访问无法重定向
其实,之前踩坑的整个过程中,是用的docker虚拟网卡桥接的网络模式搭建一个本地可用的Redis Cluter模式集群,因为宿主机的IP也不固定,所以redis.conf文件中cluster-announce-ip
设置的是docker虚拟网卡的网关地址,这样就造成了一个问题,如果进入到其中一个Redis节点的容器内,整个cluster集群是可用的。但是如果在宿主机访问这个Redis集群的时候,因为加了端口映射,6379-6384端口也都能通过127.0.0.1访问,不涉及重定向的get,set指令也都能正常执行,但是一旦涉及到slot的重定向,redis返回的重定向IP是docker容器内部的IP 172.18.0.1,这个IP在宿主机是无法访问的,因此会导致重定向失败。
所以,本文后来又在之前的基础上加了不需要对外提供服务的部分,即使用host网络模式搭建集群,这样就可以正常重定向了,也就可以在本地的项目中调用该redis集群了。
但是也导致了本文看起来有点复杂,有点混乱。暂时先这样吧,细心看应该还是能看懂的,毕竟也不是太难的东西。以后有时间可能改改吧,目前为止已经耗费了太多精力了。
参考资料
http://www.taodudu.cc/news/show-568884.html?action=onClick
https://www.cnblogs.com/mrhelloworld/p/docker12.html
https://blog.youkuaiyun.com/flyzing/article/details/113178480
https://zenn.dev/kentama/articles/5d79ba1e28cddd
https://qa.1r1g.com/sf/ask/2697273841/