1. 程式人生 > 實用技巧 >k8s搭建EFK日誌管理系統

k8s搭建EFK日誌管理系統

efk就是目前比較受歡迎的日誌管理系統。kubernetes可以實現efk的快速部署和使用,通過statefulset控制器部署elasticsearch元件,用來儲存日誌資料,

還可通過volumenclaimtemplate動態生成pv實現es資料的持久化。通過deployment部署kibana元件,實現日誌的視覺化管理。

通過daemonset控制器部署fluentd元件,來收集各節點和k8s叢集的日誌。

EFK元件介紹

在K8s叢集上執行多個服務和應用程式時,日誌收集系統可以幫助你快速分類和分析由Pod生成的大量日誌資料。K8s中比較流行的日誌收集解決方案是Elasticsearch、Fluentd和Kibana(EFK)技術棧,也是官方推薦的一種方案。

Elasticsearch是一個實時的,分散式的,可擴充套件的搜尋引擎,它允許進行全文字和結構化搜尋以及對日誌進行分析。它通常用於索引和搜尋大量日誌資料,也可以用於搜尋許多不同種類的文件。

Elasticsearch通常與Kibana一起部署,kibana是Elasticsearch 的功能強大的資料視覺化的dashboard(儀表板)。Kibana允許你通過Web介面瀏覽Elasticsearch日誌資料,也可自定義查詢條件快速檢索出elasticccsearch中的日誌資料。

Fluentd是一個流行的開源資料收集器,我們將在Kubernetes 叢集節點上安裝 Fluentd,通過獲取容器日誌檔案、過濾和轉換日誌資料,然後將資料傳遞到 Elasticsearch 叢集,在該叢集中對其進行索引和儲存。

我們先來配置啟動一個可擴充套件的 Elasticsearch 叢集,然後在Kubernetes叢集中建立一個Kibana應用,最後通過DaemonSet來執行Fluentd,以便它在每個Kubernetes工作節點上都可以執行一個 Pod。

資料下載

1.下文需要的yaml檔案所在的github地址如下:

https://github.com/luckylucky421/efk

下面實驗用到yaml檔案大家需要從上面的github上clone和下載到本地,解壓,然後把解壓後的yaml檔案傳到k8s叢集的master節點上,

如果直接複製貼上格式可能會有問題。

2.下文裡提到的efk元件需要的映象獲取方式在百度網盤,連結如下:

連結:https://pan.baidu.com/s/1lsP2_NrXwOzGMIsVCUHtPw

提取碼:kpg2

3.實驗之前需要把映象上傳到k8s叢集的各個節點,通過docker load -i 解壓,這樣可以保證下面的yaml檔案可以正常執行,否則會存在映象拉取失敗問題:

docker  load -i busybox.tar.gz
docker load -i elasticsearch_7_2_0.tar.gz
docker load -i fluentd.tar.gz
docker load -i  kibana_7_2_0.tar.gz
docker load -i  nfs-client-provisioner.tar.gz
docker load -i nginx.tar.gz

正文-安裝efk元件

下面的步驟在k8s叢集的master1節點操作

#建立名稱空間

在安裝Elasticsearch叢集之前,我們先建立一個名稱空間,在這個名稱空間下安裝日誌收工具elasticsearch、fluentd、kibana。我們建立一個kube-logging名稱空間,將EFK元件安裝到該名稱空間中。

1.建立kube-logging名稱空間

cat kube-logging.yaml

kind: Namespace
apiVersion: v1
metadata:
 name: kube-logging

kubectl apply -f kube-logging.yaml

2.檢視kube-logging名稱空間是否建立成功

kubectl get namespaces | grep kube-logging

顯示如下,說明建立成功

kube-logging Active 1m

#安裝elasticsearch元件

通過上面步驟已經建立了一個名稱空間kube-logging,在這個名稱空間下去安裝日誌收集元件efk,首先,我們將部署一個3節點的Elasticsearch叢集。我們使用3個Elasticsearch Pods可以避免高可用中的多節點群集中發生的“裂腦”的問題。Elasticsearch腦裂可參考https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain

1.建立一個headless service(無頭服務)

建立一個headless service的Kubernetes服務,服務名稱是elasticsearch,這個服務將為3個Pod定義一個DNS域。headless service不具備負載均衡也沒有IP。要了解有關headless service的更多資訊,可參考https://kubernetes.io/docs/concepts/services-networking/service/#headless-services。

