本文主要參考以下兩個文章,對文章內容進行翻譯整合。
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 叢集元件日誌都是像處理其他容器日誌一樣處理的。
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 日誌。
- /var/log/containers: 所有容器日誌都存在於一個單獨的位置;
- /var/log/pods/: 在此位置下,容器日誌被組織到單獨的 pod 資料夾中。
/var/log/pods/<namespace>_<pod_name>_<pod_id>/<container_name>/
.每個 pod 資料夾包含單個容器資料夾及其各自的日誌檔案。每個資料夾都有一個命名方案;
另外,如果您的底層容器工程師是 docker,您將在 /var/lib/docker/containers
資料夾中找到日誌。
日誌以 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>/
。下面的圖片顯示了一個例子。
此外,這些日誌檔案由 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 格式儲存。因此,如果開啟任何一個日誌檔案,就會發現每個日誌條目都有三個鍵。
log
-實際的日誌資料stream
– 寫入日誌的流time
– Timetamp 時間表
Kubernetes 日誌的型別
說到 Kubernetes,以下是不同型別的日誌。
-
Application logs: 來自使用者部署的應用程式的日誌。應用程式日誌有助於理解應用程式內部發生的事情。
-
Kubernetes Cluster components(叢集元件):來自 api-server、 kube-scheduler、 etcd、 kube-proxy 等的日誌。
-
Kubernetes Audit logs(審計日誌): 所有與 API 伺服器記錄的 API 活動相關的日誌。主要用於調查可疑的 API 活動。
Kubernetes Logging 架構
如果我們將 Kubernetes 叢集作為一個整體,那麼我們將需要統一收集日誌。但是 Kubernetes 並不提供任何日誌收集功能,因此您需要設定一個集中的日誌後端(例如: Elasticsearch) ,並將所有日誌傳送到日誌後端。下面的影像描繪了一個 high-level 的 Kubernetes Logging 架構。
讓我們瞭解一下日誌記錄的三個關鍵元件。
- Logging Agent: 一個日誌代理,可以在所有的 Kubernetes 節點中作為 daemonset 執行,它將日誌不斷地集中到日誌後端。日誌代理也可以作為 sidecar 容器執行。例如 Fluentd。
- Logging Backend: 一個集中的系統,能夠儲存、搜尋和分析日誌資料。
- Log Visualization: 以儀表板的形式視覺化日誌資料的工具。
Kubernetes Logging 模式
本節將研究一些 Kubernetes 日誌記錄模式,以便將日誌流傳到日誌後端。有三種關鍵的 Kubernetes 叢集日誌記錄模式
- Node level logging agent
- Streaming sidecar container
- Sidecar logging agent
讓我們詳細研究一下每個方案的特點。
Node Level Logging Agent
在這種方法中,每個節點執行著一個代理(例如: Fluentd)讀取使用容器 STDOUT 和 STDERR 流建立的日誌檔案,然後將其傳送給像 Elasticsearch 這樣的日誌後端。這是一種常用的日誌記錄模式,不需要任何開銷就可以很好地工作。
而云原生中的 12因素應用程式方法 也建議將日誌流傳到 STDOUT。
Streaming sidecar container
當應用程式不能直接向 STDOUT 和 STDERR 流寫入日誌時,這種 Sidecar 方法非常有用。
Pod 中的應用程式容器將所有日誌寫入容器中的一個檔案,然後 Pod 中存在一個 sidecar 容器從該日誌檔案中讀取資料並將其傳輸到 STDOUT 和 STDERR,最後利用 Node Level Logging Agent 的方式收集。
應用程式的日誌自定義檔案 -> 重新將流輸出到 STDOUT -> 容器引擎收集
Sidecar Logging Agent
在這種方法中,日誌不會被流送到 STDOUT 和 STDERR。相反,一個帶有日誌代理的 sidecar 容器將與應用程式容器一起執行。然後,日誌代理將直接將日誌流傳到日誌後端。
也就是說,Pod 中的 sidecar 容器,把日誌1直接推送到日誌儲存後端,不需要容器引擎的收集。
這種方法有兩個缺點。
- 將日誌記錄代理作為 sidecar 執行是資源密集型的,即會消耗大量 IO。
- 您不能使用
kubectl logs
命令獲得日誌,因為 Kubelet 不會處理日誌。
Kubernetes Logging 工具
Kubernetes 最常用的開源日誌堆疊是 EFK (Elasticsearch,Flunentd/Fluent-but,和 Kibana)。
EFK 方案包含以下三大部件:
- Elasticsearch – Log 聚合器
- Flunetd/Fluentbit – Logging 代理(Fluentbit 是為集裝箱工作負載設計的輕量級代理)
- Kibana – Log 視覺化和儀表板工具
當涉及到像 Google GKE、 AWS 和 Azure AKS 這樣的管理 Kubernetes 服務時,它整合了特定於雲的集中式日誌記錄。因此,當您部署託管 kubernetes 叢集時,您將獲得在相應的日誌記錄服務中啟用日誌監視的選項。比如說:
- AWS EKS uses Cloud
- Google GKE uses Stackdriver monitoring
- Azure AKS uses Azure Monitor
需要在雲上操作。
此外,組織可能會使用企業日誌解決方案,比如 Splunk。在這種情況下,日誌被轉發給 Splunk 監控,並遵守組織的日誌保留規範。以下是一些企業日誌解決方案。
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 日誌聚合和分析的流行且最佳的開源選擇。
- Elasticsearch 是一個分散式和可擴充套件的搜尋引擎,通常用於篩選大量的日誌資料。它是一個基於 Lucene 搜尋引擎(來自 Apache 的搜尋庫)的 NoSQL 資料庫。它的主要工作是儲存日誌和從 Fluentd 中取回日誌。
- Fluentd 是日誌收集處理器,它是一個開源日誌收集代理,支援多個資料來源和輸出格式。此外,它還可以將日誌轉發給 Stackdriver、 Cloudwatch、 Elasticsearch、 Splunk、 Bigquery 等。簡而言之,它是日誌資料生成系統和日誌資料儲存系統之間的統一層。
- 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 的高階架構。
EKF 元件部署說明如下:
- Fluentd: 在需要從所有節點收集容器日誌時作為守護程式部署。它連線到 Elasticsearch 服務端點以轉發日誌。
- Elasticsearch:在儲存日誌資料時作為狀態集部署。我們還公開 Fluentd 和 kibana 的服務端點以連線到它。
- 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 響應。
部署 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 正在正確執行。
部署 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
進入後臺。
2,選擇 Kibana 部分下的“ Index Patterns”選項。
3,使用 logstash-*
模式建立一個新的 Index Patten,然後點選 Next step
。
4,在選項找到 @timestamp
,然後點選 Create index pattern
現在已經建立了索引模式,我們可以前往控制檯,在控制檯中,您將能夠看到所有由 Fluentd 匯出的日誌,如下圖所示,這些日誌來自我們的 test-pod
。
接下來,讀者可以根據官方文件或其他資料,繼續深入學習探究圖表的製作和日誌分析,以便在生產中應用 EFK 三件套。
如果想深入 Kibana 皮膚,筆者推薦你閱讀這篇部落格:https://devopscube.com/kibana-dashboard-tutorial/