iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0
自我挑戰組

從 RedHat OpenShift 社群版 OKD 看 Kubernetes系列 第 13

[偷一下未來進度]Day 13 :Kubernetes 純手動部署與設定 EFK (3/4)

繼續來完成昨天未解決的 Kubernetes 部署 Elasticsearch Cluster 的相關問題,例如在部署多個 Elasticsearch Master 角色時會出現腦裂問題以及如何整合永久性儲存、角色與Production mode要設定的東西。今天會一次性的將這些東西講解完並且實際部署到 Kubernetes 環境上,我自己認為 EFK 最困難的點是在於 Elasticsearch Cluster 的部署,過了這個坑後就沒什麼太大的痛點

整合

max_map_count-sidecar mode

要先按照Elasticsearch Production mode的建議將 Elasticsearch Pod Linux Kernel 中的 max_map_count 參數調整到262144,此一部分我採用的是 sidecar 模式,透過 sidecar 幫我處理 max_map_count 參數。

...
      initContainers:
      - image: alpine:3.6
        command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
        name: elasticsearch-logging-init
        securityContext:
          privileged: true
...

Elasticsearch master/data node-muti deployment

設定 Elasticsearch 角色,由於 Elasticsearch 系統節點可以分為兩個角色( Master Node 與 Worker Node ),這邊我會分成兩個 Deployment 來部署,其中一個 Deployment 專門用來部署 Elasticsearch Master Node ,另外一個 Deployment 用來部署 Elasticsearch Data Node。

          env:
            - name: "cluster.name"
              value: "elasticsearch-cluster"
            - name: "node.master"
              value: "true"
            - name: "node.data"
              value: "false"
            - name: "node.ingest"
              value: "false"

