Redis

使用 3 VMs 實作 Redis cluster

石偉琪 Vicky Shih 2022/12/20 14:09:53
1975

1. 系統架構說明

本次使用了三台 VM, 每台 VM 上各自啟動了兩個 redis instance,分別啟動在 port 6379 以及 6380

3 個 VM 的資訊如下:

hostname IP OS master slave
node1 172.16.143.101 CentOS 7.6 最小安裝 172.16.143.101:6379 172.16.143.101:6380
node2 172.16.143.102 CentOS 7.6 最小安裝 172.16.143.102:6379 172.16.143.102:6380
node3 172.16.143.103 CentOS 7.6 最小安裝 172.16.143.103:6379 172.16.143.103:6380

 

為顧及資料的安全性,稍後在設定 cluser replicate 時,會進行如下的設定

  • node1:6379 --> node2:6380
  • node2:6379 --> node3:6380
  • node3:6379 --> node1:6380

 

 

2. 下載與編譯 Redis: 以 7.0.4 為例

建議將 source 下載到其中一台主機上編譯即可,稍後再以 copy 的方式,複製到其他的主機上

 

2.1 下載 redis

從 Redis 官方網站下載最新的版本: https://redis.io/download/
本文件會以當時的最新版本 7.0.4 為例,可以透過 wget 將 7.0.4.tar.gz 下載回來

[root@node1 src]# wget https://github.com/redis/redis/archive/7.0.4.tar.gz

 

2.2 編譯 redis

將 redis 下載回來後,接著就可以準備要用來編譯 redis 的環境。在編譯 redis 時,會用到 make 以及 gcc。以 CentOS 7.6 最小安裝為例,並不會具備 gcc,所以還需要先使用以下的指令安裝 gcc 以及相關的套作

[root@node1 src]# yum install -y gcc

安裝完畢後,先來確認一下

[root@node1 src]# which make
/usr/bin/make

[root@node1 src]# which gcc
/usr/bin/gcc

[root@node1 src]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)

接著,先解開剛剛下載的 7.0.4.tar.gz,解開後,會多了一個 redis-7.0.4 目錄

[root@node1 src]# tar xfp 7.0.4.tar.gz

再來,就可以 make; make install 來進行編譯 redis 了

[root@node1 redis-7.0.4]# pwd
/src/redis-7.0.4

[root@node1 redis-7.0.4]# make; make install

make install 會把 redis 安裝在 /usr/local/bin 這個目錄之下,稍後可以直接把這個目錄下的檔案 tar 起來,copy 至另兩台主機,然後解開至相同的路徑下即可

[root@node1 redis-7.0.4]# cd /usr/local/bin/
[root@node1 bin]# ls -al
total 21492
drwxr-xr-x.  2 root root      134 Jul 19 16:17 .
drwxr-xr-x. 12 root root      131 May  5 13:05 ..
-rwxr-xr-x.  1 root root  5197832 Jul 19 16:17 redis-benchmark
lrwxrwxrwx.  1 root root       12 Jul 19 16:17 redis-check-aof -> redis-server
lrwxrwxrwx.  1 root root       12 Jul 19 16:17 redis-check-rdb -> redis-server
-rwxr-xr-x.  1 root root  5411024 Jul 19 16:17 redis-cli
lrwxrwxrwx.  1 root root       12 Jul 19 16:17 redis-sentinel -> redis-server
-rwxr-xr-x.  1 root root 11390176 Jul 19 16:17 redis-server

稍後要使用 redis-server 來啟動 redis

至此,完成 redis server 編譯工作

 

 

3. 系統參數調整

啟動 redis 之前,需要調整一些系統參數,請在 3 個 node 上設定以下的參數

 

3.1 Open files

稍後會使用 redis 這個帳號來啟動 redis,所以在這裡要先在每一個 node 新增 redis 這個帳號

[root@node1 src]# useradd -m -p redis123 redis

其中:

  • -m: 一併建立 home directory
  • -p redis123: 設定該 user 的密碼為 redis123

帳號建立好了之後,open file 的預設值是 1024

[redis@node1 ~]$ id
uid=1000(redis) gid=1000(redis) groups=1000(redis) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

[redis@node1 ~]$ ulimit -n
1024

請以 root 執行以下的指令修改 redis 這個使用者的 open file

