最近想把之前做過的日志項目及個人的思考梳理一下,于是有了本文。
背景
我們這邊應用部署的環境比較復雜,主要有以下幾種:
- 機器直接部署
- 通過原生Docker部署
- 通過kubernates集群部署
部署環境不統一,導致查看應用日志很不方便。
業務需求
與部署環境對應,對于日志收集需求分為以下幾類:
- 機器上的文本日志(直接運行在物理機或者虛擬機中的應用日志)
- 運行在docker容器中的應用日志
- 運行在kubernates集群中的應用日志
具體業務需求可以拆分為:
- 按照項目、應用、實例維度檢索日志并支持搜索關鍵字高亮(因為大家檢索日志的時候,肯定是檢索某個項目、某個應用、某個實例的日志)
- 支持檢索出某條想要的日志后,可以查看上下文(查看該日志所在日志文件的日志上下文)
- 支持日志下載(目前支持兩種場景:搜索結果下載、上下文下載;支持兩種方式:在線下載、離線下載)
- 支持自動化批量部署、卸載Agent,部署、卸載過程可視化
- 單實例支持多elasticsearch集群
- 支持文本日志、docker日志、k8s日志并能與將日志與其業務意義對應上。(即不管是哪種日志形式、來源,最終都需要與業務意義上的項目、應用、實例對應起來,因為對于日志的使用者來說,查詢日志的出發點肯定是查詢某個項目、某個應用(可以不選)、某個實例(可以不選)、某段時間的日志。)
- 支持部署到業務自己的集群中
需求已經明確了,下面看一下業界方案。
業界日志系統架構

- Collector的作用是:
- 清洗、匯聚數據,減少對于后端集群的壓力。
- 安全,不允許Agent直連kafka等內部集群,保證一定的安全性,即使后端發生調整也能保證對于Agent連接、認證方式的穩定。
- MQ的作用是削峰填谷、解耦、多次消費。
上圖的架構是業界比較通用的一種架構,對于各種場景都考慮的比較全。
既然業界的架構已經這么完備,那么我們是否就直接采用呢?
對于我們而言,有以下幾個問題:
- 涉及的組件比較多,鏈路比較長,運維比較麻煩
- 這一整套架構,不利于單獨部署(比如某個業務應用部署機房網絡是隔離的,而且項目又不大,只能提供有限的幾臺機器,這時候如果需要部署業界這套架構的話,資源就會比較受限,如果想做到即支持業界架構組件的可插拔(比如可靈活的決定是否需要Collector、MQ),那么就需要運維幾套配置或代碼)
- 最關鍵的就是其中組件提供的功能,我們目前用不到。比如MQ的削峰填谷、多次消費。
組件選擇
選擇組件,我們這邊主要是從以下幾個方面進行考量的:
- 組件對應的開源生態完整、活躍度高
- 對應的技術棧是我們所熟悉的,我們這邊語言技術棧主要是JAVA、Go,如果組件語言是C、Ruby,應該就被排除了。
- 運維成本
- 易部署、性能好
Agent
一提到日志收集方案,大家第一個想到的肯定是ELK(Elasticsearch、Logstash、Kibana ),但Logstash依賴于JVM不管是性能還是簡潔性,都不是日志收集agent的首選。
個人感覺一個好的agent應該是資源占用少,性能好,不依賴別的組件,可以獨立部署。而Logstash明顯不符合這幾點要求,也許正是基于這些考慮elastic推出了Filebeat。
Collector、MQ
Elasticsearch集群在部署的時候,一般都是提前估計好容量、機器、shard等信息,因為Elasticsearch集群運行后,再水平拓展,比較麻煩,而我們這邊由于業務及成本限制無法很好的預估容量,所以就結合公司實際要求:使用日志服務的業務方自帶機器,也就是業務方會有獨立的Elasticsearch集群。
每個業務方都使用自己的Elasticsearch集群,所以集群壓力不會很大,從而Collector、MQ這兩個組件對于我們的作用也就很小了。
ETL
因為Elasticsearch Ingest Node完全可以滿足我們的解析需求,所以就沒有必要再引入Logstash等相關組件了。
到這里,基本可以看出我們的架構如下:

