從ELK到EFK,日誌系統的高階玩法兒
作為中國最大的線上教育站點,滬江日誌服務的使用者包含滬江網校、交易、金融、CCtalk(直播平臺) 等多個部門的多個產品的日誌搜尋分析業務,每日產生的各類日誌有好十幾種,每天處理約10億條(1TB)日誌,熱資料保留最近7天資料,冷資料永久儲存。
為什麼做日誌系統
首先,什麼是日誌? 日誌就是程式產生的,遵循一定格式(通常包含時間戳)的文字資料。
通常日誌由伺服器生成,輸出到不同的檔案中,一般會有系統日誌、 應用日誌、安全日誌。這些日誌分散地儲存在不同的機器上。
通常當系統發生故障時,工程師需要登入到各個伺服器上,使用 grep / sed / awk 等 Linux 指令碼工具去日誌裡查詢故障原因。在沒有日誌系統的情況下,首先需要定位處理請求的伺服器,如果這臺伺服器部署了多個例項,則需要去每個應用例項的日誌目錄下去找日誌檔案。每個應用例項還會設定日誌滾動策略(如:每天生成一個檔案),還有日誌壓縮歸檔策略等。
這樣一系列流程下來,對於我們排查故障以及及時找到故障原因,造成了比較大的麻煩。因此,如果我們能把這些日誌集中管理,並提供集中檢索功能,不僅可以提高診斷的效率,同時對系統情況有個全面的理解,避免事後救火的被動。
我認為,日誌資料在以下幾方面具有非常重要的作用:
- 資料查詢:通過檢索日誌資訊,定位相應的 bug ,找出解決方案
- 服務診斷:通過對日誌資訊進行統計、分析,瞭解伺服器的負荷和服務執行狀態
- 資料分析:可以做進一步的資料分析,比如根據請求中的課程 id ,找出 TOP10 使用者感興趣課程。
針對這些問題,為了提供分散式的實時日誌蒐集和分析的監控系統,我們採用了業界通用的日誌資料管理解決方案 - 它主要包括 Elasticsearch 、 Logstash 和 Kibana 三個系統。通常,業界把這套方案簡稱為ELK,取三個系統的首字母,但是我們實踐之後將其進一步優化為EFK,F代表Filebeat,用以解決Logstash導致的問題。下面,我們展開詳細介紹。
文中涉及的 ELK stack 版本是:
Elasticsearch 5.2.2
Logstash 5.2.2
Kibana 5.2.2
Filebeat 5.2.2
Kafka 2.10
Logstash :資料收集處理引擎。支援動態的從各種資料來源蒐集資料,並對資料進行過濾、分析、豐富、統一格式等操作,然後儲存以供後續使用。
Kibana :視覺化化平臺。它能夠搜尋、展示儲存在 Elasticsearch 中索引資料。使用它可以很方便的用圖表、表格、地圖展示和分析資料。
Elasticsearch :分散式搜尋引擎。具有高可伸縮、高可靠、易管理等特點。可以用於全文檢索、結構化檢索和分析,並能將這三者結合起來。Elasticsearch 基於 Lucene 開發,現在使用最廣的開源搜尋引擎之一,Wikipedia 、StackOverflow、Github 等都基於它來構建自己的搜尋引擎。
Filebeat :輕量級資料收集引擎。基於原先 Logstash-fowarder 的原始碼改造出來。換句話說:Filebeat就是新版的 Logstash-fowarder,也會是 ELK Stack 在 shipper 端的第一選擇。
既然要談 ELK 在滬江系統中的應用,那麼 ELK 架構就不得不談。本次分享主要列舉我們曾經用過的 ELK 架構,並討論各種架構所適合的場景和優劣供大家參考
簡單版架構
這種架構下我們把 Logstash 例項與 Elasticsearch 例項直接相連。Logstash 例項直接通過 Input 外掛讀取資料來源資料(比如 Java 日誌, Nginx 日誌等),經過 Filter 外掛進行過濾日誌,最後通過 Output 外掛將資料寫入到 ElasticSearch 例項中。
這個階段,日誌的收集、過濾、輸出等功能,主要由這三個核心元件組成 Input 、Filter、Output。
Input:輸入,輸入資料可以是 File 、 Stdin(直接從控制檯輸入) 、TCP、Syslog 、Redis 、Collectd 等。
Filter:過濾,將日誌輸出成我們想要的格式。Logstash 存在豐富的過濾外掛:Grok 正則捕獲、時間處理、JSON 編解碼、資料修改 Mutate 。Grok 是 Logstash 中最重要的外掛,強烈建議每個人都要使用 Grok Debugger 來除錯自己的 Grok 表示式。
grok {
match => ["message", "(?m)\[%{LOGLEVEL:level}\] \[%{TIMESTAMP_ISO8601:timestamp}\] \[%{DATA:logger}\] \[%{DATA:threadId}\] \[%{DATA:requestId}\] %{GREEDYDATA:msgRawData}"]
}
Output:輸出,輸出目標可以是 Stdout (直接從控制檯輸出)、Elasticsearch 、Redis 、TCP 、File 等
這是最簡單的一種ELK架構方式,Logstash 例項直接與 Elasticsearch 例項連線。優點是搭建簡單,易於上手。建議供初學者學習與參考,不能用於線上的環境。
叢集版架構
這種架構下我們採用多個 Elasticsearch 節點組成 Elasticsearch 叢集,由於 Logstash 與 Elasticsearch 採用叢集模式執行,叢集模式可以避免單例項壓力過重的問題,同時在線上各個伺服器上部署 Logstash Agent,來滿足資料量不大且可靠性不強的場景。
資料收集端:每臺伺服器上面部署 Logstash Shipper Agent 來收集當前伺服器上日誌,日誌經過 Logstash Shipper 中 Input外掛、Filter外掛、Output 外掛傳輸到 Elasticsearch 叢集。
資料儲存與搜尋:Elasticsearch 配置預設即可滿足,同時我們看資料重要性來決定是否新增副本,如果需要的話,最多一個副本即可。
資料展示:Kibana 可以根據 Elasticsearch 的資料來做各種各樣的圖表來直觀的展示業務實時狀況。
這種架構使用場景非常有限,主要存在以下兩個問題:
- 消耗伺服器資源:Logstash 的收集、過濾都在伺服器上完成,這就造成伺服器上佔用系統資源較高、效能方面不是很好,除錯、跟蹤困難,異常處理困難;
- 資料丟失:大併發情況下,由於日誌傳輸峰值比較大,沒有訊息佇列來做緩衝,就會導致 Elasticsearch 叢集丟失資料。
這個架構相對上個版本略微複雜,不過維護起來同樣比較方便,同時可以滿足資料量不大且可靠性不強的業務使用。
引入訊息佇列
該場景下面,多個數據首先通過 Lostash Shipper Agent 來收集資料,然後經過 Output 外掛將資料投遞到 Kafka 叢集中,這樣當遇到 Logstash 接收資料的能力超過 Elasticsearch 叢集處理能力的時候,就可以通過佇列就能起到削峰填谷的作用, Elasticsearch 叢集就不存在丟失資料的問題。
目前業界在日誌服務場景中,使用比較多的兩種訊息佇列為 :Kafka VS Redis。儘管 ELK Stack 官網建議使用 Redis 來做訊息佇列,但是我們建議採用 Kafka 。主要從下面兩個方面考慮:
- 資料丟失:Redis 佇列多用於實時性較高的訊息推送,並不保證可靠。Kafka保證可靠但有點延時;
- 資料堆積:Redis 佇列容量取決於機器記憶體大小,如果超過設定的Max memory,資料就會拋棄。Kafka 的堆積能力取決於機器硬碟大小。
綜合上述的理由,我們決定採用 Kafka 來緩衝佇列。不過在這種架構下仍然存在一系列問題:
- Logstash shipper 收集資料同樣會消耗 CPU 和記憶體資源;
- 不支援多機房部署。
這種架構適合較大叢集的應用部署,通過訊息佇列解決了訊息丟失、網路堵塞的問題。
多機房部署
隨著滬江業務的飛速增長,單機房的架構已經不能滿足需求。不可避免的,滬江的業務需要分佈到不同機房中,對於日誌服務來說也是不小的挑戰。當然業界也有不少成熟的方法,比如阿里的單元化、騰訊的 SET 方案等等。
最終我們決定採用單元化部署的方式來解決 ELK 多機房中遇到的問題(延時、專線流量過大等),從日誌的產生、收集、傳輸、儲存、展示都是在同機房裡面閉環消化,不存在跨機房傳輸與呼叫的問題。因為互動緊密的應用盡量部署在同機房,所以這種方案並不會給業務查詢造成困擾。
Logstash、Elasticsearch、Kafka、Kibana 四個叢集都部署到同一機房中,每個機房都要每個機房自己的日誌服務叢集,比如A機房業務的日誌只能傳輸給本機房 Kafka ,而A機房 Indexer 叢集消費並寫入到A機房 Elasticsearch 叢集中,並由A機房 Kibana 叢集展示,中間任何一個步驟不依賴B機房任何服務。
引入Filebeat
Filebeat 是基於原先 logstash-forwarder 的原始碼改造出來的,無需依賴 Java 環境就能執行,安裝包10M不到。
如果日誌的量很大,Logstash 會遇到資源佔用高的問題,為解決這個問題,我們引入了Filebeat。Filebeat 是基於 logstash-forwarder 的原始碼改造而成,用 Golang 編寫,無需依賴 Java 環境,效率高,佔用記憶體和 CPU 比較少,非常適合作為 Agent 跑在伺服器上。
下面看看Filebeat的基本用法。編寫配置檔案,從 Nginx access.log 中解析日誌資料:
# filebeat.yml
filebeat.prospectors:
- input_type: log
paths: /var/log/nginx/access.log
json.message_key:
output.elasticsearch:
hosts: ["localhost"]
index: "filebeat-nginx-%{+yyyy.MM.dd}"
我們來看看壓測資料:
壓測環境
- 虛擬機器 8 cores 64G記憶體 540G SATA盤
- Logstash 版本 2.3.1
- Filebeat 版本 5.5.0
壓測方案
Logstash / Filebeat 讀取 350W 條日誌 到 console,單行資料 580B,8個程序寫入採集檔案。
壓測結果
專案 | workers | cpu usr | 總共耗時 | 收集速度 |
---|---|---|---|---|
Logstash | 8 | 53.7% | 210s | 1.6w line/s |
Filebeat | 8 | 38.0% | 30s | 11w line/s |
Filebeat 所消耗的CPU只有 Logstash 的70%,但收集速度為 Logstash 的7倍。從我們的應用實踐來看,Filebeat 確實用較低的成本和穩定的服務質量,解決了 Logstash 的資源消耗問題。
最後,分享給大家一些血淚教訓,希望大家以我為鑑。
Indexer 執行一段時間後自動掛掉
突然有一天監控發現日誌不消費了,排查下來發現消費 Kafka 資料的 indexer 掛掉了。所以,Indexer 程序也是需要用 supervisor 來監控的,保證它時刻都在執行。
Java異常日誌輸出
開始我們在通過 grok 切割日誌的時候,發現 Java 的 Exception 日誌輸出之後,會出現換行的問題。後來使用 Logstash codec/multiline 外掛來解決。
input {
stdin {
codec => multiline {
pattern => "^\["
negate => true
what => "previous"
}
}
}
由於時區導致日誌8小時時差
Logstash 2.3版本 date外掛配置如下,檢視解析結果發現@timestamp比中國時間早了8小時。
解決方案 Kibana 讀取瀏覽器的當前時區,然後在頁面上轉換時間內容的顯示。
date {
match => [ "log_timestamp", "YYYY-MM-dd HH:mm:ss.SSS" ]
target => "@timestamp"
}
Grok parse failure
我們遇到線上 node 日誌突然有幾天日誌查看不出來。後來拉出原始日誌對比才發現生成出來的日誌格式不正確,同時包含 JSON 格式和非 JSON 格式的日誌。但是我們用grok解析的時候採用是 json 格式。建議大家輸出日誌保證格式一致同時不要出現空格等異常字元,可以使用線上 grok debug來除錯正則。
總結
基於 ELK stack 的日誌解決方案的優勢主要體現於:
- 可擴充套件性:採用高可擴充套件性的分散式系統架構設計,可以支援每日 TB 級別的新增資料。
- 使用簡單:通過使用者圖形介面實現各種統計分析功能,簡單易用,上手快。
- 快速響應:從日誌產生到查詢可見,能達到秒級完成資料的採集、處理和搜尋統計。
- 介面炫麗:Kibana 介面上,只需要點選滑鼠,就可以完成搜尋、聚合功能,生成炫麗的儀表板。
參考資料
作者曹林華,原文載於微信公眾號「滬江技術學院」。