Kubernetes 叢集日誌 和 EFK 架構日誌方案

痴者工良發表於2022-02-15


本文主要參考以下兩個文章,對文章內容進行翻譯整合。

https://devopscube.com/kubernetes-logging-tutorial/

https://devopscube.com/setup-efk-stack-on-kubernetes/

第一部分:Kubernetes 日誌

在這個 Kubernetes 日誌教程中,您將學習 Kubernetes 叢集日誌中涉及的關鍵概念和工作流。

當涉及到 Kubernetes 生產除錯時,日誌起著至關重要的作用。它可以幫助你理解正在發生的事情,哪裡出了問題,甚至是哪裡可能出問題。作為一名 DevOps 工程師,您應該清楚地瞭解 Kubernetes 日誌以解決叢集和應用程式問題。

Kubernetes Logging 是如何工作的

在 Kubernetes,大多陣列件都是以容器方式執行的。在 kubernetes 架構中,一個應用程式 Pod 可以包含多個容器,大多數 Kubernetes 叢集元件都是這樣,如 api-server、 kube-scheduler、 Etcd、 kube 代理等等,會以容器方式執行。但是,kubelet 元件以本機 systemd 服務執行。

在這一節中,我們將看看日誌是如何為 Kubernetes Pod 工作的。它可以是一個 Application Pod 或 Kubernetes component Pod。我們還將研究如何管理 kubelet systemd 日誌。

通常,我們在 Kubernetes 上部署的任何 Pod 都會將日誌寫入 stdout 和 stderr 流,而不是將日誌寫入專用的日誌檔案。但是,來自每個容器的對 stdout 和 stderr 的流都以 JSON 格式儲存在檔案系統中。底層容器引擎完成這項工作,它被設計用來處理日誌記錄。例如,Docker 容器引擎。

筆者注:這段話的意思是容器應用的日誌通過控制檯輸出時,會被容器引擎收集,這些日誌流會被以 Json 檔案的形式儲存到檔案系統中。

容器的日誌收集方式後面提到。

注意: 所有 kubernetes 叢集元件日誌都是像處理其他容器日誌一樣處理的。

Kubernetes 叢集日誌 和 EFK 架構日誌方案

Kubelet 在所有節點上執行,以確保節點上的容器正常執行。它還負責執行的靜態 Pod 以及,如果 kubelet 作為一個系統服務執行,它將日誌記錄到 journald(systemd-journald )。

另外,如果容器沒有將日誌傳輸到 stdout 和 stderr,您將不會使用 kubetl logs 命令獲得日誌,因為 kubelet 無法訪問日誌檔案。

筆者注:例如 Pod 在節點 B 中執行,但是你在 A 節點執行 kubectl logs 命令,Pod 的日誌不會憑空飛過去,是通過 kubelet 傳輸過去的。

這一小節說的是,程式的日誌要通過 stdout 和 stderr 輸出,才會被 Kubernetes 利用(kubectl logs),而在節點間傳輸日誌的元件叫 kubelet。因為 kubelet不是以 Pod 而是以 systemd 的形式執行,因此 kubelet 自身的日誌要通過 systemd-journald 檢視。

Kubernetes Pod 日誌儲存位置

您可以在以下每個工作節點的目錄中找到 kubernetes 儲存的 Pod 日誌。

  1. /var/log/containers: 所有容器日誌都存在於一個單獨的位置;
  2. /var/log/pods/: 在此位置下,容器日誌被組織到單獨的 pod 資料夾中。/var/log/pods/<namespace>_<pod_name>_<pod_id>/<container_name>/.每個 pod 資料夾包含單個容器資料夾及其各自的日誌檔案。每個資料夾都有一個命名方案;

另外,如果您的底層容器工程師是 docker,您將在 /var/lib/docker/containers 資料夾中找到日誌。

image-20220213091744268

日誌以 Json 的形式記錄,還會記錄日誌的一些屬性資訊。

{"log":"Shutting down, got signal: Terminated\n","stream":"stderr","time":"2021-11-09T06:14:42.535854831Z"}

如果您登入到任何 Kubernetes 工作者節點並轉到 /var/log/containers 目錄,您將找到該節點上執行的每個容器的日誌檔案。日誌檔案命名方案遵循 /var/log/pods/<namespace>_<pod_name>_<pod_id>/<container_name>/。下面的圖片顯示了一個例子。

image-20220213091606019

此外,這些日誌檔案由 Kubelet 控制,因此當您執行 kubectl logs命令時,Kubelet 會在終端中顯示這些日誌。

