1. 程式人生 > >基於Kafka和ElasticSearch,LinkedIn如何構建實時日誌分析系統?

基於Kafka和ElasticSearch,LinkedIn如何構建實時日誌分析系統?

今天,和跟大家分享我們在用ElasticSearch和Kafka做日誌分析的時候遇到的問題,系統怎麼樣一步一步演變成現在這個版本。你如果想拿ElasticSearch和Kafka來做日誌分析的話,會有一些啟發。全文主要包括以下幾個Topic:

 

日誌分析系統的基本需求;

LinkedIn的日誌系統演進過程;

我們的經驗和教訓。

 

為什麼要做日誌分析系統?

首先,什麼是日誌?簡單的說日誌就是一個結構化的資料+時間戳。計算機開始日誌就已經存在,從那時候就有各種各樣的工具來幫我們分析、解析或者查詢日誌。

 

一開始做這個東西的時候,很多團隊覺得不是很需要,工程師登入到伺服器上面做一些cat或者grep和簡單的表示式處理就夠了,可以找到需要的資訊。如果不夠的話,比如在很多臺機器上的話,有mssh、cssh等工具。

 

還不夠的話,可以自己寫工具,有一次我發現在我們的生產伺服器上面,有一個SRE寫了一套系統,從自己的桌上型電腦,做了一個ssh tunnel到實際的生產系統裡面做了遠端程式碼呼叫,遠端把那些檔案拿回來,這是一個一級的安全生產事故,是非常不負責任的事情,但是這也就暴露了我們確實有這個需求。

 

當我們有五萬臺伺服器,五百多個微服務的時候,你不可能指望每個人都非常熟練的去解決這樣的事情。開發或者運維經常會遇到這樣的需求,比如拿某兩個時間點之間的所有的日誌,只需要看WARN或者ERROR或者FATAL的訊息,並且有十幾個錯誤是已知的,要忽略。

 

這個服務是跑在好幾個資料中心幾百臺伺服器上面,還需要關心有沒有新的錯誤,這個錯誤是不是由於某個特定的使用者造成的,或者某些特定的使用者行為造成的,比如說他post了什麼,或者request的長度超過一個固定長度;這個伺服器上的錯誤資訊有沒有和其他伺服器上的錯誤資訊相關聯。給我30分鐘我有可能寫出來一個四五行的grep命令去幾百臺伺服器上把日誌拉下來,但如果在凌晨三點鐘,這就是一個不太可能的任務。

 

日誌分析系統需要滿足以下基本的要求:

對於重要的日誌,滿足索引,檢索、排序、分類,並且提供一定程度的視覺化、分析日誌的功能;

可根據資料規模橫向擴充套件。因為網際網路的發展非常非常的快,我們希望找到一個解決方案,不要過了一年甚至半年,當伺服器或者使用者數量加倍以後,解決方案就完全不可用。需要找到一個方案,當用戶數量加倍時,簡單的加幾臺機器或者伺服器就可以繼續使用;

 

這套系統能夠很輕易的擴充套件,因為很多公司已經有了很多的報警或者監控系統。可以方便的通過API或者通過擴充套件接入到已經有的監控、報警,或者其他系統裡面。

 

還有一些其他擴充套件性的需求,包括日誌取樣,提高安全性、保護日誌裡面包含的資訊,也是後面著重談的一個問題。

 

LinkedIn日誌系統演進

回到四年前,從LinkedIn成立到2012年我們有一個系統叫Splunk,非常好用,只有一個問題,太貴了。2012年的時候,我們當時生產環境有三、四千臺的伺服器,續簽第二年的合約時他們的報價是2000萬美元一年。這實在是不可以接受的,並且那個時候是2012年,我們現在的伺服器臺數、使用者請求數已經翻了差不多十倍,當時的價格如果是2000萬,現在更多,因為它是根據資料量來算license的。

 

