2023年2月9日 星期四

使用 docker-compose 建立 Redis Cluster

 先建立如下的資料結構:

專案目錄名為 "Redis Cluster Test",
裡面放了 docker-compose 所需的 docker-compose.yml 檔和一個名為 redis 的資料夾,
在 redis 的資料夾中放置 Redis node 所需的 redis.conf 客制設定檔
和 docker service 的 Dockerfile 設定檔。

以下是各檔的內容 (以 Redis Cluster Test 為專案根目錄):

/redis/Dockerfile:
FROM redis
COPY redis.conf /usr/local/etc/redis/redis.conf 
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
裡面把 redis.conf 放至 Docker container 中,並指定了使用 redis.conf 執行 redis-server 啟動 Redis 服務。

/redis/redis.conf:
cluster-enabled yes
裡面的內容很簡單,只有一行,使用 cluster-enabled yes 開啟 Cluster 功能。

/docker-compose.yml:
version: '3.4'
services:
    redis-node-1:
        build: ./redis 
        container_name: redis-node-1
        ports: 
            - 6379:6379 
            - 16379:16379 
        entrypoint: [redis-server, /usr/local/etc/redis/redis.conf, --port,"6379", --cluster-announce-ip, "<your server IP or hostname>"]
    redis-node-2:
        build: ./redis 
        container_name: redis-node-2
        ports: 
            - 6380:6380 
            - 16380:16380
        entrypoint: [redis-server, /usr/local/etc/redis/redis.conf, --port,"6380", --cluster-announce-ip, "<your server IP or hostname>"]
    redis-node-3:
        build: ./redis 
        container_name: redis-node-3
        ports: 
            - 6381:6381 
            - 16381:16381 
        entrypoint: [redis-server, /usr/local/etc/redis/redis.conf, --port,"6381", --cluster-announce-ip, "<your server IP or hostname>"]
    redis-node-4:
        build: ./redis 
        container_name: redis-node-4
        ports: 
            - 6382:6382 
            - 16382:16382 
        entrypoint: [redis-server, /usr/local/etc/redis/redis.conf, --port,"6382", --cluster-announce-ip, "<your server IP or hostname>"]
    redis-node-5:
        build: ./redis  
        container_name: redis-node-5
        ports: 
            - 6383:6383 
            - 16383:16383 
        entrypoint: [redis-server, /usr/local/etc/redis/redis.conf, --port,"6383", --cluster-announce-ip, "<your server IP or hostname>"]
    redis-node-6:
        build: ./redis  
        container_name: redis-node-6
        ports: 
            - 6384:6384 
            - 16384:16384 
        entrypoint: [redis-server, /usr/local/etc/redis/redis.conf, --port,"6384", --cluster-announce-ip, "<your server IP or hostname>"]
    redis-cluster-creator:
        image: redis
        container_name: redis-cluster-creator
        entrypoint: [/bin/sh, -c, 'echo "yes" | redis-cli --cluster create host.docker.internal:6379 host.docker.internal:6380 host.docker.internal:6381 host.docker.internal:6382 host.docker.internal:6383 host.docker.internal:6384 --cluster-replicas 1']
        depends_on: 
          - redis-node-1 
          - redis-node-2
          - redis-node-3 
          - redis-node-4
          - redis-node-5 
          - redis-node-6
在 docker-compose.yml 中,設置了 6 個 Redis node 作為 Redis Cluster 之用,
6 個 Redis node 全部都是使用同一個 Dockerfile 和 redis.conf 所建構出來的,
這邊要注意的是 ports 需要向外開放兩個 port,
第一個 port 是用來跟 client 端通信的,例如 6379、6380 等,這是我們平常要登入 Redis 時會用到的 port,暫且稱做 client port 好了。
第二個 port 官方名字叫做 Cluster bus port, 是用來讓 Redis node 跟 node 之間通信用的,預設為 client port 再加上 10000,不過也可以在 redis.conf 檔裡用 cluster-port 參數來修改,
可參考官方的說明: Scaling with Redis Cluster 。

<your server IP or hostname> 請用 docker 所在的 server host IP 或 hostname 來取代,
測試時可用 host.docker.internal (在 docker container 中可用此 hostname 連上 server host) 來測試 (實務上請用真正的 IP 或 hostname)。

redis-cluster-creator 只是一個用來執行完 Redis Cluster 設置建立 Cluster 後就關掉的 docker service,
使用 depends_on 等待各 Redis node 啟動完成後,便會執行以下命令:
echo "yes" | redis-cli --cluster create host.docker.internal:6379 host.docker.internal:6380 host.docker.internal:6381 host.docker.internal:6382 host.docker.internal:6383 host.docker.internal:6384 --cluster-replicas 1

