苑舒斌,宗曉萌,于俊濤
中海油信息科技有限公司北京分公司,北京,100010
隨著信息化程度的日益加深,以及對信息安全和技術資產的日益重視,越來越多的企業將保護核心的數據資產和防范企業核心業務數據泄漏提上日程。對此,一種可行的解決思路為推進云辦公,實現重要數據的隔離、備份和流轉審計。在此場景下,可能涉及大量的物理服務器和虛擬主機,需要進行監控,以及時發現系統問題,復盤歷史故障[1]。因此需要一種高度可擴展、可配置的監控報警平臺,實現對服務器系統的在線監控報警。平臺應當可定制被監控的數據種類,必要時隨時通過擴展模塊增加新數據。采集來的數據需匯總到平臺集中存儲,并集中進行分析報警。針對報警功能,應允許用戶便利地定制報警規則,基于采集到的數據按需報警。同時,平臺中的數據采集模塊可能運行在服務器本身以及虛擬主機上,因此采集模塊必須輕量化,減少不必要的開銷,并在采集架構設計上實現必要的彈性,可輕松應對不同規模的系統。
現有的服務器監控平臺,如Zabbix、Ganglia、Nagios等,他們各具特色,但在集群構建、報警規則定義、規模橫向擴展能力、二次開發能力、監控目標擴展能力、故障容錯能力等方面各自有一定的缺陷[1]。本文設計的平臺依托已有集群環境中的服務組件構建,設計上側重于規則模板的靈活構建和可編程性。
基于以上考慮,平臺架構設計將側重于分布式、可擴展、可配置和輕量化等方面。
圖1所示為系統的整體功能框圖。在被監控的服務器、虛擬主機等設備中安裝采集程序,負責對終端數據的初步收集。然后通過采集程序的接口模塊進行整理,變為標準消息內容,發送到消息隊列。根據系統規模不同,消息隊列可組建為集群模式。作為消息隊列的消費者,入庫程序也可根據處理負載情況動態增減。最終,入庫程序將監控數據保存到時序數據庫中,供用戶做歷史數據查詢時展示使用,并可作為復雜報警功能的判斷條件。報警程序的數據來源可以是數據庫,但是數據庫本身可靠性較低,尤其是系統發生故障的情況下,難以保證數據庫系統仍可以正常運行。因此,報警判斷依賴的原始數據必須可由入庫程序直接提供。

圖1 系統整體框圖
運行于主機中的采集程序負責定時獲取對象的運行狀態數據,如CPU、內存、硬盤、網絡、IPMI等硬件運行狀態,以及運行于其上的關鍵進程、數據庫、消息隊列、虛擬機、Docker服務等軟件運行狀態[2]。因為采集程序需要運行在大量主機上,所以首先要考慮其運行效率,需以最小的資源占用實現采集需求;同時,也要便于擴展,能便利地加入新的監控對象采集需求,甚至可以由用戶定制需求。
一方面,消息隊列實現了橫向擴展需求。依靠隊列提供的集群功能、消息路由和負載均衡功能,可快速配置適應不同規模的接入主機數量,并可隨時增減作為消息消費者的入庫程序模塊。另一方面,消息隊列提供了數據緩存和持久化功能[3]。當網絡抖動或其他意外導致系統負載波動時,消息隊列可為系統提供必要的緩沖;入庫程序和數據庫本身發生故障,也可為待處理的數據提供持久化功能,保證已采集的數據不因平臺故障而丟失。
入庫程序消費消息隊列數據,包括數據預處理、摘要數據生成、數據入庫、數據自動分表和過期數據清理等功能模塊,如圖2所示。來自消息隊列的原始采集數據可能具有不同的數據命名方式,或者數據單位。同時,一部分需入庫的數據可能需要通過收集到的原始數據計算后才能得到。因此,首先需對收到的數據進行預處理,之后將這些數據轉換為對應數據庫的入庫語句存入數據庫。這里考慮到數據庫存入可能失敗,批量處理失敗的數據應退回到消息隊列,等待后續處理,如圖2中虛線所示。故障診斷及數據展示過程中,會需要系統能提供較長時間范圍內原始數據的統計信息,如最大值、最小值、平均值等,因此入庫程序還需提供數據摘要服務,計算原始數據若干時間范圍內等級的統計結果,并存入數據庫以供查詢。常見的時序數據庫也支持持續查詢等摘要生成功能,但基于數據庫本身功能的摘要生成就缺乏靈活性,且對各種異常情況缺乏容錯能力,因此由程序實現該功能。另外,出于數據庫查詢性能和數據量控制的考慮,程序還應提供自動分庫分表和過期數據清理功能。為了實現分布式數據存儲的一致性,并保證分布式程序有一致的分表命名邏輯,分表按照固定的UTC時間間隔進行。程序入庫的數據也會同時創建一份緩存數據,供故障診斷模塊跳過數據庫直接訪問使用。緩存數據應按照入庫時間戳進行組織,根據實際診斷需求,緩存多個不同時間點的數據。通常進行故障判斷時,需要對相鄰多個時間點的數據進行分析計算。

