B 站日誌系統的前世此生

王翔宇node

Bilibili資深運維研發工程師。曾就任於百度、餓了麼,2017年加入B站,負責B站日誌平臺的設計和開發工做。web

B站的日誌系統(Billions)從2017年5月份開始建設,基於elastic stack,面向全站提供統一的日誌採集、檢索、監控服務。目前集羣規模20臺機器,接入業務200+,單日日誌量10T+。apache

藉此機會跟你們分享一些B站在日誌系統的建設、演進以及優化的經歷。因爲經驗尚少,拋磚引玉,歡迎你們一塊兒交流討論。文章主要分爲三個部分:原有日誌系統現有系統演進將來的展望json

原有日誌系統

在Billions以前,B站內部並無統一的日誌平臺,基本是業務之間各自爲戰,既有基於ELK的比較前瞻的方式,又有服務器上使用tail/grep比較基本原始的方式,水平良莠不齊。在瞭解各個產品線的狀況後,存在的問題和訴求主要有如下幾點:api

  1. 方案各異。 因爲各個部門自行實現日誌方案,沒有專人維護,廣泛存在維護成本高、系統不穩定、丟日誌、易用性不足的狀況。緩存

  2. 業務日誌沒有統一的規範。業務日誌格式各式各樣,致使最直接的問題就是沒法按照統一的規則對日誌進行切分,這無疑大大的增長了日誌的分析、檢索成本。安全

  3. 對PAAS支持很差。公司內部正在大面積推廣應用容器化,可是並無一個好的日誌方案支撐容器內應用日誌的採集。服務器

  4. 日誌利用程度低。對於日誌的利用程度廣泛停留於日誌檢索的水平,受限於工具未對日誌的價值進行進一步挖掘,例如:日誌監控、統計分析、調用鏈分析。markdown

針對上述問題,提出新的日誌系統的設計目標以下:架構

  • 業務日誌平滑接入:業務日誌接入日誌系統,只須要進行簡單的配置;日誌平臺也只須要進行一些基本的配置,無須涉及日誌內容等業務信息。

  • 多樣性支持:環境多樣:物理機(虛擬機)、容器;來源多樣:系統日誌、業務日誌、中間件日誌……;格式多樣:單行/多行, plain/json。

  • 日誌挖掘:快速可查,日誌監控,統計分析。

  • 系統可用性:數據實時性;丟失率可控(業務分級、全鏈路監控)。

Billions的演進

系統的初建

日誌規範

爲了解決業務日誌格式多樣性問題,統一制定了日誌格式規範,使用JSON做爲日誌的輸出格式。格式要求:

  1. 必須包含四類元信息:

    • time: 日誌產生時間,ISO8601格式

    • level:日誌等級, FATAL、ERROR、WARN、INFO、DEBUG

    • app_id:應用id,用於標示日誌來源,與公司服務樹一致,全局惟一

    • instance_id:實例id,用於區分同一應用不一樣實例,格式業務方自行設定

  2. 日誌詳細信息統一保存到log字段中。

  3. 除上述字段以外,業務方也能夠自行添加額外的字段。

  4. json的mapping應保持不變:key不能隨意增長、變化,value的類型也應保持不變。

例如:

{"log": "hello billions, write more", "level": "INFO", "app_id": "testapp111", "instance_id": "instance1", "time": "2017-08-04T15:59:01.607483", "id": 0}複製代碼{"log": "hello billions, write more", "level": "INFO", "app_id": "testapp111", "instance_id": "instance1", "time": "2017-08-04T15:59:01.607483", "id": 0}

日誌系統技術方案

日誌從產生到消費,主要經歷如下幾個階段:採集->傳輸->切分->檢索。

日誌採集