從2012年到2014年,因為我們當時決定不用Splunk,就進入了一個混沌期,這段時間非常痛苦,大家都有需求,但是沒有人有方法,很多人開始搞自己的小動作,做些小工具。我之前看了一下內部工具庫,裡面有二、三十個用python或者shell寫的小工具,這些小工具是用來找一個時間段內的log或者特定使用者的log,但是有很大的浪費,很多工具重複的寫,也非常難維護。

 

2014年到2015年這一年多的時間我們痛下決心,一定要做一套能夠橫跨LinkedIn所有log的系統,並且推廣到整個LinkedIn。當時選擇了ELK,它的優點就是:開源,釋出週期非常快,當然也有缺點,它非常的新,所以有很多小毛病。

 

相信大家很多人已經知道ELK是什麼——ElasticSearch、Logstash、Kibana。ElasticSearch就是基於Lucene的儲存,索引,搜尋引擎;Logstash是提供輸入輸出以及轉換處理外掛的日誌標準化管道;Kibana提供視覺化和查詢ES的使用者介面。

LinkedIn日誌系統演進V1

每個人花30分鐘就可以在自己的電腦或者生產環境上搭一個這樣的東西,Log通過Logstash讀出來,放到ElasticSearch裡,然後Kibana去讀。這步做完以後其實就能達到非常好的效果。LinkedIn所有的業務群都會要求有一個異常面板,比如說支付系統業務群,它大概有十個左右不同的小服務。

 

當報警系統發現支付系統有了各種各樣的問題之後,我們第一步就是到異常面板來看Log裡面有沒有什麼東西,可以根據時間線上看,有沒有哪些新的服務在近期有了新的log或者error log數量不一樣。並且它會根據不同的exception/java stack拿出來做count,這也給分析帶來很大的幫助,還可以寫出很多複雜的query。

 

第一個版本非常簡單,我們只把它應用到了一兩個非常關鍵的系統上。這套系統做完之後我們做了一個對比,平均故障解決時間從以前的五十幾分鍾縮短到小於30分鐘。我們的線上系統一般最快會花5到10分鐘才有一個不是很關鍵的警報出來,如果能很快發現問題在哪裡,解決這個問題,比如說一個簡單的rollback操作,在幾百臺機器上也會花5到10分鐘的時間,真正留給人根據log去判斷問題在哪裡的時間也只有短短的5到10分鐘。

 

如果不是有一個異常面板能看到所有的資訊,比如有沒有哪個伺服器的異常比其他伺服器多,有沒有一個異常是突然今天出了很多次的,或者有沒有一個伺服器今天出了很多的異常,最起碼要花二十到三十分鐘的時間手工的看log。

 

第一個版本有幾個問題,第一是Logstash Agent的維護不是很理想,Logstash是基於Ruby的,啟動它就要有幾十兆記憶體被jvm吃掉,我們想避免在每個機器上都要起一個Logstash。並且在我們使用過程中Logstash非常不穩定,莫名其妙就死掉了,還需要一個守護程序守護它。第二個是log標準化,也是非常頭疼的問題。

 

第一個Logstash Agent的問題通過引入Kafka解決,引入Kafka後不需要每個host上有agent。LinkedIn內部有專門的SRE團隊維護Kafka,很便宜,不需要花錢去維護。對於Log標準化,我們花了非常多的時間看,LinkedIn有99%的服務都是java service,有15種以上log,最重要的是access log和application log。我們做的一件事情是通過java Container logger標準化直接寫入Kafka。有的程式直接寫到kafka,不上磁碟,有的程式還要同時寫到磁盤裡面,這是可配置的。

 

這些是通過LinkedIn standard container一起rollout到所有的service上。開發人員什麼都不用管,只要在程式裡寫logger.info/logger.error,那些資訊就會直接進到Kafka。對於程式日誌,預設警告以上的級別進入Kafka,可以線上通過jmx控制。對於訪問日誌,我們有10%取樣,可以通過ATS入口動態控制。

 

LinkedIn日誌系統演進V2

