在 Kubernetes 部署 Redis Cluster

最近在 Kubernetes 上部暑相關基礎服務,大部份的服務都有對應的 Helm 可以使用,沒有遇到太多問題,唯獨 Redis,因為團隊使用 Redis Cluster,,而 Helm 官方的兩個 chart helm/charts/stable/redishelm/charts/stable/redis-ha 都無法滿足需求,雖然 helm/charts/stable/redis 具有 cluster 相關設定,但實測下只是 master-slave + sentinel 的 cluster,並非 Redis cluster,後來同事建議試試 KubeDB 的 Redis cluster,過程中遇到不少問題,紀錄一下避免改天部署時還不起來該怎麼解決問題

基本環境說明

以下 VM 透過 Azure 建立

  1. CentOS 7.6 * 3

    • Standard B2ms
    • 2 vcpus, 8 GiB memory
    • 200 GB Standard SSD
  2. VM 配置說明

    請確保各個 node 間可以透過 host name 連線

    NameIP用途
    node110.0.0.4ansible-client,master,etcd
    node210.0.0.5node,etcd
    node310.0.0.6node,etcd
  3. Kubernetes 1.14.3

  4. Kubespray 2.10

  5. Helm 2.13.1

  6. KubeDB 0.12.0

  7. Redis 5.0.3

安裝 KubeDB Operator

[請在 master node 上操作] 以下使用 Helm 安裝,也可以使用 script 來執行安裝,請參考 Installation GuideScript tab 內容

  1. 加入 appscode (KudeDB 的開發公司) 的 helm repo

    helm repo add appscode https://charts.appscode.com/stable/
    
  2. 更新 Helm repo

    helm repo update
    
  3. 安裝 kubedb operator chart

    helm install appscode/kubedb --name kubedb-operator --version 0.12.0 --set apiserver.enableMutatingWebhook=false --set apiserver.enableValidatingWebhook=false --namespace kube-system
    
    • 未關閉 apiserver.enableMutatingWebhook 錯誤

      使用 kubedb 建立 redis 時會出現錯誤

      • 錯誤訊息

        Error from server (InternalError): error when creating "redis-cluster.yaml": Internal error occurred: failed calling webhook "redis.mutators.kubedb.com": the server is currently unable to handle the request
        
      • 錯誤截圖

        4webhookfail

    • 未關閉 apiserver.enableValidatingWebhook 錯誤

      使用 kubedb 建立 redis 時會出現錯誤

      • 錯誤訊息

        Error from server (InternalError): error when creating "redis-cluster.yaml": Internal error occurred: failed calling webhook "redis.validators.kubedb.com": the server is currently unable to handle the request
        
      • 錯誤截圖

        5validatefail

  4. 確認成功註冊 CRD

    kubectl get crds -l app=kubedb
    

    1registcrd

  5. 安裝 KubeDB catalog

    helm install appscode/kubedb-catalog --name kubedb-catalog --version 0.12.0 --namespace kube-system
    
  6. 確認成功安裝

    KubeDB operator pods 是 Running

    kubectl get pods --all-namespaces -l app=kubedb
    

    2operatorrunning

安裝 KubeDB CLI

  1. 下載編譯完成的 sh

    wget -O kubedb https://github.com/kubedb/cli/releases/download/0.12.0/kubedb-linux-amd64 \
    && chmod +x kubedb \
    && sudo mv kubedb /usr/local/bin/
    
  2. 確認成功安裝

    kubedb version
    

    3kubedbversion

