KubeSphere 多行日誌採集方案深度探索

KubeSphere發表於2022-11-29
作者:大飛哥,視源電子運維工程師,KubeSphere 使用者委員會廣州站站長

採集落盤日誌

日誌採集,通常使用 EFK 架構,即 ElasticSearch,Filebeat,Kibana,這是在主機日誌採集上非常成熟的方案,但在容器日誌採集方面,整體方案就會複雜很多。我們現在面臨的需求,就是要採集容器中的落盤日誌。

容器日誌分為標準輸出日誌和落盤日誌兩種。應用將日誌列印在容器標準輸出 STDOUT 中,由容器執行時(Docker 或 Containerd)把標準輸出日誌寫入容器日誌檔案中,最終由採集器匯出。這種日誌列印採集是業界推薦方案。但對於不列印標準輸出而直接將日誌落盤的情況,業界最常用見的方案是,使用 Sidecar 採集落盤日誌,把落盤日誌列印到容器標準輸出中,再利用標準輸出日誌的採集方式輸出。

對於 KubeSphere 使用者,只需要兩步即可:第一在專案中開啟收集捲上日誌,第二在工作負載中配置落盤檔案路徑。具體操作見下圖所示。

上述兩個步驟,會自動在容器中注入 Filebeat Sidecar 作為 logging-agent,將落盤日誌列印輸出在容器標準輸出中。Filebeat 配置可透過 ConfigMap 修改。

$ kubectl get cm -n kubesphere-logging-system logsidecar-injector-configmap -o yaml
## Filebeat 配置
filebeat.inputs:
- type: log
  enabled: true
  paths:
  {{range .Paths}}
  - {{.}}
  {{end}}
output.console:
  codec.format:
    string: '%{[log.file.path]} %{[message]}'
logging.level: warning

接入第三方日誌服務

預設 KubeSphere 將日誌採集到叢集內建 Elasticsearch 中,資料儲存週期為 7 天,這對於生產服務動輒 180 天的日誌儲存需求,顯然無法滿足。企業運維團隊都會建立集中化的日誌服務,將叢集內日誌接入到第三方日誌服務中,已是必然選擇。我們來看如何操作。