這是第二個版本,可以看到在生產環境的Java service那邊,Host上已經沒有Logstash,所有的log直接寫入Kafka,Logstash從Kafka裡消費這些日誌,寫到ElasticSearch裡面去。

 

第二個版本還會出現一些問題,比如說一個服務出現問題的時候會影響整個ELK cluster。我們出現過這樣的情況,一個團隊寫了一個新的服務,把所有的log的級別都定義成error,整個ElasticSearch就被它沖垮了。很多時候還會出現網路飽和的情況。

 

很簡單,第二步非常簡單的決定就是把它進行拆分優化:

首先按照業務功能拆分ELK cluster,比如說負責支付的,跟錢有關的系統用一個叢集;所有跟使用者登陸有關的系統,安全有關的系統用一個叢集;

將Logstash和ElasticSearch分開執行。ElasticSearch是磁碟密集的操作,Logstash是CPU密集的操作,我們當時想把他們放到一個物理機上,但是試下來相互影響還是挺大的,所以決定把Logstash跟其他的系統混用,與ElasticSearch分開執行;

 

對於每個Kafka話題,Logstash數量不少於話題partition數量。LinkedIn有500多個服務,每個服務會產生訪問日誌、程式日誌兩個Kafka topic。Logstash消費Kafka的時候,如果consumer數量少於partition數量,會觸發Kafka一個隱藏的漏洞,導致Kafka partition不均勻,出現各種詭異的問題。我們的建議是,一般情況下,每個topic有四到八個partition,然後根據情況設定相應數量的Logstash。

 

LinkedIn日誌系統演進V3

根據業務需求拆分,我們拆出來20幾個這樣的相同的叢集,這個版本,還有一些問題。首先是跨業務分組叢集的查詢,雖然很多的時候,一個問題在一個業務分組裡面就能找到,但是還有很多的時候要跨到另外一個叢集裡面才能找到問題到底是從哪開始的。

 

第二,千萬不要跨資料中心做ElasticSearch的叢集,非常非常差,根本跑不起來。即使兩個資料中心非常近這樣做也不好,尤其當資料量上來之後,會有一些非常詭異的問題。資料量非常大的ElasticSearch叢集我們會要求它全部在一個機架上,如果有一個伺服器在另一個機架上都會出現timeout的問題。

為了解決剛剛說的問題,我們引入Tribe,用下來的感覺可以用,但是這明顯不是一個他們支援的功能。Tribe理念很好,但是它不支援分層,我們需要兩種不同的Tribe,首先要能跨業務分組,還要跨資料中心。

 

最好的情況是做一個分層的結構,資料中心在最外面,業務分組在最裡面,但是從設計理念上是另外一個概念,所以不行。只能退而求其次,在一個數據中心裡面會有跨所有業務分組的一個Tribe,還會對相同的業務分組有一個跨資料中心的Tribe,有兩個不同型別的Tribe進行查詢。

 

LinkedIn日誌系統演進V4

到這一步,基本上功能實現的差不多了,就開始慢慢的把500多個服務的日誌打到Kafka裡面,大概花了一兩個月,發現consume跟不上,遇到了效能瓶頸。用ElasticSearch超過50或者100臺伺服器,就會遇到很多這樣的瓶頸。我們花了三個月的時間,做了各種效能調優。

 

這一步算是最後一步,首先理解了一下自己的業務邏輯,我們要做的事情非常明白,非常單一,就是需要實時的,或者準實時的log來做線上的trouble shooting,基本上不會用到14天以前的資料,保留14天以前的資料就是為了看兩週的週期而已。

 

今天的問題都解決不完,根本沒有時間考慮幾個月之前的問題,實際的業務狀態是24小時之內的日誌查詢的最多,14天以前用的非常少,查詢速度要求在15秒之內,超過30秒就timeout了。索引速度30秒以內可以接受,超過5分鐘會觸發警報。

 

LinkedIn日誌系統演進V5

