k8s1.20版本部署Redis叢集(三主三從)

追梦$少年發表於2024-06-27

一、準備工作

  1. 主機規劃
節點 IP
k8s-master1 192.168.2.245
k8s-master2 192.168.2.246
k8s-master3 192.168.2.247
k8s-node1 192.168.2.248
NFS、Rancher 192.168.2.251

注意:本文采用三主三從叢集模式。redis叢集至少要有6個節點,由於資源限制無法部署那麼多node節點,所以6個pod都只能跑在node1節點上。

  1. 版本介紹
服務 版本
centos 7.9
Rancher(單節點) 2.5.12
kubernetes 1.20.15
Redis 7.0.11

二、邏輯圖

k8s1.20版本部署Redis叢集(三主三從)

本次建立6個Redis服務,並使用configmap持久化redis配置檔案;需要注意的是,本文沒有使用傳統的pv,pvc方式做持久化資料儲存,而是使用storageclass呼叫provisioner,自動給pod建立的pvc分配pv並繫結,從而達到持久化儲存的效果。

簡單直觀看下圖:

k8s1.20版本部署Redis叢集(三主三從)

接下來我們就一起來部署redis叢集吧!

概念解釋:

Provisioner:Provisioner是StorageClass中必須的一個資源,它是儲存資源自動調配器,可以將其看作是後端儲存驅動。對於NFS型別,K8S沒有提供內部Provisioner,但可以使用外部的Provisioner。Provisioner必須符合儲存卷的開發規範(CSI)。本文件中使用NFS提供的Provisioner。

三、部署Redis叢集

1. 安裝NFS服務

NFS Server IP(服務端):192.168.2.251

NFS Client IP(客戶端):192.168.2.245

  1. NFS Server端安裝NFS
    操作主機:NFS、Rancher|192.168.2.251
# 1.安裝nfs與rpc
yum install -y nfs-utils rpcbind
# 檢視是否安裝成功
rpm -qa | grep nfs
rpm -qa | grep rpcbind

k8s1.20版本部署Redis叢集(三主三從)

# 2.建立共享儲存資料夾,並授權
mkdir -p /nfs/k8s_data
chmod 777 /nfs/k8s_data/

# 3.配置nfs
vim /etc/exports
/nfs/k8s_data 192.168.2.0/24(rw,no_root_squash,no_all_squash,sync)
注:
rw read-write 讀寫
ro read-only 只讀
sync 請求或寫入資料時,資料同步寫入到NFS server的硬碟後才返回。資料安全,但效能降低了
async 優先將資料儲存到記憶體,硬碟有空檔時再寫入硬碟,效率更高,但可能造成資料丟失。
root_squash 當NFS 客戶端使用root使用者訪問時,對映為NFS 服務端的匿名使用者
no_root_squash 當NFS 客戶端使用root 使用者訪問時,對映為NFS服務端的root 使用者
all_squash 不論NFS 客戶端使用任何帳戶,均對映為NFS 服務端的匿名使用者
# 4.啟動服務
systemctl start nfs
systemctl start rpcbind
#新增開機自啟
systemctl enable nfs
systemctl enable rpcbind

# 5.配置生效
exportfs -r

# 6.檢視掛載情況
showmount -e localhost
#輸出下面資訊表示正常
Export list for localhost:
/nfs/k8s_data 192.168.2.0/24
  1. NFS Client安裝NFS
    操作主機:除了NFS server,其他所有主機
yum -y install nfs-utils

2. 修改API配置

Kubernetes v1.20 (opens new window)開始,預設停用了 metadata.selfLink 欄位,並計劃在 1.21 版本中刪除該欄位,然而,部分應用仍然依賴於這個欄位,例如 nfs-client-provisioner。如果仍然要繼續使用這些應用,將需要重新啟用該欄位。

由於我們後面要使用nfs-client-provisioner,所以,如果你的Kubernetes版本是1.20.x版本,需要提前修改api 配置,重啟metadata.selfLink欄位。

# 新增方式:
vim /etc/kubernetes/manifests/kube-apiserver.yaml

# 把下面配置增加進去
--feature-gates=RemoveSelfLink=false

k8s1.20版本部署Redis叢集(三主三從)

如果沒有修改配置,後面我們檢視nfs-client-provisioner服務日誌時,可能會出現報錯:unexpected error getting claim reference: selfLink was empty, can’t make reference,就是由於沒有開啟這個欄位導致的。

3. 建立持久卷PVC

當有很多的資料卷需要建立或者管理時,Kubernetes解決這個問題的方法是提供動態配置PV的方法,可以自動建立PV。管理員可以部署PV配置器(provisioner),然後定義對應的StorageClass,這樣開發者在建立PVC的時候就可以選擇需要建立儲存的型別,PVC會把StorageClass傳遞給PV provisioner,由provisioner自動建立PV。