Kubelet Logs

對於 Kubelet,您可以使用 journalctl 從單個工作者節點訪問日誌。例如,使用以下命令檢查 Kubelet 日誌。

journalctl -u kubelet
journalctl -u kubelet -o cat

如果 Kubelet 在沒有 systemd 的情況下執行,您可以在 /var/log 目錄中找到 Kubelet 日誌。

Kubernetes 容器日誌格式

下面是容器日誌的其中一行資料示例:

{"log":"Shutting down, got signal: Terminated\n","stream":"stderr","time":"2021-11-09T06:14:42.535854831Z"}

如前所述,所有日誌資料都以 JSON 格式儲存。因此,如果開啟任何一個日誌檔案,就會發現每個日誌條目都有三個鍵。

  1. log-實際的日誌資料
  2. stream – 寫入日誌的流
  3. time – Timetamp 時間表

Kubernetes 日誌的型別

說到 Kubernetes,以下是不同型別的日誌。

  1. Application logs: 來自使用者部署的應用程式的日誌。應用程式日誌有助於理解應用程式內部發生的事情。

  2. Kubernetes Cluster components(叢集元件):來自 api-server、 kube-scheduler、 etcd、 kube-proxy 等的日誌。

  3. Kubernetes Audit logs(審計日誌): 所有與 API 伺服器記錄的 API 活動相關的日誌。主要用於調查可疑的 API 活動。

Kubernetes Logging 架構

如果我們將 Kubernetes 叢集作為一個整體,那麼我們將需要統一收集日誌。但是 Kubernetes 並不提供任何日誌收集功能,因此您需要設定一個集中的日誌後端(例如: Elasticsearch) ,並將所有日誌傳送到日誌後端。下面的影像描繪了一個 high-level 的 Kubernetes Logging 架構。

img

讓我們瞭解一下日誌記錄的三個關鍵元件。

  1. Logging Agent: 一個日誌代理,可以在所有的 Kubernetes 節點中作為 daemonset 執行,它將日誌不斷地集中到日誌後端。日誌代理也可以作為 sidecar 容器執行。例如 Fluentd。
  2. Logging Backend: 一個集中的系統,能夠儲存、搜尋和分析日誌資料。
  3. Log Visualization: 以儀表板的形式視覺化日誌資料的工具。

Kubernetes Logging 模式

本節將研究一些 Kubernetes 日誌記錄模式,以便將日誌流傳到日誌後端。有三種關鍵的 Kubernetes 叢集日誌記錄模式

  1. Node level logging agent
  2. Streaming sidecar container
  3. Sidecar logging agent

讓我們詳細研究一下每個方案的特點。

Node Level Logging Agent

img

在這種方法中,每個節點執行著一個代理(例如: Fluentd)讀取使用容器 STDOUT 和 STDERR 流建立的日誌檔案,然後將其傳送給像 Elasticsearch 這樣的日誌後端。這是一種常用的日誌記錄模式,不需要任何開銷就可以很好地工作

而云原生中的 12因素應用程式方法 也建議將日誌流傳到 STDOUT。

參考地址:https://12factor.net/logs

Streaming sidecar container

img

當應用程式不能直接向 STDOUT 和 STDERR 流寫入日誌時,這種 Sidecar 方法非常有用。

Pod 中的應用程式容器將所有日誌寫入容器中的一個檔案,然後 Pod 中存在一個 sidecar 容器從該日誌檔案中讀取資料並將其傳輸到 STDOUT 和 STDERR,最後利用 Node Level Logging Agent 的方式收集。

應用程式的日誌自定義檔案 -> 重新將流輸出到 STDOUT -> 容器引擎收集

Sidecar Logging Agent

img

在這種方法中,日誌不會被流送到 STDOUT 和 STDERR。相反,一個帶有日誌代理的 sidecar 容器將與應用程式容器一起執行。然後,日誌代理將直接將日誌流傳到日誌後端。

也就是說,Pod 中的 sidecar 容器,把日誌1直接推送到日誌儲存後端,不需要容器引擎的收集。

這種方法有兩個缺點。

  1. 將日誌記錄代理作為 sidecar 執行是資源密集型的,即會消耗大量 IO。
  2. 您不能使用 kubectl logs 命令獲得日誌,因為 Kubelet 不會處理日誌。

Kubernetes Logging 工具

Kubernetes 最常用的開源日誌堆疊是 EFK (Elasticsearch,Flunentd/Fluent-but,和 Kibana)。