圖2 入庫程序功能框圖
報警程序首先讀取數據緩存中的數據進行分析,判斷是否滿足報警條件。如果數據不存在,再嘗試到數據庫中查詢。通常報警分析都需要利用近期采集的多個數據進行判斷,而其中某些部分在下次分析中也需要用到。緩存可以減少對數據庫的訪問壓力,也可以保證當數據庫不可用時,僅依賴近期數據的簡單報警分析便可正常進行。
報警程序的規則配置接口直接面向用戶,其設計的關鍵是如何使用戶能高效便捷地創建自定義報警規則,輸出自定義的報警通知信息。為了給用戶最大的規則定制空間,需要接口支持數學計算、字符串處理、條件判斷、循環控制等功能[1]。接口中,提供用戶獲取指定時間點采集數據的能力。基于以上考慮,系統選取字符串模板引擎作為報警功能判斷分析過程實現的核心。
最后,發現系統報警后需要對歷史數據進行分析、明確問題原因,所以采集數據的圖形化展示是必不可少的功能。因為數據存儲部分依賴時序數據庫實現,因此該功能可簡單地通過數據庫支持的數據可視化工具實現。
本研究針對平臺架構中涉及的關鍵部分實現進行詳細介紹。
數據采集部分側重輕量化和可擴展性。基于主機狀態監控的基本需求,對CPU、內存、硬盤、網絡、IPMI等硬件運行狀態的采集是必要功能。通過對現有解決方案的篩選,找到符合平臺需求的開源項目Collectd。
Collectd程序本身以C語言編寫,運行時占用的CPU和內存資源都很少。其通過模塊擴展的方式,默認支持對常見硬件數據、關鍵進程數據、常用服務器組件數據、虛擬機和容器數據的采集功能[5]。同時以Python腳本形式,可以方便地編寫自定義擴展模塊。程序還提供了網絡協議、AQMP隊列協議、服務器文件輸出等多種采集數據輸出方式。基于其UDP網絡傳輸協議,甚至可以將多個Collectd采集程序的實例級聯,實現分級的數據收集,也可以直接將數據輸出到InfluxDB時序數據庫。此處,基于系統橫向擴展和數據可靠性的考慮,我們選擇用RabbitMQ消息隊列作為數據處理的中間件。因此,Collectd在平臺中將利用AQMP協議的輸出方式連接到中心消息隊列。程序的功能框圖如圖3所示。

圖3 Collectd功能框圖
采集數據具有如下屬性:采樣時間、主機名、采集模塊、模塊實例、數據名稱、數據值,分別表示數據采樣的實際時間、采樣發生的主機名稱、執行采樣的功能模塊、采樣的目標對象和采樣參數的名稱。因此,采集到的數據都可用五元組(時間,主機,模塊,實例,名稱)唯一標識。例如在時間點1000對主機H1的CPU狀態采樣,得到的結果為各個CPU核心的空閑時間、等待時間等數據。因此,表示核心2 IDLE比例的數據可用五元組(1000,H1,CPU,2,IDLE)進行標識。
應用中,發現Collectd自帶的AQMP隊列協議輸出模塊每收到一條輸入數據即發送一條MQ消息。此舉可能導致網絡中有大量短TCP報文傳送,影響網絡性能。因此對模塊進行修正,實現單條MQ消息中批量發送采集數據。
因為持續監控產生的數據量很大,而對其查詢的條件主要為數據產生時間,因此適合使用時序數據庫進行保存。常見的開源選擇包括Elasticsearch、InfluxDB等。
因為不同種類采集數據的內容相差很大,而同類數據基本有相同的數據項,實踐中選擇將來自不同采集模塊的數據單獨保存到不同的數據表中。不考慮定期分表的影響,每個采集模塊的數據存到同一表中。表中除時間列外,另有主機名、采集模塊名、數據實例名等索引字段,而每個數據的名稱作為表的數據列,與采集數據唯一標識的五元組對應。同時,為了便于數據查詢,規定所有數據時間需整理到規定的整數倍采樣時間的間隔時點。
為了便于后續進行數據展示和報警規則編寫,入庫程序同時還會對入庫數據進行摘要計算,將摘要結果同樣寫入數據庫。對于一種原始數據表,根據需要可設置多個不同等級的摘要表。摘要表中,每個原始數據對應其最大值、最小值、累加和、平方和、有效數據個數等5個摘要數據。實際期望得到的統計數據是原始數據在摘要時間間隔中的最大值、最小值、平均值和標準差,設置摘要數據是為了屏蔽摘要過程意外中斷、數據丟失、進程重啟等意外情況對摘要結果精度的影響。
如圖4所示為摘要過程的計算原理,展示了兩個低等級摘要數據如何計算對應的高等級摘要數據。對于原始采樣數據,可認為其最大值、最小值、累加和都是其自身,平方和是自身平方。后續摘要時,下一摘要等級應記錄的最大值、最小值、累加和、平方和,則分別是上一摘要等級中對應時間范圍內所有數據的最大值、最小值、累加和、平方和。可以認為每個采樣間隔中各個摘要結果的數量級大致相同,因此該摘要過程不會導致數值誤差積累。最終,摘要等級的一個時間間隔,記錄了對應所有原始采樣點數據的摘要值。根據記錄中的有效數據數量、累加和、平方和,即可獲得摘要時間區間內精確的平均值和標準差。

