容器監控實踐—Prometheus存儲機制

概述

Prometheus提供了本地存儲,即tsdb時序數據庫,本地存儲給Prometheus帶來了簡單高效的使用體驗,prometheus2.0之後壓縮數據能力也獲得了很大的提高。能夠在單節點的狀況下知足大部分用戶的監控需求。html

但本地存儲也限制了Prometheus的可擴展性,帶來了數據持久化等一系列的問題。爲了解決單節點存儲的限制,prometheus沒有本身實現集羣存儲,而是提供了遠程讀寫的接口,讓用戶本身選擇合適的時序數據庫來實現prometheus的擴展性。linux

Prometheus 1.x版本的TSDB(V2存儲引擎)基於LevelDB,而且使用了和Facebook Gorilla同樣的壓縮算法,可以將16個字節的數據點壓縮到平均1.37個字節。git

Prometheus 2.x版本引入了全新的V3存儲引擎,提供了更高的寫入和查詢性能算法

如下全部內容均基於prometheus2.7版本sql

本地存儲

存儲原理

Prometheus按2小時一個block進行存儲,每一個block由一個目錄組成,該目錄裏包含:一個或者多個chunk文件(保存timeseries數據)、一個metadata文件、一個index文件(經過metric name和labels查找timeseries數據在chunk文件的位置)。數據庫

最新寫入的數據保存在內存block中,達到2小時後寫入磁盤。爲了防止程序崩潰致使數據丟失,實現了WAL(write-ahead-log)機制,啓動時會以寫入日誌(WAL)的方式來實現重播,從而恢復數據。json

刪除數據時,刪除條目會記錄在獨立的tombstone文件中,而不是當即從chunk文件刪除。緩存

經過時間窗口的形式保存全部的樣本數據,能夠明顯提升Prometheus的查詢效率,當查詢一段時間範圍內的全部樣本數據時,只須要簡單的從落在該範圍內的塊中查詢數據便可。bash

這些2小時的block會在後臺壓縮成更大的block,數據壓縮合併成更高level的block文件後刪除低level的block文件。這個和leveldb、rocksdb等LSM樹的思路一致。服務器

這些設計和Gorilla的設計高度類似,因此Prometheus幾乎就是等於一個緩存TSDB。它本地存儲的特色決定了它不能用於long-term數據存儲,只能用於短時間窗口的timeseries數據保存和查詢,而且不具備高可用性(宕機會致使歷史數據沒法讀取)。

內存中的block數據未寫入磁盤時,block目錄下面主要保存wal文件:

./data/01BKGV7JBM69T2G1BGBGM6KB12
./data/01BKGV7JBM69T2G1BGBGM6KB12/meta.json
./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000002
./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000001

持久化的block目錄下wal文件被刪除,timeseries數據保存在chunk文件裏。index用於索引timeseries在wal文件裏的位置。

./data/01BKGV7JC0RY8A6MACW02A2PJD
./data/01BKGV7JC0RY8A6MACW02A2PJD/meta.json
./data/01BKGV7JC0RY8A6MACW02A2PJD/index
./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks
./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks/000001
./data/01BKGV7JC0RY8A6MACW02A2PJD/tombstones

存儲配置

對於本地存儲,prometheus提供了一些配置項,主要包括:

  • --storage.tsdb.path: 存儲數據的目錄,默認爲data/,若是要掛外部存儲,能夠指定該目錄
  • --storage.tsdb.retention.time: 數據過時清理時間,默認保存15天
  • --storage.tsdb.retention.size: 實驗性質,聲明數據塊的最大值,不包括wal文件,如512MB
  • --storage.tsdb.retention: 已被廢棄,改成使用storage.tsdb.retention.time

Prometheus將全部當前使用的塊保留在內存中。此外,它將最新使用的塊保留在內存中,最大內存能夠經過storage.local.memory-chunks標誌配置。

監測當前使用的內存量:

  • prometheus_local_storage_memory_chunks
  • process_resident_memory_bytes