EFK 方案包含以下三大部件:

  1. Elasticsearch – Log 聚合器
  2. Flunetd/Fluentbit – Logging 代理(Fluentbit 是為集裝箱工作負載設計的輕量級代理)
  3. Kibana – Log 視覺化和儀表板工具

當涉及到像 Google GKE、 AWS 和 Azure AKS 這樣的管理 Kubernetes 服務時,它整合了特定於雲的集中式日誌記錄。因此,當您部署託管 kubernetes 叢集時,您將獲得在相應的日誌記錄服務中啟用日誌監視的選項。比如說:

  1. AWS EKS uses Cloud
  2. Google GKE uses Stackdriver monitoring
  3. Azure AKS uses Azure Monitor

需要在雲上操作。

此外,組織可能會使用企業日誌解決方案,比如 Splunk。在這種情況下,日誌被轉發給 Splunk 監控,並遵守組織的日誌保留規範。以下是一些企業日誌解決方案。

  1. Logz.io
  2. Splunk
  3. Elastic
  4. Sumologic
  5. LogDNA

Kubenretes Logging 與 EFK

在 Kubernetes 中,目前其中一個最好的開源日誌方案是 EFK ,它包含 Elasticsearch、 Fluentd 和 Kibana 三個部分。

在第二部分中,我們將在 Kubernetes 叢集中,部署 EFK 日誌方案。

第二部分:EFK 實踐

第一部分,其中為初學者介紹了 Kubernetes 日誌基本原理和模式。在第二部分中,您將學習如何在 Kubernetes 叢集上設定用於日誌流、日誌分析和日誌監視的 EFK。

在 Kubernetes 叢集上執行多個應用程式和服務時,將所有應用程式和 Kubernetes 叢集日誌流到一個集中的日誌基礎設施中,以便於日誌分析,這樣做更有意義。第二部分旨在通過 EFK 堆疊向您介紹 Kubernetes 日誌的重要技術方面。

EFK Stack

EFK 代表 Elasticsearch、 Fluentd 和 Kibana。EFK 是用於 Kubernetes 日誌聚合和分析的流行且最佳的開源選擇。

  1. Elasticsearch 是一個分散式和可擴充套件的搜尋引擎,通常用於篩選大量的日誌資料。它是一個基於 Lucene 搜尋引擎(來自 Apache 的搜尋庫)的 NoSQL 資料庫。它的主要工作是儲存日誌和從 Fluentd 中取回日誌。
  2. Fluentd 是日誌收集處理器,它是一個開源日誌收集代理,支援多個資料來源和輸出格式。此外,它還可以將日誌轉發給 Stackdriver、 Cloudwatch、 Elasticsearch、 Splunk、 Bigquery 等。簡而言之,它是日誌資料生成系統和日誌資料儲存系統之間的統一層。
  3. Kibana 是一個用於查詢、資料視覺化和儀表板的 UI 工具。它是一個查詢引擎,允許您通過 web 介面探索您的日誌資料,為事件日誌構建視覺化,特定於查詢過濾資訊以檢測問題。您可以使用 Kibana 虛擬地構建任何型別的儀表板。Kibana Query Language (KQL)用於查詢 elasticsearch 資料。在這裡,我們使用 Kibana 在 elasticsearch 中查詢索引資料。

此外,Elasticsearch 還可以幫助解決大量非結構化資料分離的問題,目前許多組織都在使用 Elasticsearch,通常跟 Kibana 部署在一起。

注意: 當涉及到 Kubernetes 時,FLuentd 是最好的選擇,因為比 logstash 更好,因為 FLuentd 可以解析容器日誌而不需要任何額外的配置。此外,這是一個 CNCF 專案。

在 Kubernetes 上設定 EFK

接下來我們將一步步在 Kubernetes 中部署和配置 EFK,你可以在 Kubernetes EFK Github repo 中找到本部落格中使用的所有部署定義檔案,每個 EFK 元件的 YAML 定義檔案都放在不同的目錄中。

首先克隆倉庫:

git clone https://github.com/scriptcamp/kubernetes-efk

注意,在本文部署的 EFK 元件,都會在 Kubernetes 預設的 default 名稱空間中。

倉庫的檔案結構如下:

├── kubernetes-efk
│   ├── README.md
│   ├── elasticsearch
│   │   ├── es-sts.yaml
│   │   └── es-svc.yaml
│   ├── fluentd
│   │   ├── fluentd-ds.yaml
│   │   ├── fluentd-rb.yaml
│   │   ├── fluentd-role.yaml
│   │   └── fluentd-sa.yaml
│   ├── kibana
│   │   ├── kibana-deployment.yaml
│   │   └── kibana-svc.yaml
│   └── test-pod.yaml