以上命令相當於是先執行了
redis-cli --cluster create host.docker.internal:6379 host.docker.internal:6380 host.docker.internal:6381 host.docker.internal:6382 host.docker.internal:6383 host.docker.internal:6384 --cluster-replicas 1

再執行手動輸入的 yes,因為 redis-cli --cluster create 執行中會詢問
Can I set the above configuration? (type 'yes' to accept): 
需要手動輸入 yes 才能繼續下去。

redis-cli --cluster create 是建立 cluster 的指令,它將各 Redis node 連結起來成為 Cluster,
參數意義如下:
redis-cli --cluster create <node-1 IP or hostname:<port> <node-2 IP or hostname:<port> ....<可多個 Redis node>...... --cluster-replicas <一個 Redis master node 要有幾個依附的 Redis slave node>

它會自己選擇哪些要做為 Redis master node,哪些要做為 Redis slave node。

參考資料:

  1. Redis - Official Image | Docker Hub
  2. Redis configuration file example | Redis
  3. Scaling with Redis Cluster
  4. Redis|用 Docker 架設 Redis Cluster

2023年2月8日 星期三

Redis 好用指令紀錄

#檢查 Cluster 情況 (未登入 Redis node 時):

redis-cli --cluster check <redis node domain or IP>:<port>

範例:

redis-cli --cluster check xxx.xxx.xxx:6379

修復 Redis Server Cluster Slot 分配不正常等 node 問題:

redis-cli --cluster fix xxx.xxx.xxx:6379

回應範例(以下為三組 Master node 和 Slave node,實際上可能會有更多組):

xxx.xxx.xxx:6379 (1ba418d4...) -> 4 keys | 5461 slots | 1 slaves.
xxx.xxx.xxx:6381 (4131a66e...) -> 4 keys | 5461 slots | 1 slaves.
xxx.xxx.xxx:6380 (ba5a4554...) -> 4 keys | 5462 slots | 1 slaves.
[OK] 12 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node xxx.xxx.xxx:6379)
M: 1ba418d421ac322943423091360c909a549b3d39 xxx.xxx.xxx:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 431b199172871c4c61f4e2f48ed1b9117dd3e801 xxx.xxx.xxx:6384
   slots: (0 slots) slave
   replicates 1ba418d421ac322943423091360c909a549b3d39
S: 2fb427cf8acb3521c67c45fc2cfe1ac2a669a3c0 xxx.xxx.xxx:6383
   slots: (0 slots) slave
   replicates 4131a66e5cadfd91c48b824d5288ddb1445d8bd1
M: 4131a66e5cadfd91c48b824d5288ddb1445d8bd1 xxx.xxx.xxx:6381
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: ba5a4554bc90d750de37a5da39b93dca896bab72 xxx.xxx.xxx:6380
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 70712afa54623608772bafd1926305e895d7d076 xxx.xxx.xxx:6382
   slots: (0 slots) slave
   replicates ba5a4554bc90d750de37a5da39b93dca896bab72
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
在回應中, "M" 代表 Master node、"S" 代表依附於 Master node 的 "Slave node",
Slave node 的 replicates 值指出了它依附的是哪一個 Master node

#登入 Redis node (會進入 Redis 互動 command line 模式,"-c" 代表 Cluster Mode):

redis-cli -h xxx.xxx.xxx -p 6379 -c

#查看 Cluster 狀態(登入 Redis node 後):

cluster info

回應範例:

cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:3649
cluster_stats_messages_pong_sent:3775
cluster_stats_messages_sent:7424
cluster_stats_messages_ping_received:3770
cluster_stats_messages_pong_received:3646
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:7421
total_cluster_links_buffer_limit_exceeded:0
#啟動 Redis node:
redis-server <Redis config 檔的位置> --port <Redis node 的 port> --cluster-announce-ip "<Redis node 的 IP 或 hostname>"
範例:
redis-server /usr/local/etc/redis/redis.conf --port "6379" --cluster-announce-ip "xxx.xxx.xxx"
#建立 Redis Cluster:
redis-cli --cluster create <Redis node-1 IP or hostname:<port> <Redis node-2 IP or hostname:<port> ...<可多個 Redis node>....... --cluster-replicas <一個 Redis master node 要有幾個依附的 Redis slave node>
範例:
redis-cli --cluster create xxx.xxx.xxx:6379 xxx.xxx.xxx:6380 xxx.xxx.xxx:6381 xxx.xxx.xxx:6382 --cluster-replicas 1

#清除cluster中所有 node 的 key資料 (等同於對每個 cluster 中的所有 node 執行 flushall)
redis-cli --cluster call <cluster 中的任一node ip 或 hostname>:<Redis node 的 port> flushall
範例:
redis-cli --cluster call xxx.xxx.xxx:6379 flushall