cat elasticsearch_svc.yaml

kind: Service
apiVersion: v1
metadata:
  name: elasticsearch
  namespace: kube-logging
  labels:
    app: elasticsearch
spec:
  selector:
    app: elasticsearch
  clusterIP: None
  ports:
    - port: 9200
      name: rest
    - port: 9300
      name: inter-node

在kube-logging名稱空間定義了一個名為 elasticsearch 的 Service服務,帶有app=elasticsearch標籤,當我們將 ElasticsearchStatefulSet 與此服務關聯時,服務將返回帶有標籤app=elasticsearch的 Elasticsearch Pods的DNS A記錄,然後設定clusterIP=None,將該服務設定成無頭服務。最後,我們分別定義埠9200、9300,分別用於與 REST API 互動,以及用於節點間通訊。使用kubectl直接建立上面的服務資源物件:

kubectl apply -f elasticsearch_svc.yaml

檢視elasticsearch的service是否建立成功

kubectl get services --namespace=kube-logging

看到如下,說明在kube-logging名稱空間下建立了一個名字是elasticsearch的headless service:

NAME            TYPE        CLUSTER-IP   EXTERNAL-IP  PORT(S)             AGEelasticsearchClusterIPNone<none>9200/TCP,9300/TCP2m

現在我們已經為 Pod 設定了無頭服務和一個穩定的域名.elasticsearch.kube-logging.svc.cluster.local,接下來我們通過 StatefulSet來建立具體的 Elasticsearch的Pod 應用。

2.通過statefulset建立elasticsearch叢集

Kubernetes statefulset可以為Pods分配一個穩定的標識,讓pod具有穩定的、持久的儲存。Elasticsearch需要穩定的儲存才能通過POD重新排程和重新啟動來持久化資料。更多關於kubernetes StatefulSet可參考

https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/。

1)下面將定義一個資源清單檔案elasticsearch_statefulset.yaml,首先貼上以下內容:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch

上面內容的解釋:在kube-logging的名稱空間中定義了一個es-cluster的StatefulSet。然後,我們使用serviceName 欄位與我們之前建立的ElasticSearch服務相關聯。這樣可以確保可以使用以下DNS地址訪問StatefulSet中的每個Pod:,es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local,其中[0,1,2]與Pod分配的序號數相對應。我們指定3個replicas(3個Pod副本),將matchLabels selector 設定為app: elasticseach,然後在該.spec.template.metadata中指定pod需要的映象。該.spec.selector.matchLabels和.spec.template.metadata.labels欄位必須匹配。

2)statefulset中定義pod模板,內容如下:

. . .
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"

上面內容解釋:在statefulset中定義了pod,容器的名字是elasticsearch,映象是docker.elastic.co/elasticsearch/elasticsearch:7.2.0。使用resources欄位來指定容器需要保證至少有0.1個vCPU,並且容器最多可以使用1個vCPU(這在執行初始的大量提取或處理負載高峰時限制了Pod的資源使用)。瞭解有關資源請求和限制,可參考https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/。暴漏了9200和9300兩個埠,名稱要和上面定義的 Service 保持一致,通過volumeMount聲明瞭資料持久化目錄,定義了一個data資料卷,通過volumeMount把它掛載到容器裡的/usr/share/elasticsearch/data目錄。我們將在以後的YAML塊中為此StatefulSet定義VolumeClaims。

最後,我們在容器中設定一些環境變數:

cluster.name

Elasticsearch     叢集的名稱,我們這裡是 k8s-logs。

node.name

節點的名稱,通過metadata.name來獲取。這將解析為 es-cluster-[0,1,2],取決於節點的指定順序。

discovery.zen.ping.unicast.hosts

此欄位用於設定在Elasticsearch叢集中節點相互連線的發現方法。
我們使用 unicastdiscovery方式,它為我們的叢集指定了一個靜態主機列表。
由於我們之前配置的無頭服務,我們的 Pod 具有唯一的DNS域es-cluster-[0,1,2].elasticsearch.logging.svc.cluster.local,
因此我們相應地設定此變數。由於都在同一個 namespace 下面,所以我們可以將其縮短為es-cluster-[0,1,2].elasticsearch。
要了解有關 Elasticsearch 發現的更多資訊,請參閱 Elasticsearch 官方文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html。

discovery.zen.minimum_master_nodes

