KubeSphere 部署 Zookeeper 實戰教程

前言

知識點

  • 定級:入門級
  • 如何利用 AI 助手輔助運維工作
  • 單節點 Zookeeper 安裝部署
  • 集羣模式 Zookeeper 安裝部署
  • 開源應用選型思想

實戰服務器配置(架構 1:1 復刻小規模生產環境,配置略有不同)

主機名 IP CPU 內存 系統盤 數據盤 用途
ks-master-0 192.168.9.91 4 8 50 100 KubeSphere/k8s-master
ks-master-1 192.168.9.92 4 8 50 100 KubeSphere/k8s-master
ks-master-2 192.168.9.93 4 8 50 100 KubeSphere/k8s-master
ks-worker-0 192.168.9.95 4 16 50 100 k8s-worker/CI
ks-worker-1 192.168.9.96 4 16 50 100 k8s-worker
ks-worker-2 192.168.9.97 4 16 50 100 k8s-worker
storage-0 192.168.9.81 2 4 50 100+ ElasticSearch/GlusterFS/Ceph/Longhorn/NFS/
storage-1 192.168.9.82 2 4 50 100+ ElasticSearch/GlusterFS/Ceph/Longhorn
storage-2 192.168.9.83 2 4 50 100+ ElasticSearch/GlusterFS/Ceph/Longhorn
registry 192.168.9.80 2 4 50 200 Sonatype Nexus 3
合計 10 32 88 500 1100+

實戰環境涉及軟件版本信息

  • 操作系統:openEuler 22.03 LTS SP2 x86_64
  • KubeSphere:3.3.2
  • Kubernetes:v1.24.12
  • Containerd:1.6.4
  • GlusterFS:10.0-8
  • KubeKey: v3.0.8
  • Zookeeper:3.8.2

簡介

今天我們的實戰內容採用場景模擬的形式,模擬真實運維工作中,必然會遇到的一個場景。

作爲一個初入職場剛接觸雲原生運維的運維小白,Boss 今天給我安排了一個高難度的任務,對,你沒看錯就是高難度的。

高難度 = 2 M 1 D= 聽過、沒見過、沒幹過、時間短

Boss 提出的任務要求整理如下(都是我根據 Boss 的原話,自己理解、猜測、搜索整理的,實際上 Boos 根本沒說幾個字):

  • 在 K8s 集羣上部署一個單節點模式的 Zookeeper
  • 在 K8s 集羣上部署集羣模式的 Zookeeper
  • 使用場景:研發、測試(後期很有可能直接轉生產)
  • 任務交付期:明天(領導認爲很簡單)
  • 核心要求:能用就行(自己理解的)

既然不能拒絕,時間緊任務重,那就幹吧,沖沖衝!!!

2. 任務實現思路分析

工欲善其事,必先利其器。既然是 2 M 1 D 級別的任務,先搬出 AI 時代運維三板斧中的前兩板,看看是否能解決問題。

由於只用了兩板斧就搞定了,所以就沒用到第三板。

第一板斧——AI 助手

提示詞: "我要在 K8s 上部署 zookeerper。分別給我一個單節點和集羣模式的部署方案,注意需要使用存儲類 glusterfs、需要有 SVC、需要有 zoo.cfg 的 ConfigMap"

重點說明: 本人學藝不精,AI 助手玩的還不熟,提示詞寫的不好,所以最終效果也不是太好。