日誌採集針對非落盤和落盤兩種方式。

  • 對於業務模塊日誌,統一按照日誌規範而且經過非落盤的方式進行輸出。針對此類場景,與平臺技術部合做,基於go咱們開發了log agent模塊。

    • log agent部署在物理機上,暴露出一個domain sock文件,程序將日誌經過unixgram方式輸出到domain sock。

    • 對於運行在PAAS上的應用,在container初始化的時候,sock文件被默認mount到container內部,這樣容器內的程序就能夠輸出日誌。

    • log agent分爲兩個部分,collector和sender。collector用於接收日誌,sender用於向傳輸系統發送日誌。二者直接經過一個文件緩存進行交互。這樣在日誌傳輸系統故障的狀況下,依賴本地緩存能夠保證日誌的正常接收。

    • 咱們提供了不一樣語言對應的日誌庫(sdk),程序能夠快速接入日誌系統。

  • 非業務模塊(中間件、系統模塊、接入層)日誌,因爲定製化能力較差,咱們經過讀取生成的日誌文件完成日誌的採集。

    • 咱們採用的elastic stack中的filebeat進行採集,filebeat具備方便部署、配置簡單、資源消耗低的優點,並且支持多行日誌的拼接。

    • 物理機上部署一個單獨的filebeat進程,每一類日誌對應一個單獨的配置文件。

    • 每一條日誌都會被單獨打上一個app_id標籤,這個相似業務日誌的app_id字段,這樣在最終消費日誌的時候就能夠進行區分了。

    • filebeat會自動標示日誌來源機器,這樣也就具備了區分同一應用不一樣實例的能力。

日誌採集

公司內部已經有了統一的數據傳輸平臺(lancer),lancer的優點以下:

  • 基於flume+kafka作二次定製化開發,內部自動負載均衡,容量可水平擴展。

  • 數據接收端實現了一套可靠的數據傳輸協議,完善的鏈路監控,數據傳輸安全可靠。

  • 能夠根據業務須要對接不一樣的消費方式(kafka、hdfs)。

  • 有專業的團隊進行7*24維護。

所以咱們直接選擇lancer做爲咱們的日誌傳輸系統。

  • log agent中的sender模塊基於lancer的定製化的數據傳輸協議發送日誌,最終日誌被傳輸到kafka集羣中的不一樣topic(根據日誌流量,配置topic),後續從kafka消費日誌,全部的topic採用一個統一的prefix。

  • 因爲暫時沒有精力對filebeat進行二次定製化開發,所以filebeat直接將日誌輸出到lancer的kafka集羣。

日誌切分

日誌切分模塊的主要做用是從kafka消費日誌,對日誌進行處理(字段提取、格式轉換),最終存儲到elasticsearch的對應的index中。咱們使用logstash做爲咱們的日誌切分方案。

其中:

  • 對於按照日誌規範生成的日誌,日誌的kafka topic採用了統一的前綴,所以咱們採用topics_pattern的方式來消費日誌。

  • logstash的partition_assignment_strategy要設置爲」org.apache.kafka.clients.consumer.RoundRobinAssignor」,默認的策略(Range partitioning)會致使partition分配不均,若是採用默認的策略,當consumer(logstash數量*worker數量)的數量大於topic partition數量時,partition老是隻會被分配給固定的一部分consumer。

  • 對於非標準格式日誌,因爲logstash single event pipeline的限制,所以缺少對於多配置的支持(期待6.0的multi event pipeline)。每種日誌配置不一樣,所以須要單獨的logstash進程進行消費。

日誌檢索

