攜程實時智能異常檢測平臺的算法及工程實現

在運維領域,異常檢測是很重要的基礎,決定了告警的數量和質量,也影響了發現異常之後所需要採取的行動的可靠性。

然而實際生產環境下異常的判斷往往帶有主觀性,準確率、召回率等指標缺乏客觀依據,標註成本又高,導致誤報和漏報始終都是一對矛盾的存在。

本文主要介紹我們在這些已知問題基礎上,進一步優化“異常檢測”算法的探索和成果,並介紹瞭如何解決告警實時性的工程技術方案。

引言

日常工作中我們經常會接收到頻繁的異常告警,處理起來眼花繚亂,容易遺漏問題點。如何降低誤報率,讓有限的注意力集中在真正需要關注的異常上?

在機器學習領域異常檢測是個很大的主題,傳統的統計學方法就有很多,深度學習領域也有不同的算法模型來檢測異常,很多論文都對此做過研究和探索,然而直接借鑑並不能讓我們獲得理想的效果,需要一種方法可以幫助我們:

  • 降低報警總量到可以人工逐個處理的程度;
  • 不能以增加漏報真正的故障爲代價;
  • 提升告警的實時性;
  • 算法即服務,有較強的可移植性。

大而全的監控衍生出的問題

不管運維還是開發,大家都明白一個道理,系統跑得好不好,監控工具少不了,監控是我們的眼睛。

特別是規模比較大的系統,我們需要一個大而全的實時監控系統,如果這個系統還能判斷業務異常,並能實時發送給相應的人員來處理,那就更棒了。

DevOPS火了這麼多年,相信很多同仁也在自己的公司實施部署了具備這樣能力的監控系統,我們想和大家探討的是,接下來可能會面臨一些什麼問題,以及我們一路是怎麼走過來的。

當我們有了一個這麼強大的實時監控告警系統,將幾千上萬數十萬個監控指標接入進去的時候,問題就來了,這麼多指標該如何去設置告警?

不那麼重要的指標可以不設告警,等出問題的時候再來追蹤查看;有些系統級的指標可以設置簡單的告警規則,比如CPU使用率、磁盤利用率,超過一定百分比報出來就好;還有很多指標不那麼好設置統一的規則,比如訪問量,響應時間,連接數,錯誤量,下單量,怎麼樣纔算是不正常?這在很多時候是個感性的判斷。

爲了讓自己變得理性,我們自然而然地會去量化,常見的量化方式是和過去的某一個時間相比較,比如和一週前的同一時刻前後幾分鐘的均值相比,降幅如果超過了一定比例,並且連續出現,就判斷爲異常。這種方式面臨兩個侷限:

  1. 成千上萬個指標,需要人工去設置,費事費力,業務變化會導致數據形態上的變化,規則維護成本很高;

  2. 每個指標的業務背景不同,降幅超過多少纔算是異常,這是個藝術,沒有標準答案,每個人都有自己的判斷,需要經驗。

如果監控系統是我們的眼睛,那麼報警系統就是神經。面對這麼多監控數據,怎麼樣讓神經既不那麼敏感,也不那麼大條,還能自動適應各種刺激,這是我們嘗試去探索和改善的問題。很自然的,我們希望引入一套算法來解決這個問題。

統計模型的困擾

一旦引入算法,在整個建模過程中,繞不開兩個問題:明確的算法評估標準和足夠的樣本數量。

首先,明確的評估標準,在算法迭代和檢驗階段非常重要,否則算法調優就沒了方向。雖然二分類問題在理論上有召回率和準確率這些評價指標,但這兩個指標在我們這種檢測場景下本身不可衡量。

原因是不管監控如何強大,總有我們發現不了的異常,而一個人認爲的異常,換一個人來看,也許就不認爲是異常。異常本身沒有明確的定義,也沒有一個全量的邊界,如何檢驗一個算法的好壞?

其次,生產系統中能夠確定的異常,相對於監控的數據總量來說,樣本非常的少,需要大量標註,標註又是個體力活,況且在這個場景下,標註人員的標準都不一致,而且這些少量的樣本還分佈在這麼多的指標裏,我們如果要從這麼少的樣本里學習出一些特徵,也將是非常困難的。

那怎麼辦?

面對這兩個近乎無解的問題,我們沒有什麼特別好的方法,但希望能找到一個子集,在這個子集閾內這兩個問題是有確定邊界的,然後尋求一個最優解,再慢慢拓展到整個數據集合。

首先是對算法模型的評估標準,我們需要知道哪些異常,或者說故障,是大家一致認可沒有歧義的。

我們有個NOC團隊負責7*24小時接受來自各個渠道的告警和報障,包括監控系統、用戶報障、內部人員發現的問題,如果經確認是一個影響了訂單的生產故障,則會記錄下來,這就給第一個問題提供了一個相對最爲接近可衡量狀態的條件。

首先一個故障只要影響了訂單,那麼對於訂單這個指標來說,這是無可爭議的異常,其次,所有問題都彙集到一個處理中心,我們可以大致認爲,這裏所記錄的問題,就代表了系統存在的所有可見異常。