[color=#31ce36]主要是修改環境中的node.master要改成false,因為這個環境變數代表該節點的是什麼角色。[name=jason ]

          env:
            - name: "cluster.name"
              value: "elasticsearch-cluster"
            - name: "node.master"
              value: "false"
            - name: "node.data"
              value: "true"
            - name: "node.ingest"
              value: "false"

Elasticsearch master split brain problem/ setting params

  1. 由於多個 Elasticsearch Master Node 在多個情況下會發生腦裂問題的問題,這邊可以調整環境變數來預防腦裂開。

這邊稍微簡單的解釋一下為什麼會有腦裂的問題(由於不是本文的重點,就輕鬆的帶過xD),一般來說正常的 Elasticsearch 叢集系統都是由統一一台 Master Node 進行管理的,一但 Master Node 死掉之後會進行選舉再推舉出一台新的 Master Node ,但是有一種情況同時有兩個 Master Node ,這時候有一部分的 Data Node 由一號 Master Node 管理,另外一部分由二號 Master Node管理,那麼問題來了....資料到底要傳到哪裡去?誰才是真正的 Master Node?

OK,這些 Elasticsearch 其實都有幫我們想好,只要修改配置參數就可以大幅度的預防腦裂的問題。

discovery.zen.minimum_master_nodes

這個參數用來決定 Elasticsearch 叢集在選舉過程中需要候選節點除來選舉(有點類似選總統時候至少要多少候選人:"P),關分野給出了一個原則這個參數基本上是設定成 N/2 +1 ( N 是叢集節點的數量,例如五個節點的 Elasticsearch 叢集中, discovery.zen.minimum_master_nodes 原則上應該要設定成 5/2 + 1 = 3 (除法的部分四捨五入)

...
            - name: "discovery.zen.minimum_master_nodes"
              value: "3"
            - name: "discovery.zen.ping_timeout"
              value: "5s"
...

Elasticsearch Data Node Persistent Volumes/ NFS StorageClass

issue03-永久性儲存問題

因為 Kubernetes 系統架構中 Pod 元件是具有臨時且不可回復的特性一旦因外在因素或是不預期情況服務中斷,儲存於 Pod 上的資料也會跟著 Pod 一起被刪除。若重新啟動新的 Pod 以取代r舊的 Pod ,新啟動的 Pod 仍然無法得知舊的 Pod 儲存了什麼資料,所以也無法回覆舊 Pod 的已儲存的資訊。

為了避免 Elasticsearch Pod 故障(搬遷、實際節點損壞、污染...現實生活中有很多可怕的情況)導致資料遺失,我在這邊採用 NFS 將 Elasticsearch 的資料儲存於 NFV 上, 因為我們要可以隨時擴展 Elasticsearch 節點,所以要生成很多的 (Persistent Volumes,PV) 與 (Persistent Volumes Claim,PVC),這個需求我們可以依賴Storage Classes的幫忙可以減少很多麻煩,Yaml 設定檔如下所示。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: log
provisioner: fuseim.pri/ifs

融合

結合上述要做的事情後,在這個地方做一個總整理。

  1. 設定 max_map_count-sidecar
  2. Deployment 設定 Master Node 與 Data Node
  3. 設定環境變數減少腦裂情況
  4. 部署永久性儲存空間

首先我先處理 Elasticsearch Master NodeDeployment檔,設定如下方yaml所示。

kind: Deployment
apiVersion: apps/v1
metadata:
  labels:
    app: elasticsearch
    role: master
  name: elasticsearch-master
  namespace: log
spec:
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
      role: master
  template:
    metadata:
      labels:
        app: elasticsearch
        role: master
    spec:
      containers:
        - name: elasticsearch-master
          image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3
          ports:
            - containerPort: 9200
              protocol: TCP
            - containerPort: 9300
              protocol: TCP
          volumeMounts:
          - name: data
            mountPath: /usr/share/elasticsearch/data
          env:
            - name: "cluster.name"
              value: "elasticsearch-cluster"
            - name: "discovery.zen.ping.unicast.hosts"
              value: "elasticsearch-discovery"
            - name: "discovery.zen.minimum_master_nodes"
              value: "3"
            - name: "discovery.zen.ping_timeout"
              value: "5s"
            - name: "node.master"
              value: "true"
            - name: "node.data"
              value: "false"
            - name: "node.ingest"
              value: "false"
            - name: "ES_JAVA_OPTS"
              value: "-Xms256m -Xmx256m"
      initContainers:
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      volumes:
          - emptyDir: {}
            name: "data"
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: elasticsearch
  name: elasticsearch-discovery
  namespace: log
spec:
  ports:
    - port: 9300
      targetPort: 9300
  selector:
    app: elasticsearch
    role: master

部署完成後我們可以透過 kubectl 指令確認部署狀況,如此一來就得到三個 Master 的 elasticsearch 叢集了。

$kubectl get pod -n log                                           

NAME                                      READY   STATUS    RESTARTS   AGE
elasticsearch-master-65c699ddfd-fsrc5     1/1     Running   0          75m
elasticsearch-master-65c699ddfd-lhfff     1/1     Running   0          75m
elasticsearch-master-65c699ddfd-qx8j6     1/1     Running   0          75m

[color=#31ce36]從 yaml 檔我們可以觀察到,使用 init container 作為 sidecar 處理 max_map_count 。設置環境變數代表此 Deployment 所建立出來的 Pod 都是 Master ,另外也設定好預防腦裂的參數。由於 Master Node 有問題砍掉時不會影響資料,所以不用特別使用 StorageClass 去處理 Persistent Volume 的問題。
[name=jason ]

接著要來處理的是Elasticsearch Data Nodestatefulsets 檔,由於 statefulsets 處理 volume 的時候會需要 StoageClass,所以我們這邊要先把 StroageClass 的相關元件部署起來,這邊使用的是 NFS StoageClass,部署方式跟流程可以參考超哥的 Kubernetes handbook 其中的利用 NFS 动态提供 Kubernetes 后端存储卷

完成 NFS StoageClass 相關元件部署後,接著建立 StroageClass , Yaml檔如下所示。我們可以透過kubectl指令看到 nfs-client-provisioner 與 nfs StorageClass 正在運行,

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs
parameters:
  archiveOnDelete: "false"
provisioner: fuseim.pri/ifs
reclaimPolicy: Delete
volumeBindingMode: Immediate
$kubectl get pod,sc

NAME                                        PROVISIONER      AGE
storageclass.storage.k8s.io/elasticsearch   fuseim.pri/ifs   23h

NAME                                          READY   STATUS    RESTARTS   AGE
pod/nfs-client-provisioner-77577d595f-ft7qv   1/1     Running   0          94m

看到這邊表示我們已經完成了Volume的設定,可以繼續設定Elasticsearch Data Nodestatefulsets 檔,Elasticsearch Data Node 主要跟 Master Node 不一樣的地方是環境變數,需要將 node.master 改為 false ,代表該節點不為 Master 角色, yaml 檔設定如下。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch-data
  namespace: log
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
      role: data
  template:
    metadata:
      labels:
        app: elasticsearch
        role: data
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3
        ports:
        - containerPort: 9200
          protocol: TCP
        - containerPort: 9300
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: "cluster.name"
            value: "elasticsearch-cluster"
          - name: "discovery.zen.ping.unicast.hosts"
            value: "elasticsearch-discovery"
          - name: "node.master"
            value: "false"
          - name: "node.data"
            value: "true"
          - name: "ES_JAVA_OPTS"
            value: "-Xms256m -Xmx256m"
      initContainers:
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      namespace: log
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: nfs
      resources:
        requests:
          storage: 10Gi

部署完 Elasticsearch Data Node 後一樣我們透過 kubectl 去檢查部署情況,現在我們得到了一個完整的 Elasticsearch 叢集其中包含三台 Master Node 以及三台 Data Node。

$kubectl get pod -n log

NAME                                      READY   STATUS    RESTARTS   AGE
elasticsearch-data-0                      1/1     Running   0          30m
elasticsearch-data-1                      1/1     Running   0          30m
elasticsearch-data-2                      1/1     Running   0          29m
elasticsearch-master-65c699ddfd-fsrc5     1/1     Running   0          78m
elasticsearch-master-65c699ddfd-lhfff     1/1     Running   0          78m
elasticsearch-master-65c699ddfd-qx8j6     1/1     Running   0          78m

檢查叢集狀態

完成了以上非常麻煩的過程部署與設定之後,就要來驗證剛剛部署的 Elasticsearch 叢集是否能正常運作,這邊坐兩個非常簡單的測試。

  1. 查看 Elasticsearch 叢集狀態

我們可以透過 kubectl 指令獲取 Elasticsearch Pod 的 IP 資料,如 elasticsearch-master-65c699ddfd-fsrc5 這過 Pod 的 IP 是 10.233.92.33 ,知道他 IP 之後可以透過 Elasticsearch 提供的叢集環境檢查 API 檢查目前叢集的狀態。

$kubectl get pod elasticsearch-master-65c699ddfd-fsrc5 -o wide  -n log

NAME                                    READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
elasticsearch-master-65c699ddfd-fsrc5   1/1     Running   0          89m   10.233.92.33   node3   <none>           <none>

$curl 10.233.92.33:9200/_cluster/state\?pretty

{
  "cluster_name" : "elasticsearch-cluster",
  "compressed_size_in_bytes" : 517,
  "cluster_uuid" : "DijI_fcMRoGnjTkbeduF6g",
  "version" : 6,
  "state_uuid" : "aDM23u9XRy-MfYW5ef_Wtg",
  "master_node" : "9WHQl6_BRC-dO3rfhOEMrg",
  "blocks" : { },
  "nodes" : {
    "9WHQl6_BRC-dO3rfhOEMrg" : {
      "name" : "9WHQl6_",
      "ephemeral_id" : "V7JKhexdTtSiJtiDZXLCuA",
      "transport_address" : "10.233.92.33:9300",
      "attributes" : { }
    },
    "9WHQl6_BRC-dO3rfhOEMrg" : {
      "name" : "9WHQl6_",
      "ephemeral_id" : "V7JKhexdTtSiJtiDZXLCuA",
      "transport_address" : "10.233.92.33:9300",
      "attributes" : { }
    },
...

從上面這一坨資料可以看到現在 Master Node是誰,也就是 9WHQl6_BRC-dO3rfhOEMrg 這個傢伙...說實話我這邊沒設定好,沒有辦法一眼清楚的看出來Master Node 是哪個 Pod ,不過我們用那個醜醜的代號到下面 nodes 那一坨裡面找出他的 IP ,找到他的 IP 就可以知道他是哪個 Pod 囉。

  1. 刪除 Master Node 觀察 Elasticsearch 叢集狀態

接著我們可以試著刪除看看 Master Node ,按照上一小點所觀察到的目前 Elasticsearch 叢集 Master Node 是 10.233.92.33 這個傢伙,從往上找可以發現這一組 IP 是對應到 elasticsearch-master-65c699ddfd-fsrc5 這個Pod。

接著透過 kubectl 指令刪除這個 Pod 觀察 Elasticsearch 叢集環境的變化。

$kubectl get pod -n log

pod/elasticsearch-master-65c699ddfd-2bmr7     1/1     Running   0          20s
pod/elasticsearch-master-65c699ddfd-lhfff     1/1     Running   0          101m
pod/elasticsearch-master-65c699ddfd-qx8j6     1/1     Running   0          101m
...

$curl 10.233.96.35:9200/_cluster/state\?pretty

{
  "cluster_name" : "elasticsearch-cluster",
  "compressed_size_in_bytes" : 519,
  "cluster_uuid" : "DijI_fcMRoGnjTkbeduF6g",
  "version" : 9,
  "state_uuid" : "cG3xYSvNRAKGtosXULdzlg",
  "master_node" : "Kw10bQaHR7WGbySRhDetfQ",
  "blocks" : { },
  "nodes" : {
    "ZogCGxDORXaTOMMKXHFHeg" : {
      "name" : "ZogCGxD",
      "ephemeral_id" : "aTuSX0YWSCGaPqpLgrGZjA",
      "transport_address" : "10.233.92.37:9300",
      "attributes" : { }
    },
    "MJw5gT-VSCmyGrsEjDpd3g" : {
      "name" : "MJw5gT-",
      "ephemeral_id" : "UN_ltE_pQ2qdUy9jRm_9Mw",
      "transport_address" : "10.233.96.38:9300",
      "attributes" : { }
    },
    "Kw10bQaHR7WGbySRhDetfQ" : {
      "name" : "Kw10bQa",
      "ephemeral_id" : "drmY5KhNS7q8_eNFy0YI_Q",
      "transport_address" : "10.233.90.38:9300",
      "attributes" : { }
...

可以觀察到刪除前 Elasticsearch 叢集環境的 Master Node 是 9WHQl6_BRC-dO3rfhOEMrg 這個傢伙,由於我們看他不順眼惡意地把他幹掉,目前透過選舉選出來新的 Master 是 Kw10bQaHR7WGbySRhDetfQ 這個傢伙,細心一點的朋友應該可以發現...Kw10bQaHR7WGbySRhDetfQ5k 這個傢伙也是 Elasticsearch Master Deployment 部署出來的 Pod。也就是說 Master Node 的選舉是由 Elasticsearch Master Deployment 部署出來的 Pod 去選出來的。

小結

呼終於把最麻煩的 Elasticsearch Cluster 部署完了,前面有點偷懶都在介紹架構跟部屬時會發生什麼問題 xD 。(不過這邊部署的東西其實是不能上 production 的,因為還有很多細節沒有處理,要上 production 這邊可以參考 David 大大寫的鐵人文章ELK Stack 1 - Install a Self-host ELK stack on GCP

明天會很快地將Fluentd 和 Kibana 部署到我們環境上,讓大家看一下UI比較舒服(X)。


上一篇
[偷一下未來的進度] Day 12 :Kubernetes 純手動部署與設定 EFK (2/4)
下一篇
[偷一下未來進度]Day 14 :Kubernetes 純手動部署與設定 EFK (4/4)
系列文
從 RedHat OpenShift 社群版 OKD 看 Kubernetes17
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
yanchen
iT邦新手 3 級 ‧ 2021-05-12 16:50:30

感謝大大分享
但是大大是不是少給一個elasticsearch-data的 Service
大概會長這樣

kind: Service
apiVersion: v1
metadata:
  labels:
    app: elasticsearch
  name: elasticsearch-data
  namespace: log
spec:
  ports:
    - port: 9200
      targetPort: 9200
  selector:
    app: elasticsearch
    role: data

/images/emoticon/emoticon08.gif

我要留言

立即登入留言