開發Thanos的目的
在一定的集群規模下,一些問題將在負載超出一個普通的Prometheus集群承載能力后不斷被暴露出來。我們如何能夠以一個經濟可靠的方式來存儲PB級別的歷史數據?我們能夠不犧牲查詢響應時間便做到這一點嗎?我們能夠通過一個單一的查詢接口訪問到不同Prometheus服務器上的所有指標數據嗎?再者,我們能否以某種方式合並通過Prometheus高可用集群采集到的復制數據嗎?
作為這些問題的答案,我們創立了Thanos項目。在接下來的部分,我們將講述我們在開發Thanos之前是怎樣規避這些功能上的不足之處,並詳細介紹我們心中構想的一些目標。
全局查詢視圖
Prometheus主張根據功能進行分片。即便是單台Prometheus服務器也能針對幾乎所有用例提供足夠的擴展能力,將用戶從水平擴展的復雜性中解放出來。
盡管這是一個很棒的部署模型,然而用戶往往希望能夠通過相同的API或者UI訪問所有的數據 —— 即全局視圖。舉個例子,你可以在一個Grafana看板上渲染多張查詢視圖,但是每個查詢只能針對一台單個的Prometheus服務器。而另一方面,倘若使用的是Thanos,用戶將能夠從多台Prometheus服務器上查詢和聚合數據,因為所有這些數據均可以從單個端點獲取。
在此之前,Improbable為了能夠實現全局視圖,我們將我們的Prometheus實例組織成一個多層的分級聯盟。這意味着需要建立一個單一的meta Prometheus服務器,從每個“葉子”服務器采集一部分數據。
這種方式最終被證明是有問題的。它導致配置方面的負擔不斷增重,而且增加了一個額外地潛在故障點,並且需要在聯盟節點上配置一些復雜的規則從而只暴露某些數據。此外,這樣的聯盟方式並沒有實現一個真正意義上的全局視圖,因為並非所有數據都可以從單個查詢接口獲得。
與之密切相關的是一套成對的Prometheus高可用服務器采集得到的數據所提供的一個統一視圖。Prometheus的高可用模型即是單獨地采集數據兩次,這再簡單不過了。然而,要得到一張兩個數據流的合並和去重后的視圖是一場浩大的可用性改進。
毋庸置疑,我們需要一套高可用的Prometheus集群。在Improbable,我們非常重視監控每一分鍾采集得到的數據,然而每個集群部署一個單一的Prometheus實例意味着存在單點故障的風險。任何配置錯誤或者硬件故障都可以導致重要監測結果的丟失。即便是一次簡單的發布也可能會導致指標采集出現小的中斷,因為重新啟動可能會比一次采集的時間間隔要長得多。
可靠的歷史數據存儲
我們的夢想之一(也是廣大Prometheus用戶的共同夢想)便是可以擁有一個廉價的,響應及時的長期指標存儲。在Improbable,我們使用的是Prometheus 1.8,我們曾經被迫將指標保留策略設定為一個尷尬的九天。這增加了一個明顯的運維限制,即我們可以通過監控圖表回顧多久以前的情況。
Prometheus 2.0在這方面提供了不少幫助,時間序列的數據量不再影響整體的服務器性能(請參閱Fabian在KubeCon上關於Prometheus 2的演講)。不過,Prometheus仍然是將指標數據放在它的本地磁盤里。盡管高效的數據壓縮可以在一塊本地的SSD盤上獲得顯著的優勢,但是最終可以存儲多少歷史數據還是會受到限制。
此外,在Improbable,我們關注可用性,簡易性和成本。更大的本地磁盤更難操作和備份。他們更加地昂貴而且需要更多的備份工具,這帶來了不必要的復雜性。
降准采樣(Downsampling)
一旦開始查詢歷史數據,我們很快便意識到,在我們檢索幾周,幾個月,乃至最終數年的數據時,這里存在的大O復雜度會讓查詢變得越來越慢。
通常這個問題的解決辦法稱之為降准采樣,這是一個降低信號采樣率的過程。針對降准采樣處理后的數據,我們可以在“伸縮“(zoom out)到一個更大的時間范圍的同時維護相同數量的樣本數據,從而保證查詢響應的速度。
對舊數據的降准采樣是任何一款長期存儲解決方案的必然要求,這超出了普通的Prometheus集群的范疇。
額外的目標
Thanos項目的最初目標之一是無縫集成任意現有的Prometheus實例。第二個目標是操作應該盡量簡單,並且應該盡可能降低准入門檻。如果存在任何依賴關系的話,它們應當很容易滿足小型和大型用戶的需求,這也意味着基准成本是可以忽略不計的。
Thanos的架構
在上一節中我們列舉了一些我們預期的目標,我們來盤點一下這些清單,看看Thanos是如何解決這些問題的。
全局視圖
為了能夠在現有的Prometheus集群之上收獲一個全局視圖,我們需要將中央查詢層和所有服務器互聯。Thanos Sidecar組件即是擔任這樣的角色,它會被部署到每一台正在運行的Prometheus服務端一側。它充當的是一個代理服務器,通過Thanos規范化的基於gRPC的Store API提供Prometheus的本地數據,它也允許通過標簽和時間段來選擇時間序列數據。
在另外一端運行的是一個可以橫向擴展並且無狀態的Querier組件,在應答PromQL查詢時比標准的Prometheus HTTP API做的事情稍微多一些。Querier、Sidecar 以及其他的 Thanos組件通過gossip協議通信。
- 當Querier收到一個請求時,它會向相關的Store API服務器(這里即是我們的Sidecar)發送請求,並從他們的Prometheus服務器獲取時間序列數據。
- 它將這些響應的數據聚合在一起,並對它們執行PromQL查詢。 它可以聚合不相交的數據也可以針對Prometheus的高可用組進行數據去重。
它通過將完全分離的Prometheus部署整合到我們數據的一個全局視圖的方式解決了我們遇到的問題里的核心難點。事實上,Thanos可以就這樣部署,並且可以按需使用這些功能。根本不需要現有的Prometheus服務器做任何改動!
不受限的保留數據!
然而,遲早有一天,我們會希望保留超出Prometheus常規保留時間外的一些數據。為了實現這一點,我們決定使用對象存儲系統來備份我們的歷史數據。它在每個雲甚至大多數本地數據中心上均有廣泛應用,並且極具成本效益。此外,幾乎所有對象存儲解決方案均可以通過眾所周知的S3 API進行訪問。
Prometheus的存儲引擎大約每兩個小時將其最近的內存數據寫入磁盤。持久化的數據塊包含固定時間范圍內的所有數據,並且是不可變的。這一點很有用,因為這樣一來,Thanos Sidecar便可以簡單地監聽Prometheus的數據目錄變化,然后在新的數據塊出現時將它們上傳到對象存儲桶中。
在指標數據塊寫入磁盤后通過Sidecar上傳到對象存儲的一個額外好處在於它可以保持 ”scraper“(由Prometheus和其配套的Thanos Sidecar組成)足夠的輕量。這簡化了運維,成本,和系統設計。
備份我們的數據很容易。 那么再次從對象存儲中查詢數據呢?
Thanos Store組件充當了一個對象存儲里面數據的數據檢索代理。就像Thanos Sidecar那樣,它也會加入到gossip集群里並且實現Store API。這樣一來現有的Querier就可以把它也當成是類似Sidecar那樣的另外一個時間序列數據的數據源 —— 不需要任何特殊處理。
時間序列的數據塊由一些大文件組成。按需下載它們的話會顯得效率低下,而且在本地緩存它們需要巨大的內存和磁盤空間。
與之相反的是,Store Gateway知道如何處理Prometheus存儲引擎的數據格式。通過智能的查詢計划並且僅緩存必要索引部分的數據塊的方式,它能夠將一些復雜查詢降級成一個針對對象存儲里的文件最小數量的HTTP范圍請求(HTTP range request)。通過這種方式,它可以在不影響響應時間的同時將原始請求的數量降低四到六個數量級,從整體上看,很難區分這和查詢本地SSD上的數據有何差異。
如上圖所示,Thanos Querier通過利用Prometheus存儲格式的特點(可在塊文件中共存相關數據)顯著降低了針對對象存儲產品的單次請求成本。考慮到這一點,我們可以將多個字節的提取工作聚合到一個最小數量的批量調用中。
壓縮和降准采樣(Compaction & downsampling)
在時間序列的一個新數據塊成功上傳到對象存儲的那一刻起,我們便可以把它當做是Storage Gateway上一條可用的“歷史”數據。
然而,經過一段時間后,來自單一數據源(即附帶運行Sidecar的Prometheus實例)的這些數據塊逐漸累積,卻沒能充分發揮索引的全部潛能。為了解決這個問題,我們引入了一個單獨的單例組件,名為Compactor。它會簡單地將Prometheus的本地壓縮機制應用到對象存儲里的歷史數據,並且可以作為一個簡單的定時批處理任務執行。
得益於Prometheus高效的樣本壓縮技術,從數據大小的角度來看,在一個很長時間的范圍內查詢許多序列並不成問題。然而,數十億的樣本數據的解壓以及針對這些樣本執行查詢處理的潛在成本會不可避免地導致查詢延遲的急劇增長。另一方面,由於每個可用的屏幕像素上存在着數以百計的數據點,甚至因此無法渲染全分辨率的數據。這也使得降准采樣不僅可行,而且不會引起明顯的精度損失。
為了生成降准采樣的數據,Compactor會持續不斷地將序列數據聚合到5分鍾和一小時的分辨率。針對用TSDB的XOR壓縮編碼的每個原始數據塊,它將會存儲幾個不同類型的聚合數據,例如,單一數據塊的最小值,最大值,或者總和。這使得Querier能夠針對給定的PromQL查詢自動選擇合適的聚合數據。
從用戶的角度來看,使用降准采樣后的數據不需要做什么特殊配置。當用戶縮小(zoom in)和拉大(zoom out)時,Querier會自動在不同的分辨率和原始數據之間進行切換。或者,用戶也可以通過在查詢參數中指定一個自定義的“step”來直接控制它。
由於每GB的存儲成本是微乎其微的,在默認情況下,Thanos將會始終在存儲里維持原始的以及五分鍾和一小時分辨率的數據,因而無需刪除原始數據。
記錄規則(recording rule)
即便使用了Thanos,記錄規則依然是監控堆棧的重要組成部分。 它們減少了查詢的復雜度,延遲和成本。它們還為用戶提供了方便的查看指標數據重要的聚合后視圖的快捷方式。Thanos建立在普通的Prometheus實例之上,因此,在現有的Prometheus服務器里保留記錄規則(recording rule)和告警規則(alert rule)是完全有效的。但是,對於下面這些情況來說,這可能不太夠用:
- 全局的告警和規則(例如:當三個集群中的兩個以上的集群里的服務宕機時發出告警)
- 超出單台Prometheus本地保留數據的規則;
- 希望將所有規則和告警存儲在一個地方
針對上述這些情況,Thanos提供了一個名為Ruler的單獨組件,它會基於Thanos Querier執行規則並作出告警。通過公開眾所周知的Store API,查詢節點得以訪問新計算出的指標數據。隨后,它們也會備份到對象存儲並可以通過Store Gateway訪問。
Thanos的力量
Thanos非常靈活,它可以根據用戶的使用場景進行不同的設置。在對普通的Prometheus實際做遷移時這一點尤其有用。我們不妨通過一個簡單例子快速回顧一下從Thanos的組件里學到的東西。下面這個例子講解了如何將自己普通的Prometheus遷移到我們閃亮的'無限保留指標'的世界:
- 將Thanos Sidecar添加到你的Prometheus服務端 - 例如,在Kubernetes pod里運行一個相鄰容器;
- 部署一些Thanos Querier的副本以啟用數據瀏覽功能。與此同時,用戶可以輕松地在Scraper和Querier之間配置一個gossip集群。使用
thanos_cluster_members
指標來確認所有組件都已連接。
值得一提的是,僅靠上述單獨的這兩步就足以實現一個從潛在的Prometheus高可用復本獲取結果的全局視圖和無縫的數據去重!只需將儀表盤連接到Querier HTTP端點或直接使用Thanos UI即可。
但是,如果你想要實現指標數據的備份和長期保留,我們需要完成額外的下面三個步驟:
- 創建一個AWS S3或GCS存儲桶。你只需要簡單地配置一下Sidecar即可完成數據的備份。如今你也可以將本地保留策略配至最低。
- 部署一個Store Gateway然后將它加入到現有的gossip集群。做到這一點的話我們的查詢便也可以訪問到備份好的數據!
- 部署Compactor,通過應用壓縮和降准采樣來提升長期數據查詢的響應能力。
如果你想了解更多,請隨時查閱我們的Kubernetes示例清單和入門頁面!
只需要五步,我們便可以將Prometheus的實例組裝成一個強大的監控系統,為用戶提供全局視圖,無限制的保留數據並且擁有潛在的指標高可用。
http://dockone.io/article/6019