參考k8s官網:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#provisioner

參考Github開源元件:https://github.com/kubernetes-retired/external-storage/blob/master/nfs-client/deploy/

參考文章:https://dongweizhen.blog.csdn.net/article/details/130651727?spm=1001.2014.3001.5502

所以這裡使用了StorageClass的型別當做就持久化方案。

1. 建立ServiceAccount賬號

vim nfs-serviceaccount.yaml
#複製以下內容:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

# 建立資源
kubectl create -f nfs-serviceaccount.yaml

2. 建立provisioner

(也可稱為供應者、置備程式、儲存分配器)

vim nfs-client-provisioner.yaml
# 複製以下內容:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner        #這個serviceAccountName就是上面建立ServiceAccount賬號
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME      #PROVISIONER_NAME的值就是本清單的頂部定義的name
              value: nfs-client-provisioner
            - name: NFS_SERVER          #這個NFS_SERVER引數的值就是nfs伺服器的IP地址
              value: 192.168.2.251
            - name: NFS_PATH          #這個NFS_PATH引數的值就是nfs伺服器的共享目錄
              value: /nfs/k8s_data
      volumes:
        - name: nfs-client-root
          nfs:                  #這裡就是配置nfs伺服器的ip地址和共享目錄
            server: 192.168.2.251
            path: /nfs/k8s_data

# 建立資源
kubectl create -f nfs-client-provisioner.yaml

3. 建立StorageClass

vim nfs-storageclass.yaml
# 複製以下內容:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storageclass
provisioner: nfs-client-provisioner       #provisioner引數定義置備程式
reclaimPolicy: Retain             #回收策略,預設是Delete
parameters:
  archiveOnDelete: "false"

# 建立資源:
kubectl create -f nfs-storageclass.yaml 

4. 建立Redis服務

1. 建立redis配置檔案

這裡使用的是k8s的configmap型別建立的

vim redis.conf
# 複製以下內容,提示:複製配置檔案的時候最好把後面的註釋全部去掉,否則後面可能會報錯!!!!!!!

bind 0.0.0.0
protected-mode yes
port 6360                     #redis埠,為了安全設定為6360埠
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no                    #redis是否以後臺模式執行,必須設定no
supervised no
pidfile /data/redis.pid               #redis的pid檔案,放到/data目錄下
loglevel notice
logfile /data/redis_log               #redis日誌檔案,放到/data目錄下
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb                 #這個檔案會放在dir定義的/data目錄
dir /data                     #資料目錄
masterauth iloveyou                 #redis叢集各節點相互認證的密碼,必須配置和下面的requirepass一致
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
requirepass iloveyou                #redis的密碼
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"           #這個檔案會放在dir定義的/data目錄
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
cluster-enabled yes                 #是否啟用叢集模式,必須去掉註釋設為yes
cluster-config-file nodes.conf            #這個檔案會放在dir定義的/data目錄
cluster-node-timeout 15000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

建立configmap:

#建立cm,名稱為redis-conf,key為redis.conf,,value為以上建立的redis.conf配置檔案。

!!!再次提示:建立前,需要把配置檔案中的註釋都刪除掉,要不然會報錯wrong number of arguments

# 建立configmap
kubectl create configmap redis-conf --from-file=redis.conf=redis.conf

2. 建立statefulset型別的Redis叢集

這裡使用statefulsets有狀態應用來建立redis,建立sts有狀態應用需要有一個headless service,同時在sts中掛載configmap卷,使用動態分配pv用於redis資料持久化。

vim redis-cluster-sts.yaml
# 複製以下內容:

---
apiVersion: v1
kind: Service                                     #先建立一個無頭service
metadata:
  labels:                                         #service本身的標籤
    app: redis-svc
  name: redis-svc                                 #service的名稱,下面建立的StatefulSet就要引用這個service名稱
spec:
  ports:
  - port: 6360                                    #service本身的埠
    protocol: TCP
    targetPort: 6360                              #目標埠6360,redis預設埠是6379,這裡為了安全改成了6360
  selector:
    app: redis-sts                                #標籤選擇器要與下面建立的pod的標籤一樣
  type: ClusterIP
  clusterIP: None                                 #clusterIP為None表示建立的service為無頭service
---
apiVersion: apps/v1
kind: StatefulSet                                 #建立StatefulSet資源
metadata:
  labels:                                         #StatefulSet本身的標籤
    app: redis-sts
  name: redis-sts                                 #資源名稱
  namespace: default                              #資源所屬名稱空間