我們將其設定為(N/2) + 1,N是我們的群集中符合主節點的節點的數量。
我們有3個Elasticsearch 節點,因此我們將此值設定為2(向下舍入到最接近的整數)。
要了解有關此引數的更多資訊,請參閱官方 Elasticsearch 文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain。

ES_JAVA_OPTS

這裡我們設定為-Xms512m -Xmx512m,告訴JVM使用512MB的最小和最大堆。
你應該根據群集的資源可用性和需求調整這些引數。
要了解更多資訊,請參閱設定堆大小的相關文件:https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html。

3)initcontainer內容

. . .
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true

這裡我們定義了幾個在主應用程式之前執行的Init 容器,這些初始容器按照定義的順序依次執行,執行完成後才會啟動主應用容器。第一個名為 fix-permissions 的容器用來執行 chown 命令,將 Elasticsearch 資料目錄的使用者和組更改為1000:1000(Elasticsearch 使用者的 UID)。因為預設情況下,Kubernetes 用 root 使用者掛載資料目錄,這會使得 Elasticsearch 無法方法該資料目錄,可以參考 Elasticsearch 生產中的一些預設注意事項相關文件說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_notes_for_production_use_and_defaults。

第二個名為increase-vm-max-map 的容器用來增加作業系統對mmap計數的限制,預設情況下該值可能太低,導致記憶體不足的錯誤,要了解更多關於該設定的資訊,可以檢視 Elasticsearch 官方文件說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html。最後一個初始化容器是用來執行ulimit命令增加開啟檔案描述符的最大數量的。此外 Elastisearch Notes for Production Use 文件還提到了由於效能原因最好禁用 swap,當然對於 Kubernetes 叢集而言,最好也是禁用 swap 分割槽的。現在我們已經定義了主應用容器和它之前執行的Init Containers 來調整一些必要的系統引數,接下來我們可以新增資料目錄的持久化相關的配置。

4)在 StatefulSet 中,使用volumeClaimTemplates來定義volume 模板即可:

. . .
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: do-block-storage
      resources:
        requests:
          storage: 10Gi

我們這裡使用 volumeClaimTemplates 來定義持久化模板,Kubernetes 會使用它為 Pod 建立 PersistentVolume,設定訪問模式為ReadWriteOnce,這意味著它只能被 mount到單個節點上進行讀寫,然後最重要的是使用了一個名為do-block-storage的 StorageClass 物件,所以我們需要提前建立該物件,我們這裡使用的 NFS 作為儲存後端,所以需要安裝一個對應的 provisioner驅動。

5)建立storageclass,實現nfs做儲存類的動態供給
#安裝nfs服務,選擇k8s叢集的master1節點,k8s叢集的master1節點的ip是192.168.0.6:

yum安裝nfs

yum install nfs-utils -y

systemctl start nfs

chkconfig nfs on

在master1上建立一個nfs共享目錄

mkdir /data/v1 -p

cat /etc/exports

/data/v1 192.168.0.0/24(rw,no_root_squash)

exportfs -arv

使配置檔案生效

systemctlrestart nfs

#實現nfs做儲存類的動態供給

建立執行nfs-provisioner的sa賬號

catserviceaccount.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner

kubectl apply -f serviceaccount.yaml

對sa賬號做rbac授權

cat rbac.yaml

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-provisioner
  apiGroup: rbac.authorization.k8s.io

kubectl apply -f rbac.yaml

通過deployment建立pod用來執行nfs-provisioner

cat deployment.yaml

kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-provisioner
spec:
  selector:
    matchLabels:
      app: nfs-provisioner
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
        - name: nfs-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner:latest
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: example.com/nfs
            - name: NFS_SERVER
              value: 192.168.0.6
            - name: NFS_PATH
              value: /data/v1
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.0.6
            path: /data/v1

kubectl apply -f deployment.yaml

kubectl get pods

看到如下,說明上面的yaml檔案建立成功:

NAME                               READY   STATUS   RESTARTS   AGE
nfs-provisioner-595dcd6b77-rkvjl   1/1    Running   0          6s

注:上面yaml檔案說明:

 - name: PROVISIONER_NAME
   value: example.com/nfs

#PROVISIONER_NAME是example.com/nfs,example.com/nfs需要跟後面的storageclass的provisinoer保持一致

- name: NFS_SERVER
  value: 192.168.0.6 

#這個需要寫nfs服務端所在的ip地址,大家需要寫自己的nfs地址

 - name: NFS_PATH
    value: /data/v1   