準備 StorageClass 與 Persistent Volume

  1. 建立 StorageClass

    • 新增 storageclass.yaml

      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: local-redis
      provisioner: kubernetes.io/no-provisioner
      volumeBindingMode: WaitForFirstConsumer
      
    • 使用 stroageclass.yaml 建立 StorageClass

      kubectl apply -f storageclass.yaml
      
    • 未建立錯誤 StorageClass (錯誤會出現在實際安裝 redis cluster 時)

      使用 kubedb describe rd -n demo redis-cluster 可以看到錯誤

      • 錯誤訊息

        spec.storage.storageClassName "local-redis" not found
        
      • 錯誤截圖

        6storageclasserror

  2. 建立 Persistent Volume

    我個人開發測試使用 Local Persistent Volume, redis cluster 有 6 個 node,每個 node 都需要一個 volume

    • 建立六個資料夾並給予權限

      以下動作在 node1 上執行 (會影響 pv.yaml 設定),請自行替換正確路徑直至建立 六 個資料夾

      mkdir -p /mnt/disk/vol1 && chcon -Rt svirt_sandbox_file_t /mnt/disk/vol1 && chmod 777 /mnt/disk/vol1
      mkdir -p /mnt/disk/vol2 && chcon -Rt svirt_sandbox_file_t /mnt/disk/vol2 && chmod 777 /mnt/disk/vol2
      mkdir -p /mnt/disk/vol3 && chcon -Rt svirt_sandbox_file_t /mnt/disk/vol3 && chmod 777 /mnt/disk/vol3
      mkdir -p /mnt/disk/vol4 && chcon -Rt svirt_sandbox_file_t /mnt/disk/vol4 && chmod 777 /mnt/disk/vol4
      mkdir -p /mnt/disk/vol5 && chcon -Rt svirt_sandbox_file_t /mnt/disk/vol5 && chmod 777 /mnt/disk/vol5
      mkdir -p /mnt/disk/vol6 && chcon -Rt svirt_sandbox_file_t /mnt/disk/vol6 && chmod 777 /mnt/disk/vol6
      
    • 新增六個 pv.yaml

      請自行替換 metadata.name,spec.local.path 直至建立 六 個 pv.yaml,另外如果資料夾不是建立在 node1 上的請修改 spec.nodeAffinity.required.nodeSelectorTerms.matchExpressions.key.values

      ```yaml
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: local-pv1
      spec:
        capacity:
          storage: 1Gi
        volumeMode: Filesystem
        accessModes:
        - ReadWriteOnce
        persistentVolumeReclaimPolicy: Recycle
        storageClassName: local-redis
        local:
          path: /mnt/disk/vol1
        nodeAffinity:
          required:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - node1
      ```
      
    • 使用 pv.yaml 建立 Local Persistent Volume

      重複 六 次直至建立 六 個 pv

      kubectl apply -f pv1.yaml
      

      快速建立 六 個 pv

      kubectl create -f pv1.yaml -f pv2.yaml -f pv3.yaml -f pv4.yaml -f pv5.yaml -f pv6.yaml
      

      快速刪除 六 個 pv

      kubectl delete -f pv1.yaml -f pv2.yaml -f pv3.yaml -f pv4.yaml -f pv5.yaml -f pv6.yaml
      
    • 未建立 PV 錯誤

      使用 kubectl -n demo describe pod redis-cluster-shard0-0 可以看見錯誤訊息

      • 錯誤訊息

        0/3 nodes are available: 3 node(s) didn't find available persistent volumes to bind.
        
      • 錯誤截圖

        7pvfail

使用 KubeDB 安裝 Redis Cluster

  1. 建立 demo kubernetes namespace

    可忽略,但後續動作 yaml 中的 namespace 記得要改

    kubectl create ns demo
    
  2. 建立 redis-cluster.yaml

    apiVersion: kubedb.com/v1alpha1
    kind: Redis
    metadata:
      name: redis-cluster
      namespace: demo
    spec:
      version: 5.0.3-v1
      mode: Cluster
      cluster:
        master: 3
        replicas: 1
      storageType: Durable
      storage:
        resources:
          requests:
            storage: 1Gi
        storageClassName: "local-redis"
        accessModes:
        - ReadWriteOnce
      terminationPolicy: Pause
      updateStrategy:
        type: RollingUpdate
    
  3. 使用 redis-cluster.yaml 建立 Redis

    kubedb create -f redis-cluster.yaml
    
  4. 確認成功安裝

    • 檢查 type 為 redis 且名稱為 redis-cluster 的所有 resource 狀態

      kubedb describe redis -n demo redis-cluster
      
    • 成功建立 三 個 StatefulSet 且各有 二 個 pod 運行中

      kubedb get statefulset -n demo
      

      8statefulsetok

    • 連線至 redis 中確認 cluster 狀態

      kubectl -n demo exec -it redis-cluster-shard0-0 redis-cli cluster nodes
      

      9clusternodes

移除 KubeDB

  1. 下載 sh 並執行清除所有 object

    curl -fsSL https://raw.githubusercontent.com/kubedb/cli/0.12.0/hack/deploy/kubedb.sh | bash -s -- --uninstall --namespace=kube-system --purge
    
  2. 移除 KubeDB operator chart

    helm del kubedb-operator --purge --no-hooks
    
  3. 移除 KubeDB catalog

    helm del kubedb-catalog --purge --no-hooks
    
  4. 移除沒用到的 PVC

    kubectl get pvc -n demo --no-headers -o custom-columns=":metadata.name" | xargs -n 1 kubectl -n demo delete pvc
    

心得

本來就沒有 Kubernetes 的 production 相關經驗,一下跳到 Helm 不打緊,還用上了 Operator,還好同事很強大,讓我越級打怪還沒遇到解決不了的問題 讚啦 ~~~

回到這次透過 KubeDB 安裝 Redis Cluster 上,一開始依網站上的 Helm 安裝步驟操作總是遇到問題,同事建議改用網站上的 custom install script 停用 webhook 才過了第一關,直到後來在紀錄安裝步驟時才發現 Helm 其實也有提供停用 webhook 的參數設定,只是網站上沒提到,參數設定說明是在 GitHub 上翻到的,類似的情況還發生在一些 yaml 設定,官網上的說明文件有時比較新、比較正確,不過偶爾會反過來,GitHub 比較正確,就這樣兩邊參照、比對、嘗試可是吃盡了苦頭呀

除此之外不知道是我的基礎知識不足還是官網的文件東漏西漏,我個人是踩了不少雷才試出個正確的流程,而同事使用 Minikube 到是沒遇到什麼問題 XD,我人品真這麼差嗎 ~~~

參考資訊

  1. Installation Guide
  2. Kubernetes (5) Local Persistent Volumes – A Step-by-Step Tutorial
  3. KubeDB - Redis Cluster
  4. kubedb/installer
  5. Uninstall KubeDB