Kafka上K8S實戰

得物技術發表於2022-03-02

背景

隨著業務規模的發展,需要的kafka叢集越來越來,這給部署與管理帶來了很大的挑戰。我們期望能夠利用K8S優秀的擴容能力與快速部署能力,為日常的工作減負。所以就kafka上K8S的可行性方案進行了調研。

像kafka叢集這種,涉及到的元件比較多,且都是有狀態的叢集,業界採用自定義operator的解決方案。目前GitHub上有多個相關的倉庫,根據社群活躍度及使用數等綜合考慮,此次採用Strimzi Github地址

kafka元件互動圖

方案

  1. 使用阿里雲K8S叢集部署Strimzi
  2. 由於組內使用的kafka是由開源版本二次開發而來,所以需要維護一個自定義的Strimzi-kafka映象
  3. Strimzi管理kafka叢集,其中包含kafka、zk、kafka-exporter、
  4. 使用zoo-entrance 代理叢集中的zk GitHub地址
  5. 部署prometheus,採集kafka和zk的metrics
  6. 開啟服務埠,暴露kafka及zk給K8S叢集外部使用

實戰過程

構建自定義kafka映象

  • 從公司Git上拉取最新程式碼 strimzi-kafka-operator (與開源版本有些微的改動,做實驗可直接用開源版)
  • 在docker-images 資料夾下,有個Makefile檔案,執行其中的docker_build, 它會去執行其中的build.sh指令碼;此步會從官網拉取kafka的安裝包,我們需要將這一步的包修改為我司內部的安裝包。
  • 構建完映象,映象在本地,我們需要將映象上傳到公司內部的harbor伺服器上

部署operator

每個K8S叢集僅需部署一個operator

  • 充分必要條件:一個健康的k8s叢集
  • 建立namespace, 如已有則跳過,預設使用kafka,kubectl create namespace kafka
  • 從公司Git上拉取最新程式碼(地址在前邊)
  • 目前檔案中預設監聽的是名稱為 kafka 的namespace,如果需要修改則執行 sed -i 's/namespace: ./namespace: kafka/' install/cluster-operator/RoleBinding*.yaml (將命令中的kafka/ 替換掉)
  • 然後將所有檔案都應用一下 kubectl apply -f install/cluster-operator/ -n kafka
  • 此時稍等片刻,就能檢視到建立的自定義資源以及operator了 kubectl get pods -nkafka,
  • 從阿里雲的k8s管控臺檢視這些資源的建立情況,以及operator的執行情況

部署kafka叢集

確保你的operator已經部署成功,且kafka部署的namespace需在上邊operator的監控中

  • 還是來到最新的程式碼目錄中,其中examples/kafka目錄下邊就是本次部署所需要的檔案了
  • 部署 kafka及zk
    • 檢視kafka-persistent.yaml, 該檔案就是核心檔案了,這個檔案部署了kafka與zk及kafka-exporter, 部分內容如下:
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
  name: my-cluster
spec:
  kafka:
    version: 2.8.1
    replicas: 3
    resources:
      requests:
        memory: 16Gi
        cpu: 4000m
      limits:
        memory: 16Gi
        cpu: 4000m
    image: repository.poizon.com/kafka-operator/poizon/kafka:2.8.4
    jvmOptions:
      -Xms: 3072m
      -Xmx: 3072m
    listeners:
      - name: external
        port: 9092
        type: nodeport
        tls: false
      - name: plain
        port: 9093
        type: internal
        tls: false
    config:
      offsets.topic.replication.factor: 2
      transaction.state.log.replication.factor: 2
      transaction.state.log.min.isr: 1
      default.replication.factor: 2
      ***
    template:
      pod:
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                    - key: strimzi.io/name
                      operator: In
                      values:
                        - my-cluster-kafka
                topologyKey: "kubernetes.io/hostname"
    storage:
      type: persistent-claim
      size: 100Gi
      class: rocketmq-storage
      deleteClaim: false
    metricsConfig:
      type: jmxPrometheusExporter
      valueFrom:
        configMapKeyRef:
          name: kafka-metrics
          key: kafka-metrics-config.yml
  zookeeper:
    replicas: 3
    resources:
      requests:
        memory: 3Gi
        cpu: 1000m
      limits:
        memory: 3Gi
        cpu: 1000m
    jvmOptions:
      -Xms: 2048m
      -Xmx: 2048m
    jmxOptions: {}
    template:
      pod:
        affinity:
          podAntiAffinity:
          ***
    storage:
      type: persistent-claim
      size: 50Gi
      class: rocketmq-storage
      deleteClaim: false
    metricsConfig:
      type: jmxPrometheusExporter
      valueFrom:
        configMapKeyRef:
          name: kafka-metrics
          key: zookeeper-metrics-config.yml
    ***
    ***
    • 可修改kafka叢集的名稱,在第四行的name屬性,目前預設為 my-cluster
    • 可修改kafka的Pod個數,即節點數,預設為3
    • 可修改Pod配置 記憶體CPU
    • 可修改kafka JVM 啟動的堆記憶體大小
    • 可修改kafka的配置,在36行 config配置
    • 可修改磁碟型別及大小,型別為第50行,可修改為其它的儲存類,目前可選為高效雲盤、SSD、ESSD
    • zk修改同kafka,可修改的東西類似, 且在同一個檔案中
    • 檔案下邊是kafka與zk需要暴露的metrics,可按需求增刪改
    • 修改完配置之後,直接執行 kubect apply -f kafka-persistent.yaml -nkafka 即可建立
  • 部署 zk代理
    • 部署完zk的代理,我們需要在k8s控制檯上 建立一個loadbalance服務將這個代理暴露給叢集外的應用進行連線。具體操作:k8s控制檯-->網路-->服務-->建立(選擇loadbalance建立,然後找到zoo-entrance這個應用即可)
  • 部署 zk-exporter
    • 官方operator中沒有zk-exporter, 我們採用 https://github.com/dabealu/zo...
    • 在資料夾中的zk-exporter.yaml 檔案中,我們僅需要修改被監聽的zk的地址(spec.container.args)
    • 執行kubectl apply -f zk-exporter.yaml即可部署完成
  • 部署 kafka-jmx
    • 由於ingress不支援tcp連線,而loadbalance的成本又過高,所以kafka 的 jmx 使用nodeport對外暴露
    • 可以在阿里雲控制檯上建立相應的nodeport,也可以使用kafka-jmx.yaml 檔案的方式建立