圖4 摘要計算圖示
數據庫中保存的數據除原始數據外,還有各級摘要。因此訪問數據庫的數據時,需使用六元組(時間,主機,模塊,摘要等級,實例,名稱),且名稱中需以后綴的方式表示期望數據的摘要類型。
報警規則設置應當靈活、便捷,用戶可對特定范圍內的設備進行指定報警監控,報警規則編寫時能便捷地獲取監控數據,并有編寫復雜邏輯的能力。
獲取監控數據的便捷性體現為以下幾點。第一,用戶報警規則中獲取監控數據時,可便捷地指定數據六元組的內容,以精確地獲取數據。第二,六元組中的部分內容,如主機名、實例名,可指定通配符,或者給定預定義列表。第三,六元組中的各項應當具有合理的默認值,常規數據訪問必須提供的項只有時間和名稱。第四,時間參數應采用相對值,且具有容錯性,指定時間應自動取整到相應數據摘要間隔的整數倍時點。第五,規則定義參數化,普通用戶不需要修改規則腳本,直接改變規則參數即可變更規則行為。
為實現以上目標,為報警規則腳本提供get函數原型如下:
def get(tsOffset: float, itemName: string,metric: string = None, summary: string = None,host: string = None, inst: string = None)
其中只有相對時間tsOffset和變量名稱itemName為必選項;其他參數中模塊名稱metric和摘要等級summary在規則編寫時提供了默認值,因此可以省略;主機名host和模塊實例inst提供了通配符和列表語法。對于一條報警規則,在實際運行前會根據通配符到系統中確認實際可能的主機名和模塊實例列表,或者直接使用用戶給定的列表,然后對所有可能的組合都執行一次規則判斷。因此,每次規則執行,主機名和模塊實例的默認值都不相同,實現了模板化報警規則的功能,可指定規則執行的目標列表,也實現了對特定范圍內的設備進行指定報警的功能。
編寫復雜報警邏輯,指用戶在編寫報警規則時,可進行變量定義、數學計算、字符串處理、條件判斷、循環控制等操作。同時,基于安全考慮,用戶編寫的腳本還需要在沙箱環境中隔離運行。Python作為一種使用簡單的腳本語言,且較多運維人員都有應用經驗,適合作為報警規則腳本的基礎。Python環境下,Jinja模板語言具有類似Python的語法,且可以利用Python本身的函數功能,運行環境也有沙箱保證,適用于本平臺報警規則腳本編寫的基本需求。通過模板語言語法編寫報警規則,還可便利地格式化輸出報警提示信息[6]。實踐中,我們是基于Jinja模板語言提供的功能實現報警規則執行。將定義的get函數映射到模板語言函數中,供用戶獲取數據。
為了使用戶在報警規則執行、報警消息生成時可以獲取更完整的信息,也將get函數使用的默認參數和規則定義時填寫的規則名稱等作為附加信息映射到模板語言環境中,供用戶使用。
對于常見的時序數據庫,有Grafana作為開箱可用的數據展示組件[7]。因為所有數據保存到數據庫,也可便捷地集成到現有的應用環境界面中。
如圖5所示為平臺數據展示示例。Grafana提供了基礎的數據源連接、變量配置、時間縮放功能。平臺配置了4個變量,分別為模塊名稱列表、主機列表、模塊實例列表和變量名稱列表,分別表示在選定的展示時間區間內,可用的數據采集模塊名稱、選定采集模塊收集的主機名稱、選定主機上該模塊的實例列表和模塊的采集變量列表。其中,除模塊名稱外均支持多選。選擇完成后,將對每個采集參數顯示一個獨立圖表,圖表中對每個主機名稱和模塊實例名稱的組合均顯示一條折線,表征變化趨勢。圖中選定的采集模塊是CPU模塊,它采集了每個CPU核心的系統使用率、用戶使用率、中斷使用率、等待時間比例等數據。根據配置,顯示了某主機上第0、第1核心的系統使用率和用戶使用率變化。

