使用 3 VMs 實作 Redis cluster
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
請參考 官方文件