譯者 | 劉汪洋
審校 | 重樓
本文將從監(jiān)控系統(tǒng)開發(fā)者的視角,探討監(jiān)控分布式數(shù)據(jù)庫的復雜性。主要涵蓋以下幾個方面:多節(jié)點管理、網(wǎng)絡限制、以及高吞吐量問題。我們將對這些方面進行權衡,并解決以下挑戰(zhàn):
- 內(nèi)置導出器與專用導出器的選擇。
- 像 Open Telemetry 這樣的行業(yè)標準在特定情況下的限制。
- 獲取指標的架構和數(shù)據(jù)的成本。
- 全局狀態(tài)同步的一致性問題。
- 在云環(huán)境中支持連接的復雜性。
我將以我開發(fā)的分布式數(shù)據(jù)庫 Apache Ignite 云監(jiān)控解決方案為例,分享相關經(jīng)驗,希望能為想要創(chuàng)建自己監(jiān)控系統(tǒng)的開發(fā)者提供一些有價值的參考。
引言
監(jiān)控分布式數(shù)據(jù)庫的挑戰(zhàn)在于,沒有一個普遍適用的最佳解決方案。每個系統(tǒng)都有其獨特的權衡點和限制,找到完美的實現(xiàn)非常困難。我們的討論將從一個抽象的指標收集系統(tǒng)的基本示例開始。正如這個示例所示,在多個點上,我們可能會基于特定需求進行不同的討論。
圖1. 抽象指標系統(tǒng)的基本實現(xiàn)
例如,在第一個點,我們可以選擇不同的指標注冊表實現(xiàn),可以將它們存儲在內(nèi)存中或持久化到磁盤中。指標注冊表是一個在導出之前存儲測量值的中間結構。理解這一點非常重要,因為我們可能會出于不同的原因?qū)ψ远x監(jiān)控系統(tǒng)采取不同的方法:
- 空間占用——即消耗的內(nèi)存量。在內(nèi)存有限的物聯(lián)網(wǎng)應用中,需要一種占用較少內(nèi)存的解決方案。
- 內(nèi)存壓力——對象分配的結果,可能會影響使用自動垃圾回收語言的應用程序。
由于大多數(shù)場景使用的是常規(guī)內(nèi)存數(shù)據(jù)結構,不會特別關心這些問題。在第二個點,我們可以討論在遇到的背壓(backpressure)和連接丟失時的應對策略。在第三個點,我們可以評估最合適的協(xié)議和數(shù)據(jù)獲取方法。盡管我們不討論分布式系統(tǒng)中指標收集的任何具體權衡,但我們可以看到,這種簡單實現(xiàn)方法很具有爭議性。
系統(tǒng)需求
在設計系統(tǒng)時,第一步通常是估算預期的負載,包括輸入規(guī)模、擴展計劃、用戶數(shù)量和業(yè)務限制。
我們將從一個包含 N 個節(jié)點的分布式數(shù)據(jù)庫中提取指標。這些指標提供了關于對象及其相關進程的信息。
圖2. Apache Ignite 集群 N 個節(jié)點的指標導出
在我的場景中,我考慮了兩種不同類型的指標:
- 靜態(tài)定義的指標 - 如 CPU、內(nèi)存、平均事務時間 等(約 100 個)。
- 動態(tài)定義的指標 - 這些指標是基于每個條目創(chuàng)建的,例如 緩存、表、隊列、數(shù)據(jù)區(qū)域、索引 等(每個條目平均約 30 個)。
你可能已經(jīng)注意到,動態(tài)創(chuàng)建的指標數(shù)量取決于特定環(huán)境。
我們嘗試估算其數(shù)量:
- 中型應用程序中的表、緩存和索引數(shù)量約為 250。
- 因此,每個節(jié)點有 100 + 30 * 250 = 7600 個指標。
- 平均集群大約有 10 個節(jié)點。
- 全局集群的指標總數(shù)為 7600 * 10 = 76,000。
- 考慮到 Gauge 指標 包含字段:Name、Description、Unit 和 Data。當指標數(shù)量為 76,000 時,負載大小約為 256 字節(jié) * 76,000 ≈ 每次請求約 19MB。
如上所述,不加過濾地發(fā)送所有指標可能導致空間浪費和不必要的網(wǎng)絡流量。此外,通常這些指標通過節(jié)點聚合獲得,監(jiān)控 7600 個不同的指標也不現(xiàn)實。
指標收集系統(tǒng)的一個優(yōu)勢是其高效的擴展能力。這樣我們能夠為每個集群分配獨立的實例來存儲數(shù)據(jù),確保數(shù)據(jù)獨立且有效分區(qū)。假設我們有一個微服務,可以為每個實例服務 50 個集群。盡管一個集群有數(shù)千個指標,但典型用戶使用的指標卻很少。
他們有 12 個儀表板 + 12 個警報,每個儀表板/警報大約有 4 個指標。因此,當指標收集間隔為 5 - 10 秒時:
- 50 個集群 _ (12 個儀表板 + 12 個警報) _ 4 個指標 _ (31 _ 24 _ 60 _ 60 / 5) 每月間隔 ≈ 每月存儲 2,571,264,000 個指標
- 2,571,264,000 * (64 + 32) 位指標大小(以 Ignite 持久化緩存中的位為單位)≈ 230 GB
為了解決占用過多存儲空間的問題,我們對指標進行壓縮,只存儲時間戳(作為 int64)和值(作為 int32)。壓縮算法對這種類型的數(shù)據(jù)壓縮效果非常好。如果使用時間序列數(shù)據(jù)庫,相同時間的時間戳將不會重復。
因此,如果沒有數(shù)據(jù)過期策略,我們每月需要提供 230 GB 的存儲空間。免費用戶通常有 7 天的數(shù)據(jù)保留期,付費用戶有更長的保留期以覆蓋我們的成本。
此外,另一個需要考慮的假設是讀/寫比率。這有助于我們?yōu)閿?shù)據(jù)選擇合適的數(shù)據(jù)庫。然而,在我們的案例中,我們將數(shù)據(jù)存儲在 Apache Ignite 中。
逐步解決方案
數(shù)據(jù)獲取方法
問題: 你更喜歡哪種類型的收集模式?(選項:推送 / 拉取 / 兩者皆可 / 我沒有特別的意見。)
在處理數(shù)據(jù)傳輸?shù)墓艿罆r,通常有兩種主要方法:要么將數(shù)據(jù)推送到管道中,要么將數(shù)據(jù)從管道中拉出。在我們的具體場景中,管道指的是網(wǎng)絡,我們考慮兩種數(shù)據(jù)獲取方法。
推送(CollectD、Zabbix 和 InfluxDB)
使用推送方法,每個應用實例會在設定的時間間隔內(nèi)推送數(shù)據(jù)。這意味著數(shù)據(jù)會根據(jù)預定義的計劃或頻率,從應用程序主動推送到數(shù)據(jù)庫或存儲系統(tǒng)。
圖3. 推送獲取方法示意圖
優(yōu)點:
- 不需要復雜的服務發(fā)現(xiàn)。
缺點:
- 需要在每個獨立的代理節(jié)點上進行配置。
雖然可以使用 Ansible 等自動化工具簡化配置過程,但每個節(jié)點的配置依然耗時且容易出錯。此外,更新配置也具有挑戰(zhàn)性,因為需要修改每個獨立節(jié)點。
拉取(Prometheus、SNMP 和 JMX)
拉取方法涉及外部監(jiān)控系統(tǒng)根據(jù)需要請求指標。
圖4. 拉取獲取方法示意圖
優(yōu)點:
- 可以在監(jiān)控系統(tǒng)內(nèi)集中管理配置。
- 自動處理背壓。
- 只請求所需的指標。
缺點:
- 需要服務發(fā)現(xiàn)。
- 監(jiān)控系統(tǒng)必須能夠遠程訪問。
在我們的案例中,拉取方法相比推送方法更有優(yōu)勢,但也存在顯著缺點。例如,網(wǎng)絡防火墻可能阻止所有從公共網(wǎng)絡到私有網(wǎng)絡的傳入連接。為了解決這個問題,我們采用了一種混合方法。
Ignite 2 采集模型
在 Ignite 2 中,我們最初采用了傳統(tǒng)的拉取方法進行指標收集,因為這是最直接的方法。然而,隨著我們增加更多集群,未經(jīng)過濾的平均響應大小每 5 秒達到 19 MB,使得處理變得困難。
圖5. Ignite 2 采集方法示意圖
為了收集指標,我們的流程包括兩個異步步驟。首先,我們發(fā)送一個不帶參數(shù)的拉取請求,開始收集所有可用的指標。然后,收集器接收這些指標并重復該過程。在隨后的迭代中,我們應用過濾器以減少后續(xù)請求中的數(shù)據(jù)量。然而,第一次請求必須是空的,以收集完整的指標架構。
當架構過時時,代理會發(fā)送新版本。代理是一個嵌入式插件,負責處理數(shù)據(jù)庫連接并響應指標請求。
存在的問題:
- 大多數(shù)拉取請求具有相同的有效負載,這導致流量浪費。
- 數(shù)據(jù)和架構請求一起發(fā)送,使得數(shù)據(jù)模型復雜,難以單獨從架構中檢索數(shù)據(jù)。
這些問題可能導致指標收集過程中的性能和效率問題。
你可能會問為什么沒有更換協(xié)議,這是因為當前系統(tǒng)支持多個版本的代理。如果更改協(xié)議,收集器將不得不支持新舊版本,增加系統(tǒng)復雜性。
Ignite 3
在與 Ignite 3 的第二版集成中,我們對協(xié)議進行了一些更改,將消息分為不同類型:架構更新消息和指標輪詢消息。這種方式使得協(xié)議更加高效。
圖6. 原型 Ignite 3 采集方法示意圖
架構收集步驟(綠色圓圈):
- 應用程序通過請求指標架構并訂閱更新來啟動過程 (1)。
- 收集器發(fā)送包含完整指標架構的響應 (2)。當架構有更新時,它會向應用程序發(fā)送更新 (2')。
指標收集步驟(紅色圓圈):
- 在指標收集過程中,收集器在第一次請求中發(fā)送過濾器設置 (1),指定未來請求中要收集的指標類型。
- 隨后,收集器發(fā)送指標請求 (2),并接收包含所請求指標的響應 (3)。
這種方法顯著減少了流量消耗,這是我們當前選用的協(xié)議。
網(wǎng)絡結構
采集模型的選擇會影響監(jiān)控系統(tǒng)的使用范圍。在設計監(jiān)控系統(tǒng)時,考慮網(wǎng)絡層的限制是非常重要的。
公共網(wǎng)絡
在公共網(wǎng)絡場景中,系統(tǒng)的每個部分都對公眾開放,通??梢允褂猛扑突蚶〔杉P汀S捎谒薪M件都是公開可用的,監(jiān)控系統(tǒng)可以輕松地使用拉取方法從應用實例請求數(shù)據(jù),或使用推送方法接收實例推送的數(shù)據(jù)。
圖7. 公共網(wǎng)絡交互
然而,這種方法在企業(yè)環(huán)境中不可行,因為它存在安全隱患,例如開放端口可能成為攻擊目標。
私有網(wǎng)絡
在私有網(wǎng)絡中,數(shù)據(jù)庫集群通常位于其中。出于安全考慮,這種網(wǎng)絡不會直接對外部開放。因此,收集器不能直接從集群拉取指標,推送采集方法在這種情況下更適合。
圖8. 私有網(wǎng)絡交互
推送模型在這種情況下具有優(yōu)勢,因為代理可以建立到收集器公共地址的外向連接。如果希望在私有網(wǎng)絡中使用拉取方法,需要在兩個網(wǎng)絡之間建立連接。這可以通過多種方式實現(xiàn),例如虛擬專用網(wǎng)絡、AWS PrivateLink、代理服務器或其他類似解決方案。這些橋接方法允許收集器通過橋接連接從私有網(wǎng)絡拉取指標。然而,這種方法需要額外的橋接基礎設施設置和維護。
Ignite 2/3 網(wǎng)絡
為了支持私有網(wǎng)絡和公共網(wǎng)絡之間的連接,我們決定不使用虛擬專用網(wǎng)絡或其他復雜方法,因為它們可能會讓客戶感到困難并導致流失。相反,我們從代理到收集器打開一個 TCP 連接(使用 WebSocket,因為大多數(shù)銀行端口已禁用),收集器使用該連接來拉取指標。
圖9. Ignite 的網(wǎng)絡交互
代理/導出器
代理/導出器是指標收集系統(tǒng)的一部分,用于從指標源收集指標并將其提供給存儲。有幾種方法可以放置導出器:應用程序可以在類路徑中包含一個代理,這樣代理就會與應用程序一起啟動。代理可以位于應用程序外部,但在同一實例或同一網(wǎng)絡中。應用程序能夠支持使用 JMX 和 REST 等直接調(diào)用的開放協(xié)議,這種情況類似于內(nèi)部的代理。
圖10. 導出器代理
應用程序內(nèi)部的代理
在應用程序內(nèi)部運行的代理類似于包含在應用程序運行時的庫,例如 Prometheus 代理。采用這種方法的一個優(yōu)勢是可以輕松地為缺乏指標的系統(tǒng)添加指標導出,并通過依賴于易于修補的常見框架來提供應用程序指標。另一個好處是,在實現(xiàn)一個抽象的指標框架時,代理可以作為特定收集系統(tǒng)的導出器實現(xiàn)。
然而,這種方法也有缺點。主要缺點是外部代碼在與應用程序(在本例中是數(shù)據(jù)庫)相同的運行時執(zhí)行,這可能導致以下副作用:
- 導出器內(nèi)部使用緩沖區(qū)可能導致內(nèi)存不足的錯誤進而引發(fā)崩潰。
- 代碼中的錯誤,尤其是使用不安全代碼時,可能會導致在不同字節(jié)順序環(huán)境中出現(xiàn)問題,從而引發(fā)崩潰。
- 額外的內(nèi)存占用和垃圾回收暫停。
- 滾動升級問題,即必須停止數(shù)據(jù)庫實例以更新依賴項。
- 潛在的安全問題。
應用程序外部的代理
應用程序外部的代理是在與業(yè)務應用程序相同實例中安裝外部代理應用程序的方式。這種方法消除了對收集系統(tǒng)的直接依賴以及應用程序內(nèi)部代理方法的缺點。
然而,這種方法帶來了額外的成本,因為需要管理兩個應用程序。盡管性能可能略有下降,但這并不是關鍵問題。除非有下面要討論的一個問題,否則這種方法應該成為首選。
Ignite 代理和我們的討論
我們?yōu)?Ignite 2 開發(fā)了一個插件,該插件與我們的云建立連接并發(fā)送指標。我們還在探索將相同方法用于 Ignite 3 集成的可能性。起初,我們考慮使用外部導出器,因為它對客戶集群更安全,但我們還必須考慮減少手動安裝步驟的業(yè)務價值。
我們曾考慮支持兩種類型的代理——內(nèi)部和外部——并在性能和穩(wěn)定性更好的情況下推薦遷移到外部版本。然而,這種方法將需要雙倍的工作量。
因此,我們決定堅持使用內(nèi)部版本的代理。雖然這種方法有其優(yōu)勢,特別是在分布式系統(tǒng)中,我們將在下面進一步探討。簡而言之,我們可以利用對負責共識的內(nèi)部數(shù)據(jù)庫服務的訪問,并將其重用于我們的目的。
可擴展性(全局或本地指標)
在接下來,我們將探討在分布式環(huán)境中監(jiān)控的挑戰(zhàn)。與監(jiān)控單個應用程序相比,我們將深入了解推送/拉取采集方法在微服務架構或分布式數(shù)據(jù)庫中的應用。
每個節(jié)點上的推送
我認為,每個節(jié)點上的推送模型是解決分布式系統(tǒng)中許多問題的理想方法。這種方法涉及每個節(jié)點與收集器之間建立直接連接,對于處理腦裂場景非常有利。如果集群的一部分分離,它將自動停止發(fā)送指標。
圖11. 每個節(jié)點上的推送
優(yōu)點:
- 自動擴展,無需服務發(fā)現(xiàn)。
- 減少重新發(fā)送數(shù)據(jù)的流量。
- 消除單點故障。
缺點:
- 套接字連接可能會成為問題,但可以通過使用虛擬地址和超過 65k 的連接來緩解。
- 合并數(shù)據(jù)時,時間同步可能會有挑戰(zhàn)。
每個節(jié)點上的拉取
在傳統(tǒng)架構中,在分布式系統(tǒng)的每個節(jié)點上使用拉取采集方法需要更復雜的交互模式。為了適應節(jié)點的啟動和與其他實例失去連接的情況,每個節(jié)點必須在服務發(fā)現(xiàn)組件中注冊自己。
圖12. 每個節(jié)點上的拉取
我們可以通過使用反向連接使這個解決方案更簡單,如圖例所示,但這需要管理多個連接,這使得它比推送方法更復雜。
圖13. 每個節(jié)點上的推送
此外,還需要考慮集群仍在運行但某些節(jié)點無法與收集器建立連接的情況。
單一協(xié)調(diào)器與跟隨者
接下來的方法與之前討論的推送/拉取方法類似,但有一些不同。在這種方法中,集群中有一個節(jié)點作為協(xié)調(diào)器代理,它與指標收集器建立連接,并從其他節(jié)點收集中間指標。
圖14. 代理協(xié)調(diào)器節(jié)點
這種方法在管理連接和基于時間戳同步數(shù)據(jù)方面更簡單,但與之前的方法一樣,也存在一些缺點。
- 首先,可能會導致流量消耗增加,因為指標被提交了兩次。
- 其次,如果協(xié)調(diào)器接收到大量數(shù)據(jù)批次,可能會導致內(nèi)存不足。
- 最后,管理腦裂場景可能更加棘手。
我們的解決方案
正如之前提到的,我們選擇使用內(nèi)部代理來解決問題,盡管這種方法存在一些缺點。這種方法使我們能夠?qū)⒛X裂問題的處理委托給集群??紤]一個協(xié)調(diào)器節(jié)點分裂集群的場景。
圖15. 集群腦裂第一步
圖16. 集群腦裂第二步
為了在使用內(nèi)部代理的解決方案中處理腦裂問題,我們決定將責任委托給集群。如果協(xié)調(diào)器節(jié)點與集群的其余部分分裂,則必須選擇一個新的協(xié)調(diào)器代理。選擇新的代理協(xié)調(diào)器并非易事,假設集群保證最早的節(jié)點會在正確的分裂部分中,我們的算法選擇最早的可用節(jié)點作為新的代理協(xié)調(diào)器。
協(xié)議
回到簡單的交互圖,還未討論一個方面——通信協(xié)議。
圖17. 交互圖
Open Telemetry
在我看來,使用廣泛接受的行業(yè)標準如 Open Telemetry 作為通信協(xié)議是最好的選擇。盡管它可能不是一個完美的解決方案,但它有幾個優(yōu)點,例如:
- 無需為流行的收集系統(tǒng)實現(xiàn)適配器。
- 協(xié)議基于最佳實踐。
需要注意的是,這些常見的解決方案可能并不適用于每一個獨特的場景。
Rest / GraphQL / 類 SQL
另一種流行的協(xié)議是 Rest,它的優(yōu)點在于使用和檢查都很簡單。例如,可以通過瀏覽器進行檢查。
自定義協(xié)議(自定義 - TCP / UDP, Protobuff)
自定義的協(xié)議可以更好地解決特定的邊緣情況,因為開發(fā)人員了解所使用的監(jiān)控系統(tǒng)。然而,缺點是如果使用其他收集系統(tǒng),則需要支持多個適配器,并且存在重復解決行業(yè)標準協(xié)議中已解決問題的風險。
我們的選擇
在我們的討論中,我們發(fā)現(xiàn)系統(tǒng)設計需求的主要問題是全量獲取的大消息大小以及需要保持架構值的更新。因此,我們最終決定實現(xiàn)自己的自定義協(xié)議。
圖18. Ignite 協(xié)議的原型
該協(xié)議在上一節(jié)中已詳細描述,這里就不再贅述了。
總結
總之,我試圖解決在開發(fā)過程中提出的基本問題。
譯者介紹
劉汪洋,51CTO社區(qū)編輯,昵稱:明明如月,一個擁有 5 年開發(fā)經(jīng)驗的某大廠高級 Java 工程師,擁有多個主流技術博客平臺博客專家稱號。
原文標題:Why Monitoring a Distributed Database is More Complex Than You Might Expect,作者:Stepachev Maksim