監測當前使用的存儲指標:

  • prometheus_local_storage_memory_series: 時間序列持有的內存當前塊數量
  • prometheus_local_storage_memory_chunks: 在內存中持久塊的當前數量
  • prometheus_local_storage_chunks_to_persist: 當前仍然須要持久化到磁盤的的內存塊數量
  • prometheus_local_storage_persistence_urgency_score: 緊急程度分數

prometheus 2.0的存儲升級

prometheus 2.0於2017-11-08發佈,主要是存儲引擎進行了優化。

性能的總體提升:

  • 與 Prometheus 1.8 相比,CPU使用率下降了 20% - 40%
  • 與 Prometheus 1.8 相比,磁盤空間使用率下降了 33% - 50%
  • 沒有太多查詢,平均負載的磁盤 I/O<1%

在Kubernetes集羣這樣的動態環境中,prometheus的數據平面一般看起來是這種樣式

  • 垂直維度表示全部存儲的序列
  • 水平維度表示樣本傳播的時間

如:

requests_total{path="/status", method="GET", instance="10.0.0.1:80"}
requests_total{path="/status", method="POST", instance="10.0.0.3:80"}
requests_total{path="/", method="GET", instance="10.0.0.2:80"}

Prometheus按期爲全部系列收集新數據點,這意味着它必須在時間軸的右端執行垂直寫入。可是,在查詢時,咱們可能但願訪問平面上任意區域的矩形(各類label條件)

所以爲了可以在大量數據中有效地查找查詢序列,咱們須要一個索引。

在Prometheus 1.x存儲層能夠很好地處理垂直寫入模式,可是隨着規模增大,索引或出現一些問題,所以在2.0版本中從新設計了存儲引擎和索引,主要改造是:

樣本壓縮

現有存儲層的樣本壓縮功能在Prometheus的早期版本中發揮了重要做用。單個原始數據點佔用16個字節的存儲空間。但當普羅米修斯每秒收集數十萬個數據點時,能夠快速填滿硬盤。

但,同一系列中的樣本每每很是類似,咱們能夠利用這一類樣品(一樣label)進行有效的壓縮。批量壓縮一系列的許多樣本的塊,在內存中,將每一個數據點壓縮到平均1.37字節的存儲。

這種壓縮方案運行良好,也保留在新版本2存儲層的設計中。具體壓縮算法能夠參考:Facebook的「Gorilla」論文中

時間分片

咱們將新的存儲層劃分爲塊(block),每一個塊在一段時間內保存全部序列。每一個塊充當獨立數據庫。

這樣每次查詢,僅檢查所請求的時間範圍內的塊子集,查詢執行時間天然會減小。

這種佈局也使刪除舊數據變得很是容易(這在1.x的存儲設計中是一個很耗時的操做)。但在2.x中,一旦塊的時間範圍徹底落後於配置的保留邊界,它就能夠徹底丟棄。

索引

通常prometheus的查詢是把metric+label作關鍵字的,並且是很寬泛,徹底用戶自定義的字符,所以沒辦法使用常規的sql數據庫,prometheus的存儲層使用了全文檢索中的倒排索引概念,將每一個時間序列視爲一個小文檔。而metric和label對應的是文檔中的單詞。

例如,requests_total{path="/status", method="GET", instance="10.0.0.1:80"}是包含如下單詞的文檔:

  • __name__="requests_total"
  • path="/status"
  • method="GET"
  • instance="10.0.0.1:80"

基準測試

cpu、內存、查詢效率都比1.x版本獲得了大幅度的提高

具體測試結果參考:https://dzone.com/articles/pr...

故障恢復

若是您懷疑數據庫中的損壞引發的問題,則能夠經過使用storage.local.dirtyflag配置,來啓動服務器來強制執行崩潰恢復。