輸入上面的提示詞以後,AI 助手-3.5 吧啦吧啦返回了一堆文字給我,我只取了精華作爲展示。

  • 單節點部署配置文件(zookeeper-single.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: zookeeper-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: glusterfs
  resources:
    requests:
      storage: 10Gi

---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper
spec:
  selector:
    app: zookeeper
  ports:
    - protocol: TCP
      port: 2181
      targetPort: 2181

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: zookeeper-config
data:
  zoo.cfg: |
    tickTime=2000
    dataDir=/data
    clientPort=2181

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: zookeeper
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zookeeper
  template:
    metadata:
      labels:
        app: zookeeper
    spec:
      containers:
      - name: zookeeper
        image: zookeeper:3.7.0
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
        ports:
        - containerPort: 2181
        volumeMounts:
        - name: zookeeper-data
          mountPath: /data
        env:
        - name: ZOOKEEPER_CONFIG_FILE
          value: /opt/zookeeper-3.7.0/conf/zoo.cfg
        - name: ZOO_SERVERS
          value: "server.1=zookeeper-0.zookeeper.default.svc.cluster.local:2888:3888;2181"
      volumes:
      - name: zookeeper-data
        persistentVolumeClaim:
          claimName: zookeeper-data
      - name: zookeeper-config
        configMap:
          name: zookeeper-config
  • 集羣模式部署配置文件(zookeeper-cluster.yaml):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: zookeeper-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: glusterfs
  resources:
    requests:
      storage: 10Gi

---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper
spec:
  selector:
    app: zookeeper
  ports:
    - protocol: TCP
      port: 2181
      targetPort: 2181

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: zookeeper-config
data:
  zoo.cfg: |
    tickTime=2000
    dataDir=/data
    clientPort=2181
    initLimit=5
    syncLimit=2
    server.1=zookeeper-0.zookeeper.default.svc.cluster.local:2888:3888
    server.2=zookeeper-1.zookeeper.default.svc.cluster.local:2888:3888
    server.3=zookeeper-2.zookeeper.default.svc.cluster.local:2888:3888

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zookeeper
spec:
  serviceName: zookeeper
  replicas: 3
  selector:
    matchLabels:
      app: zookeeper
  template:
    metadata:
      labels:
        app: zookeeper
    spec:
      containers:
      - name: zookeeper
        image: zookeeper:3.7.0
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
        ports:
        - containerPort: 2181
        volumeMounts:
        - name: zookeeper-data
          mountPath: /data
        env:
        - name: ZOOKEEPER_CONFIG_FILE
          value: /opt/zookeeper-3.7.0/conf/zoo.cfg
      volumes:
      - name: zookeeper-data
        persistentVolumeClaim:
          claimName: zookeeper-data
      - name: zookeeper-config
        configMap:
          name: zookeeper-config

分析完內容,我發現我好像懂了點,但是又沒有完全懂,返回的結果中單節點和集羣模式的配置文件看着差不太多,分析一下結果,順便捋一下思路:

  • kind 類型不同: 單節點是 Deployment, 集羣模式是 StatefulSet,這個目前看起來沒毛病,思路是對的
  • Deployment 副本數爲 1,StatefulSet 副本數爲 3
  • 集羣模式沒有使用 volumeClaimTemplates
  • 集羣模式 zoo.cfg 配置文件中多了幾個 server 的配置項,但是好像沒有實現 myid 的處理

必須有一定的 K8s 和 Zookeeper 相關知識積累才能分析出 AI 助手 給出的結果是否符合需求,否則根本看不懂。

第二板斧——搜索引擎

搜索引擎,在AI 助手出來以前在運維輔助中排名第一,現在暫居第二了,估計也沒機會重回巔峯了(謹代表個人排名意見)。

看完了 AI 助手 的輸出結果,覺得整體結構內容沒問題,但是感覺細節上還是差那麼點意思,尤其是集羣模式的部署。我們會在本文第三小節驗證。

同時,我還發現了一點 image: zookeeper:3.7.0,既然引用的 Image 是 DockerHub 官方的,那麼 DockerHub 上一定有對應的 Image 及容器化部署的使用方法。

這也就帶來一個新的靈感和一種新的學習方法,同時也是最簡單直接的 Docker 部署轉化爲 Kubernetes 部署的方法,直接去看 Docker 的部署命令、啓動命令、相關參數,然後直接搬到 K8s 上就可以。

直接轉到 DokcerHub 官網,我們直接用關鍵詞 zookeeper 搜索一下。

搜索結果截圖:

簡單的思考分析一下搜索結果:

  • 排名最高的兩個 zookeeper 鏡像,下載量都超過 100M+, 分別是 Docker 官方和 Bitnami 出品的。

  • 上週下載量最多的是 Bitnami 出品的 zookeeper,下載量高達 1,140,215,比 DockerHub 出品的 zookeeper 下載量 681,115,多了一倍還多。

小提示:Bitnami, 十幾年前我就開始用,它們出品的一鍵部署安裝包,當時就是很牛的一鍵部署中間件解決方案服務商,現在應該是更全面、更牛了。

爲什麼要做上面的分析?

  • 當我們做開源技術選型的時候,主要的決定因素之一就是使用者數量,使用者太少說明產品還不成熟,代表着出了問題你都沒地方尋求幫助。
  • 我的技術選型幾個原則:首選官方(Apache 官方沒有,只能選 DockerHub 官方)、用戶量大(100M+)、維護更新頻繁(7 days ago)。
  • 除了 DockerHub 出品的 zookeeper 鏡像之外,Bitnami 出品的鏡像也是一個很好的選擇,羣衆的眼睛是雪亮的,如果不好用也不可能這麼多人推薦、下載。
  • AI 助手 給出的示例用的是 DockerHub 官方的鏡像。

上面說了那麼多,好像沒有說到第二板斧的重點搜索引擎AI 助手 給出的結果中,單節點模式看着沒什麼問題,但是集羣模式總感覺少點啥,重點的 myid 的處理方式我就沒看到。因此,我又用關鍵詞 StatefulSet 部署 Zookeeper 在搜索引擎中搜索了一番。

搜索結果中有兩個方向的思路比較有參考價值:

  • 基於 K8s 官方文檔給出的 Zookeeper 部署方案。

K8s 官網的一個教程案例 Running ZooKeeper, A Distributed System Coordinator,這個例子看着比較複雜,而且引入了幾個新的技術。

  • 基於 Bitnami 製作的鏡像提供的 Zookeeper 集羣部署方案

梳理清楚已經獲取的信息,爲了快速完成領導交付的任務,今天的驗證測試方案順序如下:

  • AI 助手 提供的單節點部署配置
  • AI 助手 提供的集羣模式部署配置
  • Bitnami 提供的集羣模式部署方案
  • K8s 官網 Zookeeper 部署案例

因爲,單節點部署比較簡單。所以,測試問題重點就在於 AI 助手Bitnami 提供的集羣模式部署配置是否可行,如果方案可行就沒官網案例什麼事了,如果不行再去實驗 K8s 官網 Zookeeper 部署案例,但是說實話我暫時還很不想碰,因爲這個案例裏有個技術點我壓根兒就沒聽過,真要搞的話又會引入新的問題。

Zookeeper 單節點部署

我覺得 AI 助手 返回的單節點的部署方案和配置文件看着還可以沒啥問題。但是,也不要直接複製、粘貼,拿來即用。一定要多參考 DockerHub 官網的 Zookeeper 相關示例,二者相結合,寫出來的纔是更靠譜的資源清單。

思路梳理

在 K8s 集羣上部一套單節點的 Zookeeper 需要的資源清單如下:

  • PersistentVolumeClaim

  • ConfigMap:zoo.cfg

  • Deployment

  • Cluster Service

  • External Service(可選)

知道了需要完成的任務目標,接下來結合 AI 助手給出的配置和官方配置參數,生成一套資源配置清單。

注意: 實踐證明,AI 助手 給出的也只是一個大概,細節還是有很多不足的地方,下面示例中的所有資源配置清單,都是參考官方配置參數和實際使用需求整理的。

簡單說一下修改了哪些內容。

  • Zookeeper 版本選擇,使用了落後官方最新的穩定版 3.9.0 一個版本的 3.8.2 替換 AI 助手給出的配置方案中的 3.7.0
  • 增加了 dataLog 的配置
  • 完善了資源限制的配置
  • 完善了 zoo.cfg 的配置

資源配置清單

如無特殊說明,所有涉及 K8s 的操作都在 Master-0 節點上執行 , 配置文件根目錄爲 /srv/opsman/k8s-yaml

  • 創建資源清單文件夾
cd /srv/opsman/k8s-yaml
mkdir -p zookeeper/single
cd zookeeper/single
  • vi zookeeper-pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: zookeeper-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: glusterfs
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: zookeeper-datalog
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: glusterfs
  resources:
    requests:
      storage: 2Gi

說明: 後端存儲類使用的 GlusterFS

  • vi zookeeper-cm.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: zookeeper-config
data:
  zoo-cfg: |
    tickTime=2000
    dataDir=/data
    dataLogDir=/datalog
    clientPort=2181
    initLimit=10
    syncLimit=5
  • vi zookeeper-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: zookeeper
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zookeeper
  template:
    metadata:
      labels:
        app: zookeeper
    spec:
      containers:
      - name: zookeeper
        image: zookeeper:3.8.2
        resources:
          requests:
            cpu: 50m
            memory: 500Mi
          limits:
            cpu: '2'
            memory: 4000Mi
        ports:
        - name: zookeeper-2181
          containerPort: 2181
          protocol: TCP
        volumeMounts:
          - name: config
            mountPath: /conf
          - name: data
            mountPath: /data
          - name: datalog
            mountPath: /datalog
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: zookeeper-data
      - name: datalog
        persistentVolumeClaim:
          claimName: zookeeper-datalog
      - name: config
        configMap:
          name: zookeeper-config
          items:
            - key: zoo-cfg
              path: zoo.cfg

---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper
spec:
  ports:
    - name: zookeeper-2181
      protocol: TCP
      port: 2181
      targetPort: 2181
  selector:
    app: zookeeper
  type: ClusterIP

Deployment 和 Cluster Service 放在了一個配置文件。

  • vi zookeeper-external-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper-external-svc
  labels:
    app: zookeeper-external-svc
spec:
  ports:
    - name: tcp-zookeeper-external
      protocol: TCP
      port: 2181
      targetPort: 2181
      nodePort: 32181
  selector:
    app: zookeeper
  type: NodePort

注意:可選配置項,如果不需要被 K8s 集羣之外的服務訪問,則不需要配置。

部署資源

  • 部署 PersistentVolumeClaim
kubectl apply -f zookeeper-pvc.yaml
  • 部署 ConfigMap
kubectl apply -f zookeeper-cm.yaml
  • 部署 Deployment
kubectl apply -f zookeeper-deploy.yaml
  • 部署 External Service
kubectl apply -f zookeeper-svc.yaml

K8s 部署資源驗證

  • 驗證 PersistentVolumeClaim
[root@ks-master-0 single]# kubectl get pvc -o wide
NAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
zookeeper-data      Bound    pvc-371c9406-1757-451a-9c89-bed47ac71dd4   1Gi        RWO            glusterfs      12s   Filesystem
zookeeper-datalog   Bound    pvc-457a134c-0db2-4efc-902c-555daba2057e   2Gi        RWO            glusterfs      11s   Filesystem
  • 驗證 Deployment
[root@ks-master-0 single]#  kubectl get deploy -o wide
NAME        READY   UP-TO-DATE   AVAILABLE   AGE    CONTAINERS   IMAGES            SELECTOR
zookeeper   1/1     1            1           5m1s   zookeeper    zookeeper:3.8.2   app=zookeeper
  • 驗證 Pod
[root@ks-master-0 single]#  kubectl get pod -o wide
NAME                         READY   STATUS        RESTARTS        AGE     IP             NODE          NOMINATED NODE   READINESS GATES
zookeeper-bcfc6cc5c-bh56m    1/1     Running       0               54s     10.233.120.8   ks-worker-1   <none>           <none>
  • 驗證 Service
[root@ks-master-0 single]# kubectl get svc -o wide
NAME                                                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE    SELECTOR
zookeeper                                                ClusterIP   10.233.58.30    <none>        2181/TCP         59s    app=zookeeper
zookeeper-external-svc                                   NodePort    10.233.40.37    <none>        2181:32181/TCP   59s    app=zookeeper

Zookeeper 服務可用性驗證

在 K8s 集羣內部驗證

  • 在 K8s 上創建一個 Zookeeper Client Pod 驗證
kubectl run zookeeper-client --image=zookeeper:3.8.2
  • 驗證 Zookeeper Server 連通性
# 進入 Zookeeper Client 容器內部
kubectl exec -it zookeeper-client -- bash

# 連接 Zookeeper Server
bin/zkCli.sh -server 10.233.58.30:2181

# 成功結果如下
[root@ks-master-0 single]# kubectl exec -it zookeeper-client -- bash
root@zookeeper-client:/apache-zookeeper-3.8.2-bin# bin/zkCli.sh -server 10.233.58.30:2181
Connecting to 10.233.58.30:2181
2023-08-07 07:44:16,110 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:zookeeper.version=3.8.2-139d619b58292d7734b4fc83a0f44be4e7b0c986, built on 2023-07-05 19:24 UTC
2023-08-07 07:44:16,117 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:host.name=zookeeper-client
2023-08-07 07:44:16,117 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.version=11.0.20
2023-08-07 07:44:16,117 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.vendor=Eclipse Adoptium
2023-08-07 07:44:16,117 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.home=/opt/java/openjdk
2023-08-07 07:44:16,117 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.class.path=/apache-zookeeper-3.8.2-bin/bin/......(此處有省略)
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.io.tmpdir=/tmp
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:java.compiler=<NA>
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:os.name=Linux
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:os.arch=amd64
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:os.version=5.10.0-153.12.0.92.oe2203sp2.x86_64
2023-08-07 07:44:16,118 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:user.name=root
2023-08-07 07:44:16,119 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:user.home=/root
2023-08-07 07:44:16,119 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:user.dir=/apache-zookeeper-3.8.2-bin
2023-08-07 07:44:16,119 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:os.memory.free=42MB
2023-08-07 07:44:16,119 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:os.memory.max=256MB
2023-08-07 07:44:16,120 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:os.memory.total=48MB
2023-08-07 07:44:16,123 [myid:] - INFO  [main:o.a.z.ZooKeeper@637] - Initiating client connection, connectString=10.233.58.30:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@18bf3d14
2023-08-07 07:44:16,128 [myid:] - INFO  [main:o.a.z.c.X509Util@78] - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation
2023-08-07 07:44:16,134 [myid:] - INFO  [main:o.a.z.ClientCnxnSocket@239] - jute.maxbuffer value is 1048575 Bytes
2023-08-07 07:44:16,143 [myid:] - INFO  [main:o.a.z.ClientCnxn@1741] - zookeeper.request.timeout value is 0. feature enabled=false
Welcome to ZooKeeper!
2023-08-07 07:44:16,171 [myid:10.233.58.30:2181] - INFO  [main-SendThread(10.233.58.30:2181):o.a.z.ClientCnxn$SendThread@1177] - Opening socket connection to server zookeeper.default.svc.cluster.local/10.233.58.30:2181.
2023-08-07 07:44:16,173 [myid:10.233.58.30:2181] - INFO  [main-SendThread(10.233.58.30:2181):o.a.z.ClientCnxn$SendThread@1179] - SASL config status: Will not attempt to authenticate using SASL (unknown error)
2023-08-07 07:44:16,185 [myid:10.233.58.30:2181] - INFO  [main-SendThread(10.233.58.30:2181):o.a.z.ClientCnxn$SendThread@1011] - Socket connection established, initiating session, client: /10.233.118.8:55022, server: zookeeper.default.svc.cluster.local/10.233.58.30:2181
JLine support is enabled
2023-08-07 07:44:16,251 [myid:10.233.58.30:2181] - INFO  [main-SendThread(10.233.58.30:2181):o.a.z.ClientCnxn$SendThread@1452] - Session establishment complete on server zookeeper.default.svc.cluster.local/10.233.58.30:2181, session id = 0x1000178f5af0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: 10.233.58.30:2181(CONNECTED) 0]
  • 創建測試數據,驗證服務可用性