[root@node1 src]# cat >> /etc/security/limits.conf << EOF
redis   soft    nofile  10032
redis   hard    nofile  10032
EOF

修改完畢後,再以 ulimit -n 確認

[root@node1 src]# su - redis
Last login: Mon Jul 25 13:16:38 CST 2022 on pts/2
[redis@node1 ~]$ ulimit -n
10032

最後要看到出現的結果為設定的 10032

接下來要設定 vm.overcommit_memory 以及 somaxconn 這兩個系統參數,這兩個都是設定在 /etc/sysctl.conf 裡

 

3.2 vm.overcommit_memory

在 Redis 的網頁上,建議將 vm.overcommit_memory 設定為 1,設定的方式如下:

[root@node1 redis-7.0.4]# echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf

[root@node1 redis-7.0.4]# sysctl -p
vm.overcommit_memory = 1

關於這個系統參數,在 RedHat 官方網頁 上,有提到以下關於 vm.overcommit_memory 的說明

定義接受或拒絕大型記憶體需求的狀況。此參數有三種可用值:

0 — 預設設定。kernel 會進行探索式的記憶體過度寫入處理,方法是預測記憶體的可用量、並讓公然違規的需求失敗。不幸的是,因為記憶體是以探索式、而非精準的演算法則來分配,因此這設定有時會過度使用系統上的可用記憶體。

1 — kernel 不進行記憶體過度寫入處理。在此設定下,過度使用記憶體的機會會增加,但對於頻繁存取記憶體的任務來說,效能也會增加。

2 — kernel 拒絕相等或大於總可用置換空間與實體記憶體比例(於 overcommit_ratio 指定)的記憶體需求。如果您想要降低記憶體過度寫入的風險,這是最佳設定。

 

3.3 somaxconn

這個參數預設值為 128,redis 建議調到 1024。設定的方法是修改 /etc/sysctl.conf 裡的 net.core.somaxconn,設定的指令如下:

[root@node1 src]# echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf
[root@node1 src]# sysctl -p
vm.overcommit_memory = 1
net.core.somaxconn = 1024

 

3.4 TPH

請執行以下的指令設定 TPH

[root@node1 ~]# echo never > /sys/kernel/mm/transparent_hugepage/enabled

[root@node1 ~]# echo "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.d/rc.local

 

更多請參考 RedHat 官方文件

 

 

4. 設定啟動 redis 時使用之設定檔並啟動 redis

 

4.1 設定 redis 設定檔

設定好相關的系統參數後,就可以設定啟動 redis 時的設定檔了

redis 的設定與 data 全部都放到 /redis 目錄之下,分為 6379 以及 6380 兩個目錄,分別對應到 listen port 6379(master)與 port 6380(slave)

其中:

1️⃣ redis.conf 為啟動 redis 時所讀取的設定檔
2️⃣ cluster.conf 則是 cluster 的設定檔

請參考上圖,將設定檔複製到三台主機上指定的路徑之下,並更改 /redis 這個目錄的 owner

[root@node1 ~]# mkdir -p /redis/{6379,6380}/data

[root@node1 ~]# cp -av /src/redis-7.0.4/redis.conf /redis/6379/redis.conf
[root@node1 ~]# cp -av /src/redis-7.0.4/redis.conf /redis/6380/redis.conf

[root@node1 ~]# chown -R redis:redis /redis

