Filebeat優化實踐

Filebeat優化實踐

背景介紹

目前比較主流的日誌採集系統有ELK(ES+Logstash+Kibana),EFK(ES+Fluentd+Kibana)等。因爲Logstash出現較早,大多很多天志文件蒐集採用了Logstash。但因爲Logstash是JRuby實現的,性能開銷較大,所以咱們的日誌蒐集採用的Filebeat,而後發送到Logstash進行數據處理(例如:解析json,正則解析文件名稱等),最後由Logstash發送到Kafka或者ES。這種方式雖然減輕了每一個節點的處理壓力,但部署Logstash的節點性能開銷依舊很大,並且常常出現Filebeat沒法發送數據到Logstash的狀況。node

拋棄Logstash

因爲Logstash性能開銷較大,爲了提升客戶端的日誌採集性能,又減小數據傳輸環節和部署複雜度,並更充分地將 Go 語言的性能優點利用於日誌解析,因而決定在 Filebeat 上經過開發插件的方式,實現針對公司日誌格式規範的解析,直接做爲 Logstash 的替代品。git

開發本身的Processor

咱們的平臺是基於Kubernetes的,所以咱們須要解析每一條日誌的source,從日誌文件名稱中獲取Kubernetes資源名稱,以肯定該條日誌的發往Topic。解析文件名稱須要用到正則匹配,但因爲正則性能開銷較大,若是每一條日誌都用正則解析名稱將會帶來比較大的性能開銷,所以咱們決定採用緩存來解決這一問題。即每一個文件只解析一次名稱,存放到一個Map變量中,若是已經解析過的文件名稱則再也不解析。這樣大大提升了Filebeat的吞吐量。github

性能優化

Filebeat配置文件以下,其中kubernetes_metadata是本身開發的Processor。json

################### Filebeat Configuration Example #########################

############################# Filebeat ######################################
filebeat:
  # List of prospectors to fetch data.
  prospectors:
    -
      paths:
        - /var/log/containers/*
      symlinks: true
#     tail_files: true
      encoding: plain
      input_type: log
      fields:
        type: k8s-log
        cluster: cluster1
        hostname: k8s-node1
      fields_under_root: true
      scan_frequency: 5s
      max_bytes: 1048576        # 1M

  # General filebeat configuration options
  registry_file: /data/usr/filebeat/kube-filebeat.registry

############################# Libbeat Config ##################################
# Base config file used by all other beats for using libbeat features

############################# Processors ######################################
processors:
- decode_json_fields:
    fields: ["message"]
    target: ""
- drop_fields:
    fields: ["message", "beat", "input_type"]
- kubernetes_metadata:
  # Default

############################# Output ##########################################

# Configure what outputs to use when sending the data collected by the beat.
# Multiple outputs may be used.
output:
  file: 
    path: "/data/usr/filebeat"
    filename: filebeat.log

測試環境:緩存

初版性能數據以下:性能優化

平均速度 100萬條總時間
11970 條/s 83.5秒

生成的CPU火焰圖以下 輸入圖片說明運維

從火焰圖中能夠看出 CPU 時間佔用最多的主要有兩塊。一塊是 Output 處理部分,寫文件。另外一塊就比較奇怪了,是 common.MapStr.Clone() 方法,竟然佔了 34.3% 的 CPU 時間。其中Errorf 佔據了21%的CPU時間。看下代碼:工具

func toMapStr(v interface{}) (MapStr, error) {
	switch v.(type) {
	case MapStr:
		return v.(MapStr), nil
	case map[string]interface{}:
		m := v.(map[string]interface{})
		return MapStr(m), nil
	default:
		return nil, errors.Errorf("expected map but type is %T", v)
	}
}

errors.Errorf生成error對象佔據了大塊時間,把這一塊判斷邏輯放到MapStr.Clone()中就能夠避免產生error,到此你是否是該有些思考?go的error雖然是很好的設計,但不能濫用,不能濫用,不能濫用!不然你可能會爲此付出慘痛的代價。性能

優化後:測試

平均速度 100萬條總時間
18687 條/s 53.5秒

處理速度居然提升了50%多,沒想到幾行代碼的優化,吞吐量居然能提升這麼多,驚不驚喜,意不意外。 再看下修改後的火焰圖

輸入圖片說明

發現MapStr.Clone() 的性能消耗幾乎能夠忽略不計了。

進一步優化:

咱們的日誌都是Docker產生的,使用 JSON 格式,而 Filebeat 使用 Go 自帶的 encoding/json 包是基於反射實現的,性能有必定問題。 既然咱們的日誌格式是固定的,解析出來的字段也是固定的,這時就能夠基於固定的日誌結構體作 JSON 的序列化,而沒必要用低效率的反射來實現。Go 有多個針對給定結構體作 JSON 序列化 / 反序列化的第三方包,這裏使用的是 easyjson:https://github.com/mailru/easyjson。

因爲解析的日誌格式是固定的,因此提早定義好日誌的結構體,而後使用easyjson解析。 處理速度性能提高到

平均速度 100萬條總時間
20374 條/s 49秒

但這樣修改後就會使decode_json_fields 這個processor只能處理特定的日誌格式,適用範圍會有所下降。因此json解析這塊暫時沒有修改。

總結

日誌處理一直是系統運維中比較重要的環節,不管是傳統的運維方式仍是基於Kubernetes(或者Mesos,Swarm等)的新型雲平臺日誌蒐集都格外重要。不管選用哪一種方式蒐集日誌,都有可能遇到性能瓶頸,但一小段代碼的改善就可能徹底解決了你的問題,路漫漫其修遠兮,優化永無止境。

須要稍做說明的是:

  • Filebeat 開發是基於 5.5.1 版本,Go 版本是 1.8.3
  • 測試中Filebeat使用runtime.GOMAXPROCS(1)限制只使用一個核
  • 因爲測試是在同一臺機器上使用相同數據進行的,將日誌輸出到文件對測試結果影響不大。

參考連接: https://mp.weixin.qq.com/s?__biz=MzIwMzg1ODcwMw==&mid=2247486717&idx=1&sn=37fae9ba997b156c2ccb5f28803130b7&chksm=96c9ba9da1be338b040041a60a1b8553563363e9f1b27225bfd6829b3de758d6b8e641a48041#rd

相關文章
相關標籤/搜索