# 創建測試數據
[zk: 10.233.58.30:2181(CONNECTED) 0] create /test test-data1
Created /test

# 讀取測試數據
[zk: 10.233.58.30:2181(CONNECTED) 1] get /test
test-data1

在 K8s 集羣外部驗證。

本文直接使用 K8s Master-0 節點安裝 Zookeeper 客戶端進行測試驗證。

  • 安裝 openjdk,僅限於測試驗證。
yum install java-11-openjdk
  • 安裝 Zookeeper 客戶端,到 Zookeeper 官網找相應版本的軟件包。本文選擇 3.8.2 作爲測試版本。
# 下載並解壓 Zookeeper(在國內源下載)
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.8.2/apache-zookeeper-3.8.2-bin.tar.gz

tar xvf apache-zookeeper-3.8.2-bin.tar.gz
  • 驗證 Zookeeper Server 連通性
cd apache-zookeeper-3.8.2-bin/bin/
./zkCli.sh -server 192.168.9.91:32181

# 成功結果如下(結果有省略)
[root@ks-master-0 bin]# ./zkCli.sh -server 192.168.9.91:32181
/usr/bin/java
Connecting to 192.168.9.91:32181
2023-08-07 15:46:53,156 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:zookeeper.version=3.8.2-139d619b58292d7734b4fc83a0f44be4e7b0c986, built on 2023-07-05 19:24 UTC
......

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: 192.168.9.91:32181(CONNECTED) 0]
  • 創建測試數據,驗證服務可用性