接著,就可以來設定 redis 啟動時所讀取的設定檔 redis.conf 了。請修改以下的這些參數

  • bind: 這裡設定的是 redis 要 listen 的 IP address,預設值為 127.0.0.1,可以改為各主機自己的 IP,或是 0.0.0.0

    bind 172.16.143.101
    
  • cluster-enabled: 是否要啟動為 cluster mode

    cluster-enabled yes
    
  • cluster-config-file: cluster 的設定檔

    這裡只要指定檔名就好。該檔案一開始啟動 redis 時並不存在,稍後設定好 cluster 後,檔案就會自動出現在指定的路徑之下

    cluster-config-file cluster.conf
    
  • cluster-require-full-coverage:這個設定是當有結果當機時,整個 16384 slots 會不會都受到影響。為避免全體服務受到影響,請設定為 no

    cluster-require-full-coverage no
    
  • daemonize: 這個設定是關於在 redis server 啟動後,是否要以 daemon 的方式在背景執行

    daemonize yes
    
  • logfile: 指定 redis server 的 log 存放位置與檔案名稱

    因為每台主機上都執行了兩個 redis instance,請事先新增 /var/log/redis 這個目錄,並設定成 redis 這個 user 可以讀寫。

    其中,listen port 6379 的 redis,logfile 請設定為 /var/log/redis/redis-6379.log; 而 listen port 6380 的 redis,logfile 則設定為 /var/log/redis/redis-6380.log

    logfile "/var/log/redis/redis-6379.log"
    
  • maxmemory:設定 redis server 能夠使用的 memory 最大值,請視實際狀況而定

    maxmemory 4g
    
  • port:指定 redis 啟動後,要 listen 的 port;listen 6379 的就填 6379;反之則填 6380

    port 6379
    
  • dir: data 要存放的路徑

    dir /redis/6379/data
    

    redis 啟動之前,請記得先將目錄建好,並設定好 owner

    [root@node1 redis]# ls -ald /redis/{6379,6380}/data
    drwxrwxr-x. 2 redis redis 26 Jul 27 10:27 /redis/6379/data
    drwxrwxr-x. 2 redis redis 26 Jul 27 10:28 /redis/6380/data
    

 

4.2 啟動 redis

設定好 redis 設定檔後,就可以 redis 這個使用者啟動 redis

[root@node1 redis]# su - redis
Last login: Wed Jul 27 17:22:51 CST 2022 on pts/0

[redis@node1 ~]$ redis-server /redis/6379/redis.conf
[redis@node1 ~]$ redis-server /redis/6380/redis.conf

[redis@node1 ~]$ netstat -lptn | grep redis
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 172.16.143.101:16379    0.0.0.0:*               LISTEN      3487/redis-server 1
tcp        0      0 172.16.143.101:16380    0.0.0.0:*               LISTEN      3493/redis-server 1
tcp        0      0 172.16.143.101:6379     0.0.0.0:*               LISTEN      3487/redis-server 1
tcp        0      0 172.16.143.101:6380     0.0.0.0:*               LISTEN      3493/redis-server 1

啟動後,可以查看 log 來確認啟動的過程中,有無報錯

[redis@node1 ~]$ tail -f /var/log/redis/redis-6379.log
3551:M 27 Jul 2022 17:27:18.285 * Node configuration loaded, I'm ba6f2dd83176f077922607b610c9a4d3988c4d16
3551:M 27 Jul 2022 17:27:18.286 * Running mode=cluster, port=6379.
3551:M 27 Jul 2022 17:27:18.286 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
3551:M 27 Jul 2022 17:27:18.286 # Server initialized
3551:M 27 Jul 2022 17:27:18.286 * Loading RDB produced by version 7.0.4
3551:M 27 Jul 2022 17:27:18.286 * RDB age 58 seconds
3551:M 27 Jul 2022 17:27:18.286 * RDB memory usage when created 1.51 Mb
3551:M 27 Jul 2022 17:27:18.286 * Done loading RDB, keys loaded: 0, keys expired: 0.
3551:M 27 Jul 2022 17:27:18.286 * DB loaded from disk: 0.000 seconds
3551:M 27 Jul 2022 17:27:18.286 * Ready to accept connections

以上述這段例子為例,因為沒有設定到 somaxconn 這個參數,在啟動時就可以看到這個提示。如果是正常啟動,應該會看到像以下的 log

[redis@node1 redis]$ cat redis-6379.log
1902:C 28 Jul 2022 10:07:07.847 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1902:C 28 Jul 2022 10:07:07.848 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=1902, just started
1902:C 28 Jul 2022 10:07:07.848 # Configuration loaded
1902:M 28 Jul 2022 10:07:07.849 * monotonic clock: POSIX clock_gettime
1902:M 28 Jul 2022 10:07:07.850 * No cluster configuration found, I'm f7c497d54b0eddef97c8a70d338a6cd8171222a3
1902:M 28 Jul 2022 10:07:07.853 * Running mode=cluster, port=6379.
1902:M 28 Jul 2022 10:07:07.853 # Server initialized
1902:M 28 Jul 2022 10:07:07.854 * Ready to accept connections

當三個 VM 上的 redis 都啟動後,就可以進行下一個步驟,設定 redis cluster

 

 

