本文整理自李虓在QCon上海2016的演講,原題為:《LinkedIn 基於 Kafka 和 ElasticSearch 的實時日誌分析系統》。回復關鍵詞「領英」,下載完整版PPT。
今天,和跟大家分享我們在用ElasticSearch和Kafka做日誌分析的時候遇到的問題,系統怎麼樣一步一步演變成現在這個版本。你如果想拿ElasticSearch和Kafka來做日誌分析的話,會有一些啟發。全文主要包括以下幾個Topic:
日誌分析系統的基本需求;
LinkedIn的日誌系統演進過程;
我們的經驗和教訓。
首先,什麼是日誌?簡單的說日誌就是一個結構化的數據+時間戳。計算機開始日誌就已經存在,從那時候就有各種各樣的工具來幫我們分析、解析或者查找日誌。
一開始做這個東西的時候,很多團隊覺得不是很需要,工程師登錄到伺服器上面做一些cat或者grep和簡單的表達式處理就夠了,可以找到需要的信息。如果不夠的話,比如在很多臺機器上的話,有mssh、cssh等工具。
還不夠的話,可以自己寫工具,有一次我發現在我們的生產伺服器上面,有一個SRE寫了一套系統,從自己的臺式機,做了一個ssh tunnel到實際的生產系統裡面做了遠程代碼調用,遠程把那些文件拿回來,這是一個一級的安全生產事故,是非常不負責任的事情,但是這也就暴露了我們確實有這個需求。
當我們有五萬臺伺服器,五百多個微服務的時候,你不可能指望每個人都非常熟練的去解決這樣的事情。開發或者運維經常會遇到這樣的需求,比如拿某兩個時間點之間的所有的日誌,只需要看WARN或者ERROR或者FATAL的消息,並且有十幾個錯誤是已知的,要忽略。
這個服務是跑在好幾個數據中心幾百臺伺服器上面,還需要關心有沒有新的錯誤,這個錯誤是不是由於某個特定的用戶造成的,或者某些特定的用戶行為造成的,比如說他post了什麼,或者request的長度超過一個固定長度;這個伺服器上的錯誤信息有沒有和其他伺服器上的錯誤信息相關聯。給我30分鐘我有可能寫出來一個四五行的grep命令去幾百臺伺服器上把日誌拉下來,但如果在凌晨三點鐘,這就是一個不太可能的任務。
日誌分析系統需要滿足以下基本的要求:
對於重要的日誌,滿足索引,檢索、排序、分類,並且提供一定程度的可視化、分析日誌的功能;
可根據數據規模橫向擴展。因為網際網路的發展非常非常的快,我們希望找到一個解決方案,不要過了一年甚至半年,當伺服器或者用戶數量加倍以後,解決方案就完全不可用。需要找到一個方案,當用戶數量加倍時,簡單的加幾臺機器或者伺服器就可以繼續使用;
這套系統能夠很輕易的擴展,因為很多公司已經有了很多的報警或者監控系統。可以方便的通過API或者通過擴展接入到已經有的監控、報警,或者其他系統裡面。
還有一些其他擴展性的需求,包括日誌採樣,提高安全性、保護日誌裡面包含的信息,也是後面著重談的一個問題。
回到四年前,從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的用戶界面。
每個人花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入口動態控制。
這是第二個版本,可以看到在生產環境的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。
根據業務需求拆分,我們拆出來20幾個這樣的相同的集群,這個版本,還有一些問題。首先是跨業務分組集群的查詢,雖然很多的時候,一個問題在一個業務分組裡面就能找到,但是還有很多的時候要跨到另外一個集群裡面才能找到問題到底是從哪開始的。
第二,千萬不要跨數據中心做ElasticSearch的集群,非常非常差,根本跑不起來。即使兩個數據中心非常近這樣做也不好,尤其當數據量上來之後,會有一些非常詭異的問題。數據量非常大的ElasticSearch集群我們會要求它全部在一個機架上,如果有一個伺服器在另一個機架上都會出現timeout的問題。
為了解決剛剛說的問題,我們引入Tribe,用下來的感覺可以用,但是這明顯不是一個他們支持的功能。Tribe理念很好,但是它不支持分層,我們需要兩種不同的Tribe,首先要能跨業務分組,還要跨數據中心。
最好的情況是做一個分層的結構,數據中心在最外面,業務分組在最裡面,但是從設計理念上是另外一個概念,所以不行。只能退而求其次,在一個數據中心裏面會有跨所有業務分組的一個Tribe,還會對相同的業務分組有一個跨數據中心的Tribe,有兩個不同類型的Tribe進行查詢。
到這一步,基本上功能實現的差不多了,就開始慢慢的把500多個服務的日誌打到Kafka裡面,大概花了一兩個月,發現consume跟不上,遇到了性能瓶頸。用ElasticSearch超過50或者100臺伺服器,就會遇到很多這樣的瓶頸。我們花了三個月的時間,做了各種性能調優。
這一步算是最後一步,首先理解了一下自己的業務邏輯,我們要做的事情非常明白,非常單一,就是需要實時的,或者準實時的log來做在線的trouble shooting,基本上不會用到14天以前的數據,保留14天以前的數據就是為了看兩周的周期而已。
今天的問題都解決不完,根本沒有時間考慮幾個月之前的問題,實際的業務狀態是24小時之內的日誌查詢的最多,14天以前用的非常少,查詢速度要求在15秒之內,超過30秒就timeout了。索引速度30秒以內可以接受,超過5分鐘會觸發警報。
採用冷熱分區的方式解決這個問題,我們測試了很多種不同的硬體,最後選定了在非常重要和數據量很大的集群上用ssd做熱索引,24小時之內的索引全部上到SSD機器上,超過24小時就挪到普通的機器上。相當於在集群裡邊,有一個熱的Cluster,數據全面走到熱的cluster裡面,24小時以後,會被挪到冷的cluster。做了這個之後,系統變得比較穩定,功能也正常,內部會根據需求保留7到14天的數據。
這步之後,我們把它推廣到LinkedIn整個公司,第二天我接到法務和安全部的電話。當我們做了一個非常容易的系統,把所有的log展現給所有人的時候,如果大家能很輕易的把4億用戶的用戶名、密碼、郵箱等信息搜出來,這個事情就非常嚴重,所以我們又做了幾個調整。
第一個是定期掃描所有的ES,根據特定的關鍵字來防止敏感信息進入日誌,如果進入馬上報警。還有用戶隱私的問題,所有Elasticsearch的查詢記錄同樣送入Kafka,並對敏感業務部門的訪問進行隔離,所有訪問日誌定期審核。我們的做法是在前面加一個Nginx,在nginx上可以做訪問控制,把用戶所有的訪問日誌全部送回Kafka做定期審核,有一個掃描進程定期的掃描各種各樣的關鍵字。
這是我們現在生產系統裡的狀態,有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 團隊,致力於讓開發和運維團隊緊密配合,在新功能上線和產品穩定性之間找到最佳平衡。
QCon是由InfoQ主辦的全球頂級技術盛會,每年在倫敦、北京、東京、紐約、聖保羅、上海、舊金山召開。
QCon北京2017將於4月16日~18日在北京·國家會議中心舉行,精心設計了支撐海量業務的網際網路架構、大規模網關系統、微服務實踐、快速進化的容器生態、智能化運維、網際網路廣告系統實踐、大數據實時計算與流處理和金融科技轉型與未來等30來個專題,涵蓋架構、大數據、雲計算、移動、前端、人工智慧等熱點領域,將邀請來自Google、Facebook、阿里巴巴、騰訊、百度、美團點評、愛奇藝等典型網際網路公司的技術專家,分享技術領域最新成果。敬請期待。
具體詳戳 「 閱讀原文 」訪問大會官網!
今日薦文點擊下方圖片即可閱讀
是「技術」還是「業務」在驅動公司的發展?這個隊你怎麼站?