# 創建測試數據
[zk: 192.168.9.91:32181(CONNECTED) 0] create /test2 test2-data1
Created /test2

# 讀取測試數據
[zk: 192.168.9.91:32181(CONNECTED) 1] get /test2
test2-data1

至此,Boss 交代的任務完成了一半,已經實現了單節點 Zookeeper 的部署,並在 K8s 集羣內部和外部分別做了連通性、可用性測試。

但是,時間已超期,已經來到了第二天,所以說啊,對於一個未知的任務,實現起來,根本沒有 Boss 想象的那麼簡單。

不過,由於完成了單節點的任務,先上交匯報給 Boss,並說明一下實現思路、過程,部署過程中遇到的問題及解決方案(切記不要直接跟 Boss 說這個很難,你預估的時間有問題,那麼說純屬找抽)。

按上面的套路彙報完,得到了 Boss 的理解和認可(Boss 其實還是很好說話的,只要你能以理說服他),讓我先把單節點的 Zookeeper 環境交給測試使用,再去繼續研究集羣模式的部署方案。

這波操作不僅沒有受到批評,還給自己爭取了時間,完美!!!

集羣模式 Zookeeper 部署

單節點部署 Zookeeper 的任務完成以後,接下來開始研究集羣模式的 Zookeeper 部署,AI 助手給出的默認示例根本就不靠譜,我也懶得再調教他了。