elasticsearch集羣規模爲:master node*3, hot node*20, stale node*20,client node*2。es版本爲5.4.3,集羣配置以下:

  • 數據機器(40core,256G內存, 1T ssd, 6T*4 SATA)採用冷熱分離的方案:同時部署一個hot node和stale node。hot node使用ssd做爲存儲介質,接收實時日誌。stale node使用sata盤做爲存儲介質,存儲歷史日誌(只讀不寫)。每日固定時間進行熱->冷遷移。

  • client node對外提供讀取api,對接kibana、管理程序(好比curator、cerebro等)。

  • index管理(遷移、刪除)採用curator,日誌默認保留7天。

  • es集羣配置優化借鑑了不少社區的建議,就不詳細介紹了。

  • 使用template進行index mapping的管理。

  • index提早一天進行建立,防止集中建立致使數據沒法寫入。

  • es監控:調研了官方的x-pack monitor,因爲x-pack monitor功能不足(例如缺乏對於線程池的監控),而且不能進行報警,最終選擇自研。公司內部監控系統基於Prometheus,咱們開發了es_exporter負責採集es的狀態信息,最終監控報警經過Prometheus實現。報警主要包含關鍵指標,例如:es cluster的狀態信息、thread rejected數量、node節點數量、unassign shard數量。

通過上述步驟,最終日誌就能夠在kibana上進行查詢。第一階段,日誌系統的總體架構爲:

系統迭代

隨着接入的日誌量愈來愈大,漸漸出現一些問題和新的需求,Billions主要在如下方面進行了升級迭代。

shard管理

最初採用了es默認的管理策略,全部的index對應5*2個shard(5個primary,5個replica),帶來的主要問題以下:

  • 每一個shard都是有額外的開銷的(內存、文件句柄),大部分的index的數量都比較小,徹底沒有必要建立5個shard。

  • 某些index的數據量很大(大於500GB/day),單個shard對應的數據量就會很大,這樣會致使檢索的速度不是最優, 而且磁盤write IO集中在少數機器上。

針對上述問題,開發了index管理模塊(shard mng),根據index的歷史數據量(昨日數據),決定建立明日index對應shard數量,目前策略爲30GB/shard,shard數量上限爲15。經過以上優化,集羣shard數量下降了70%+,磁盤IO使用也更加高效。

日誌採樣

某些業務的日誌量很大(大於500GB/day),多爲業務的訪問日誌,對日誌而言,「大量數據中的一小部分就足以進行問題排查和趨勢發現」,與研發和運維進行溝通,這個觀點也獲得認同。所以在數據採集源頭log agent(collector模塊)中增長了日誌採樣(log sample)功能:

  • 日誌採樣以app_id爲維度,INFO級別如下日誌按照比例進行隨機採樣,WARN以上日誌所有保留。

  • log agent接入公司配置中心,採樣比例保存在配置中心,能夠動態生效。

  • 有個細節額外說明下:因爲要獲取日誌內的app_id字段,若是直接進行json解析, cpu消耗將很是之高。後續咱們改進爲字符查找(bytes.Index ),解決了這個問題。

針對日誌量大的業務進行採樣,在不影響使用的狀況下,節省了大量的es資源。目前天天減小3T+的日誌寫入。

data node硬件瓶頸解決

晚上20:00-24:00是B站業務的高峯期,同時也是日誌流量的高峯期。隨着流量的增加,常常收到日誌延遲的報警(日誌沒有及時的寫入es),經過觀察監控,主要發現兩個現象:

  • hot node出現了較多bulk request rejected,同時logstash收到了不少的429響應。嘗試調大了thread pool size和 queue_size,可是問題依然存在。

  • hot node機器長時間出現io wait現象,同時SSD Disk io Utiliztion 100% 。

經過以上現象,懷疑是SSD IO不足致使的es寫入拒絕。後續對SSD進行了性能測試和對比,發現此型機器上SSD的寫性能較差。爲了減小SSD IO壓力,咱們將一部分實時寫流量遷移到了stale node(stale node以前不接受實時寫流量,寫入壓力很小),日誌延遲的問題暫時得以解決。終極解決辦法:data node的機型爲40 core CPU,256G內存,1T SSD+4*6T SATA,很明顯此機型SSD從性能和容量上都是瓶頸,爲了提高此機型的利用率和解決SSD IO性能瓶頸,最終咱們爲每臺機器添加了2*1.2T pcie SSD,一勞永逸!