所以,我們把目光鎖定在所有訂單類型的監控項上,一是因爲只有這類監控的異常纔是會被廣泛認可,二是這類問題只要發現就不會被錯過,一定會有人確認是不是一個真的異常。這就爲我們的算法迭代和優化提供了一個明確的檢驗標準和可靠標註。

算法選擇和設計目標

我們以訂單這類指標爲入手點來調試算法,接下來就是算法的選擇。

不論採用哪種算法來檢測,一個顯而易見的好處就是可以減少規則的維護成本,我們不再需去給每一個指標設定合適的告警規則。

目前業界採用比較多的方式是引入統計分析的各種方法,框定一個滑動的樣本集,對這個樣本集進行一些數據處理和轉化,經過歸一化,去週期,去趨勢,再將最新採集到的數據點經過同樣的轉換,和樣本集的殘差序列的統計量進行比較,比如距離、方差、移動平均、分位數等,超出一定的範圍就判斷爲異常,或是綜合各種離羣點計算的方法來做個投票,多數算法認爲異常則報異常。

起初我們也借鑑了這種做法,卻發現雖然可以不用維護告警規則了,但報警的質量並沒有提升。

和規則化告警面臨的問題是類似的,當我們把置信度設成一個理論上的顯著值,比如95%時,會檢測到很多的異常,當我們不勝其煩,把置信度調高時,報出的異常總量是會不斷減少,但與此同時,會發現相比於原來的規則化告警,遺漏沒有報出來的故障也逐漸增多,誤報和漏報始終都是一對矛盾在出現在天平的兩端。

通常的思路是,當兩個因素互斥的時候,我們需要尋找一個折中點,讓兩者達到均衡以獲取一個相對最穩妥的結果。

但生產環境不接受穩妥,寧可報警多一些,也不能接受漏報。但另一方面,報警接收人員要開始神經衰弱了,每個都仔細排查沒那麼多時間,萬一漏看一個剛好和故障相關,又得擔責任,他們想收到真正值得排查的告警。現實逼得我們不能搞平衡,必須魚和熊掌要兼得,這並不容易。

我們需要設計一套新的算法,降低報警總量到可以人工逐個處理的程度,同時不能以增加漏報真正的生產訂單故障爲代價,並且這套算法的設計還不能太複雜,影響到告警的實時性,最好還能做到算法即服務,有較強的可移植性,提供給其他的監控系統使用。

自然而然的,基於神經網絡的深度學習算法成爲我們進一步探索的工具。

RNN模型比較適合處理序列變化的數據,符合我們時序特徵的場景,而他的改進版LSTM模型,能夠通過控制傳輸狀態來選擇性地記住較重要的長期數據,能在更長的序列上有良好的表現,業界也有很多成功的應用。這裏重點介紹一下如何引入到我們的場景中。

算法的描述和檢驗

這是一個離線訓練的過程示意圖。

我們把歷史數據拿過來,先做個清洗工作,對缺失值進行插補以及節假日數據的剔除。剔除節假日是因爲訓練當中如果包含了這部分數據,模型就會有偏差。當然缺失值如果超過20%,那就乾脆不訓練。

然後對這個序列做特徵提取,特徵工程的目標是把時序序列分爲三大類,週期型、平穩型、非週期。

我們使用了多尺度滑動窗口時序特徵的方法,將一個滑動窗口內的數據和前n個週期做統計量上的對比,均值、方差、變化率等這些,這樣基本上就可以把明顯的週期性和平穩型數據給分離出來。

剩下的時序中,有些是波動很大的隨機序列,有的則是帶有趨勢的週期性序列,通過時序分析法把週期性去掉,再用頻域分析嘗試分解成頻譜。對於帶有明顯頻譜的,則歸類爲週期型時序,而頻譜雜亂的,則歸類爲非週期性。

爲什麼要分成三類後面會講到。分出來之後我們定義LSTM需要的各個變量,然後是調用TensorFlow進行LSTM模型訓練、驗證和調參的過程。

在線計算檢測階段,滑動窗口取最近的10個數據點,用前5個點作爲模型的輸入來預測後1個點的值,循環輸入模型直到預測出後5個點的值,並用這幾個預測數據點和實際值進行比較。

除非遇到極端情況,否則只一個點是無法判斷是否異常的,所以我們需要推測5個點,不是將來的5個點,而是過去的5個點,結合一些基本的規則,來對這5個點的實際數據做出異常判斷。

這裏還是需要結合最基本的規則,而什麼樣的數據類型採用哪些基本的規則,經過反覆嘗試發現是不同的,這就是爲什麼我們要先將數據指標分類。

我們將異常分爲連續1個點、2個點、3個點這幾種情況,每種情況下抽象出來一些最基本的規則,連續幾個異常點,結合不同的變化幅度,臨近週期內的統計量變化,以及多個點位的形態變化,設定了數套規則集,對應了高中低3個不同的檢驗敏感度,並調校驗證不同敏感度下模型的表現。