spec:
  selector:                                       #標籤選擇器,要與下面pod模板定義的pod標籤保持一致
    matchLabels:
      app: redis-sts
  replicas: 6                                     #副本數為6個,redis叢集模式最少要為6個節點,構成3主3從
  serviceName: redis-svc                          #指定使用service為上面我們建立的無頭service的名稱
  template:                     
    metadata:
      labels:                                     #pod的標籤,上面的無頭service的標籤選擇器和sts標籤選擇器都要與這個相同
        app: redis-sts
    spec:
#     affinity:
#       podAntiAffinity:                         #定義pod反親和性,目的讓6個pod不在同一個主機上,實現均衡分佈,這裡我的node節點不夠,所以不定義反親和性
#         preferredDuringSchedulingIgnoredDuringExecution:
#         - weight: 100
#           podAffinityTerm:
#             labelSelector:
#              matchExpressions:
#               - key: app
#                 operator: In
#                 values:
#                 - redis-sts
#             topologyKey: kubernetes.io/hostname
      containers:
      - name: redis                               #容器名稱
        image: redis:latest                       #redis映象
        imagePullPolicy: IfNotPresent             #映象拉取策略
        command:                                  #定義容器的啟動命令和引數
          - "redis-server"
        args:
          - "/etc/redis/redis.conf"
          - "--cluster-announce-ip"                              #這個引數和下面的這個引數
          - "$(POD_IP)"                                                  #這個引數是為了解決pod重啟ip變了之後,redis叢集狀態無法自動同步問題
        env:
        - name: POD_IP                                                   #POD_IP值引用自status.podIP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP   
        ports:                                    #定義容器埠
        - name: redis-6360                        #為埠取個名稱為http
          containerPort: 6360                     #容器埠
        volumeMounts:                             #掛載點
        - name: "redis-conf"                      #引用下面定義的redis-conf卷
          mountPath: "/etc/redis"                 #redis配置檔案的掛載點
        - name: "redis-data"                      #指定使用的卷名稱,這裡使用的是下面定義的pvc模板的名稱
          mountPath: "/data"                      #redis資料的掛載點
        - name: localtime                                                 #掛載本地時間
          mountPath: /etc/localtime
          readOnly: true
      restartPolicy: Always
      volumes:
      - name: "redis-conf"                        #掛載一個名為redis-conf的configMap卷,這個cm卷已經定義好了
        configMap:
          name: "redis-conf"
          items:
            - key: "redis.conf"
              path: "redis.conf"
      - name: localtime                                                 #掛載本地時間
        hostPath:
          path: /etc/localtime
#          type: File
  volumeClaimTemplates:                           #定義建立pvc的模板
    - metadata:
        name: "redis-data"                        #模板名稱
      spec:
        resources:                                #資源請求
          requests:
            storage: 100M                         #需要100M的儲存空間
        accessModes:                            
        - ReadWriteOnce                           #訪問模式為RWO
        storageClassName: "nfs-storageclass"      #指定使用的儲存類,實現動態分配pv

# 建立資源
kubectl create -f  redis-cluster-sts.yaml 
# 檢視狀態:
kubectl get sts,pvc,pv

k8s1.20版本部署Redis叢集(三主三從)

服務全部就緒,資料卷也已繫結完成。

4. 組建Redis叢集

介紹兩種組建叢集的方法,分為手動和自動,選擇自己喜歡的一個方式操作。

這裡使用了域名的方式讓他們相互通訊,這樣可以避免pod被重啟ip變化,導致叢集出現問題。

根據K8s機制,<pod-name>.<svc-name>.<namespace>.svc.cluster.local這個 DNS 記錄,正是 Kubernetes 專案為 Pod 分配的唯一的可解析身份,這樣Pod就可以透過名字編號進行區分了,並且可以相互訪問。

1. 自動組建叢集

這種方式,Redis會自動分配主節點和從節點,不需要手工指定。

kubectl exec -it redis-sts-0 -- redis-cli -a iloveyou --cluster create --cluster-replicas 1 $(kubectl get pods -l app=redis-sts -o jsonpath='{range.items[*]}{.metadata.name}.redis-svc:6360 {end}')

輸入yes,正式開始組建redis叢集

最後返回:

[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

2. 手動組建叢集

檢視下Pod的資訊:

[root@k8s-master01 redis]# kubectl get pod  -l app=redis-sts
NAME          READY   STATUS    RESTARTS   AGE
redis-sts-0   1/1     Running   0          61m
redis-sts-1   1/1     Running   0          61m
redis-sts-2   1/1     Running   0          61m
redis-sts-3   1/1     Running   0          61m
redis-sts-4   1/1     Running   0          61m
redis-sts-5   1/1     Running   0          61m

手動建立redis叢集的master節點,指定redis-sts-0、redis-sts-2、redis-sts-4的pod為master節點,同樣適用DNS解析域名的方式。

 kubectl exec -it redis-sts-0 -- redis-cli -a iloveyou --cluster create redis-sts-0.redis-svc:6360 redis-sts-2.redis-svc:6360 redis-sts-4.redis-svc:6360
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 3 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
M: 0708f2d7db318bc8651dd422d28627957756472d redis-sts-0.redis-svc:6360   #0708f2d7db318bc8651dd422d28627957756472d叢集ID
   slots:[0-5460] (5461 slots) master
M: 0b5c2c8d2907dbb56c6e40baf0272917e59d960f redis-sts-2.redis-svc:6360   #同上
   slots:[5461-10922] (5462 slots) master
M: 8ace82454363dc760fe856712fc0fbb220b2e2d5 redis-sts-4.redis-svc:6360    # 同上
   slots:[10923-16383] (5461 slots) master
Can I set the above configuration? (type 'yes' to accept): yes           #輸入yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node redis-sts-0.redis-svc:6360)
M: 0708f2d7db318bc8651dd422d28627957756472d redis-sts-0.redis-svc:6360
   slots:[0-5460] (5461 slots) master
M: 0b5c2c8d2907dbb56c6e40baf0272917e59d960f 10.42.2.38:6360
   slots:[5461-10922] (5462 slots) master
M: 8ace82454363dc760fe856712fc0fbb220b2e2d5 10.42.3.71:6360
   slots:[10923-16383] (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

為每一個master節點新增slave節點

# redis-sts-0 主   >>    redis-sts-1 從
kubectl exec -it redis-sts-0 -- redis-cli -a iloveyou --cluster add-node redis-sts-1.redis-svc:6360 redis-sts-0.redis-svc:6360 --cluster-slave --cluster-master-id 0708f2d7db318bc8651dd422d28627957756472d


# redis-sts-2 主   >>    redis-sts-3 從
kubectl exec -it redis-sts-0 -- redis-cli -a iloveyou --cluster add-node redis-sts-3.redis-svc:6360 redis-sts-2.redis-svc:6360 --cluster-slave --cluster-master-id 0b5c2c8d2907dbb56c6e40baf0272917e59d960f

# redis-sts-4 主   >>    redis-sts-5 從
kubectl exec -it redis-sts-0 -- redis-cli -a iloveyou --cluster add-node redis-sts-5.redis-svc:6360 redis-sts-4.redis-svc:6360 --cluster-slave --cluster-master-id 8ace82454363dc760fe856712fc0fbb220b2e2d5

注:

–cluster add-node 引數指定要加入的slave節點

–cluster-master-id 引數指定該slave節點對應的master節點的id

5. 驗證叢集

使用下面這條命令驗證叢集狀態,注意–cluster check 後面僅需指定任意一個節點ip即可,這裡的range.items[0]就表示指定第一個redis-sts-0的ip,如下所示,叢集狀態正常

[root@k8s-master01 redis]# kubectl exec -it redis-sts-0 -- redis-cli -a iloveyou --cluster check  $(kubectl get pods -l app=redis-sts -o jsonpath='{range.items[0]}{.metadata.name}:6360 {end}')
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
redis-sts-0:6360 (0708f2d7...) -> 0 keys | 5461 slots | 1 slaves.
10.42.3.71:6360 (8ace8245...) -> 0 keys | 5461 slots | 1 slaves.
10.42.2.38:6360 (0b5c2c8d...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node redis-sts-0:6360)
M: 0708f2d7db318bc8651dd422d28627957756472d redis-sts-0:6360
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 8ace82454363dc760fe856712fc0fbb220b2e2d5 10.42.3.71:6360
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
M: 0b5c2c8d2907dbb56c6e40baf0272917e59d960f 10.42.2.38:6360
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 9683517103c33656e8fb1b666064011d74addf52 10.42.1.16:6360
   slots: (0 slots) slave
   replicates 0b5c2c8d2907dbb56c6e40baf0272917e59d960f
S: 6f17691791b9755956868d14b7fc0e1c59bf4fb2 10.42.0.25:6360
   slots: (0 slots) slave
   replicates 8ace82454363dc760fe856712fc0fbb220b2e2d5
S: 22038a5bb2e31d385ce016ba1cd9d8f13eb4e0d8 10.42.0.24:6360
   slots: (0 slots) slave
   replicates 0708f2d7db318bc8651dd422d28627957756472d
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

參考文章:

部署Redis7.0叢集6節點三主三從(完整版)

Redis6節點叢集搭建

K8s搭建nfs-storageclass

K8s搭建nfs-storageclass

Kuboard官網文件

Kubernetes官網文件

相關文章