在K8S中搭建Redis Cluster
Redis Cluster
你不必等到具備卓越能力再開始行動,但你必須展開行動才能臻至卓越,這次試著在K8S中搭建Redis Cluster,並理解每個步驟所代表的意義。。
在進入到Redis叢集(Cluster)之前,先來看看Redis除了單體Standalone跟叢集以外,還有哪幾種執行模式
主從模式(Master-Slave)
在主從模式下,寫入的動作只能對Master,Slave只能進行讀取的動作,一個Master可以有很多個Slave,Master會把資料同步給Slave,Master如果故障了,資料就沒辦法寫入,這模式單純是用讀寫分離來加速讀取的效率,如果需要HA或Failover,這模式並不合適.Slave再多都不會增加整體的記憶體使用空間.
哨兵模式(Sentinel)
哨兵模式顧名思義就是透過站哨的衛兵監控著Master,如果Master無法運作了,就會讓某個Slave提升為Master,但只靠一個哨兵的觀察就判定狀態會顯得太過武斷,而雙數的哨兵投票會很沒效率,可能會出現票數相同的情形,此時就要讓某個哨兵可以投兩票,然後再投一次,所以哨兵數量最好是單數,投票一次就會有結果.也因此,哨兵最少數量是3個.
哨兵模式示意圖
哨兵模式簡單說就是增加了HA能力的主從模式,所以相同的,多Slave並不會增加整體記憶體空間.
叢集模式(Cluster)
叢集模式解決了單體(Standalone)、主從(Master-Slave)、哨兵(Sentinel)等模式的記憶體空間無法擴充的問題.
搭建Redis Cluster至少要有三組Master-Slave的組合
也就是要有3個Master Node跟3個Slave Node
最基本的叢集模式由三主三從組成,主節點故障後,從節點會補上,但若補上的從節點又故障了,也就是整個叢集的主節點少於3個,此時整個叢集將無法運作.
最基本的叢集配置
為了解決上述的單點失敗問題,Redis叢集有個Slave節點飄移機制,以下圖為例,當Master 1故障時,Slave 1接手變成Master節點,此時叢集就有了單點失敗的風險,叢集會檢查哪裡有多的Slave節點,如果有多的,會移動一個Slave過去,也就是Slave 3、Slave 4、Slave 5其中一個會被移去當Slave 1的Slave.
接著試著在K8S (Minikube)中搭建Redis Cluster
先建立一個namespace來安裝redis
1
kubectl create namespace redis-cluster
因為POD重新啟動就會變回Image原來的樣子,我們必須有個能保存AOF或RDB的儲存區,有6個redis node,所以我們先建立6個PV (Persistent Volume),存取模式是ReadWriteOnce,確保每個PV只會被分配到一個PVC,hostPath的部分是Node上的資料夾,用Local File System只能在one node cluster中使用,依照實際狀況調整.產生一個pv.yaml。(因為我們這裡是用minikube,hostPath不存在也不會出錯,minikube會自己產生一個出來。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-cluster-pv1
labels:
type: local
spec:
storageClassName: redis-cluster-storage-class
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/redis1"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-cluster-pv2
labels:
type: local
spec:
storageClassName: redis-cluster-storage-class
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/redis2"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-cluster-pv3
labels:
type: local
spec:
storageClassName: redis-cluster-storage-class
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/redis3"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-cluster-pv4
labels:
type: local
spec:
storageClassName: redis-cluster-storage-class
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/redis4"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-cluster-pv5
labels:
type: local
spec:
storageClassName: redis-cluster-storage-class
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/redis5"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-cluster-pv6
labels:
type: local
spec:
storageClassName: redis-cluster-storage-class
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/redis6"
把PV建起來
1
kubectl apply -f pv.yaml
我們希望每個POD在reschedule時,都能mount相同的pvc,所以要做成StatefulSet,得先做個headless service。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#headless.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-headless-server
namespace: redis-cluster
labels:
app: redis-cluster
spec:
ports:
- name: redis-port
port: 6379
clusterIP: None
selector:
app: redis-cluster
1
kubectl apply -f headless.yaml
用ConfigMap做redis的設定檔
1
2
3
4
5
6
7
8
9
10
11
12
13
#configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-conf
namespace: redis-cluster
data:
redis.conf: |+
appendonly yes
cluster-enabled yes
cluster-config-file /var/lib/redis/nodes.conf
dir /var/lib/redis
port 6379
1
kubectl apply -f configmap.yaml
接著要把StatefulSet建立起來了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#redis.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: redis-cluster
spec:
selector:
matchLabels:
app: redis-cluster
serviceName: "redis-cluster"
replicas: 6
template:
metadata:
labels:
app: redis-cluster
spec:
containers:
- name: redis
image: "redis:6.2.7"
command:
- "redis-server"
args:
- "/etc/redis/redis.conf"
- "--protected-mode"
- "no"
- "--cluster-announce-ip"
- "$(MY_POD_IP)"
ports:
- name: redis
containerPort: 6379
protocol: "TCP"
- name: cluster
containerPort: 16379
protocol: "TCP"
volumeMounts:
- name: "redis-conf"
mountPath: "/etc/redis"
- name: "redis-data"
mountPath: "/var/lib/redis"
env:
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumes:
- name: "redis-conf"
configMap:
name: "redis-conf"
items:
- key: "redis.conf"
path: "redis.conf"
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "redis-storage-class"
resources:
requests:
storage: 5Gi
1
kubectl apply -f redis.yaml
輸入指令,可以看到POD有正常啟動
試著去看log,可以發現因為還沒進行cluster的指令設定,所以cluster還沒運作起來
先取得所有POD的ip
執行創建redis cluster的指令
1
kubectl -n redis-cluster exec -it redis-0 -- redis-cli --cluster create 172.17.0.6:6379 172.17.0.7:6379 172.17.0.8:6379 172.17.0.9:6379 172.17.0.10:6379 172.17.0.11:6379 --cluster-replicas 1
redis自己分配3個master,3個slave
輸入yes後,會自己建立完成,對cluster做個簡單的壓測
1
kubectl -n redis-cluster exec -it redis-0 -- redis-benchmark -n 1000000 -t set,get -P 16 -q -h 172.17.0.6 -p 6379 --cluster
這次使用的檔案可以在GitHub找到。