我們以兩個指標作爲算法迭代和優化的依據:

  1. 和現有的規則化系統相比,同樣的監控指標下,算法產生的告警量是否少於當前規則系統所產生的告警量;

  2. 以所有彙集到NOC的生產故障爲全集,通過算法檢出的數量是否高於當前規則系統檢出的數量。

最終得到這樣一個結果: 相比於規則化告警系統,算法的告警量平均壓縮了10倍左右,而檢出的故障數量反而還略高。

雖然故障檢出率提升了,但還是存在漏報的情況,我們分析下這些漏報的故障,主要表現爲以下幾個方面:

  • 指標看山去正常波動,肉眼及系統均無法檢出(用戶人工報障);
  • 業務訂單量較小導致漏報—— 絕對值越小,少量波動就會造成巨大的影響;
  • 歷史數據波動比較劇烈,異常下跌幅度小—— 數據隨機性太強,不容易準確分辨;
  • 趨勢變化明顯—— 縮短模型訓練週期捕捉週期性。

實時性工程

儘管如此,這已經是一個讓各方都能相對接受的結果,而需要解決的最後一個問題就是實時性。

我們原本的算法都是python的實現,在實時處理上python沒有什麼優勢,我們把採集到的數據落地,清洗插補,過模型計算,再觸發報警,這些沒有辦法流式處理,只能通過cron的方式一步步調用。

而大家知道cron的最小時間間隔是1分鐘,這就導致了從收到異常數據點到發出報警信息,中間需要經歷3~4分鐘的時間間隔,而一個規則告警系統,數據落地之後只需要對比規則,只需要1~2分鐘時間就能將告警發出來,對於重要的業務指標來說,這兩三分鐘的報警延遲就意味着真金白銀的損失,也是不可接受的。

實時化工程的方案選型,我們考慮了storm/spark streaming/Flink這幾種,最終選擇了Flink,原因在於它能滿足我們的要求,滑動窗口靈活,數據可以基於自身的時間戳來統計,不會因爲數據延遲而落到下一個時間窗口來統計,這會給我們減少很多數據處理的麻煩事兒。加上Flink本身是爲實時計算設計的,容錯性比較好,我們不用考慮很多數據達到的異常問題。同時也預留了支持秒級數據採集粒度的能力。

但是有一個問題就是檢測結果的數據校驗需要實時計算,否則來回調用python會增加時間消耗,所以就將算法以Java代碼重新實現。

我們的系統設計是這樣的:

用戶將每個指標的實時數據按照一定格式推送到Kafka隊列中,並且通過Portal確定哪些指標是需要做異常檢測的,如果指標有歷史數據的話,提供2周的歷史數據用於訓練模型,或者可以不提供,等待兩週的數據積累。

我們拿到這些數據之後,對所有滿足訓練條件的指標(有足夠的歷史數據)進行離線訓練,生成模型之後放在HDFS中,Flink加載新生成的模型,每個流過的指標如果有匹配的模型,則流入模型計算,否則丟掉,最後將計算結果回吐到指定的Kafka隊列,供用戶方消費。所有模型每兩週重新訓練一次,若發現用戶上傳新的指標,則觸發訓練,Flink每5分鐘檢查一次最新的模型並加載替換老模型。

這樣一來,實時數據不需要落地就能進入模型馬上計算得出結果,相比於規則告警系統先落地再計算的方式平均提升了40s左右,更接近實時。而這種方式也將算法和工程實現抽象出來,對外以隊列的方式提供輸入輸出,任何一套監控系統只要按照約定的格式傳入時序數值,就能使用這套方案來進行實時檢測。

算法的侷限

這就是我們目前爲止的嘗試,大家可以發現,這種方式還是存在不少侷限性的,比如每一個指標訓練一個模型,每個模型十幾兆,說實話不小,每次訓練需要10分鐘,所有模型全量訓練一次,對服務器的壓力不小。

10000個指標如果CPU資源不夠的話,光訓練就要好幾天,運行時也需要100G+內存來加載,所需的計算資源隨着需要檢測的指標數量呈線性增長關係。

其次對絕對值較小的指標沒有好的解決方案,因爲這類指標稍有變化,震盪幅度就特別大,很容易誤報。還有就是非週期性隨機序列,這種指標無法學習出什麼模式,也就無法判斷了。

尾聲

我們現在仍然在不斷嘗試,希望能提取出一個通用模型,以在面對更大監控項的時候能節省算力。也非常希望能和這方面感興趣的朋友們多多交流學習,路漫漫其修遠,我們一直都在探索的路上,心存敬畏。


作者簡介

陳劍明,攜程網站運營中心數據分析高級經理,負責網站容量規劃、ATP基線預測及RCA損失計算、成本分攤、運維數據倉庫建設,利用機器學習和深度學習相結合,進行運維方向的數據分析與預測。本文來自陳劍明在“2018攜程技術峯會”上的分享,首發與公衆號“攜程技術中心”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章