若是沒有幫助,或者若是您只想刪除現有的數據庫,能夠經過刪除存儲目錄的內容輕鬆地啓動:

  • 1.中止服務:stop prometheus.
  • 2.刪除數據目錄:rm -r <storage path>/*
  • 3.啓動服務:start prometheus

遠程存儲

Prometheus默認是本身帶有存儲的,保存的時間爲15天。但本地存儲也意味着Prometheus沒法持久化數據,沒法存儲大量歷史數據,同時也沒法靈活擴展。
爲了保證Prometheus的簡單性,Prometheus並無從自身集羣的維度來解決這些問題,而是定義了兩種接口,remote_write/remote_read,將數據拋出去,你本身處理。

Prometheus的remote_storage 實際上是一個adapter,至於在adapter的另外一端是什麼類型的時序數據庫它根本不關心,若是你願意,你也能夠編寫本身的adpater。

如:存儲的方式爲:Prometheus —-發送數據—- > remote_storage_adapter —- 存儲數據 —-> influxdb。

prometheus經過下面兩種方式來實現與其餘的遠端存儲系統對接:

  • Prometheus 按照標準的格式將metrics寫到遠端存儲
  • Prometheus 按照標準格式從遠端的url來讀取metrics

遠程讀

在遠程讀的流程當中,當用戶發起查詢請求後,Promthues將向remote_read中配置的URL發起查詢請求(matchers,ranges),Adaptor根據請求條件從第三方存儲服務中獲取響應的數據。同時將數據轉換爲Promthues的原始樣本數據返回給Prometheus Server。

當獲取到樣本數據後,Promthues在本地使用PromQL對樣本數據進行二次處理。

遠程寫

用戶能夠在Promtheus配置文件中指定Remote Write(遠程寫)的URL地址,一旦設置了該配置項,Prometheus將樣本數據經過HTTP的形式發送給適配器(Adaptor)。而用戶則能夠在適配器中對接外部任意的服務。外部服務能夠是真正的存儲系統,公有云的存儲服務,也能夠是消息隊列等任意形式。

配置

配置很是簡單,只須要將對應的地址配置下就行

remote_write:
  - url: "http://localhost:9201/write"

remote_read:
  - url: "http://localhost:9201/read"

社區支持

如今社區已經實現瞭如下的遠程存儲方案

  • AppOptics: write
  • Chronix: write
  • Cortex: read and write
  • CrateDB: read and write
  • Elasticsearch: write
  • Gnocchi: write
  • Graphite: write
  • InfluxDB: read and write
  • OpenTSDB: write
  • PostgreSQL/TimescaleDB: read and write
  • SignalFx: write

可使用讀寫完整的InfluxDB,咱們使用了多prometheus server同時遠程讀+寫,驗證了速度仍是能夠的。而且InfluxDB生態完整,自帶了不少管理工具。

容量規劃

在通常狀況下,Prometheus中存儲的每個樣本大概佔用1-2字節大小。若是須要對Prometheus Server的本地磁盤空間作容量規劃時,能夠經過如下公式計算:

磁盤大小 = 保留時間 * 每秒獲取樣本數 * 樣本大小

保留時間(retention_time_seconds)和樣本大小(bytes_per_sample)不變的狀況下,若是想減小本地磁盤的容量需求,只能經過減小每秒獲取樣本數(ingested_samples_per_second)的方式。

所以有兩種手段,一是減小時間序列的數量,二是增長採集樣本的時間間隔。

考慮到Prometheus會對時間序列進行壓縮,所以減小時間序列的數量效果更明顯。

其餘

遠程讀寫解決了Promtheus的數據持久化問題。使其能夠進行彈性擴展。另外還支持聯邦集羣模式,用於解決橫向擴展、網絡分區的問題(如地域A+B+C的監控數據,統一彙總到D),聯邦集羣的配置將在後面的Promthues高可用文章中詳細說明。

附:kubecon2018上講Prometheus 2.0的帥哥

還有一本專門講Prometheus的書:Prometheus: Up & Running(600多頁...)

國內沒找到賣的,找到了一本英文pdf的,還在翻譯理解中,有新的內容會繼續同步在這個系列博客

。。。又找到一本:https://www.prometheusbook.com/

參考資料:

本文爲容器監控實踐系列文章,完整內容見:container-monitor-book

相關文章
相關標籤/搜索