打造雲原生大型分散式監控系統 (一): 大規模場景下 Prometheus 的優化手段

imroc-github發表於2020-04-06

概述

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

大規模場景下 Prometheus 的痛點

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

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

從服務維度拆分 Prometheus

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

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

對超大規模的服務做分片

想象一下,如果叢集節點數量達到上千甚至幾千的規模,對於一些節點級服務暴露的指標,比如 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

拆分引入的新問題

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

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

集中資料儲存

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

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

然後 Grafana 新增我們使用的時序資料庫作為資料來源來查詢監控資料來展示,架構圖:

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

Prometheus 聯邦

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

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

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

Prometheus 高可用

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

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

這裡的負載均衡可以根據實際環境選擇合適的方案,可以用 Nginx 或 HAProxy,在 Kubernetes 環境,通常使用 Kubernentes 的 Service,由 kube-proxy 生成的 iptables/ipvs 規則轉發,如果使用 Istio,還可以用 VirtualService,由 envoy sidecar 去轉發。

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

總結

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

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

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

更多原創文章乾貨分享,請關注公眾號
  • 打造雲原生大型分散式監控系統 (一): 大規模場景下 Prometheus 的優化手段
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章