大约有3个多月没有写过任何前端代码了,最近被安排的一个task是在docker下搭建Redis Cluster,且能从外部访问到。一开始使用Docker Hub上现成的docker image,再写个docker-compose.yml轻松就能搭建一个可以内部通讯的Redis Cluster。但是当用Java写了一个Client从外部访问的时候,一直提示Cannot connect server, connection refused这类错误信息。后来认真阅读了Redis和Docker的官方文档后才发现,想要搭建一个可以从外部访问的Redis Cluster, 只能使用Docker network中的host driver,而host network目前只有在Linux hosts上才支持。为了证明这两句话并非空穴来风,特此引用并附上链接。所以下文的内容需要在Linux上操作才可以创建出被外部应用访问到的Redis Cluster。

In order to make Docker compatible with Redis Cluster you need to use the host networking mode of Docker.

                                                   From https://redis.io/topics/cluster-tutorial

The host networking driver only works on Linux hosts, and is not supported on Docker Desktop for Mac, Docker Desktop for Windows, or Docker EE for Windows Server.                                                                              From https://docs.docker.com/network/network-tutorial-host/

 

    言归正传,本文最终会搭建出有6个node的redis-cluster,其中3个为master,另外3个为salve。现在开始 step by step 的搭建:

第一步,准备一个 redis-cluster.tmp 文件,稍后会用这个文件依次创建出6份带不同端口号的redis.conf文件。

复制代码
port ${PORT}
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
cluster-announce-ip ${CLUSTER_ANNOUNCE_IP} #这里需要指定为Linux的host IP
cluster-announce-port ${PORT} 
cluster-announce-bus-port 1${PORT}
复制代码

这里有2个点需要注意,一个是我们用"cluster-enabled yes"表示要支持redis cluster;第二个重要的点是cluster-announce-ip,这个地址就是redis node和外部应用通信使用的,如果没有设置,如果在docker下使用的是bridge network(Docker默认的网络模式),那么每个redis node会被分配一个以172开头的Docker的内部IP地址,如果使用的是host network,那么redis node的IP就是127.0.0.1。但是这2个地址都没有办法直接从外部访问到,所以我们要设置一个cluster-announce-ip,这个地址就是你部署docker的Linux主机的IP地址。

 

第二步,运行以下bash命令,为每个redis node创建redis.conf文件。注意,CLUSTER_ANNOUNCE_IP需要修改为你正在使用的Linux的host IP。

for port in `seq 7000 7005`; do \
  mkdir -p ./${port}/conf \
  && PORT=${port} envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf \
  && CLUSTER_ANNOUNCE_IP=127.0.0.1 envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf \
  && mkdir -p ./${port}/data; \
  done

这里主要是指定端口号和外部可以访问的IP地址,执行完毕后,会出现6个从7000-7005的目录,并且每个目录下的conf目录里都有一份redis.conf文件。打开redis.conf文件就看到之前定义的变量已经被替换为具体执行的值了。

 

第三步,运行以下bash命令,使用刚刚生成的redis.conf文件,启动6个docker container。

for port in `seq 7000 7005`; do \
docker run -d -ti -p ${port}:${port} \
-v $(pwd)/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v $(pwd)/${port}/data:/data \
--restart always --name redis-${port} --net host \
--sysctl net.core.somaxconn=1024 redis redis-server /usr/local/etc/redis/redis.conf; \
done

这里需要注意的是我们使用--net host来指定说我们的container使用host network而不是默认的bridge network。运行完成后,通过下面的命令可以查看到刚刚创建出来的container。

docker ps | grep redis

 

至此,我们已经把6个redis node准备好了,接下来需要把它们连接起来。

第四步,通过以下命令进入到端口号为7000的redis container中,

docker exec -it redis-7000 bash

然后执行以下命令,期间会看到一个选择是否用此配置继续的询问,输入y即可。

redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1

这里请注意,127.0.0.1只作为示例使用,需要将它替换为实际操作环境的IP。

成功之后,可以通过以下命令查看所有的redis nodes,

redis-cli -p 7000 cluster nodes

如果想要测试redis cluster的内部通讯,可以执行以下命令,

复制代码
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000
"world"
复制代码

如果想要从外部的Java程序测试,可以使用以下代码,

复制代码
import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedisClusterTest {

    public static void main(String[] args) {
        Config config = new Config();

        config.useClusterServers()
                .addNodeAddress("redis://127.0.0.1:7000");

        RedissonClient redisson = Redisson.create(config);
        // perform operations
        RBucket<String> bucket = redisson.getBucket("simpleObject");
        bucket.set("This is object value");
        RMap<String, String> map = redisson.getMap("simpleMap");
        map.put("mapKey", "This is map value");
        String objectValue = bucket.get();
        System.out.println("stored object value: " + objectValue);
        String mapValue = map.get("mapKey");
        System.out.println("stored map value: " + mapValue);
        redisson.shutdown();
    }
}
复制代码

这里,虽然我们只调用了一次addNodeAddress,实际Redisson内部在连接到指定的redis node之后,会执行cluster nodes去获取其他结点的IP地址和port,而这里显示的IP就是我们设置的cluster-announce-ip,如果不设置的话,那就是docker内部的IP地址,所以我们就没办法从外部访问了。

好啦,通过以上步骤就可以在linux环境下搭建一个可以从外部访问的Redis Cluster啦,可能步骤会比较麻烦,不过比起直接用一个docker-compose.yml来说,可以帮助我们了解到每一步做了什么。

另外申明一下,我一开始主要参考了Docker Redis 5.0 集群(cluster)搭建 这篇文章才得以顺利搭建出cluster,关于外部通讯的部分又是查了一些其他资料,最终综合有了此文。

PS. 后续有需要的话,我打算做一个docker image挂在Docker Hub上,这样一个docker-compose.yml就可以帮我们把以上内容cover掉了。

 

Posted on   單筱寒  阅读(4093)  评论(2编辑  收藏  举报
点击右上角即可分享
微信分享提示