轉(zhuǎn)-基于ELK的億級實時日志分析平臺實踐
1. ELK是什么拯杠,為什么要使用ELK速和;
2. 斗魚ELK日志分析平臺實踐圆雁;
3. 高并發(fā)環(huán)境下的ELK相關(guān)優(yōu)化地淀;
在講解ELK之前我先來闡述一下日志在互聯(lián)網(wǎng)應(yīng)用中的重要性偿渡。在互聯(lián)網(wǎng)行業(yè)里日志數(shù)據(jù)非常重要臼寄,形式也多種多樣。通過日志我們可以計算請求量溜宽、流量來源分析吉拳、了解用戶行為、鑒別作弊用戶(如:是否是機器人)等等适揉。
在計算PV留攒、UV的場景下,根據(jù)業(yè)務(wù)需求我們通常以離線方式(MR / HIVE)隔天進行報表相關(guān)數(shù)據(jù)生產(chǎn)嫉嘀。但是對于故障排查肯定是希望能夠快速的進行日志查詢炼邀、定位、解決問題剪侮,對于實時性要求非常高汤善。
舉個例子,對于一個大流量的Web應(yīng)用通常以Stateless方式設(shè)計,這樣可以更方便的進行水平擴容红淡。但是隨著應(yīng)用實例數(shù)量越來越多不狮,我們查詢?nèi)罩揪驮絹碓嚼щy。在沒有日志系統(tǒng)的情況下在旱,首先我們需要定位到請求的服務(wù)器地址摇零,如果每臺服務(wù)器都部署了多個應(yīng)用實例,我們則需要去每個應(yīng)用實例的日志目錄下去找日志文件桶蝎。每個服務(wù)可能還會設(shè)置日志滾動策略(如:每200M一個文件)驻仅,還有日志壓縮歸檔策略。
我們查詢一條出錯信息就要在茫茫多的日志文件里去找到它登渣,于是使出我們的十八般武藝head less tail grep wc awk count cut噪服,但是如果需要統(tǒng)計最近3天的某個接口的異常次數(shù)。胜茧。粘优。。
除了上面出現(xiàn)的狀況我們還需要考慮:日志量太大如何歸檔呻顽、文本搜索太慢怎么辦雹顺、如何多維度查詢,ELK就是幫我們來解決這些問題的廊遍。
1. ELK是什么嬉愧,為什么要使用ELK
ELK 是elastic公司提供的一套完整的日志收集、展示解決方案喉前,是三個產(chǎn)品的首字母縮寫没酣,分別是ElasticSearch、Logstash 和 Kibana卵迂。
ElasticSearch簡稱ES裕便,它是一個實時的分布式搜索和分析引擎,它可以用于全文搜索狭握,結(jié)構(gòu)化搜索以及分析。它是一個建立在全文搜索引擎 Apache Lucene 基礎(chǔ)上的搜索引擎疯溺,使用 Java 語言編寫论颅。
Logstash是一個具有實時傳輸能力的數(shù)據(jù)收集引擎,用來進行數(shù)據(jù)收集(如:讀取文本文件)囱嫩、解析恃疯,并將數(shù)據(jù)發(fā)送給ES。
Kibana為 Elasticsearch 提供了分析和可視化的 Web 平臺墨闲。它可以在 Elasticsearch 的索引中查找今妄,交互數(shù)據(jù),并生成各種維度表格、圖形盾鳞。
2. 斗魚ELK日志分析平臺實踐犬性;
最初我們引入ELK的原因是實現(xiàn)一個小需求,實時統(tǒng)計服務(wù)器500錯誤數(shù)量腾仅,在使用過程中發(fā)現(xiàn)ELK的功能確實非常強大乒裆,隨后接入其它業(yè)務(wù)部門應(yīng)用日志,解決日志查詢慢的痛點推励,加速錯誤定位鹤耍。于2016年底接入全站所有服務(wù)器日志,并完成了ES 5.0的升級验辞。
隨著對ELK技術(shù)棧的不斷深入稿黄,我們將監(jiān)控報警系統(tǒng)的數(shù)據(jù)存儲層由HBase替換成ES。監(jiān)控報警系統(tǒng)的數(shù)據(jù)存儲層主要數(shù)據(jù)類型是時間序列數(shù)據(jù)跌造,如某一時刻的應(yīng)用CPU使用率杆怕。通常在做數(shù)據(jù)展示時我們會將數(shù)據(jù)先以某一時間粒度(如:5分鐘)進行后端聚合匯總再交給前端進行渲染,并且這個時間粒度會隨著用戶選擇的時間區(qū)間而變化鼻听,針對此類需求我們依靠ES提供的強大數(shù)據(jù)聚合特性極大減少了開發(fā)成本财著。
最初我們并沒有使用ELK全套技術(shù)棧,我們使用了Flume(一個Java編寫的日志采集撑碴、聚合和傳輸系統(tǒng)類似上述的Logstash)作為日志收集Agent撑教。Flume使用
tail -f
的方式實時收集日志文件中追加的文本內(nèi)容。通過Flume配置文件中定義的正則表達式對日志文本進行字段分割醉拓,隨后寫入ElasticSearch伟姐,整體架構(gòu)如下圖:
在使用過程中發(fā)現(xiàn)Flume偶爾會OOM,原因是使用了Flume的Memory Channel亿卤。使用此方式時愤兵,F(xiàn)lume會將
tail -f 讀取到的數(shù)據(jù)寫入內(nèi)存,隨后輸出到ES排吴。但是一旦數(shù)據(jù)輸出速度小于數(shù)據(jù)讀取速度時內(nèi)存中積壓的數(shù)據(jù)就會越來越多秆乳,從而產(chǎn)生OOM。
于是我們將Flume中的Memory Channel替換成File Channel钻哩,該方式在數(shù)據(jù)讀取之后屹堰,將數(shù)據(jù)寫入Flume中的本地文件存儲再讀取(個人覺得不是很優(yōu)雅)街氢。
使用過程中還發(fā)現(xiàn)在日志收集過程中Flume搶占了大量CPU資源用于日志解析(通過正則)扯键,導(dǎo)致服務(wù)器中其他應(yīng)用資源不足。于是我們搭建了一個Flume日志聚合層珊肃,將日志解析工作放在聚合層中完成荣刑,Agent只做數(shù)據(jù)收集馅笙、推送。隨后架構(gòu)變成下面這個樣子厉亏。
聚合層中的Flume實例為無狀態(tài)節(jié)點董习,如果性能不足可以增加聚合層中的Flume實例數(shù)量,但是要在Agent端配置文件中添加新Flume聚合層實例的IP端口信息叶堆,并重新啟動阱飘。
穩(wěn)定一段時間后,問題又來了虱颗。tail
-f 方式收集日志在Flume Agent重啟時會丟失重啟時間段的數(shù)據(jù)沥匈。嘗試使用Flume提供的 Spooling Directory
Source
也并不能完美的解決問題(需要修改所有日志文件存儲方式,每分鐘去切割文件忘渔,如果跟其他業(yè)務(wù)團隊這樣說高帖,估計會被打死)。并且我們當時正在準備進行ES的1.x
to 2.x版本的升級畦粮,但是Flume并不支持數(shù)據(jù)寫入ES 2.x版本散址,于是我們果斷放棄了Flume投入Logstash的懷抱。
Logstash相對于Flume提供了更加豐富的日志處理器宣赔,并且支持最新ES版本(畢竟是親兒子)预麸。Logstash在進行文本數(shù)據(jù)收集時并沒有使用
tail -f 這種簡單粗暴的方式,而是在本地文件中記錄了日志文件被讀取到的位置儒将,完美解決了上面說的升級重啟時丟失數(shù)據(jù)的問題吏祸。
將Flume聚合層替成了Kafka消息隊列,我們的監(jiān)控系統(tǒng)通過Kafka提供的API實時獲取Kafka中的隊列Metrics钩蚊,并設(shè)置閾值進行報警贡翘,這種集中式的隊列監(jiān)控也是Flume聚合層無法做到的。
在這種標準的ELK架構(gòu)下砰逻,日志收集已經(jīng)非常穩(wěn)定鸣驱,但是還有2點不足:
1. Logstash體積太大,依賴Java環(huán)境蝠咆,其他部門的運維同學(xué)每次都要先部署一套JDK踊东,不開心;
2. Logstash日志解析資源占用偏高刚操;
為了減少Agent體積闸翅,方便運維同學(xué)部署,我們將Logstash替換成了FileBeat(也是elastic公司的產(chǎn)品)赡茸、Rsyslog(用來收集系統(tǒng)日志)缎脾,而且這2個組件無需依賴Java環(huán)境就能運行祝闻,安裝包10M不到占卧。并且Agent內(nèi)存占用也得到了極大改善遗菠。
針對第二個日志解析資源占用偏高的問題。Logstash的核心代碼是用ruby語言開發(fā)华蜒,雖然是運行在jruby上辙纬,但是由于中間涉及到數(shù)據(jù)結(jié)構(gòu)的轉(zhuǎn)化,性能是跟用原生的java語言運行在jvm上肯定是有所差距的叭喜,于是我們在部分場景使用Hangout替換了Logstash贺拣。
最后針對日志存不規(guī)范的情況,一個日志文件中日志有多種結(jié)構(gòu)捂蕴,可能正則表達式(或者GROK)不是很好寫譬涡,即便是寫出來之后性能也達不到要求。針對這種日志不規(guī)范的這種情況我們自己寫了一些基于規(guī)則判斷的解析器代替正則表達式去完成解析工作啥辨,性能獲得極大提升涡匀,日志解析時的CPU占用率降低了一個數(shù)量級。
說到這里溉知,一個能應(yīng)對高并發(fā)的ELK架構(gòu)終于成型了陨瘩,上圖。
從上圖中可以看到级乍,這里部署了多套ES集群(A舌劳、B、C)玫荣。這樣做是為了避免所有業(yè)務(wù)放在一個大集群中相互影響甚淡。如此一來每個業(yè)務(wù)(或者某些業(yè)務(wù))都有獨立的集群,更加方便運維崇决。
Kibana與多個ES集群之間通過Tribe Node進行交互材诽,Tribe Node作為集群聯(lián)邦節(jié)點負責將請求路由到正確的后端ES集群上,做到后端ES集群拓撲變化時對使用方透明恒傻。
目前整個斗魚負責日志這一塊的ELK集群總共包含50+物理服務(wù)器脸侥、700T數(shù)據(jù)、日增數(shù)據(jù)量為15T盈厘。
3. 高并發(fā)環(huán)境下的ELK相關(guān)優(yōu)化睁枕;
首先來介紹索引的。在最初我們以天為單位沸手,使用ES中的Index
templates方式自動創(chuàng)建索引外遇。但是一旦ES節(jié)點出現(xiàn)故障,由于分片中的數(shù)據(jù)量非常大契吉,分片恢復(fù)速度十分緩慢跳仿,于是我們將索引改為按小時劃分。按小時劃分后每個索引中的數(shù)據(jù)自然少了許多捐晶,當ES故障時可以更快的恢復(fù)當前索引菲语,不阻塞后續(xù)的數(shù)據(jù)寫入妄辩。
針對索引的優(yōu)化我們關(guān)閉了_all及其他不必要的field以降低索引大小切關(guān)閉字段分詞。這樣設(shè)置以后查詢只能通過某個具體的Key去查詢(類似level:ERROR)山上,在日志場景下完全夠用眼耀。可以將日志打印成JSON格式佩憾,減少日志解析工作量哮伟。通過索引模板設(shè)置按照某一規(guī)則解析Field的類型,如:將xxxCount妄帘、xxxSize的Field自動解析成數(shù)字類型楞黄,其它字段解析成text等等。
關(guān)于索引分片抡驼,在服務(wù)器壓力不大的前提下谅辣,索引的分片數(shù)量盡量少,只要能滿足業(yè)務(wù)正常執(zhí)行婶恼,越多的分片在查詢時需要合并的次數(shù)也就越多桑阶。通常每個分片只設(shè)置1個副本,副本分片在ES中默認配置下是以同步方式寫入的勾邦,每次寫入數(shù)據(jù)時當所有副本寫入完成以后才返回結(jié)果蚣录,也就是說副本越多寫入壓力越大且耗時越長,所以設(shè)置1個副本保證最基本的容災(zāi)即可眷篇。
如果使用副本異步同步萎河,ES主分片寫入完成后不會等待副本分片寫入完畢,直接返回結(jié)果給客戶端蕉饼。在高并發(fā)環(huán)境下日志生產(chǎn)的速度很快虐杯,在日志解析器速度夠快的情況下會直接對ES發(fā)起第二批寫入請求,這樣循環(huán)下來會導(dǎo)致ES副本在高峰期永遠同步不完昧港,失去容災(zāi)的意義擎椰,所以不推薦使用。
當索引穩(wěn)定無數(shù)據(jù)寫入時创肥,對索引進行ForceMerge达舒。ForceMerge操作會合并分片中的segment,簡單點解釋就是可以把多個小文件中的數(shù)據(jù)合并到一個大文件中再進行索引排序叹侄,可明顯提升查詢性能巩搏。ForceMerge對服務(wù)器資源消耗比較高,并且執(zhí)行時間很長(基于索引的大兄捍)贯底,建議在集群壓力最小的時候(比如凌晨,通過定時任務(wù))執(zhí)行撒强。
下面再來談一下ES節(jié)點的部署方式禽捆。最重要的一點就是節(jié)點角色獨立糯俗,在一個小型ES集群里每個節(jié)點通常既作為Master又負責數(shù)據(jù)處理,但是在一個大型集群中節(jié)點角色混合會發(fā)生不穩(wěn)定的情況睦擂。比如當數(shù)據(jù)壓力過大時導(dǎo)致頻繁GC、甚至節(jié)點掉線杖玲。無論GC導(dǎo)致的停頓還是節(jié)點掉線均會影響該節(jié)點上Master角色所提供的服務(wù)顿仇,如果該Master是當前活動的主節(jié)點的話,掉線就又會產(chǎn)生重新選主的行為摆马,代價還是蠻大的臼闻。
我們服務(wù)器的配置為2CPU32核 / 128G RAM / 16 * 6T SATA。每個集群上Master部署奇數(shù)個(3~7個)囤采,同時設(shè)置discovery.zen.minimum_
master_nodes=master總數(shù)/2+1 避免集群腦裂述呐,內(nèi)存不宜過多,控制GC導(dǎo)致的停頓時間蕉毯。
每個Data節(jié)點分配30G內(nèi)存乓搬,最多不超過31.xxG內(nèi)存,不同JDK版本的這個邊界值不一樣代虾,少于該邊界值時JVM會采用內(nèi)存對象指針壓縮技術(shù)提高內(nèi)存利用率(至于如何確定這個邊界值进肯,我會在最后給出鏈接)。那么對于128GB內(nèi)存的服務(wù)器我們該如何分配內(nèi)存呢棉磨?我們將每臺服務(wù)器部署了2個Data節(jié)點實例江掩,加起來占用大約一半內(nèi)存,剩余的內(nèi)存留給操作系統(tǒng)緩存乘瓤,在搜索時Lucence會查找Segment文件环形,這時如果命中操作系統(tǒng)緩存會大幅度提升搜索速度。
在服務(wù)器啟動多個Data節(jié)點實例的情況下要注意一個問題衙傀,一旦服務(wù)器宕機有可能導(dǎo)致主從分片均不可用(主從分片被分配在同一臺服務(wù)器的不同實例上了)抬吟。針對這種情況需要開啟?cluster.routing.allocation.
same_shard.host?選項,禁止主從分片被分到同一臺服務(wù)器上统抬,保證服務(wù)器宕機時有副本分片可用拗军。
還有,注意給每個ES節(jié)點分配不同的磁盤蓄喇,避免節(jié)點之間的IO競爭发侵。
關(guān)于ES相關(guān)的優(yōu)化其實還有很多,網(wǎng)上一搜一大把這里就不一一列舉妆偏。調(diào)優(yōu)的過程中最基礎(chǔ)的還是對物理服務(wù)器以及集群做持續(xù)監(jiān)控刃鳄,通過ES提供的CAT
API我們可以很方便的獲取集群中的各項指標,如查詢負載钱骂、查詢延遲叔锐、索引隊列長度挪鹏、請求拒絕次數(shù)等等,我們可以將這些指標數(shù)據(jù)通過腳本定時讀取然后回寫到ES中愉烙,在KIBANA上建立這些數(shù)據(jù)的可視化圖形讨盒,這樣一個簡單的監(jiān)控系統(tǒng)就出來了。
服務(wù)器級別的監(jiān)控能最快落地的方式就是搭一個Zabbix步责,主要了解CPU返顺、內(nèi)存、IO等硬件資源使用情況蔓肯,方便定位問題遂鹊,找出性能瓶頸。
在來說一下ES的安全問題蔗包。在2017年年初的時候暴露在互聯(lián)網(wǎng)上的ElasticSearch集群在全球范圍遭到大量劫持秉扑,黑客在ES中留下了一條與贖金要求相關(guān)的索引定義,具體如下所示:
文本內(nèi)容寫道调限,如果希望恢復(fù)數(shù)據(jù)需要向黑客的賬戶中支付比特幣舟陆,如何避免這種悲劇發(fā)生呢?
1耻矮、最關(guān)鍵的一點就是不要將ES暴露在互聯(lián)網(wǎng)中吨娜,這一點應(yīng)該是針對所有服務(wù)器應(yīng)用。除非直接與用戶交互淘钟,所有應(yīng)用都不應(yīng)該暴露在互聯(lián)網(wǎng)中宦赠,Web應(yīng)用應(yīng)該通過Nginx反向代理并且僅暴露需要交互接口,防止黑客調(diào)用那些私有接口以及使用Web容器漏洞米母。
如果服務(wù)器配置了外網(wǎng)IP勾扭,可以在外網(wǎng)交換機上封鎖不需要的端口。如果不方便這么做铁瞒,則開啟服務(wù)器上的iptables(記得高并發(fā)情況下增大iptables跟蹤表大小妙色,這里就不展開了),只允許部分端口的入站請求慧耍。還可以將所有服務(wù)設(shè)置成只監(jiān)聽本地局域網(wǎng)請求(ES中可以設(shè)置network.bind_host=192.168.x.x)身辨。
2.
關(guān)閉不必要的ES HTTP端口。用戶能連接到ES的HTTP端口芍碧,就可以通過REST
API去對集群進行操作煌珊。僅開啟Client節(jié)點上的HTTP端口,并通過反向代理方式暴露給用戶泌豆,配置反向代理屏蔽那些有風險的API接口并開啟訪問日志定庵,做到操作能夠被審計。
3. 及時更新ElasticSearch版本,ElasticSearch完全遵循語義化版本號(x.y.z)蔬浙,小版本的升級基本不會出現(xiàn)兼容性問題猪落。
主版本號(x):當你做了不兼容的 API 修改;
次版本號(y):當你做了向下兼容的功能性新增畴博;
修正版本號(z):當你做了向下兼容的問題修正笨忌;
4. 修改默認的ElasticSearch集群名稱,避免出現(xiàn)集群中出現(xiàn)陌生節(jié)點俱病;
5. 禁用通配符刪除索引(小手一抖官疲,數(shù)據(jù)帶走);
6. 使用非Root用戶啟動ES庶艾;
問答:
1、斗魚有用到RabbitMQ或者Kafka之類的消息隊列嗎擎勘?消息隊列適合哪種使用場景咱揍,有哪些注意點?Redis如何和消息隊列結(jié)合使用棚饵,Redis使用有哪些注意事項煤裙?
答:消息隊列的場景還是在于數(shù)據(jù)驅(qū)動的場景,如果上游服務(wù)不關(guān)注下游的執(zhí)行結(jié)果噪漾,很適合使用消息隊列硼砰。Redis也有消息隊列的功能,但是好像并不支持多消費者欣硼。只是一個FIFO的隊列
2题翰、目前斗魚這邊接入elk平臺的業(yè)務(wù)有哪些共同特性?碰到es 慢寫入和慢查詢的時候诈胜,排查的步驟有哪些豹障?關(guān)于es使用object與嵌套結(jié)構(gòu)的分別是什么場景?
答:碰到ES寫入慢和查詢慢的時候焦匈,先是查看監(jiān)控血公、硬件資源以及ES中各種線程池的狀況。
3缓熟、kafka消息隊列支持更新隊列里面的消息嗎累魔?支持優(yōu)先級嗎?
答:不支持
4够滑、在Flume無法支撐當前業(yè)務(wù)系統(tǒng)時如何進行技術(shù)替換以及可行性分析垦写,
答:如果只是Flume寫Kafka這一個流程,換Logstash就行了彰触。關(guān)于技術(shù)選型梯澜,可以看一下谷歌指數(shù)、GITHUB星星數(shù)量對比一下同類框架,社區(qū)活躍才是真的好
5晚伙、kafka消息持久化支持嗎吮龄?就是kafka服務(wù)掛了之后,消息是否會丟失
答:消息不會丟失的咆疗,但是Kafka刪除數(shù)據(jù)是基于時間漓帚,比如一周,這個不會一直存儲午磁。Kafka還是針對于大數(shù)據(jù)場景尝抖,會發(fā)生數(shù)據(jù)丟失的情況,測試過迅皇。如果是敏感數(shù)據(jù)還是使用RabbitMQ
6昧辽、請問有沒有好的社區(qū)推薦?
答:推薦ES的官方文檔還是很全的登颓,最好的社區(qū)就是GITHUB以及ES官方論壇然后國內(nèi)也有個問答網(wǎng)站的?https://elasticsearch.cn/搅荞,還有關(guān)注ES公眾號
7、kafka的消息也是存在內(nèi)存中的嗎框咙?
答:是直接落盤咕痛, Kafka有個ACK機制,如果是不需要ACK是會丟的喇嘱,看自己的設(shè)置茉贡。
8、一般程序都是用log4j和log back的者铜,為啥不自定義appender 通過zookeeper +kafka 消費kafka 數(shù)據(jù)直接建立索引到es 這樣不是快腔丧?
答:1. 做基礎(chǔ)組件一定要使用最小依賴,給應(yīng)用依賴留有空間作烟,讓應(yīng)用方不要有顧慮悔据。
2. Agent收集方便停機維護,即使Kafka掛了也不會丟消息俗壹。
3. 不能保證Kafka的日志Appender日志不會產(chǎn)生其他阻塞情況科汗。
9、收集到的數(shù)據(jù)進入了HSDF了嗎绷雏?
答:目前沒有持久化需求头滔,如果想永久保留日志,可以新建個Kafka消費者寫HDFS涎显。
10坤检、斗魚有用日志做調(diào)用鏈嗎?
答:有日志調(diào)用鏈期吓,正在做可視化的東西早歇,調(diào)用鏈可以參考PinPoint,用JavaAgent做logger的字節(jié)碼織入,只不過PinPoint運行時依賴有點重箭跳。調(diào)用鏈主要還是記錄調(diào)用的setp以及他的深度晨另,可以想象成一個樹形結(jié)構(gòu)雾消,可以定義一個ThreadLocal變量存放這些信息家浇。
11、日志告警的定制化需求矛物,比如多少分鐘內(nèi)出現(xiàn)多少次錯誤就報警
答:我們開發(fā)了自己的告警平臺屉来,可以做到“包括日志告警的定制化需求路翻,比如多少分鐘內(nèi)出現(xiàn)多少次錯誤就報警”這種定定制話的需求,如果想在公司里開發(fā)一套茄靠,可以先玩玩Zabbix茂契,學(xué)習一些它的理念,很有幫助慨绳。
12掉冶、用了jstom這種流式處理吧?
答:用了儡蔓,還有spark streaming