Redis 叢集偶數節點跨地域部署之高可用測試

2023-07-25 06:00:36

筆者目前所在公司存在多套 Redis 叢集:

  • A 叢集 主 + 從 共 60 個分片,部署在 3 + 3 臺物理機上,每臺機器各承載 10 個埠
  • 主庫 30 個埠在廣州,從庫 30 個埠在中山
  • B 叢集共 72 個埠,部署架構一模一樣

上雲後,均為廣東的某個雲廠商的 2 個可用區,不再使用 IDC 資料中心,部署架構一致。

有人提出了一個很耐人尋味的問題:

這個架構有問題,如果兩地之間網路故障,必定會出現腦裂!

真的會出現腦裂嗎?

不至於吧!網路分割區後,理論上廣州機房是可用的,中山因為沒有主(存取從庫將槽位重定向回主庫),所以中山機房不可用。所以只有一個機房可寫,不會腦裂。

猜想終究是猜想,實踐出真知!現在 docker 太方便了,搭一個叢集模擬一下就 OK 了~

準備環境:

  • 2 臺測試機器,模擬雙機房環境
  • 每臺機器啟動 6 個埠,通過 redis-trib 搭建叢集

建立以下資料夾,並準備 docker-compose.yml:

mkdir -p ./data/redis/8001/data && \
mkdir -p ./data/redis/8002/data && \
mkdir -p ./data/redis/8003/data && \
mkdir -p ./data/redis/8004/data && \
mkdir -p ./data/redis/8005/data && \
mkdir -p ./data/redis/8006/data && \
mkdir -p ./data/redis/9001/data && \
mkdir -p ./data/redis/9002/data && \
mkdir -p ./data/redis/9003/data && \
mkdir -p ./data/redis/9004/data && \
mkdir -p ./data/redis/9005/data && \
mkdir -p ./data/redis/9006/data

廣州機房 6 個埠:

version: '3'

services:
 redis_gz_1:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/8001/data:/data
  environment:
   - REDIS_PORT=8001

 redis_gz_2:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/8002/data:/data
  environment:
   - REDIS_PORT=8002

 redis_gz_3:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/8003/data:/data
  environment:
   - REDIS_PORT=8003

 redis_gz_4:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/8004/data:/data
  environment:
   - REDIS_PORT=8004

 redis_gz_5:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/8005/data:/data
  environment:
   - REDIS_PORT=8005

 redis_gz_6:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/8006/data:/data
  environment:
   - REDIS_PORT=8006

中山機房 6 個埠:

version: '3'

services:
 redis_zs_1:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/9001/data:/data
  environment:
   - REDIS_PORT=9001

 redis_zs_2:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/9002/data:/data
  environment:
   - REDIS_PORT=9002

 redis_zs_3:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/9003/data:/data
  environment:
   - REDIS_PORT=9003

 redis_zs_4:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/9004/data:/data
  environment:
   - REDIS_PORT=9004

 redis_zs_5:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/9005/data:/data
  environment:
   - REDIS_PORT=9005

 redis_zs_6:
  image: publicisworldwide/redis-cluster
  network_mode: host
  volumes:
   - ./data/redis/9006/data:/data
  environment:
   - REDIS_PORT=9006

docker-compose up 啟動後,使用以下命令搭建叢集:

docker run --rm -it inem0o/redis-trib create --replicas 1 \
10.43.2.6:8001 \
10.43.2.6:8002 \
10.43.2.6:8003 \
10.43.2.6:8004 \
10.43.3.7:9004 \
10.43.2.6:8005 \
10.43.3.7:9005 \
10.43.2.6:8006 \
10.43.3.7:9006 \
10.43.3.7:9001 \
10.43.3.7:9002 \
10.43.3.7:9003

你會發現叢集搭起來了!有以下提示資訊:

...master:
10.43.2.6:8001
10.43.3.7:9004
10.43.2.6:8002
10.43.3.7:9005
10.43.2.6:8003
10.43.3.7:9006
...
Adding replica 10.43.3.7:9001 to 10.43.2.6:8001
Adding replica 10.43.2.6:8004 to 10.43.3.7:9004
Adding replica 10.43.3.7:9002 to 10.43.2.6:8002
Adding replica 10.43.2.6:8005 to 10.43.3.7:9005
Adding replica 10.43.3.7:9003 to 10.43.2.6:8003
Adding replica 10.43.2.6:8006 to 10.43.3.7:9006
...

此時,叢集是 廣州、中山 各 3 個 master,不符合我們的場景,需要手工切換一下主從:

# 分別在從庫 3 個埠做主從切換 10.43.2.6:9004-9006
redis-cli -h 10.43.2.6 -p 8004 CLUSTER FAILOVER
OK
redis-cli -h 10.43.2.6 -p 8005 CLUSTER FAILOVER
OK
redis-cli -h 10.43.2.6 -p 8006 CLUSTER FAILOVER
OK

3 個埠提主成功,10.43.2.6 此時執行 6 個 master,而 10.43.3.7 執行 6 個 slave 範例。

如何斷網?很簡單,iptables 無敵!

我們在廣州(10.43.2.6)丟掉中山(10.43.3.7)的包就好了:

iptables -I INPUT -s 10.43.3.7 -pudp --dport 18001:18006 -j DROP && \
iptables -I INPUT -s 10.43.3.7 -ptcp --dport 18001:18006 -j DROP && \
iptables -I INPUT -s 10.43.3.7 -ptcp --dport 8001:8006 -j DROP && \
iptables -I INPUT -s 10.43.3.7 -pudp --dport 8001:8006 -j DROP

執行後,中山一直列印重連主庫失敗的紀錄檔,主庫也探測到從庫斷開了,通過 CLUSTER NODES 命令可以獲取各個節點狀態。

結論一:A [6Master/0Slave] + B [0Master/6Slave],A 機房可讀可寫,B 機房不可讀不可寫(CLUSTERDOWN)

報錯資訊如下:

10.43.3.7:9006> set a12 2
(error) CLUSTERDOWN The cluster is down

另外,我還測試了主庫分佈在雙機房的情況:

結論二:A [4Master/2Slave] + B [2Master/4Slave],A 機房可讀可寫,B 機房不可讀不可寫(CLUSTERDOWN)

結論三:A [3Master/3Slave] + B [3Master/3Slave],AB 機房均不可讀不可寫(CLUSTERDOWN)

為什麼不可讀?

因為請求從庫它會自動轉發(MOVED)到主庫,而主庫不可用(達不到半數以上節點),所以徹底涼了!

解決辦法是不使用偶數節點,極端情況下(master 均等分佈兩地)會導致整個叢集不可用。

實驗完,不要忘了刪掉規則,恢復網路:

iptables -D INPUT -s 10.43.3.7 -pudp --dport 18001:18006 -j DROP && \
iptables -D INPUT -s 10.43.3.7 -ptcp --dport 18001:18006 -j DROP && \
iptables -D INPUT -s 10.43.3.7 -ptcp --dport 8001:8006 -j DROP && \
iptables -D INPUT -s 10.43.3.7 -pudp --dport 8001:8006 -j DROP

(完)


文章來源於本人部落格,釋出於 2022-03-12,原文連結:https://imlht.com/archives/254/