阿里萬億交易量級下的秒級監控
本文整理自 GOPS2017全球運維大會·上海站演講《萬億交易量級下的秒級監控》
作者簡介
孔羅星
癲行
研發效能事業部-監控平臺-技術專家
2014年加入阿里巴
曾在福建富士通開發CMDB, 監控等運維相關係統
6年工作經驗, 從事過DBA, SA, Python開發, Java開發
我要講的內容是偏工程的:怎麼在萬億交易量下實現足夠實時的秒級監控?
先介紹一下監控系統 Sunfire,它是阿里集團的業務監控系統,前身是螞蟻的 xflush, 支援應用標準化監控,如作業系統,JVM,中介軟體等。除此之外還有更強大的日誌監控能力,大多數業務的監控指標都從應用的日誌中抽取。目前覆蓋了集團幾乎所有 BU 和絕大多數業務,每分鐘處理 TB 級日誌。
下面將從以下四個方面進行講解:
- 架構
- 規模與挑戰
- 技術選擇
- 方向
一、架構
每分鐘處理這麼大的TB級日誌量,我們是怎麼設計架構去實現它的呢?
1.1、傳統日誌監控
上圖是傳統的日誌監控,現在大多數監控平臺採用的一個方案。Agnet 檢測日誌變化增量推送,經過訊息中介軟體如 kafka,流式計算引擎如 Jstorm/flink 去消費 kafka 產生出來的資料,中間的流式計算可能有多步的處理,最後流向 DB,很傳統的架構。
這種架構會有一個問題就是:某一分鐘的資料,何時可以發報警?
1.2、流式計算的問題
Process Time 超過 Event Time Window
我們最早嘗試了上面傳統的架構,但是,會有一個問題,我到底什麼時候這個資料才能發報警呢?因為這個架構最麻煩的是我不知道什麼時候資料已經全部到齊了。如果機器很多,agent 返回資料的時間並不確定, 要保證所有機器日誌採齊了資料才準確,這在流式計算裡很難處理。
這是個經典的問題, 有兩篇文章很詳細的講解了流式計算中如何解決這種問題:
https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-101https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-102
但是資料丟了就是丟了, 無論怎麼樣就是不準了,也很難拍出一個 delay 的時間確保資料可以用來發報警, 那麼當資料不準時, 我們能不能知道不準了呢? 為了解決這個問題我們走了另一條路線: 讓主動權留在服務端。
1.3、Sunfire 功能結構
這是 Sunfire 的功能結構,比較重要是 Sunfire-lika 模組,它用來支撐整個計算框架的,就是執行緒模型、訊息排程處理、故障自愈恢復都是通過這個模組實現的。
1.4、Sunfire 架構
這是 Sunfire 架構圖。這個架構圖是怎麼工作的呢?首先有三個角色 Brain、Reduce 和 Map。這三個角色我們統稱為計算模組。
ConfigDB 裡面配置了監控項。監控項會定義配置需要從哪個應用、哪個路徑採集日誌、採集回來的日誌應該做哪些的處理、根據什麼樣的規則進行計算。
Brain 會按照週期從 ConflgDB 裡讀取配置,生成拓撲。然後安裝到 Reduce 上面,Reduce 把拓撲再分解成它的子任務,再安裝到 Map 上面,最後 map 去拉日誌。
這裡畫了兩個租戶,租戶 A 和共享租戶,其實就是資源是獨享的還是共用的。因為我們有一些核心的交易監控,也有一些不太重要的,還有很多邊緣業務。如果是很重要的使用者,比如說交易,我們就單獨給它一個租戶,它的所有計算資源都是它自己獨享的。對於一些邊緣的業務是可以共用伺服器的。我們現在有80多個租戶,基本上一個租戶對應一個大的業務。
1.5、時序圖
用時序圖的視角看一下上面的任務。這個拓撲包括了配置,也包括這個拓撲任務從多少個伺服器,到底從哪些伺服器上去採集日誌,都是在這個拓撲裡面完成的。有了這個拓撲,才有了節點故障時候,恢復它的前提條件。因為拓撲裡面包含了所有資訊,無論是哪個節點掛掉了,上游都能用它來恢復下游節點。
把這個拓撲安裝到一臺 Reduce 上面去,然後 Reduce 會把它分解掉。假如我有1臺 Reduce,有100個 map,Reduce 會把這個任務分節成100個 Map。如果這時候有1000個 Agent,有可能每個 Map 會採集10個 Agent 的日誌,最終 Map 去 Agent 拉取日誌,然後再一步步往回走,Map 做初步的計算, Reduce 再做進一步的聚合存入到 HBase,然後最終返回給 Brain,告訴它這個任務完成了。
這裡面存在很多可能會出問題的點,因為叢集非常龐大,跑著跑著機器可能就掛掉了,這對我們來說是很正常的,一天掛掉十幾臺機器也是常有的事。下面說一下怎麼解決可靠性的問題。
1.6、關鍵點
上面架構有兩個關鍵點:
- 一個是 Preload,就是任務是提前註冊的。它不是在需要的時候才生成任務。我們把任務提前下發下去了,有什麼好處呢?假如叢集有一些壞掉的機器可能網路很慢也可能連不上,在這個階段就可以提前發現這些機器遮蔽掉,在後面真正去做任務的時候,延遲就會相應的降低很多,因為不需要再去等去重試了。
同時,Preload 是輸入共享的前提,因為不同的人會配同樣的日誌,並且規則可能也是類似的,我們在這裡會做輸入共享,去共享日誌的採集來減少頻寬和 CPU 的消耗,也會共享中間一部分計算的結果。
- 另一個是 Pull,主動權控制在服務端,就是服務端發現數據拉不上來,想要放棄還是重試,可以由自己做出決定了。最終服務端會決定多長的時間內,一定把這些全部都處理完,而不會過了很長一段時間還有資料突然推上來的問題了。還有就是 push 時, 有可能遇到網路抖動, 導致失敗, 重試也不成功, 但在 pull 模式下, 相當於把 agent 作為 hadoop 中的 hdfs 節點, 只要日誌還在, 我們就有補資料的機會
另外,降低使用者開銷對我們來說也是比較重要的,像雙十一場景,交易的應用開銷非常大,我們一定要儘量降低它們的開銷。比如佔了10%的 CPU,交易的使用者就受不了這個開銷。因此,所有的計算都是在服務端完成,也使得我們的叢集規模非常大。
二、規模與挑戰
2.1、挑戰
挑戰主要來自於這四個方面,都是因為規模而引起的挑戰。
2.2、規模
現在有80多個租戶,租戶基本上一個租戶對應一個大的業務,比如交易是一個租戶,阿里媽媽是一個租戶,高德是一個租戶。部署機器最多的時候有6000多臺,上面的應用有8000多個,每分鐘處理的日誌量在3000GB 以上,這只是常態化的日誌量並不是最高峰的日誌量。這麼大的日誌量用一個訊息中介軟體去承載也是很困難的, 這也是我們沒用流式計算的原因之一.
2.3、場景挑戰
- 某應用有上萬臺伺服器,每分鐘產生的日誌量近1T,如何在秒級完成採集並輸出準確的結果?
- 假如有很多人配置了基於該日誌的監控項,如何降低開銷?
- 假如過程中有伺服器宕機了怎麼辦?
2.4、快速
我們怎麼實現快速拉取呢?
在 server 端,其中核心的鏈路是非同步的,所有的通訊也是非同步的,沒有一個地方允許有鎖。這兩個是通過上面提到的 lika 框架來實現的,lika 框架沒有什麼特別神奇的地方,把 Akka 的一些核心理念拿出來做了一個簡化的框架,更簡單更容易維護。
在 Agent 端,最重要的是用了 Zero-copy,使得讀日誌不經過任何 CPU 的處理,直接通過 socket 傳送出去。這樣最大的好處是對使用者極小開銷,壞處就是不能壓縮了。
RandomAccessfile 是配合動態二分法來使用的,配日誌的時候沒有讓使用者指定時間欄位應該在哪個位置,時間是什麼格式的,這些都是我們自己判斷的。以及怎麼知道使用者的某個週期應該推上去的日誌是哪些呢?就通過動態二分法來實現的。
Brain 生成拓撲的時候,是有時間戳的。agent 拿到以後,簡單來說先看頭和尾有沒有,因為日誌是不斷打出來的,採集也是不斷進行的,尾部拿到的概率特別大。如果不在就根據這個時間去找,把它做二分查詢,最後找到時間。上面提到的唯一開銷就來自這裡,要去猜時間在哪,在極端情況下對使用者的 CPU 也能控制在8%以下。
2.5、準確
準確性從這個系統一開始設計時貫穿始終的,也是我們為什麼在一開始沒有用流式計算的原因。
我們除了 pull 的機制來把控制權保持在服務端之外,還設計了齊全度,這對我們來說是非常重要的。傳統的監控一個指標產生一個值就行了,我們每一個值還會對應一個相對應的齊全度。
這個齊全度代表什麼意思呢?比如1000臺機器裡面有幾臺機器的網路不通或者機器掛掉了,因為機器多了什麼問題都會有, 這很正常。
我們會在最後採集完成的時候,多打出來一個指標說1000臺機器採集成功900臺,失敗100臺,成功率是90%。這時候使用者就有參考了,如果此時發現交易量下跌了,一看齊全度也下跌了,基本上可以認為是採集的問題導致的下跌,有可能並不是真正的業務下跌,可以來找我們看為什麼採集缺失。
因此齊全度是我們特意設計出來,為了讓使用者直觀感受到採集的完整度的一個概念。
有了上面的措施還是不能保證準確,還需要有各種各樣的測試來驗證這些設計是不是可靠的。所以在線上搭很多環境,測試同學造了各種各樣的配置,如虛擬的應用大部分機器都是壞掉的,或者大部分機器沒有產生日誌。再配合上各種各樣的日誌計算規則,去實時校驗。
準確性迴歸是我們每次釋出之前都必須做的,也是自動觸發的過程。只要我們每次打包都會觸發一次準確性校驗。自灰度就是找一些小白鼠,先發布他們,再發布重要的客戶。
2.6、穩定
上面是我列舉的一些影響系統穩定性的部分問題。最常見的像下發失敗,這種好處理,直接重試就可以了。如果已經下發成功了,但是在做的過程中失敗的,這就很麻煩了。所以我們 lika 框架很重要的一點,就是為這個服務的。比如 Brain 生成任務以後,它安裝成功了一個 Reduce,Brain 就會去守護這個 Reduce。
我們有一套機制來保證 Reduce 執行成功,直到返回成功給 Brain,這個任務才結束。如果沒返回,Brain 就會不斷探測它,一旦探測到它失敗了,比如這臺機器連不上了,或者機器是好的但是任務中間出異常掛掉了,那麼 Brain 會重試它,換一臺機器繼續做這個任務。像 Reduce 安裝完 Map 後失敗了,也是類似的邏輯。
拉日誌也會帶來一些不可控的事情,就是我不知道要拉的日誌到底有多大。有可能我這邊分配的計算機器數很少,但是使用者日誌量非常大,就有可能把我們打爆了。因此我們有一系列自我保護的邏輯,會計算每個監控項的開銷,不能高於某一個值。如果高於這個值,說明監控項消耗資源太高了,可能配了一些極其複雜的策略,這時為了自保必須把它kill掉。
我們也實現記憶體的分配策略,就是每次拉日誌的大小是計算出來的。經過一系列的因素計算出來這次能拉多少日誌。如果記憶體不夠,等一會兒再去拉。
同時,我們也做了一系列的自我監控。我們是拿自己搭的另外一套環境來監控自己。報警也是在這上面配的,來觀察各個租戶的狀態是不是正常的。
以上這些措施構成了穩定性的保證。
2.7、穩定性驗收
穩定性最終是需要驗收的,不能說我們說穩定就穩定。上圖是我們設計的一些場景。比如有多少機器宕機,看宕機的過程有沒有資料丟失或者資料不準。還有網路丟包,Hbase 服務中斷等等,再恢復看能不能恢復。再有像整個機房斷網,讓某個機房成為孤島,來驗證它的穩定性。
2.8、成本
在成本方面,叢集機器的數量比較龐大,我們一直想努力降低成本。主要通過下面三個方面來做的。
租戶間排程/輸入共享
降低成本最重要的技術手段就是做了輸入共享,輸入共享在很多情況下能減少起碼三倍或者五倍的日誌拉取。因為在多數情況下一個日誌會產出多個指標,不同的指標也可能會打到同一份日誌裡面。
怎麼做呢?Brain 提前註冊了 Reduce,Reduce 提前註冊了 Map,Map 上有一個關係,就是這個 Map 要採集哪些機器上的哪些日誌。最終可以構建出來一個關係來,就是監控項跟機器上的日誌的對應關係。比如說第一個監控項要採集100臺機器上的某個日誌,第二個監控項還是要採集這批機器上的同樣日誌。這兩個任務就合併掉了,最終所有的採集同一份日誌的任務都會被合併掉,這是提前註冊裡面可以做的事情。
關係構建好了以後,就觸發一個定時器來觸發拉取。
清理殭屍配置
我們根據某個配置它最近一段時間被多少人訪問,有沒有報警,報警後有沒有人處理,等等一系列指標計算出監控項的健康度。如果健康度太低,就會通知使用者去清理它,減少我們配置量。
統計值優先
統計值優先也是現在不得不做的一個優化。因為以前很多應用打的都是流水的日誌,以交易舉例, 交易有很多環節, 每個環節至少有一行日誌,最終有可能1億筆交易對應100億條日誌。所以會要求大的業務方,把這些值改成統計值,至少是每秒或者每分鐘聚合後的值打出來。
2.9、輸入共享
多個配置一份日誌只採集一次
三、技術選擇
這是我們做監控的過程中做的一些技術選擇。拉和推模式各有優缺點,為了準確性選擇了拉的模式,不排除推的模式也能搞定準確性,還是會走到推的路線上來,因為架構總是不斷迭代的。
計算應該在 Server 端還是 Agent 端執行呢?因為使用者接受不了 CPU 使用率過高,會影響正常業務,因此我們最終選擇所有的計算都在 Server 端完成。
對於使用開源框架還是自研框架,我們也希望用開源框架,但如果有的地方滿足不了或者開源社群的方向跟我們期望的方向不太一致,我們可能就會基於這個框架的思想定製一個簡易的。只有核心的設計,程式碼體量小維護也簡單,其實我們計算框架做出來以後,幾乎沒有產生過什麼 BUG,因外它只做了訊息分發執行緒池管理和故障守護這幾件事情。
在資料庫選擇上,當前我們是直接寫 Hbase,正在和 HiTSDB 團隊對接, 這是一個類 OpenTSDB 的儲存, 在阿里雲上也有提供。
對於監控來說,我們最終選擇的自運維,我們幾乎沒有強依賴任何系統。為什麼呢?因為我們有個理念,監控應該是最基礎的設施,如果我們強依賴別人,我們監控不了它,所以我們做了一個自運維體系。
以上就是做的一些技術選擇,經過了很多次迭代,最終走到了現在的路線。
四、方向
現在我們的方向是這四個:
- 標準化
- 一體化
- 服務化
- 智慧化
4.1、標準化: MQL
select avg(cpu.util),max(load.load1) from system where app=‘AppTest’ since 30mselect * from sunfire.1005_SM_13 since 30mselect * from spring filter class=‘classA’ and method=‘methodB’ where ip=‘192.168.1.1’ since 1h
我們叫 MQL,我們的 MQL 希望讓使用者能夠用一個通用的語法來查詢所有的監控資料。甚至是其他監控系統的資料,這樣使用者不用管資料是哪個平臺產生的。MQL 在使用上也比原來的 API 更直觀一些,會是我們後面主推的提供給使用者 API 的方式。
4.2、一體化
我們還做了很多一體化的事情,比如說發現交易下跌了,這時候交易的應用有沒有做變更,有沒有擴容、縮容重啟的操作,這是使用者關心的。我們統計出來有相當比例的故障是因為變更導致的,當業務異常的時候直觀的看到有沒有變更,可以為他省很多時間。雖然這個事情做起來很簡單,但是作用是很大的。
我們還把宿主機和網路監控也關聯起來了,現在用的都是容器,但有的問題可能是因為宿主機出問題了,或者上面負載太高了,使用者可以做出直觀的判斷。
同時,還把報警整合在釘釘裡面完成。釘釘有什麼好處呢?它跟傳統的簡訊、郵件報警不一樣,它可以有很豐富的互動。使用者可以點選進來看報警的詳情,甚至可以有曲線、報警的歷史,點進去還可以做一些重啟機器的操作,或者覺得這是個誤報我要關閉半個小時,都可以在這裡一站式完成。這比以前用簡訊收報警的方式前進了一大步。
釘釘
釘釘一站式報警處理
4.3、智慧化
在智慧化上面我們也在做很多探索,比如智慧基線。
圖上有一段虛線,是通過演算法預測出來這個曲線後面這段時間的走勢可能是什麼樣的。我們可以很直觀的判斷出來到底有沒有異常。進一步希望做到使用者不用配報警,自動幫它生成報警的閾值。
智慧基線讓使用者只要配一個規則就可以了。原先是一天內不同時間業務指標的範圍可能都不一樣,使用者只能根據時間段配了一堆規則。上圖是簡化後的規則,有了智慧基線以後只要配當前值和基線比超過百分之多少就報警,就這麼簡單。
原文來自微信公眾號:高效運維