爲什麼這麼說?

因爲我利用 AI 助手給的方案,利用 DockerHub 提供的 Zookeeper 去嘗試在 K8s 集羣上部署 Zookeeper 集羣,耗時 2 天(主要是犯病了,鑽了牛角尖,就想搞定它,無奈能力又不夠!)

接下來簡單說一下,我被折磨瘋了的兩天都做了哪些嘗試、遇到了哪些問題、有哪些心得體會(逼得我都差點祭出第三板斧了)。

  • 集羣模式的關鍵解決 myid 和 servers 的配置
  • servers 的配置這個沒有問題很好解決,可以在配置文件中直接寫入或是用 ENV 的方式注入
  • myid 是重點,DockHub 鏡像倉庫中提供的 Zookeeper 鏡像,節點 myid 不能動態配置
  • 在實驗中嘗試了 initContainersSidecarContainer、ConfigMap 掛載啓動腳本等方式,都沒有起到效果
  • 不是說 DockHub 鏡像徹底不能用,只是需要進行啓動腳本改造,甚至需要重新打 Image,太麻煩了,已經耗時 2 天了,不得不暫時放棄
  • 上面幾種嘗試以及最後的成品資源配置清單的編寫,都是在 KubeSphere 的圖形化管理控制檯下測試驗證的,比命令行界面方便了太多
  • 心得: 通往成功的路有千萬條,一條不通時可以嘗試換條路,不要死磕到底。我們的目的是爲了解決問題,能解決問題的辦法就是好辦法,鑽牛角尖的精神也要分情況

最終只能另尋出路,好在之前的調研中,已經找到了另外兩種可能的解決方案。

  • 使用 Bitnami 製作的鏡像部署 Zookeeper 集羣(最終選擇)。
  • Kubernetes 官方文檔示例中介紹的方案,該方案使用鏡像 registry.k8s.io/kubernetes-zookeeper:1.0-3.4.10,使用 PodDisruptionBudget 確保服務可用性。

說一下最終的選型理由

  • Pod Disruption Budget,有點複雜不太適合我目前段位。

Pod Disruption Budget (Pod 干擾 預算) 簡稱 PDB,Kubernetes version >= 1.21 纔可以使用 PodDisruptionBudget。PDB 的作用是將限制在同一時間因自願干擾導致的多副本應用中發生宕機的 Pod 數量。

具體的知識點,本文不細說了,反正我目前也不打算用了(唉!主要是說不明白難免誤人子弟),有興趣的可以參考官方文檔的 PDB 介紹PDB 配置案例

  • Bitnami 製作的鏡像部署 Zookeeper 集羣,該方案網上的參考案例有很多,而且該方案採用 Zookeeper 原生部署方案,沒有額外的 K8S 機制,減少了複雜度。這個也是選擇的重點

接下來,我就開始嘗試使用 Bitnami 製作的 Zookeeper 鏡像完成 Zookeeper 集羣的部署。

思路梳理

在 K8s 集羣上部一套 Zookeeper 集羣需要的資源清單如下:

  • StatefulSet

  • Headless Service

  • ConfigMap:zoo.cfg(沒有使用,所有的配置都使用 ENV 的形式)

  • ConfigMap:setup.sh(啓動腳本,計劃使用實際沒有使用,最終採取了 ENV 和 Command 方式)

  • External Service(可選)

注意:由於本文配置方案沒有考慮安全配置僅適用於開發、測試環境。不要把本文的示例直接拿到生產環境使用,必須參考官方配置文檔增加相應的 ENV 配置,方可用於生產

資源配置清單

如無特殊說明,所有涉及 K8s 的操作都在 Master-0 節點上執行 , 配置文件根目錄爲 /srv/opsman/k8s-yaml

  • 創建資源清單文件夾
cd /srv/opsman/k8s-yaml
mkdir -p zookeeper/cluster
cd zookeeper/cluster
  • vi zookeeper-svc.yaml
---
# Headless Service,用於 Zookeeper 集羣之間相互通訊
apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  labels:
    app: zookeeper