5. 設定 redis cluster

 

5.1 設定 replicate

要設定 redis-cluster,全部都透過 redis-cli 這個指令,接下來會以 redis 這個使用者來進行設定

先連到其中一台 VM 的 6379 port,會看到目前僅有一個 redis instance

[redis@node1 redis]$ redis-cli -h node1 -p 6379 cluster nodes
f7c497d54b0eddef97c8a70d338a6cd8171222a3 :6379@16379 myself,master - 0 0 0 connected

先把其他五個 instance 也加進來

加好後,就會看到有六個 master 了,接下來要設定 replicate。

以下以 172.16.143.102:6380 是從 172.16.143.101:6379 copy 為例。設定好了之後,172.16.143.102:6380 就是 172.16.143.101:6379 這個 master 的 slave 了。

先以 redis-cli 連到 172.16.143.102:6380,然後以 cluster replicate node-id 的方式設定。node-id 的找法,請從 cluster nodes 的結果裡,找出 IP:port 對應的 node-id

按照此邏輯設定好三組 replicate 後,應該要看到如下的結果:3 master + 3 slave

 

5.2 設定 slots

這裡的設定,是要把 0 ~ 16,383 總共 16,438 個 slots 分散到三個 VM 上

最後,以 cluster info 來確認一下

設定好了 cluster 之後的 /redis 目錄結構會如下所示

 

 

 

6. 測試

先簡單地設定一個名為 key1 的 key,而值為 123

redis cluster 會根據自己的演算法,會把這個 key 放在 slot 9189,也就是 172.16.143.102:6379 上。所以,當設定好了之後,會發現提示符號已經從原來的 172.16.143.101:6379> 變成 172.16.143.102:6379> 了

接下來,要模擬當 172.16.143.102 出現問題,而無法再提供 redis service 時,此時 172.16.143.103:6380 就會由原來的 172.16.143.102:6379 的 slave,變為 master 了

在 slave 接手變為 master 後,可以再由 redis-cli 連進 cluster 內,確認是否還可以查得到 key1 的值

由上圖可以看得出來,這個 request 就會轉導給 172.16.143.103:6380 了,而查出來的值,仍然是剛剛設定的 123。

確認無誤後,再來把 172.16.143.102 power on,並直接啟動 redis。當 172.16.143.102:6379 重新加入 redis cluster 後,它的角色直接就是 slave,不會被切換回 master,而且會把資料由 172.16.143.103:6380 抄寫回 172.16.143.102:6379,完全不需要額外的修復與設定

 

 

7. 設定密碼保護

設定完畢後,可以在 redis.conf 裡啟用 requirepass 這個參數

[redis@node1 ~]$ openssl rand 60 | openssl base64 -A
eVQ7UGE82yhSiGmd4ECwNlIV/RSvI2ptbje5Zra+gdtDRwfQ19JNk5Loag39HbF0xkf/Q6Q+xlrL6Tpt

然後,把這個值設定至 redis.conf 裡,再重新啟動 redis

requirepass eVQ7UGE82yhSiGmd4ECwNlIV/RSvI2ptbje5Zra+gdtDRwfQ19JNk5Loag39HbF0xkf/Q6Q+xlrL6Tpt

之後使用 redis-cli 要連至 redis 時,會發現需要認證。下圖是直接使用管理者登入

除此之外,因為 redis 已經啟用了 requirepass 這個選項,當執行 replicate 時,也需要驗證,否則會在 logfile 裡看到有錯誤的訊息類似如下:

2079:S 28 Jul 2022 15:42:31.807 # MASTER aborted replication with an error: NOAUTH Authentication required.

所以,當 requirepass 啟用時,請一併設定以下的參數

masterauth eVQ7UGE82yhSiGmd4ECwNlIV/RSvI2ptbje5Zra+gdtDRwfQ19JNk5Loag39HbF0xkf/Q6Q+xlrL6Tpt

requirepass / masterauth 設定完畢後,請記得 restart redis 讓設定生效

 

 

8. ACL

在 Redis 6 之後,有新增了 ACL 的功能,可以為不同的系統設定不同的 user,使用不同的 key,有不同的權限,以避免誤刪到不同系統使用的 keys

請參考 官方文件

石偉琪 Vicky Shih