大家好,我是 roc,來自騰訊雲容器服務(TKE)團隊,這次爲大家帶來實用的打造雲原生大型分佈式監控系統系列文章,請笑納。

No.1  概述

Prometheus 幾乎已成爲監控領域的事實標準,它自帶高效的時序數據庫存儲,可以讓單臺 Prometheus 能夠高效的處理大量的數據,還有友好並且強大的 PromQL 語法,可以用來靈活的查詢各種監控數據以及配置告警規則,同時它的 pull 模型指標採集方式被廣泛採納,非常多的應用都實現了 Prometheus 的 metrics 接口以暴露自身各項數據指標讓 Prometheus 去採集,很多沒有適配的應用也會有第三方 exporter 幫它去適配 Prometheus,所以監控系統我們通常首選用 Prometheus,本系列文章也將基於 Prometheus 來打造雲原生環境下的大型分佈式監控系統。

No.2  大規模場景下 Prometheus 的痛點

Prometheus 本身只支持單機部署,沒有自帶支持集羣部署,也就不支持高可用以及水平擴容,在大規模場景下,最讓人關心的問題是它的存儲空間也受限於單機磁盤容量,磁盤容量決定了單個 Prometheus 所能存儲的數據量,數據量大小又取決於被採集服務的指標數量、服務數量、採集速率以及數據過期時間。在數據量大的情況下,我們可能就需要做很多取捨,比如丟棄不重要的指標、降低採集速率、設置較短的數據過期時間(默認只保留15天的數據,看不到比較久遠的監控數據)。

這些痛點實際也是可以通過一些優化手段來改善的,下面我們來細講一下。

No.3  從服務維度拆分 Prometheus

Prometheus 主張根據功能或服務維度進行拆分,即如果要採集的服務比較多,一個 Prometheus 實例就配置成僅採集和存儲某一個或某一部分服務的指標,這樣根據要採集的服務將 Prometheus 拆分成多個實例分別去採集,也能一定程度上達到水平擴容的目的。

通常這樣的擴容方式已經能滿足大部分場景的需求了,畢竟單機 Prometheus 就能採集和處理很多數據了,很少有 Prometheus 撐不住單個服務的場景。不過在超大規模集羣下,有些單個服務的體量也很大,就需要進一步拆分了,我們下面來繼續講下如何再拆分。

No.4  對超大規模的服務做分片

想象一下,如果集羣節點數量達到上千甚至幾千的規模,對於一些節點級服務暴露的指標,比如 kubelet 內置的 cadvisor 暴露的容器相關的指標,又或者部署的 DeamonSet node-exporter 暴露的節點相關的指標,在集羣規模大的情況下,它們這種單個服務背後的指標數據體量就非常大;還有一些用戶量超大的業務,單個服務的 pod 副本數就可能過千,這種服務背後的指標數據也非常大,當然這是最罕見的場景,對於絕大多數的人來說這種場景都只敢 YY 一下,實際很少有單個服務就達到這麼大規模的業務。

針對上面這些大規模場景,一個 Prometheus 實例可能連這單個服務的採集任務都扛不住。Prometheus 需要向這個服務所有後端實例發請求採集數據,由於後端實例數量規模太大,採集併發量就會很高,一方面對節點的帶寬、CPU、磁盤 IO 都有一定的壓力,另一方面 Prometheus 使用的磁盤空間有限,採集的數據量過大很容易就將磁盤塞滿了,通常要做一些取捨才能將數據量控制在一定範圍,但這種取捨也會降低數據完整和精確程度,不推薦這樣做。

那麼如何優化呢?我們可以給這種大規模類型的服務做一下分片(Sharding),將其拆分成多個 group,讓一個 Prometheus 實例僅採集這個服務背後的某一個 group 的數據,這樣就可以將這個大體量服務的監控數據拆分到多個 Prometheus 實例上。

如何將一個服務拆成多個 group 呢?下面介紹兩種方案,以對 kubelet cadvisor 數據做分片爲例。

第一,我們可以不用 Kubernetes 的服務發現,自行實現一下 sharding 算法,比如針對節點級的服務,可以將某個節點 shard 到某個 group 裏,然後再將其註冊到 Prometheus 所支持的服務發現註冊中心,推薦 consul,最後在 Prometheus 配置文件加上 consul_sd_config 的配置,指定每個 Prometheus 實例要採集的 group。

- job_name: 'cadvisor-1'

consul_sd_configs:

- server: 10.0.0.3:8500

services:

- cadvisor-1 # This is the 2nd slave

在未來,你甚至可以直接利用 Kubernetes 的 EndpointSlice 特性來做服務發現和分片處理,在超大規模服務場景下就可以不需要其它的服務發現和分片機制。不過暫時此特性還不夠成熟,沒有默認啓用,不推薦用(當前 Kubernentes 最新版本爲 1.18)。

第二,用 Kubernetes 的 node 服務發現,再利用 Prometheus relabel 配置的 hashmod 來對 node 做分片,每個 Prometheus 實例僅抓其中一個分片中的數據:

- job_name: 'cadvisor-1'

metrics_path: /metrics/cadvisor

scheme: https


# 請求 kubelet metrics 接口也需要認證和授權,通常會用 webhook 方式讓 apiserver 代理進行 RBAC 校驗,所以還是用 ServiceAccount 的 token

bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token


kubernetes_sd_configs:

- role: node


# 通常不校驗 kubelet 的 server 證書,避免報 x509: certificate signed by unknown authority

tls_config:

insecure_skip_verify: true


relabel_configs:

- source_labels: [__address__]

modulus: 4 # 將節點分片成 4 個 group

target_label: __tmp_hash

action: hashmod

- source_labels: [__tmp_hash]

regex: ^1$ # 只抓第 2 個 group 中節點的數據(序號 0 爲第 1 個 group)

action: keep

No.5   拆分引入的新問題

前面我們通過不通層面對 Prometheus 進行了拆分部署,一方面使得 Prometheus 能夠實現水平擴容,另一方面也加劇了監控數據落盤的分散程度,使用 Grafana 查詢監控數據時我們也需要添加許多數據源,而且不同數據源之間的數據還不能聚合查詢,監控頁面也看不到全局的視圖,造成查詢混亂的局面。

要解決這個問題,我們可以從下面的兩方面入手,任選其中一種方案。

No.6 .  集中數據存儲

我們可以讓 Prometheus 不負責存儲,僅採集數據並通過 remote write 方式寫入遠程存儲的 adapter,遠程存儲使用 OpenTSDB 或 InfluxDB 這些支持集羣部署的時序數據庫,Prometheus 配置:

remote_write:

- url: http://10.0.0.2:8888/write

然後 Grafana 添加我們使用的時序數據庫作爲數據源來查詢監控數據來展示,架構圖:

這種方式相當於更換了存儲引擎,由其它支持存儲水平擴容的時序數據庫來存儲龐大的數據量,這樣我們就可以將數據集中到一起。OpenTSDB 支持 HBase, BigTable 作爲存儲後端,InfluxDB 企業版支持集羣部署和水平擴容(開源版不支持)。不過這樣的話,我們就無法使用友好且強大的 PromQL 來查詢監控數據了,必須使用我們存儲數據的時序數據庫所支持的語法來查詢。

No.7 .  Prometheus 聯邦

除了上面更換存儲引擎的方式,還可以將 Prometheus 進行聯邦部署。

簡單來說,就是將多個 Prometheus 實例採集的數據再用另一個 Prometheus 採集彙總到一起,這樣也意味着需要消耗更多的資源。通常我們只把需要聚合的數據或者需要在一個地方展示的數據用這種方式採集彙總到一起,比如 Kubernetes 節點數過多,cadvisor 的數據分散在多個 Prometheus 實例上,我們就可以用這種方式將 cadvisor 暴露的容器指標彙總起來,以便於在一個地方就能查詢到集羣中任意一個容器的監控數據或者某個服務背後所有容器的監控數據的聚合彙總以及配置告警;又或者多個服務有關聯,比如通常應用只暴露了它應用相關的指標,但它的資源使用情況(比如 cpu 和 內存) 由 cadvisor 來感知和暴露,這兩部分指標由不同的 Prometheus 實例所採集,這時我們也可以用這種方式將數據彙總,在一個地方展示和配置告警。

更多說明和配置示例請參考官方文檔:  https://prometheus.io/docs/prometheus/latest/federation/

No.8   Prometheus 高可用

雖然上面我們通過一些列操作將 Prometheus 進行了分佈式改造,但並沒有解決 Prometheus 本身的高可用問題,即如果其中一個實例掛了,數據的查詢和完整性都將受到影響。

我們可以將所有 Prometheus 實例都使用兩個相同副本,分別掛載數據盤,它們都採集相同的服務,所以它們的數據是一致的,查詢它們之中任意一個都可以,所以可以在它們前面再掛一層負載均衡(比如 Nginx 或 HAProxy) ,所有查詢都先經過這個負載均衡再到其中一臺 Prometheus,如果其中一臺掛掉就從負載列表裏踢掉不再轉發。

這樣就實現了 Prometheus 的高可用,簡單起見,上面的圖僅展示單個 Prometheus 的高可用,當你可以將其拓展,代入應用到上面其它的優化手段中,實現整體的高可用。

No.9  總結

通過本文一系列對 Prometheus 的優化手段,我們在一定程度上解決了單機 Prometheus 在大規模場景下的痛點,但操作和運維複雜度比較高,並且不能夠很好的支持數據的長期存儲(long term storage)。對於一些時間比較久遠的監控數據,我們通常查看的頻率很低,但也希望能夠低成本的保留足夠長的時間,數據如果全部落盤到磁盤成本是很高的,並且容量有限,即便利用水平擴容可以增加存儲容量,但同時也增大了資源成本,不可能無限擴容,所以需要設置一個數據過期策略,也就會丟失時間比較久遠的監控數據。

對於這種不常用的冷數據,最理想的方式就是存到廉價的對象存儲中,等需要查詢的時候能夠自動加載出來。Thanos 可以幫我們解決這些問題,它完全兼容 Prometheus API,提供統一查詢聚合分佈式部署的 Prometheus 數據的能力,同時也支持數據長期存儲到各種對象存儲(無限存儲能力)以及降低採樣率來加速大時間範圍的數據查詢。

下一篇我們將會介紹 Thanos 的架構詳解,敬請期待。

END

Kubernetes  線上直播班

相關文章