#這個是nfs服務端共享的目錄

 volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.0.6      

#這個是nfs服務端的ip,大家需要寫自己的nfs地址

path: /data/v1 #這個是nfs服務端的共享目錄

建立storageclass

cat class.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: do-block-storage
provisioner: example.com/nfs

kubectl apply -f class.yaml

注:

provisioner:example.com/nfs   #該值需要和provisioner配置的保持一致

6)最後,我們指定了每個 PersistentVolume 的大小為 10GB,我們可以根據自己的實際需要進行調整該值。最後,完整的elasticsaerch-statefulset.yaml資源清單檔案內容如下:

cat elasticsaerch-statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
        imagePullPolicy: IfNotPresent
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
      initContainers:
      - name: fix-permissions
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: do-block-storage
      resources:
        requests:
          storage: 10Gi
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
        imagePullPolicy: IfNotPresent
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
      initContainers:
      - name: fix-permissions
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: do-block-storage
      resources:
        requests:
          storage: 10Gi

kubectl apply -f elasticsaerch-statefulset.yaml

kubectl get pods -n kube-logging

顯示如下,說明es建立成功了:

NAME           READY   STATUS   RESTARTS   AGE
es-cluster-0   1/1    Running   0          2m8s
es-cluster-1   1/1    Running   0          117s
es-cluster-2   1/1    Running   0          107s

kubectl get svc -n kube-logging

顯示如下

NAME          TYPE             CLUSTER-IP   EXTERNAL-IP  PORT(S)             AGE
elasticsearch   ClusterIP   None        <none>       9200/TCP,9300/TCP   33m

pod部署完成之後,可以通過REST API檢查elasticsearch叢集是否部署成功,使用下面的命令將本地埠9200轉發到 Elasticsearch 節點(如es-cluster-0)對應的埠:

kubectl port-forward es-cluster-0 9200:9200 --namespace=kube-logging

然後,在另外的終端視窗中,執行如下請求,新開一個master1終端:

curl http://localhost:9200/_cluster/state?pretty

輸出如下:

{
 "cluster_name" : "k8s-logs",
 "compressed_size_in_bytes" : 348,
 "cluster_uuid" : "QD06dK7CQgids-GQZooNVw",
 "version" : 3,
 "state_uuid" : "mjNIWXAzQVuxNNOQ7xR-qg",
 "master_node" : "IdM5B7cUQWqFgIHXBp0JDg",
 "blocks" : { },
 "nodes" : {
   "u7DoTpMmSCixOoictzHItA" : {
     "name" : "es-cluster-1",
     "ephemeral_id" : "ZlBflnXKRMC4RvEACHIVdg",
     "transport_address" : "10.244.8.2:9300",
     "attributes" : { }
   },
    "IdM5B7cUQWqFgIHXBp0JDg": {
     "name" : "es-cluster-0",
     "ephemeral_id" : "JTk1FDdFQuWbSFAtBxdxAQ",
     "transport_address" : "10.244.44.3:9300",
     "attributes" : { }
   },
   "R8E7xcSUSbGbgrhAdyAKmQ" : {
     "name" : "es-cluster-2",
      "ephemeral_id" :"9wv6ke71Qqy9vk2LgJTqaA",
     "transport_address" : "10.244.40.4:9300",
     "attributes" : { }
    }
  },
  ...

看到上面的資訊就表明我們名為 k8s-logs的Elasticsearch 叢集成功建立了3個節點:es-cluster-0,es-cluster-1,和es-cluster-2,當前主節點是 es-cluster-0。

#安裝kibana元件

elasticsearch安裝成功之後,開始部署kibana

cat kibana.yaml

apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: kube-logging
  labels:
    app: kibana
spec:
  ports:
  - port: 5601
  selector:
    app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: kube-logging
  labels:
    app: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
      - name: kibana
        image: docker.elastic.co/kibana/kibana:7.2.0
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        env:
          - name: ELASTICSEARCH_URL
            value: http://elasticsearch:9200
        ports:
        - containerPort: 5601

上面我們定義了兩個資源物件,一個Service和Deployment,為了測試方便,我們將 Service 設定為了 NodePort 型別,Kibana Pod 中配置都比較簡單,唯一需要注意的是我們使用ELASTICSEARCH_URL 這個環境變數來設定Elasticsearch 叢集的端點和埠,直接使用 Kubernetes DNS 即可,此端點對應服務名稱為 elasticsearch,由於是一個 headless service,所以該域將解析為3個 Elasticsearch Pod 的 IP 地址列表。

配置完成後,直接使用 kubectl工具建立:

kubectl apply -f kibana.yaml

kubectl get pods -n kube-logging

顯示如下,說明kibana也已經部署成功了

NAME                      READY   STATUS   RESTARTS   AGE
es-cluster-0              1/1     Running  0          170m
es-cluster-1              1/1     Running  0          170m
es-cluster-2              1/1     Running  0          170m
kibana-5749b5778b-c9djr   1/1    Running   0          4m3s

kubectl get svc -n kube-logging

顯示如下:

NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP  None            <none>        9200/TCP,9300/TCP   3h28m
kibana          ClusterIP   10.104.159.24   <none>        5601/TCP            11m

修改service的type型別為NodePort:

kubectl edit svc kibana -n kube-logging

把type:ClusterIP變成type: NodePort

儲存退出之後

kubectlget svc -n kube-logging

顯示如下:

NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticsearchClusterIP   None            <none>        9200/TCP,9300/TCP   3h50m
kibana        NodePort    10.104.159.24   <none>        5601:32462/TCP      34m

在瀏覽器中開啟http://<任意節點IP>:32462即可,如果看到如下歡迎介面證明 Kibana 已經成功部署到了Kubernetes叢集之中。

#安裝fluentd元件

我們使用daemonset控制器部署fluentd元件,這樣可以保證叢集中的每個節點都可以運行同樣fluentd的pod副本,這樣就可以收集k8s叢集中每個節點的日誌,在k8s叢集中,容器應用程式的輸入輸出日誌會重定向到node節點裡的json檔案中,fluentd可以tail和過濾以及把日誌轉換成指定的格式傳送到elasticsearch叢集中。

除了容器日誌,fluentd也可以採集kubelet、kube-proxy、docker的日誌。

cat fluentd.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
  labels:
    app: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd
roleRef:
  kind: ClusterRole
  name: fluentd
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    app: fluentd
spec:
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
        imagePullPolicy: IfNotPresent
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch.kube-logging.svc.cluster.local"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENTD_SYSTEMD_CONF
            value: disable
        resources:
          limits:
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

kubectl apply -f fluentd.yaml

檢視是否部署成功

kubectl get pods -n kube-logging

顯示如下,看到status狀態是running,說明部署成功:

NAME                      READY   STATUS   RESTARTS   AGE
es-cluster-0              1/1     Running  6          57m
es-cluster-1              1/1     Running  5          57m
es-cluster-2              1/1    Running   0          45m
fluentd-fs54n             1/1     Running  0          37m
fluentd-ghgqf             1/1     Running  0          37m
kibana-5749b5778b-zzgbc   1/1    Running   0          39m

Fluentd啟動成功後,我們可以前往 Kibana 的 Dashboard 頁面中,點選左側的Discover,可以看到如下配置頁面:

在這裡可以配置我們需要的 Elasticsearch 索引,前面 Fluentd 配置檔案中我們採集的日誌使用的是 logstash 格式,這裡只需要在文字框中輸入logstash-*即可匹配到 Elasticsearch叢集中的所有日誌資料,然後點選下一步,進入以下頁面:

點選next step,出現如下

選擇@timestamp,建立索引

點選左側的discover,可看到如下:

#測試容器日誌

cat pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    imagePullPolicy: IfNotPresent
    args: [/bin/sh, -c,'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

kubectl apply -f pod.yaml

登入到kibana的控制面板,在discover處的搜尋欄中輸入kubernetes.pod_name:counter,這將過濾名為的Pod的日誌資料counter,如下所示:

通過上面幾個步驟,我們已經在k8s叢集成功部署了elasticsearch,fluentd,kibana,這裡使用的efk系統包括3個Elasticsearch Pod,一個Kibana Pod和一組作為DaemonSet部署的Fluentd Pod。

要了解更多關於elasticsearch可參考:https://www.elastic.co/cn/blog/small-medium-or-large-scaling-elasticsearch-and-evolving-the-elastic-stack-to-fit。

Kubernetes中還允許使用更復雜的日誌系統,要了解更多資訊,可參考https://kubernetes.io/docs/concepts/cluster-administration/logging/

原文連結:
https://mp.weixin.qq.com/s/SoelIGIT5lQSgJlE4MYFjg