上文說到,容器執行時會將標準輸出日誌,落盤寫入到叢集節點的日誌檔案中,Linux 系統預設在 /var/log/containers/*.log。KubeSphere 使用 FluentBitDemonSet 形式在各叢集節點上採集日誌,由 FluentBit 輸出給 ElasticSearch 服務。具體配置可參考如下兩個配置:

$ kubectl get Input -n kubesphere-logging-system tail -o yaml
$ kubectl get Output -n kubesphere-logging-system es -o yaml

我們把日誌匯出到第三方日誌服務,那就需要定製 FluentBit 輸入輸出。使用 tail 外掛採集 /var/log/containers/flux-wms-*.log 檔案中的日誌,輸出到 Kafka 中。可參考如下配置:

---
apiVersion: logging.kubesphere.io/v1alpha2
kind: Input
metadata:
  labels:
    logging.kubesphere.io/component: logging
    logging.kubesphere.io/enabled: "true"
  name: kafka-flux-wms
  namespace: kubesphere-logging-system
spec:
  tail:
    db: /fluent-bit/tail/pos.db
    dbSync: Normal
    memBufLimit: 5MB
    path: /var/log/containers/flux-wms-*.log
    refreshIntervalSeconds: 10
    tag: fluxwms.*
---
apiVersion: logging.kubesphere.io/v1alpha2
kind: Output
metadata:
  annotations:
    kubesphere.io/creator: admin
  labels:
    logging.kubesphere.io/component: logging
    logging.kubesphere.io/enabled: "true"
  name: kafka-flux-wms
  namespace: kubesphere-logging-system
spec:
  kafka:
    brokers: xxx.xxx.xxx.xxx:9092
    topics: my-topic
  match: fluxwms.*
值得注意的是,目前 FluentBit 不支援 Kafka 認證。

多行日誌的尷尬

原本以為至此就可萬事大吉,沒想到消費 kafka 日誌時突然看到,某些日誌被拆得七零八碎,不忍入目。為了支援多行日誌,直觀的想法,就是逐個元件往前排查。

前方有坑,請小心閱讀。

配置 FluentBit 支援多行日誌

FluentBit 對多行日誌的支援,需要配置 Parser,並透過 parserFirstline 指定日誌 Parser,用以解析出多行日誌塊的第一行。官方參考文件,Parser 正規表示式,根據 Filebeat 日誌輸出格式而定,可參考上文或直接看這段:string: '%{[log.file.path]} %{[message]}'

---
apiVersion: logging.kubesphere.io/v1alpha2
kind: Input
metadata:
  labels:
    logging.kubesphere.io/component: logging
    logging.kubesphere.io/enabled: "true"
  name: kafka-flux-wms
  namespace: kubesphere-logging-system
spec:
  tail:
    db: /fluent-bit/tail/pos.db
    dbSync: Normal
    memBufLimit: 5MB
    path: /var/log/containers/flux-wms-*.log
    multiline: true
    parserFirstline: kafka-flux-wms
    refreshIntervalSeconds: 10
    tag: fluxwms.*
---
apiVersion: logging.kubesphere.io/v1alpha2
kind: Parser
metadata:
  labels:
    logging.kubesphere.io/component: logging
    logging.kubesphere.io/enabled: "true"
  name: kafka-flux-wms
  namespace: kubesphere-logging-system
spec:
  regex:
    regex: '^\/data\/business-logs\/[^\s]*'

配置 Filebeat 支援多行日誌

檢視 kakfka 訊息,多行日誌仍然被拆分。難道 Filebeat 沒有支援多行日誌嗎?整個落盤日誌採集鏈條中,只要有一個環節不支援多行日誌,就會導致結果不及預期。檢視專案原始日誌檔案,發現多行日誌以時間格式開頭,於是 Filebeat 增加如下配置:

filebeat.inputs:
- type: log
  enabled: true
  paths:
  {{range .Paths}}
  - {{.}}
  {{end}}
  multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
  multiline.negate: true
  multiline.match: after
  multiline.max_lines: 100
  multiline.timeout: 10s
output.console:
  codec.format:
    string: '%{[log.file.path]} %{[message]}'
logging.level: warning

進入 Sidecar 容器,使用如下命令測試 Filebeat 輸出,確認正確分割多行日誌。

$ filebeat -c /etc/logsidecar/filebeat.yaml

不可忽視的容器執行時

按理說,FluentBit 和 Filebeat 都支援了多行日誌,kafka 應該可以正確輸出多行日誌,但結果令人失望。肯定還有哪個環節被遺漏了,在登入叢集節點主機檢視容器標準輸出日誌時,這個被忽視的點被發現啦!

## 此處直接檢視你的專案容器
$ tail -f /var/log/containers/*.log

你會發現,日誌都是 JSON 格式,並且日誌是逐行輸出的,也就是說,沒有支援多行日誌塊。本地 kubernetes 叢集使用 Docker 作為容器執行時,來檢視它的配置:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "max-concurrent-downloads": 10,
  "max-concurrent-uploads": 10,
  "bip": "192.168.100.1/24",
  "storage-driver": "overlay2",
  "storage-opts": ["overlay2.override_kernel_check=true"]
}

log-driver配置為json-file, 這也是官方預設配置,可參考官方說明,除 json 格式外,還支援如下格式:

  • local
  • gelf
  • syslog
  • fluentd
  • loki

顯然其他格式也並不理想,而且對於生產環境,切換容器執行時日誌格式,影響還是蠻大的。探索至此,這條路子難度偏大風險過高,暫時先擱置,待到身心愜意時接著玩。

去掉中間商,直達 kafka

既然上面的路子走不通,那就換個思路。Filebeat 也是 logging-agent,是支援輸出日誌到 Kafka 的,為何不省去中間環節,直奔主題呢?

$ kubectl edit cm -n kubesphere-logging-system logsidecar-injector-configmap
filebeat.inputs:
- type: log
  enabled: true
  paths:
  {{range .Paths}}
  - {{.}}
  {{end}}
  multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
  multiline.negate: true
  multiline.match: after
  multiline.max_lines: 100
  multiline.timeout: 10s
output.kafka:
  enabled: true
  hosts:
    - XXX.XXX.XXX.XXX:9092
  topic: sycx-cmes-app
## output.console:
##   codec.format:
##     string: '%{[log.file.path]} %{[message]}'
logging.level: warning

當看到 Kafka 消費者輸出完美多行日誌塊時,腦後傳來多巴胺的快感!再看一眼架構圖,我們們來做總結!

總結

最初我去 KubeSphere 社群論壇搜尋日誌採集相關帖子時,有朋友說無法實現。看到他的回覆,心底一陣絕望。如今看來,某種角度上說,他的回答沒錯,他只是說那條路走不通,但他沒說那條路能走通。

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章