EFK 架構

下圖顯示了我們將要構建的 EFK 的高階架構。

img

EKF 元件部署說明如下:

  1. Fluentd: 在需要從所有節點收集容器日誌時作為守護程式部署。它連線到 Elasticsearch 服務端點以轉發日誌。
  2. Elasticsearch:在儲存日誌資料時作為狀態集部署。我們還公開 Fluentd 和 kibana 的服務端點以連線到它。
  3. Kibana:- 作為部署部署並連線到 Elasticsearch 服務端點。

部署 Elasticsearch Statefulset

Elasticsearch 是作為 Statefulset 部署的,多個副本通過一個 headless service 彼此連線。Headless svc 在 Pod 的 DNS 域中提供幫助。

開啟 elasticsearch/es-svc. yaml 檔案,可以看到定義如下:

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

讓我們現在就創造它。

kubectl create -f es-svc.yaml

在我們開始為彈性搜尋建立 statefulset 之前,讓我們回想一下,statefulset 需要事先定義的儲存類,它可以在需要時建立卷。

注意: 雖然在生產環境中,可能需要 400-500gb SSD 支撐 ES 的儲存,由於這裡是儲存環境,因此定義 YAML 檔案中是 3GB 的 PV。

筆者因為要學習 EFK,特意掛載了一個 512GB 的企業級 SSD。

elasticsearch/es-sts.yaml 檔案中,它會自動建立一個具有 3GB 的儲存卷,其定義如下:

    spec:
      accessModes: [ "ReadWriteOnce" ]
      # storageClassName: ""
      resources:
        requests:
          storage: 3Gi

由於筆者有自己的盤,筆者掛載到了 slave2 節點中,所以這裡筆者就不用這個配置了,筆者使用 NFS - PV 的方式,當然讀者為了避免麻煩,可以繼續使用上面的配置。

讀者可以首先按照 https://k8s.whuanle.cn/5.volumes/3.nfts.html 部署自己的 NFS 儲存,然後利用下面這個模板建立 PV。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: es-pv 
spec:
  capacity:
    storage: 450Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: es-volume 
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /data/volumns/es
    server: 10.0.0.4

需讀者自行提前建立 NFS,讀者可參考 https://k8s.whuanle.cn/5.volumes/3.nfts.html 建立一個自己的跨節點的儲存系統。

將 volumeClaimTemplates 部分改成:

  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "es-volume"
      resources:
        requests:
          storage: 450Gi

然後我們看一下 es-sts.yaml 的檔案中,對於 Eelasticsearch 叢集的定義:

          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"

這裡有兩個地方要注意,一是你的叢集必須要安裝 CoreDNS,二是你的從節點數量跟 es-sts.yaml 中定義的 replicas: 3 數量一致,如果你有三個節點(一主二從),那麼 replics 要設定為 2,但是一般 Eelasticsearch 例項數量都是單數。因此筆者只設定使用一個 Eelasticsearch 例項。

然後部署 Elasticsearch :

 kubectl create -f es-sts.yaml 

驗證 Elasticsearch 部署

在 Elasticsearch Pod 進入執行狀態之後,讓我們嘗試驗證 Elasticsearch 狀態集。最簡單的方法是檢查叢集的狀態。為了檢查狀態,埠前進 Elasticsearch Pod 的 9200 埠。

kubectl port-forward es-cluster-0 19200:9200

port-forward 方式具有臨時性,避免 Elasticsearch 暴露到外網;或者使用 Service IP 等方式測試 IP 連通性,你還可以使用 Service NodePort 的方式暴露 Elasticsearch 。

要檢查 Elasticsearch 叢集的健康狀況,請在終端中執行以下命令。

curl http://localhost:19200/_cluster/health/?pretty

輸出將顯示 Elasticsearch 叢集的狀態。如果所有的步驟都被正確的執行,訪問此地址,會獲得 Json 響應。

image-20220213152739445

部署 Kibana

跟部署 Elasticsearch 一樣,可以使用一個簡單的 yaml 檔案部署 Kibana。如果您檢查以下 Kibana 部署清單檔案,我們有一個 ELASTICSEARCH_URL 定義來配置 Elasticsearch 叢集 Endpoint,Kibana 使用 Endpoint URL 連線 Elasticsearch。