採用冷熱分割槽的方式解決這個問題,我們測試了很多種不同的硬體,最後選定了在非常重要和資料量很大的叢集上用ssd做熱索引,24小時之內的索引全部上到SSD機器上,超過24小時就挪到普通的機器上。相當於在叢集裡邊,有一個熱的Cluster,資料全面走到熱的cluster裡面,24小時以後,會被挪到冷的cluster。做了這個之後,系統變得比較穩定,功能也正常,內部會根據需求保留7到14天的資料。

 

這步之後,我們把它推廣到LinkedIn整個公司,第二天我接到法務和安全部的電話。當我們做了一個非常容易的系統,把所有的log展現給所有人的時候,如果大家能很輕易的把4億使用者的使用者名稱、密碼、郵箱等資訊搜出來,這個事情就非常嚴重,所以我們又做了幾個調整。

 

第一個是定期掃描所有的ES,根據特定的關鍵字來防止敏感資訊進入日誌,如果進入馬上報警。還有使用者隱私的問題,所有Elasticsearch的查詢記錄同樣送入Kafka,並對敏感業務部門的訪問進行隔離,所有訪問日誌定期稽核。我們的做法是在前面加一個Nginx,在nginx上可以做訪問控制,把使用者所有的訪問日誌全部送回Kafka做定期稽核,有一個掃描程序定期的掃描各種各樣的關鍵字。

 

LinkedIn日誌系統演進現狀

這是我們現在生產系統裡的狀態,有20多個針對不同業務模組的ELK叢集,1000+伺服器,主要都是Elasticsearch。1分鐘之內生產系統發生的log我們這邊就可以搜尋,所有的log保留7到14天。現在大概有500億的索引文件,500到800T,之前測試時推到1500到2000T都是可以工作的。

 

因為我們是500多個service,20多個叢集,沒有辦法很好的維護這麼多叢集,所以是每個業務模組的SRE團隊維護自己的Elasticserach叢集。Virtual Team模式確保ELK的及時更新。還有一點比較關鍵,需要保證你的ES不會被沒有授權的使用者訪問,預設的情況下是不接受SSL連線的,SearchGuard和Sheild這兩種解決方式都是可以解決這個問題的。

我想著重提一下采樣方式,這個是比較有意思的,也是比較通用的方式。取樣方式是10%+特定的使用者,為什麼要這麼做?我們調過不同的比例,發現不影響,如果有問題,10%的取樣就能發現。為什麼還要特定使用者呢?很多時候,有一些active的使用者會經常給你報錯,給你反饋意見,需要及時去看到底發生了什麼事情。

 

對LinkedIn來說有大概百分之零點幾的power user在網站上非常活躍,做很多事情,我們需要把他們的log加到sample裡。所有的使用者請求,都是通過Apache Traffic Server進入資料中心,在這層它會去訪問LiX,詢問是否要對當前使用者打標籤,如果打了標籤就把這個標籤放在InvocationContext裡面。

從前臺到後臺所有的伺服器只要touch到這個request,都會在InvocationContext裡看到一個requestID。通過java container的bydefault得到requestID,把那條訪問的日誌發到Kafka裡面,通過這樣的方式做成sample。

 

這樣做有兩點好處,一點是如果有什麼問題,只需要把他的memberID或者requestID拿到最上面一層Tribe裡面,系統就會出現關於這條request的所有service的log。99.9%的情況可以一目瞭然的知道哪裡出了問題。做微服務的大家都知道,dependence非常亂,我們LinkedIn的情況,一個request會touch二三十個service。所以說有這樣一個方式是非常重要的。

 

日常運維之坑

我想聊一下我們遇到的幾個坑,ES叢集的預設名字是大坑,如果不改叢集名字就把它放在自己的電腦上或者測試系統上跑,一旦相同子網的人加了一個新的node也沒有改名字,就會自動加到你的叢集裡面。不管怎麼樣一定要改你的叢集名字。

 