spec:
  ports:
    - name: tcp-client
      protocol: TCP
      port: 2181
      targetPort: 2181
    - name: tcp-follower
      port: 2888
      targetPort: 2888
    - name: tcp-election
      port: 3888
      targetPort: 3888
  selector:
    app: zookeeper
  clusterIP: None
  type: ClusterIP

---
# Client Service,用於 K8S 集羣內的應用訪問 Zookeeper
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  labels:
    app: zookeeper
spec:
  ports:
    - name: tcp-client
      protocol: TCP
      port: 2181
      targetPort: 2181
  selector:
    app: zookeeper
  type: ClusterIP
  • vi zookeeper-sts.yaml
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zookeeper
  labels:
    app: zookeeper
spec:
  replicas: 3
  selector:
    matchLabels:
      app: zookeeper
  serviceName: zk-hs
  template:
    metadata:
      name: zookeeper
      labels:
        app: zookeeper
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zookeeper
              topologyKey: "kubernetes.io/hostname"
      containers:
        - name: zookeeper
          image: bitnami/zookeeper:3.8.2
          command:
            - bash
            - '-ec'
            - |
              HOSTNAME="$(hostname -s)"
              if [[ $HOSTNAME =~ (.*)-([0-9]+)$ ]]; then
                ORD=${BASH_REMATCH[2]}
                export ZOO_SERVER_ID="$((ORD + 1 ))"
              else
                echo "Failed to get index from hostname $HOST"
                exit 1
              fi
              exec /entrypoint.sh /run.sh
          resources:
            limits:
              cpu: 1
              memory: 2Gi
            requests:
              cpu: 50m
              memory: 500Mi
          env:
            - name: ZOO_ENABLE_AUTH
              value: "no"
            - name: ALLOW_ANONYMOUS_LOGIN
              value: "yes"
            - name: ZOO_SERVERS
              value: >
                zookeeper-0.zk-hs.default.svc.cluster.local:2888:3888
                zookeeper-1.zk-hs.default.svc.cluster.local:2888:3888
                zookeeper-2.zk-hs.ddefault.svc.cluster.local:2888:3888
          ports:
            - name: client
              containerPort: 2181
            - name: follower
              containerPort: 2888
            - name: election
              containerPort: 3888
          livenessProbe:
            tcpSocket:
              port: client
            failureThreshold: 6
            initialDelaySeconds: 30
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 5
          readinessProbe:
            tcpSocket:
              port: client
            failureThreshold: 6
            initialDelaySeconds: 5
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 5
          volumeMounts:
            - name: data
              mountPath: /bitnami/zookeeper
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: "glusterfs"
        resources:
          requests:
            storage: 2Gi

說明:

  • ENV 的配置我只用了最基本的,重點就是 ZOO_SERVERS,更多參數的用法請參考 Bitnami 官方文檔
  • Command 裏直接寫了啓動命令,也可以做成 ConfigMap 掛載爲腳本
  • default.svc.cluster.local,注意 FQDN 只能這麼寫,不要寫成自定義的,哪怕我的集羣域名是 opsman.top(這個是遺留問題,來不及細看了,回頭再說)
  • vi zookeeper-external-svc.yaml
---
# External Client Service,用於 K8S 集羣外部訪問 Zookeeper
apiVersion: v1
kind: Service
metadata:
  name: zookeeper-external-svc
  labels:
    app: zookeeper-external-svc
spec:
  ports:
    - name: tcp-zookeeper-external
      protocol: TCP
      port: 2181
      targetPort: 2181
      nodePort: 32181
  selector:
    app: zookeeper
  type: NodePort

注意:可選配置項,如果不需要被 K8s 集羣之外的服務訪問,則不需要配置。

部署資源

  • 部署 Cluster 和 Headless Service
kubectl apply -f zookeeper-svc.yaml
  • 部署 StatefulSet
kubectl apply -f zookeeper-sts.yaml
  • 部署外部 Services
kubectl apply -f zookeeper-external-svc.yaml

K8s 部署資源驗證

  • 驗證 PersistentVolumeClaim
[root@ks-master-0 cluster]# kubectl get pvc -o wide
NAME               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE    VOLUMEMODE
data-zookeeper-0   Bound    pvc-342c3869-17ca-40c7-9db0-755d5af0f85f   2Gi        RWO            glusterfs      2m7s   Filesystem
data-zookeeper-1   Bound    pvc-6744813f-0f5b-4138-8ffc-387f63044af3   2Gi        RWO            glusterfs      47s    Filesystem
data-zookeeper-2   Bound    pvc-731edc8d-189a-4601-aa64-a8d6754d93ec   2Gi        RWO            glusterfs      28s    Filesystem
  • 驗證 StatefulSet
[root@ks-master-0 cluster]# kubectl get sts -o wide
NAME        READY   AGE    CONTAINERS   IMAGES
zookeeper   3/3     2m3s   zookeeper    bitnami/zookeeper:3.8.2
  • 驗證 Pod
[root@ks-master-0 cluster]# kubectl get pod -o wide
NAME               READY   STATUS    RESTARTS   AGE     IP              NODE          NOMINATED NODE   READINESS GATES
zookeeper-0        1/1     Running   0          2m42s   10.233.118.45   ks-worker-2   <none>           <none>
zookeeper-1        1/1     Running   0          83s     10.233.120.17   ks-worker-1   <none>           <none>
zookeeper-2        1/1     Running   0          64s     10.233.115.99   ks-worker-0   <none>           <none>
  • 驗證 Service