logstash性能解決

在解決了上述es寫入瓶頸後,過了一段時間,高峯期日誌延遲的問題又出現了。此次es並無出現bulk request rejected的問題。 從整條鏈路進行排查,日誌收集和日誌傳輸上都沒有出現日誌延遲的現象,最終把注意力放在了日誌切分模塊(logstash)。

logstash性能差是社區內公認的,進一步針對logstash進行性能測試,在(2 core+4G memory)狀況下,不斷調整worker數量和pipeline.batch.size, 極限性能爲8000qps,性能的確不好,高峯期的流量爲40W/s, 所以至少須要50個logstash實例才能知足要求,顯然這樣的資源消耗沒法接受。考慮到業務日誌對應的切分功能較爲單一,所以咱們使用go自研了日誌切分模塊(billions index)。

自研的模塊性能有了很大的提高,2 core+4G memory條件下,極限性能提高到5w+qps,而且內存只消耗了150M。 線上的資源消耗從(2 core+4G memory) 30 減小到了(2 core+150M memory)15,性能問題也獲得解決。

日誌監控

隨着愈來愈多的業務的接入,日誌監控的需求愈來愈強烈。目前社區的解決方案中,Yelp的elastalert最爲成熟,功能豐富,而且也方便進行進一步的定製化。所以咱們選擇基於elastalert實現日誌監控。結合自身需求,經過查看文檔和閱讀代碼,elastalert也有一些不足之處:

  • rule存儲在文件中,不可靠而且沒法進行分佈式擴展。

  • rule配置比較複雜,不夠友好和易用。

  • 程序單點,高可用沒法保證。

  • 監控規則順序執行,效率低(若是全部規則執行時間大於執行間隔,單條規則的按期執行將沒法保證)。

針對上述不足和自身須要,咱們對於elastalert進行了二次開發:

主要的改進點包括:

  • 將全部的rule存儲在elasticsearch中,即增長了rule存儲的可靠性,也爲elastalert的分佈式實現作好準備。

  • 全部類型的日誌監控rule使用模板進行封裝,以下降配置複雜度。例如限制使用query string過濾日誌,屏蔽某些配置項等等。

  • 封裝出一套Restful api進行監控規則的增刪改查。

  • 與公司現有監控系統(Bili Moni)結合:基於web配置日誌監控,經過報警平臺發送報警。

  • 利用全局鎖解決單點問題:兩個進程一熱一冷,熱進程故障後冷會自動接手,並進行報警。

  • 對於報警內容進行了調整(格式調整,漢化),表述更加清晰。

日誌監控1.0目前已經投入使用,而且還在持續迭代。

最新Billions的架構以下:

現有問題和下一步工做

目前日誌系統還存在不少不足的地方,主要有:

  • 缺少權限控制:目前權限控制缺失,後續須要實現統一認證、基於index的受權、操做審計等功能,類比xpack。

  • 缺少全鏈路監控:日誌從產生到能夠檢索,通過多級模塊,目前監控各層獨立實現,未進行串聯,所以沒法對堆積和丟失狀況進行精準監控。

  • 日誌監控性能瓶頸:目前日誌監控爲單節點(一個熱節點工做)而且規則順序執行,後續須要優化爲分佈式架構+規則並行執行。

  • 日誌切分配置複雜:對於非標準格式日誌,基於logstash實現日誌切分,每一種日誌須要單獨的logstash實例進行消費,配置和上線過程複雜,後續須要平臺化的系統進行支撐。

上述不足之處也是下一階段咱們着重改善的地方。除此以外,基於es強大的檢索和聚合分析功能,日誌更深層次的價值挖掘也是咱們探索的方向。咱們須要努力的地方還有不少,期待和社區中的夥伴們有更深層次的溝通交流!

相關文章
相關標籤/搜索