圖5 Grafana數據展示
現通過實例展示系統中如何配置檢查特定CPU核心的使用情況,如果負荷持續超標則進行報警。
系統監控的主機中有若干名稱以“ZS-”開頭,其CPU核心20-29運行特定負載,如果CPU空閑時間持續半小時低于10%,則應報警。對于該場景,可按照表1內容配置報警規則。規則指出,該規則會使用CPU模塊采集到的5分鐘等級摘要數據,監控的主機名稱可用通配符“HOST-*”進行匹配,監控的CPU實例可用正則表達式“/2[0-9]{1}/”進行匹配。對于匹配到的CPU核心,連續獲取其最近30分鐘idle_avg數據,判斷是否低于閾值。如果所有時點都低于閾值,則說明CPU空閑時間持續偏低,應進行報警。這里用到兩個用戶定義的參數var.low和var.cnt,分別表示idle的檢測閾值和連續獲取的5分鐘摘要數據時點數量。表中定義idle的低閾值為10%,采樣時點數量是6,對應30分鐘時長。報警規則的最后一行是判斷語句,如果結果是真,則應當報警,報警內容由輸出模板格式化后給定。
由表1的配置樣例可以看出,平臺規則配置非常靈活,輸出內容也可根據設定清晰地指出報警來源。編寫規則腳本需要一定的編程基礎,但通過用戶參數的形式,使普通用戶也能夠安全地按需修訂規則行為。

表1 報警配置實例
關鍵進程重啟也是服務器監控平臺的一項重要工作內容。通常的服務重啟監控基于指定名稱進程的數量變化,或者進程啟動時間變化進行判斷。對于具有多實例的服務,常見監控平臺往往難以有效定義報警規則。基于本平臺的擴展能力,可通過插入數據預處理模塊的方式直接獲得監控進程的重啟事件,統計進程重啟次數。
如圖2所示,數據入庫程序具有數據預處理功能模塊。對于CPU、內存、關鍵進程等系統已知的數據類別,均可注冊專用的數據處理模塊,進行數據預處理。預處理框架中,可實現對輸入數據的通用處理,如字段名改寫、單位轉換、有效性檢查等。之后將收到的數據緩存在臨時結構中,等待模塊接收到完整的數據再統一進行后續處理,包括數據處理回調和實際入庫動作。
如圖6所示,針對進程重啟檢測功能,可在關鍵進程監控模塊進行數據預處理時,插入數據處理回調函數。函數接收進程數據處理模塊緩存的進程名稱、進程PID等數據,與自身狀態字典保存的內容進行對比,即可發現特定進程是否重啟。發現進程重啟后,重啟統計模塊將產生重啟數據,標記過去時間間隔內某進程發生1次重啟。重啟統計模塊產生的數據與其他從主機輸入的模塊數據具有同等地位,在平臺中同等進行數據摘要、保存、展示、報警等后續訪問。期望的預處理數據復雜時,平臺也可以支持利用多個模塊數據進行分析生成[8]。

圖6 進程重啟統計實現框圖
此方案充分利用了平臺易于擴展的優勢,高效完成進程重啟監控任務。且進程重啟數據也被入庫、摘要,可隨時查看歷史記錄。
本文針對大規模服務器集群和虛擬主機的應用場景,給出了一種高度可配置的分布式狀態監控和報警系統的基本架構,包括數據采集、消息隊列、入庫程序、報警程序、數據展示等組成部分。平臺可適應不同規模主機集群的監控報警需求,報警規則支持模板化,編寫簡單,并可設置用戶自定義規則參數。
平臺功能目前都已實現,在實際工作場景中運行良好,規則腳本提供的靈活性完全可以滿足日常監控報警功能擴展的需求。