k8s環境下單節點運行Redis集群

單機下用Redis二進制程序包搭建Redis集群的案例很多,用docker在單節點上搭集群的也很多,但是在k8s下單節點搭集群的就很少了,有的只是掛載一個臨時目錄,數據無法持久化,pod銷毀后,數據就沒了。在k8s環境下測試機又不夠的情況下使用Redis集群就不太方便了,本文就是筆者的根據自身需要實踐出來的,期間也找了很多網上資料,最后自己綜合琢磨出的解決方案,由于對docker和k8s不是很精通,可能有其他更簡單的方案,歡迎大家交流。

一、所需的背景知識

1、對docker,k8s有一定使用經驗:會編寫yaml文件,知道如何排查pod不能running的問題
2、在單機環境下搭建過Redis集群
3、如果以上的知識都不知道也沒關系,本文盡量保證你按照步驟執行不出錯。但是k8s環境你必須得有,基本的linux命令知識得有

二、軟件版本

k8s v1.10.2
docker 18.03.1-ce, build 9ee9f40
Redis5.0.5

三、準備工作

 docker pull redis:5.0.5
 mkdir redis-cluster
 cd redis-cluster/
 mkdir data

創建多個節點的數據存儲目錄,避免Redis實例啟動時配置文件沖突導致無法啟動,在單臺物理機上搭建過Redis集群的應該知道。建好6個節點的目錄,后面會用到,這里的目錄要注意權限問題,k8s啟動的pod需要讀寫這里的文件夾

for port in `seq 7001 7006`; do \
  mkdir -p ./${port}/
done

創建完成后目錄結構

目錄結構

四、編輯yaml文件

創建k8s的yaml文件
回到redis-cluster目錄下

1、創建ConfigMap

先創建redis.conf配置文件
vi redis-cm.yaml
將以下內容貼到文件中,保存退出

apiVersion: v1
kind: ConfigMap
metadata:
 name: redis-conf
data:
 redis.conf: |
         appendonly yes
         cluster-enabled yes
         cluster-config-file /var/lib/redis/nodes.conf
         cluster-node-timeout 5000
         dir /var/lib/redis
         port 6379

2、創建statefulset

vi redis-statefulset.yaml

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: redis-app
spec:
  serviceName: "redis-service"
  replicas: 6
  template:
    metadata:
      labels:
        app: redis
        appCluster: redis-cluster
    spec:
      nodeSelector:
        node: mfc    # 這里需要根據自己的k8s節點情況修改,本案例需修改成node-222
      terminationGracePeriodSeconds: 20
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - redis
              topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: "redis:latest"
        imagePullPolicy: IfNotPresent #默認情況是會根據配置文件中的鏡像地址去拉取鏡像,配置為本地有鏡像不拉取遠程,避免你的環境不能訪問外網,拉取鏡像失敗
        command:
          - "redis-server"
        args:
          - "/etc/redis/redis.conf"
          - "--protected-mode"
          - "no"
        resources:
          requests:
            cpu: "100m"
            memory: "100Mi"
        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"
      volumes:
      - name: "redis-conf"
        configMap:
          name: "redis-conf"
          items:
            - key: "redis.conf"
              path: "redis.conf"
  volumeClaimTemplates:   #可看作pvc的模板
    - metadata:
      name: redis-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

3、創建pv的yaml文件

vi pv1.yaml

kind: PersistentVolume
apiVersion: v1
metadata:
  name: redis-pv-volume1
  labels:
    type: local
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /home/bboss/redis-cluster/data/7001/

/home/bboss/redis-cluster/data/7001/此目錄為前面我們創建的redis數據存儲的6個目錄
將此文件復制5份,依次命名為pv2.yaml 到pv6.yaml
在對應的文件中將

name: redis-pv-volume1
path: /home/bboss/redis-cluster/data/7001/

這2處修改為對應文件編號的序號,例如pv2.yaml修改為

name: redis-pv-volume2
path: /home/bboss/redis-cluster/data/7002/

五、開始搭建集群

1、創建cm和pv如下圖

km create -f redis-cm.yaml
km create -f pv1.yaml
image.png

2、創建statefulset

確定當前宿主機在k8s集群中的label

kubectl get nodes --show-labels
k8s集群機器ip和對應的label

如圖,我當前部署的機器ip是222對應的node標簽名為node-222
將redis-statefulset.yaml文件中node: mfc 修改為 node: node-222,稍后啟動redis的pod是都會選擇在node-222這個節點的機器上啟動,這正是我們需要的,修改正確后

km create -f redis-statefulset.yaml

[注:我這里km是做了配置,km相當于是kubectl -n mfc-namespace 這是指定k8s的namespace的,我的namespace名是mfc-namespace,kubectl命令不指定-n 默認是使用default為名的namespace]