[root@ks-master-0 cluster]# kubectl get svc -o wide
NAME                                                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE     SELECTOR
zk-cs                                                    ClusterIP   10.233.43.229   <none>        2181/TCP                     3m58s   app=zookeeper
zk-hs                                                    ClusterIP   None            <none>        2181/TCP,2888/TCP,3888/TCP   3m58s   app=zookeeper
zookeeper-external-svc                                   NodePort    10.233.45.5     <none>        2181:32181/TCP               10s     app=zookeeper

Zookeeper 集羣狀態驗證

  • 驗證 StatefulSet 創建的 Pod 配置的主機名
[root@ks-master-0 cluster]# for i in 0 1 2; do kubectl exec zookeeper-$i -- hostname; done
zookeeper-0
zookeeper-1
zookeeper-2
  • 驗證 StatefulSet 創建的 Pod 配置的完全限定域名(Fully Qualified Domain Name,FQDN)
[root@ks-master-0 cluster]# for i in 0 1 2; do kubectl exec zookeeper-$i -- hostname -f; done
zookeeper-0.zk-hs.default.svc.cluster.local
zookeeper-1.zk-hs.default.svc.cluster.local
zookeeper-2.zk-hs.default.svc.cluster.local
  • 驗證每個 Zookeeper 服務的 myid 文件內容
[root@ks-master-0 cluster]# for i in 0 1 2; do echo "myid zookeeper-$i";kubectl exec zookeeper-$i -- cat /bitnami/zookeeper/data/myid; done
myid zookeeper-0
1
myid zookeeper-1
2
myid zookeeper-2
3
  • 驗證 Zookeeper 生成的配置文件
[root@ks-master-0 cluster]# kubectl exec -it zookeeper-0 --  cat /opt/bitnami/zookeeper/conf/zoo.cfg | grep -vE "^#|^$"
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/bitnami/zookeeper/data
clientPort=2181
maxClientCnxns=60
autopurge.snapRetainCount=3
autopurge.purgeInterval=0

preAllocSize=65536
snapCount=100000
maxCnxns=0
reconfigEnabled=false
quorumListenOnAllIPs=false
4lw.commands.whitelist=srvr, mntr
maxSessionTimeout=40000
admin.serverPort=8080
admin.enableServer=true
server.1=zookeeper-0.zk-hs.default.svc.cluster.local:2888:3888;2181
server.2=zookeeper-1.zk-hs.default.svc.cluster.local:2888:3888;2181
server.3=zookeeper-2.zk-hs.default.svc.cluster.local:2888:3888;2181
  • 驗證集羣狀態
[root@ks-master-0 cluster]# for i in 0 1 2; do echo -e "# myid zookeeper-$i \n";kubectl exec zookeeper-$i -- /opt/bitnami/zookeeper/bin/zkServer.sh status;echo -e "\n"; done
# myid zookeeper-0

/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: leader


# myid zookeeper-1

/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower


# myid zookeeper-2

/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower

Zookeeper 服務可用性驗證

在 K8s 集羣內部驗證

  • 在 K8s 上創建一個 Zookeeper Client Pod 驗證(單節點驗證時創建過,不需要再創建了)
kubectl run zookeeper-client --image=zookeeper:3.8.2
  • 驗證 Zookeeper Server 連通性
# 進入 Zookeeper Client 容器內部
kubectl exec -it zookeeper-client -- bash

# 連接 Zookeeper Server( 10.233.43.229 是 Cluster Service 的 IP)
bin/zkCli.sh -server 10.233.43.229:2181

# 成功結果如下(內容有省略)
[root@ks-master-0 cluster]# kubectl exec -it zookeeper-client -- bash
root@zookeeper-client:/apache-zookeeper-3.8.2-bin# bin/zkCli.sh -server 10.233.43.229:2181
Connecting to 10.233.43.229:2181
2023-08-08 10:08:40,864 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:zookeeper.version=3.8.2-139d619b58292d7734b4fc83a0f44be4e7b0c986, built on 2023-07-05 19:24 UTC
.....
2023-08-08 10:08:40,872 [myid:] - INFO  [main:o.a.z.ZooKeeper@637] - Initiating client connection, connectString=10.233.43.229:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@18bf3d14
2023-08-08 10:08:40,886 [myid:] - INFO  [main:o.a.z.c.X509Util@78] - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation
2023-08-08 10:08:40,892 [myid:] - INFO  [main:o.a.z.ClientCnxnSocket@239] - jute.maxbuffer value is 1048575 Bytes
2023-08-08 10:08:40,903 [myid:] - INFO  [main:o.a.z.ClientCnxn@1741] - zookeeper.request.timeout value is 0. feature enabled=false
Welcome to ZooKeeper!
2023-08-08 10:08:40,920 [myid:10.233.43.229:2181] - INFO  [main-SendThread(10.233.43.229:2181):o.a.z.ClientCnxn$SendThread@1177] - Opening socket connection to server zk-cs.default.svc.cluster.local/10.233.43.229:2181.
2023-08-08 10:08:40,923 [myid:10.233.43.229:2181] - INFO  [main-SendThread(10.233.43.229:2181):o.a.z.ClientCnxn$SendThread@1179] - SASL config status: Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2023-08-08 10:08:40,948 [myid:10.233.43.229:2181] - INFO  [main-SendThread(10.233.43.229:2181):o.a.z.ClientCnxn$SendThread@1011] - Socket connection established, initiating session, client: /10.233.118.8:38050, server: zk-cs.default.svc.cluster.local/10.233.43.229:2181
2023-08-08 10:08:41,064 [myid:10.233.43.229:2181] - INFO  [main-SendThread(10.233.43.229:2181):o.a.z.ClientCnxn$SendThread@1452] - Session establishment complete on server zk-cs.default.svc.cluster.local/10.233.43.229:2181, session id = 0x10007253d840000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: 10.233.43.229:2181(CONNECTED) 0]
  • 創建測試數據,驗證服務可用性