apiVersion: v1
kind: Service
metadata:
  labels:
    strimzi.io/cluster: my-cluster
    strimzi.io/name: my-cluster-kafka-jmx
  name: my-cluster-kafka-jmx-0
spec:
  ports:
    - name: kafka-jmx-nodeport
      port: 9999
      protocol: TCP
      targetPort: 9999
  selector:
    statefulset.kubernetes.io/pod-name: my-cluster-kafka-0
    strimzi.io/cluster: my-cluster
    strimzi.io/kind: Kafka
    strimzi.io/name: my-cluster-kafka
  type: NodePort
  • 部署 kafka-exporter-service
    • 前面部署完kafka之後,我們的配置中是開啟了exporter的。但是官方開啟完exporter之後,並沒有自動生成一個相關的service,為了讓Prometheus連線更加方便,我們部署了一個service
    • 在資料夾中kafka-exporter-service.yaml 檔案中
apiVersion: v1
kind: Service
metadata:
  labels:
    app: kafka-export-service
  name: my-cluster-kafka-exporter-service
spec:
  ports:
    - port: 9404
      protocol: TCP
      targetPort: 9404
  selector:
    strimzi.io/cluster: my-cluster
    strimzi.io/kind: Kafka
    strimzi.io/name: my-cluster-kafka-exporter
  type: ClusterIP
    • 執行kubectl apply -f kafka-exporter-service.yaml即可部署完成
  • 部署 kafka-prometheus
    • 如果將Prometheus部署在k8s叢集外,資料採集會比較麻煩,所以我們直接將Prometheus部署到叢集內
    • 在資料夾中kafka-prometheus.yaml檔案中,可以選擇性的修改其中prometheus的配置,比如需要的記憶體CPU的大小,比如監控資料儲存時間,外掛的雲盤大小,以及需要監聽的kafka與zk地址
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafka-prometheus
  labels:
    app: kafka-prometheus
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: kafka-prometheus
  serviceName: kafka-prometheus
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: kafka-prometheus
    spec:
      containers:
        - args:
            - '--query.max-concurrency=800'
            - '--query.max-samples=800000000'
            ***
          command:
            - /bin/prometheus
          image: 'repository.poizon.com/prometheus/prometheus:v2.28.1'
          imagePullPolicy: IfNotPresent
          livenessProbe:
            failureThreshold: 10
            httpGet:
              path: /status
              port: web
              scheme: HTTP
            initialDelaySeconds: 300
            periodSeconds: 5
            successThreshold: 1
            timeoutSeconds: 3
          name: kafka-prometheus
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
            requests:
              cpu: 200m
              memory: 128Mi
          volumeMounts:
            - mountPath: /etc/localtime
              name: volume-localtime
            - mountPath: /data/prometheus/
              name: kafka-prometheus-config
            - mountPath: /data/database/prometheus
              name: kafka-prometheus-db
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      terminationGracePeriodSeconds: 30
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 0
      volumes:
        - hostPath:
            path: /etc/localtime
            type: ''
          name: volume-localtime
        - configMap:
            defaultMode: 420
            name: kafka-prometheus-config
          name: kafka-prometheus-config
  volumeClaimTemplates:
    - apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: kafka-prometheus-db
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 20Gi
        storageClassName: rocketmq-storage
        volumeMode: Filesystem
      status:
        phase: Pending
    • 執行kubectl apply -f kafka-prometheus.yaml即可部署完成
    • 部署完成後將prometheus暴露給監控組的grafana,可以直連pod IP做驗證,然後在k8s管控臺的 網路-->路由-->建立, 建立一個ingress,選擇剛剛部署的這個Prometheus的service,然後找運維申請域名,即可。

總結

  • 優點
    • 快速部署叢集(分鐘級),快速叢集擴容(秒級),快速災難恢復(秒級)
    • 支援滾動更新,支援備份以及還原
  • 缺點
    • 引入較多元件,複雜度升高
    • 對K8S叢集外的訪問不太友好

文/ZUOQI

關注得物技術,做最潮技術人!

相關文章