kubectl get pods  -n mfc-namespace | grep redis 

查看6個redis節點是否都啟動完成

查看一下pod對應的ip,下一步配置集群需要知道各節點的ip,這ip是k8s分配的

kubectl get pods -o wide -n mfc-namespace | grep redis

驗證一下pvc是否都bound了

kubectl get pvc -o wide -n mfc-namespace
redis節點的ip

3、初始化Redis集群

啟動一個獨立的redis

docker run -it redis:latest bash
redis-cli --cluster create 10.254.79.20:6379 10.254.79.21:6379 10.254.79.22:6379 10.254.79.23:6379 10.254.79.24:6379 10.254.79.25:6379 --cluster-replicas 1
初始化Redis集群
接上圖

4、驗證Redis集群

日志顯示集群已經初始化好,slots也分配完成,驗證一下集群
用redis-cli隨便連接某個redis節點

/usr/local/bin/redis-cli -c -h 10.254.79.20 -p 6379
驗證集群

搭建完成了,下面來看看本地存儲的數據
隨便進到某個目錄下看看,如下圖redis集群的數據已經落到物理機上了。

3號節點的數據文件

驗證一下pod重啟后集群數據是否還在

重啟Redis集群

看ip還是原來的ip,這就是k8s定義statefulset的作用
通過剛才啟動的獨立redis再次連接集群看數據還在不在,如下圖數據都還在

驗證集群中已數據是否還在,如圖數據都還在

5、創建headlessService

到這里我們的Redis集群已經是完全可用的了,但是我們的應用要使用該集群時配置ip不好記,這時就需要用到k8s中的Service
vi redis-headlessService.yaml

apiVersion: v1
kind: Service
metadata:
  name: redis-service
  labels:
    app: redis
spec:
  ports:
  - name: redis-port
    port: 6379
  clusterIP: None
  selector:
    app: redis
    appCluster: redis-cluster

執行 km create -f redis-headlessService.yaml 創建服務
查看服務

查看redis-service服務

另外用k8s啟一個有nslookup或者ping工具的pod,驗證一下服務名是否可以訪問

驗證服務名

k8s是自帶dns功能的,k8s會根據serviceName生成對應的域名
如上圖,可以看到節點1 (10.254.79.20)的完整域名是redis-app-0.redis-service.mfc-namespace.svc.cluster.local,在k8s中同一個namespace的服務只需要前面的子域名即可,后面的會默認補齊,本例中的serviceName是redis-app-0.redis-service,這是一個節點的服務名。對于整個集群的serviceName是redis-service

用剛才的docker啟動的redis驗證一下域名是否可以連接,如下圖用域名連接失敗,因為我們這個是用docker啟動的不在k8s中,用pod的ip是可以連接的,因為pod的ip訪問是走docker的網絡。因為服務名是k8s做的路由,所以必須在k8s集群中注冊才可以使用服務名互相訪問。serviceName其實是在操作系統中做了一個轉發的動作,通過iptales做策略實現的。

用域名連接失敗

用redis集群中的任意一個節點驗證,域名是可以訪問的

用redis集群中的節點驗證

六、總結

網上大部分文章都是類似的方案,使用pv,pvc做存儲,有的pv是綁定的nfs,我也試過nfs碰到問題沒解決只能想其他辦法,但是我理解的是如果6個redis節點的配置文件nodes.conf和數據文件都在共享一個目錄,那還是會有沖突,導致redis只能啟動一個節點,而其他節點無法啟動,除非能把文件命名定義成不同節點對應不同的文件名,因為沒搭成功基于nfs的pvc存儲,不知道是不是每個節點一個隔離的pvc,所以無法知道以后會再實踐。所以本例主要是通過定義不同的pv把目錄隔離開,但是定義StatefulSet時不好指定pvc,看到有文章用到volumeClaimTemplates(可看作pvc的模板)傳輸門于是問題就應然而解了,StatefulSet在創建pod時是前一個創建成功才繼續下一個是有順序的,同時創建pvc也是有順序的,每個pvc綁定一個pv,這樣6個節點的數據文件就自動綁定到各自的目錄下了。
另外redis集群初始化不需要Ruby了,避免了安裝ruby的麻煩。

七、參考資料

[https://www.cnblogs.com/tylerzhou/p/11027559.html]
[https://v1-12.docs.kubernetes.io/zh/docs/tasks/run-application/force-delete-stateful-set-pod/]
[https://juejin.im/post/5d206b1e5188252f275fdc95]
[https://www.cnblogs.com/breezey/p/6582082.html]
[https://juejin.im/post/5c989ff2f265da60f206ffe4#heading-7]
[http://www.lxweimin.com/p/a5172b0eeae4]
https://www.cnblogs.com/xiaochangwei/p/7993065.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。