架構設計的幾個原則:
- 合適優于業界領先
- 簡單優于復雜
- 演化優于一步到位
具體實現
基于需求及EFK套件,梳理我們場景中特有的東西:
- docker日志的場景比較單一,都是通過之前一個產品A發布部署的,其docker命名規則比較統一,可以通過截取docker.container.name來獲取應用名字;同時在部署的時候,可以知道部署目標機器的ip,這樣就可以通過應用+ip來作為實例名稱。
- k8s場景也比較統一,都是通過之前一個產品B發布部署的,其pod命名規則比較統一,可以通過截取kubernetes.pod.name來獲取應用名字(但需要通過namespaces關聯到tenant,再通過tenant與項目一一對應);k8s中的pod.name就是唯一的,以此來作為實例名稱即可。
- 文本日志:因為文本日志主要的場景是已經裸機部署的應用,這種場景下,不存在應用自動遷移的情況,所以文本日志的應用名稱、實例名稱可以在部署的時候打上標簽即可。
具體規則及解析見下圖(實例部分處理暫未標注):

推薦寫日志到文本文件中,使用標準輸出就好。
到這里可以發現我們選擇Filebeat來作為日志的收集端,Elasticsearch來存儲日志并提供檢索能力。
那么,日志的清洗在哪里做呢?
日志的清洗一般有兩種方式:
- 先把日志收集到kafka,再通過Logstash消費kafka的數據,來清洗數據
- 直接通過Elasticsearch的[Ingest Node]來清洗數據,因為Ingest Node也支持Grok表達式
對于,我們的場景而言,我們需要清洗數據的要求比較簡單,主要是應用、實例名稱的截取還有文本日志中日志時間的處理(@timestamp重置,時區處理),所以我們選擇了方案2。
在我們的方案中,并沒有提供Kibana 的界面直接給用戶用,而是我們自己根據公司業務獨立開發的。
前端界面為什么不采用Kibana,而需要自己開發?
- kibana對于業務開發人員有一定的學習成本
- kibana界面沒有很好的將日志內容與業務意義關聯起來(界面選擇總比一次次的輸入要好,這也是我們將日志的項目、應用、實例等業務信息解析出來的原因)
- log-search支持Query String,因此對于熟悉kibana的開發人員來說,在我們自己開發的前端界面檢索效果是一樣的。
log-search提供的功能可以參見github:github.com/jiankunking…
如果日志需要清洗的比較多,可以采用方案1,或者先不清洗,先把數據落到Elasticsearch,然后在查詢的時候,進行處理。比如在我們的場景中,可以先把日志落到Elasticsearch中,然后在需要檢索應用名稱的時候,通過代碼來處理并獲取App名字。
監控、告警
其實基于日志可以做很多事情,比如:
- 基于日志做監控(google Dapper)
- 基于日志做告警
- 基于日志做machine Learning
具體思路,可以參見下圖:

前提:能要求使用方,按照某種規則打印日志。 監控發展:監控基本就是先打通鏈路trace,然后再在上報信息或者日志信息中,加強業務方面標識,即給監控添加業務維度方面的視角。
其它
DaemonSet
以DaemonSet方式部署Filebeat來收集日志,其實收集也是宿主機/var/lib/docker/containers目錄下的日志。 Running Filebeat on Kubernetes
Sidecar
一個POD中運行一個sidecar的日志agent容器,用于采集該POD主容器產生的日志。
莫名想起了istio。
Filebeat可以以sidecar模式來進行容器日志的收集,也就是filebeat和具體的服務容器部署在同一個pod內,指定收集日志的路徑或文件,> 即可將日志發送到指定位置或Elasticsearch這類的搜索引擎。 每個pod內部署filebeat的模式,好處是和具體的應用服務低耦合,可擴展性強,不過需要在yaml進行額外配置。