下面是 kibana-deployment.yaml 檔案的定義。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  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.5.0
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        env:
          - name: ELASTICSEARCH_URL
            value: http://elasticsearch:9200
        ports:
        - containerPort: 5601
"state":"green","message":"Status changed from yellow to green - Ready","prevState":"yellow","prevMsg":"Waiting for Elasticsearch"

現在直接部署 Kibana 即可:

kubectl create -f kibana-deployment.yaml

讓我們建立一個 NodePort 型別的 Service,通過節點 IP 地址訪問 Kibana UI。我們使用 nodePort 進行演示。然而,理想情況下,kubernetes 與 ClusterIP 服務的接入用於實際的專案實現。

kibana-svc.yaml 檔案的定義如下:

apiVersion: v1
kind: Service
metadata:
  name: kibana-np
spec:
  selector: 
    app: kibana
  type: NodePort  
  ports:
    - port: 8080
      targetPort: 5601 
      nodePort: 30000

建立 Kibana Service:

kubectl create -f kibana-svc.yaml

現在你可以通過 http://<node-ip>:3000 訪問 Kibana UI。

Pod 進入執行狀態後,讓我們嘗試驗證 Kibana 部署。最簡單的方法是通過叢集的 UI 訪問。

要檢查狀態,埠轉發 Kibana Pod 的 5601埠。如果您已經建立了 nodePort 服務,您也可以使用它(注意防火牆可能會攔截)。

kubectl port-forward <kibana-pod-name> 5601:5601

之後,通過 web 瀏覽器訪問 UI 或使用 curl 發出請求

curl http://localhost:5601/app/kibana

如果 Kibana UI 載入或出現有效的 curl 響應,那麼我們可以斷定 Kibana 正在正確執行。

image-20220213160045560

部署 Fluentd

Fluentd 被部署為守護程式,因為它必須從叢集中的所有節點流日誌。除此之外,它還需要特殊的許可權來列出和提取所有名稱空間中的 Pod 後設資料。

Kubernetes 服務帳戶用於為 Kubernetes 中的元件提供許可權,以及叢集角色和叢集繫結。讓我們繼續前進,建立所需的服務帳戶和角色。

建立 Fluentd 叢集角色

Kubernetes 中的叢集角色包含表示一組許可權的規則,對於 Fluentd,我們希望為 Pod 和名稱空間授予許可權。
fluentd-role.yaml 檔案的定義如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
  labels:
    app: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch

建立角色:

kubectl create -f fluentd-role.yaml

建立 Fluentd Service Account

在 Kubernetes 中,Service Account 是為 Pod 提供身份的實體,在這裡,我們希望建立一個 Service Account,用於 Fluentd Pods。

fluentd-sa.yaml 檔案的定義如下:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  labels:
    app: fluentd

建立 Service Account:

kubectl create -f fluentd-sa.yaml

叢集角色繫結

Kubernetes 中的叢集角色繫結將叢集角色中定義的許可權授予服務帳戶。我們希望在上面建立的角色和服務帳戶之間建立一個角色繫結。
fluentd-rb.yaml 檔案的定義如下:

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: default

建立角色繫結:

kubectl create -f fluentd-rb.yaml

部署 Fluentd DaemonSet

現在讓我們部署 Fluentd :

kubectl create -f fluentd-ds.yaml

為了驗證 fluentd 的安裝,讓我們啟動一個連續建立日誌的 pod。然後我們將嘗試在 Kibana 境內看到這些日誌。

kubectl create -f test-pod.yaml

現在,讓我們前往 Kibana,看看這個吊艙裡的日誌是否被 fluentd 拾取並儲存在 elasticsearch。遵循以下步驟:

1,點選 explore on my own 進入後臺。

image-20220213160840954

2,選擇 Kibana 部分下的“ Index Patterns”選項。

image-20220213160921037

3,使用 logstash-* 模式建立一個新的 Index Patten,然後點選 Next step

image-20220213161248126

4,在選項找到 @timestamp ,然後點選 Create index pattern

image-20220213161402788

現在已經建立了索引模式,我們可以前往控制檯,在控制檯中,您將能夠看到所有由 Fluentd 匯出的日誌,如下圖所示,這些日誌來自我們的 test-pod

image-20220213161617937

接下來,讀者可以根據官方文件或其他資料,繼續深入學習探究圖表的製作和日誌分析,以便在生產中應用 EFK 三件套。

如果想深入 Kibana 皮膚,筆者推薦你閱讀這篇部落格:https://devopscube.com/kibana-dashboard-tutorial/

相關文章