Master、Data、Client節點不要混用,使用單ES節點;JVM不要allocate超過30G的heap,我們測試發現30G左右的heap可以達到最好的performance,如果超過30G就沒有辦法完全使用;JVM版本的相容性也是一個超級大的坑,它不是最新的相容,也不是哪個版本以後,或者以前相容,是有幾個版本相容,再幾個版本不相容,再幾個版本相容。有一些相容性的問題一定要注意。

 

日常運維之硬體

講一下剛才已經提到的硬體的選擇,要根據資料量,業務的情況,索引和查詢速度的要求決定,像我們的要求是一定要索引快,因為要做實時的故障排查,資料的型別我們只做兩個,一個是訪問log,一個是application log,這些決定了我們使用的硬體是12核/64G/2x1TB的硬碟,有八九百臺都是這樣的配置。

 

有一小部分是資料量非常大但是對查詢的速度要求不是很高,比如做自動查詢的那套,我們會把資料丟到JBODs裡面,索引速度優先考慮的話,會用100臺左右的SSD。

 

日常運維之叢集

叢集沒有magic number,必須要用實際的生產資料,一遍一遍試,才能試出來用什麼樣的配置能達到最好的效果,這幾個是我們的learning:

橫向擴充套件,不要縱向擴充套件,不要試圖用更大的memory或者最快的CPU,不會有太大的效果,我們大概測過四五個版本,基本上沒怎麼成功;

每個shard不要超過50G,只要超過50G我們的shard就會莫名其妙的失聯;

關閉冗餘,我們把replication關了是為了更快。關了之後如果出現硬體故障,從Kafka那邊讀,資料很快就能回來;

每天建新的索引,有一個叢集因為資料量非常大,會做每個小時的索引,二三個資料量比較小的叢集每週做一個索引;

只分析必要欄位,IP欄位、裝置版本,這些欄位完全沒有必要做分析,只要能整欄位查詢就行了,把它關掉,可以省很多的空間,index速度也會快很多。

 

日常運維之工具

還有就是一些工具,剛剛提到主動掃描敏感資訊的就是一個script在後臺根據一些關鍵字掃描敏感資訊;結合警報系統,相信每個公司都有自己的警報系統,ElasticAlert是一個開源的解決方案,我們自己也有內建的,根據讀出來的資訊的關鍵字做警報;迴圈刪除index用的Curator;系統健康狀態監控是自己做的監控系統,官方的Marvel也很好用。

 

提高程式碼質量

做完這套系統以後,我們並不是完全被動的狀態,只能用這套系統來做故障排查,我們發現這套系統裡面有些指標可以很好的反映開發人員的程式碼的質量。

 

請求數/日誌總行數,對於每個service都不一樣,但是大概有一個區間,如果出了這個區間,可能這個service就有一些問題;

請求數量/錯誤(異常)行數,這個也有一個區間,我們這邊的區間是1000個請求,大概產生2000到5000行的日誌,500到3000行的異常數量,這個是我們可以接受的,經常會有service超出這個範圍10倍,通過這個就能發現異常有沒有處理。

 

通過這些發現一些有意思的metric返回給開發人員,幫助他們提高程式碼的質量,這是一個比較有意思的發現。國外和國內大家做的東西很多很像,遇到的問題也很像,希望能給大家一些啟發。

 

作者介紹

李虓(Li Xiao),LinkedIn SRE 團隊高階技術經理。帶領 SRE 團隊負責 LinkedIn 線上支付系統,高階會員功能,公共 API 介面,視訊上傳和分享等系統的運維,同時負責搭建 LinkedIn 生產環境日誌檢索系統,Rest.Li 框架效能優化等跨功能專案。在加入 LinkedIn 之前有著豐富的 Java 開發和專案管理經驗。四年前加入 LinkedIn SRE 團隊,致力於讓開發和運維團隊緊密配合,在新功能上線和產品穩定性之間找到最佳平衡。