# 創建測試數據
[zk: 10.233.43.229:2181(CONNECTED) 0] create /test test-data1
Created /test

# 讀取測試數據
[zk: 10.233.43.229:2181(CONNECTED) 1] get /test
test-data1

在 K8s 集羣外部驗證。

  • 驗證 Zookeeper Server 連通性
# 進入 Zookeeper 安裝包的 bin 目錄
cd apache-zookeeper-3.8.2-bin/bin/

# 連接 Zookeeper Server( 192.168.9.91 是 K8S Master-0 節點的 IP,32181 是 External Service 定義的 NodePort 端口號)
./zkCli.sh -server 192.168.9.91:32181

# 成功結果如下(結果有省略)
[root@ks-master-0 bin]# ./zkCli.sh -server 192.168.9.91:32181
/usr/bin/java
Connecting to 192.168.9.91:32181
2023-08-08 18:13:52,650 [myid:] - INFO  [main:o.a.z.Environment@98] - Client environment:zookeeper.version=3.8.2-139d619b58292d7734b4fc83a0f44be4e7b0c986, built on 2023-07-05 19:24 UTC
......
2023-08-08 18:13:52,660 [myid:] - INFO  [main:o.a.z.ZooKeeper@637] - Initiating client connection, connectString=192.168.9.91:32181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@5c072e3f
2023-08-08 18:13:52,666 [myid:] - INFO  [main:o.a.z.c.X509Util@78] - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation
2023-08-08 18:13:52,671 [myid:] - INFO  [main:o.a.z.ClientCnxnSocket@239] - jute.maxbuffer value is 1048575 Bytes
2023-08-08 18:13:52,686 [myid:] - INFO  [main:o.a.z.ClientCnxn@1741] - zookeeper.request.timeout value is 0. feature enabled=false
Welcome to ZooKeeper!
2023-08-08 18:13:52,708 [myid:192.168.9.91:32181] - INFO  [main-SendThread(192.168.9.91:32181):o.a.z.ClientCnxn$SendThread@1177] - Opening socket connection to server ks-master-0/192.168.9.91:32181.
2023-08-08 18:13:52,709 [myid:192.168.9.91:32181] - INFO  [main-SendThread(192.168.9.91:32181):o.a.z.ClientCnxn$SendThread@1179] - SASL config status: Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2023-08-08 18:13:52,721 [myid:192.168.9.91:32181] - INFO  [main-SendThread(192.168.9.91:32181):o.a.z.ClientCnxn$SendThread@1011] - Socket connection established, initiating session, client: /192.168.9.91:45004, server: ks-master-0/192.168.9.91:32181
2023-08-08 18:13:52,776 [myid:192.168.9.91:32181] - INFO  [main-SendThread(192.168.9.91:32181):o.a.z.ClientCnxn$SendThread@1452] - Session establishment complete on server ks-master-0/192.168.9.91:32181, session id = 0x10007253d840001, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: 192.168.9.91:32181(CONNECTED) 0]

  • 創建測試數據,驗證服務可用性
# 創建測試數據
[zk: 192.168.9.91:32181(CONNECTED) 0] create /test2 test2-data1
Created /test2

# 讀取測試數據(讀取了 2次 測試數據)
[zk: 192.168.9.91:32181(CONNECTED) 1] get /test
test-data1
[zk: 192.168.9.91:32181(CONNECTED) 2] get /test2
test2-data1

至此,實現了 Zookeeper 集羣模式部署,並在 K8S 集羣內部和外部分別做了連通性、可用性測試。

在 KubeSphere 管理控制檯驗證

截幾張圖看一看 Zookeeper 相關資源在 KubeSphere 管理控制檯中展示效果。

  • StatefulSet

  • Pods

  • Service

總結

本文詳細介紹了 Zookeeper 單節點和集羣模式在基於 KubeSphere 部署的 K8s 集羣上的安裝部署、測試驗證的過程。具體涉及的內容總結如下。

  • 如何利用 AI 助手 和 搜索引擎輔助完成運維工作。
  • 如何利用 DockerHub 官方提供的 Zookeeper 鏡像,在 K8s 集羣上部署單節點 Zookeeper 服務並驗證測試。
  • 如何利用 Bitnami 提供的 Zookeeper 鏡像,在 K8s 集羣上部署 Zookeeper 集羣服務並驗證測試。
  • 介紹了一種使用 PodDisruptionBudget 部署 Zookeeper 集羣的示例,但是並未實際驗證。

本文的配置方案可直接用於開發測試環境,對於生產環境也有一定的借鑑意義。

本文由博客一文多發平臺 OpenWrite 發佈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章