如何在Docker下搭建可以外部訪問的Redis Cluster


    大約有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掉了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM