如何用Prometheus監控十萬container的Kubernetes集羣

概述

不久前,咱們在文章《如何擴展單個Prometheus實現近萬Kubernetes集羣監控?》中詳細介紹了TKE團隊大規模Kubernetes聯邦監控系統Kvass的演進過程,其中介紹了針對規模較大的集羣,咱們是如何經過修改Prometheus代碼來實現橫向擴縮容的。通過方案上的改進,Kvass目前已經支持以Sidecar的方式實現Prometheus集羣化,而不是修改Prometheus代碼。因爲方案對社區有必定價值,團隊決定將項目開源出來,分享給社區。項目地址node

本文首先將給出Prometheus的單機性能瓶頸,以及現有的社區集羣化方案,隨後將詳細介紹開源版Kvass的設計思想,使用案例及壓測結果。linux

另外,騰訊雲容器團隊在Kvass的設計思想上進一步優化,構建了支持多集羣的高性能雲原生監控服務,產品目前已正式公測,歡迎讀者試用。傳送門: https://console.cloud.tencent...git

後續章節咱們也將直接使用該產品來展現Kvass對大規模集羣的監控能力。
1github

Prometheus

Prometheus依靠其強勁的單機性能,靈活的PromSQL,活躍的社區生態,逐漸成爲雲原生時代最核心的監控組件,被全球各大產商用於監控他們的核心業務。golang

然而,面對大規模監控目標(數千萬series)時,因爲原生Prometheus只有單機版本,不提供集羣化功能,開發人員不得不經過不斷增長機器的配置來知足Prometheus不斷上漲的內存。api

單機性能瓶頸

咱們對單機Prometheus進行的壓測,用以探測單個Prometheus分片的合理負載,壓測的目標有兩個。緩存

  • 肯定target數目對Prometheus負載的關係
  • 肯定series數目和Prometheus負載的關係

target相關性

咱們保持總series爲100萬不變, 經過改變target個數,觀察Prometheus負載變更。
2
壓測結果架構

target數量 CPU (core) mem (GB)
100 0.17 4.6
500 0.19 4.2
1000 0.16 3.9
5000 0.3 4.6
  • 從表中咱們發現target數目的改動對Prometheus負載的影響並非強相關的。在target數目增加50倍的狀況下,CPU消耗有小量增加,可是內存幾乎不變。

series相關性

咱們保持target數目不變,經過改變總series數,觀察Prometheus的負載變更。併發

3

壓測結果app

series數量 (萬) CPU (core) mem (GB) 查詢1000 series 15m數據(s)
100 0.191 3.15 0.2
300 0.939 20.14 1.6
500 2.026 30.57 1.5
  • 從表中,Prometheus的負載受到series的影響較大,series越多,資源消耗越大。
  • 當series數據超過300萬時,Prometheus內存增加較爲明顯,須要使用較大內存的機器來運行。
壓測過程當中,咱們使用了工具去生成預期數目的series,工具生成的series每一個label的長度及值的長度都較小,固定爲10個字符左右。咱們的目的是觀察相對負載變化,實際生產中因爲label長度不一樣,服務發現機制的消耗不一樣,相同的series數目所消耗的負載會比壓測中高很多。

現有集羣化方案

針對單機Prometheus在大規模數據監控時的性能瓶頸問題,社區目前已經存在一些分片化方案,主要包括如下幾種。

hash_mod

Prometheus官方支持經過Relabel機制,在配置文件中,對採集上來的數據進行hash,經過在不一樣Prometheus實例的配置文件中指定不一樣的moduleID來進行分片化,而後經過聯邦,Thanos等方式將數據進行統一彙總,以下圖所示,讀者也能夠直接參考【官方文檔】。
4

配置文件分割

還有一種方法是根據業務進行job層面的分割,不一樣Prometheus使用徹底獨立的採集配置,其中包含了不一樣的job,。
5

上述方案存在的問題

不管是hash_mod的方式,仍是配置文件分割的方式,其本質都是將數據切分到多個採集配置中,由不一樣Prometheus進行採集。二者都存在如下幾個缺點。

  • 對預監控數據要有所瞭解:使用上述方法的前提是使用者必須對監控對象會上報的數據有所瞭解,例如必須知道監控對象會上報某個用於hash_mod的label,或者必須知道不一樣job的總體規模,才能對job進行劃分。
  • 實例負載不均衡:雖然上述方案預期都是但願將數據打散到不一樣Prometheus實例上,但實際上經過某些label的值進行hash_mod的,或者乾脆按job進行劃分的方式並不能保證每一個實例最終所採集的series數是均衡的,實例依舊存在內存佔用太高的風險。
  • 配置文件有侵入:使用者必須對原配置文件進行改造,加入Relabel相關配置,或者將一份配置文件劃分紅多份,因爲配置文件再也不單一,新增,修改配置難度大大增長。
  • 沒法動態擴縮容:上述方案中的因爲配置是根據實際監控目標的數據規模來特殊制定的,並無一種統一的擴縮容方案,能夠在數據規模增加時增長Prometheus個數。固然,用戶若是針對本身業務實際狀況編寫擴縮容的工具確實是能夠的,可是這種方式並不能在不一樣業務間複用。
  • 部分API再也不正常:上述方案將數據打散到了不一樣實例中,而後經過聯邦或者Thanos進行彙總,獲得全局監控數據,可是在不額外處理的狀況下會致使部分Prometheus 原生API沒法獲得正確的值,最典型的是/api/v1/targets ,上述方案下沒法獲得全局targets值。

Kvass的原理

設計目標

針對上述問題,咱們但願設計一種無侵入的集羣化方案,它對使用者表現出來的,是一個與原生Prometheus配置文件一致,API兼容,可擴縮容的虛擬Prometheus。具體而言,咱們有如下設計目標。

  • 無侵入,單配置文件:咱們但願使用者看到的,修改的都是一份原生的配置文件,不用加任何特殊的配置。
  • 無需感知監控對象:咱們但願使用者再也不須要預先了解採集對象,不參與集羣化的過程。
  • 實例負載儘量均衡:咱們但願能根據監控目標的實際負載來劃分採集任務,讓實例儘量均衡。
  • 動態擴縮容:咱們但願系統可以根據採集對象規模的變化進行動態擴縮容,過程當中數據不斷點,不缺失。
  • 兼容核心PrometheusAPI:咱們但願一些較爲核心的API,如上邊提到的/api/v1/target接口是正常的。

架構

Kvass由多個組件構成,下圖給出了Kvass的架構圖,咱們在架構圖中使用了Thanos,實際上Kvass並不強依賴於Thanos,能夠換成其餘TSDB。

  • Kvass sidecar: 用於接收Coordinator下發的採集任務,生成新的配置文件給Prometheus,也服務維護target負載狀況。
  • Kvass coordinator: 該組件是集羣的中心控制器,負責服務發現,負載探測,targets下發等。
  • Thanos 組件: 圖中只使用了Thanos sidecar與Thanos query,用於對分片的數據進行彙總,獲得統一的數據視圖。

Coordinator

Kvass coordinaor 首先會代替Prometheus對採集目標作服務發現,實時得到須要採集的target列表。

針對這些target,Kvass coordinaor會負責對其作負載探測,評估每一個target的series數,一旦target負載被探測成功,Kvass coordinaor 就會在下個計算週期將target分配給某個負載在閾值如下的分片。

Kvass coordinaor 還負責對分片集羣作擴縮容。
7

服務發現

Kvass coordinaor引用了原生Prometheus的服務發現代碼,用於實現與Prometheus 100%兼容的服務發現能力,針對服務發現獲得的待抓取targets,Coordinaor會對其應用配置文件中的relabel_configs進行處理,獲得處理以後的targets及其label集合。服務發現後獲得的target被送往負載探測模塊進行負載探測。

負載探測

負載探測模塊從服務發現模塊得到處理以後的targets,結合配置文件中的抓取配置(如proxy,證書等)對目標進行抓取,隨後解析計算抓取結果,得到target的series規模。

負載探測模塊並不存儲任何抓取到的指標數據,只記錄target的負載,負載探測只對target探測一次,不維護後續target的負載變化,長期運行的target的負載信息由Sidecar維護,咱們將在後面章節介紹。

target分配與擴容

在Prometheus單機性能瓶頸那一節,咱們介紹過Prometheus的內存和series相關,確切來講,Prometheus的內存和其head series直接相關。Prometheus 會將最近(默認爲2小時)採集到的數據的series信息緩存在內存中,咱們若是能控制好每一個分片內存中head series的數目,就能有效控制每一個分片的內存使用量,而控制head series實際就是控制分片當前採集的target列表。

基於上邊的思路,Kvass coordinaor會週期性的對每一個分片當前採集的target列表進行管理:分配新target,刪除無效target。

在每一個週期,Coordinaor會首先從全部分片得到當前運行狀態,其中包括分片當前內存中的series數目及當前正在抓取的target列表。隨後針對從服務發現模塊獲得的全局target信息進行如下處理

  • 若是該target已經被某個分片抓取,則繼續分配給他,分片的series數不變。
  • 若是該target沒有任何分片抓取,則從負載探測模塊得到其series(若是還未探測完則跳過,下個週期繼續),從分片中挑一個目前內存中series加上該target的series後依然比閾值低的,分配給他。
  • 若是當前全部分片無法容納全部待分配的targets,則進行擴容,擴容數量與全局series總量成正比。

target遷移和縮容

在系統運行過程當中,target有可能會被刪除,若是某個分片的target被刪除且超過2小時,則該分片中的head series就會下降,也就是出現了部分空閒,由於target分配到了不一樣分片,若是有大量target被刪除,則會出現不少分片的內存佔用都很低的狀況,這種狀況下,系統的資源利用率很低,咱們須要對系統進行縮容。

當出現這種情時,Coordinaor會對target進行遷移,即將序號更大的分片(分片會從0進行編號)中的target轉移到序號更低的分片中,最終讓序號低的分片負載變高,讓序號高的分片徹底空閒出來。若是存儲使用了thanos,並會將數據存儲到cos中,則空閒分片在通過2小時候會刪除(確保數據已被傳到cos中)。

多副本

Kvass的分片當前只支持以StatefulSet方式部署。

Coordinator將經過label selector來得到全部分片StatefulSet,每一個StatefulSet被認爲是一個副本,StatefulSet中編號相同的Pod會被認爲是同一個分片組,相同分片組的Pod將被分配相同的target並預期有相同的負載。
8

/api/v1/targets接口

上文提到Coordinator根據配置文件作了服務發現,獲得了target列表,因此Coordinator實際上能夠獲得/api/v1/targets接口所須要的返回結果集合,可是因爲Coordinator只作了服務發現,並不進行實際採集,因此target的採集狀態(例如健康狀態,上一次採集時間等)都沒法直接得知。

當Coordinator接收到/api/v1/targets請求時,他會基於服務發現獲得的target集合,結合向Sidecar(若是target已分配)或向探測模塊(target還未分配)詢問target採集狀態,綜合後將正確的/api/v1/targets結果返回。

Sidecar

上一節介紹了Kvass coordinaor的基本功能,要想系統正常運行,還須要Kvass sidecar的配合,其核心思想是將配置文件中全部服務發現模式所有改爲static_configs並直接將已經relabel過的target信息寫入配置中,來達到消除分片服務發現和relabel行爲,只採集部分target的效果。
9

每一個分片都會有一個Kvass sidecar,其核心功能包括從Kvass coordinator接受本分片負責的target列表,生成新的配置文件給該分片的Prometheus使用。另外,Kvass sidecar還會劫持抓取請求,維護target最新負載。Kvass sidecar還做爲PrometheusAPI的網關,修正部分請求結果。
10

配置文件生成

Coordinaor通過服務發現,relabel及負載探測後,會將target分配給某個分片,並將target信息下發給Sidecar,包括

  • target的地址,
  • target預估的series值
  • target的hash值
  • 處理完relabel以後的label集合。

Sidecar根據從Coordinator獲得的target信息,結合原始配置文件,生成一個新的配置文件給Prometheus使用,這個新的配置文件作了以下改動。

  • 將全部服務發現機制改成static_configs模式,並直接寫入target列表,每一個target包含通過relabel以後的label值
  • 因爲如今target已經relabel過了,因此刪除job配置中的relabel_configs項,可是依舊保留metrics_rebale_configs
  • 將target的label中的scheme字段所有替換成http,並將原schme以請求參數的形式加入到label集合中
  • 將target的job_name以請求參數的形式加入到label集合中* 注入proxy_url將全部抓取請求代理到Sidecar

11

咱們來看一個例子,假如原來的配置是一個kubelet的採集配置

global:
  evaluation_interval: 30s
  scrape_interval: 15s
scrape_configs:
- job_name: kubelet
  honor_timestamps: true
  metrics_path: /metrics
  scheme: https
  kubernetes_sd_configs:
  - role: node
  bearer_token: xxx
  tls_config:
    insecure_skip_verify: true
  relabel_configs:
  - separator: ;
    regex: __meta_kubernetes_node_label_(.+)
    replacement: $1
    action: labelmap

經過注入將生成一個新的配置文件

global:
  evaluation_interval: 30s
  scrape_interval: 15s
scrape_configs:
- job_name: kubelet                                        
  honor_timestamps: true                                                                      
  metrics_path: /metrics                                   
  scheme: https  
  proxy_url: http://127.0.0.1:8008 # 全部抓取請求代理到Sidecar
  static_configs:                                          
  - targets:                                               
    - 111.111.111.111:10250                                   
    labels:                                                
      __address__: 111.111.111.111:10250                      
      __metrics_path__: /metrics                           
      __param__hash: "15696628886240206341"                
      __param__jobName: kubelet                            
      __param__scheme: https  # 保存原始的scheme                             
      __scheme__: http        # 設置新的scheme,這將使得代理到Sidecar的抓取請求都是http請求
      # 如下是通過relabel_configs處理以後獲得的label集合
      beta_kubernetes_io_arch: amd64                       
      beta_kubernetes_io_instance_type: QCLOUD             
      beta_kubernetes_io_os: linux                         
      cloud_tencent_com_auto_scaling_group_id: asg-b4pwdxq5
      cloud_tencent_com_node_instance_id: ins-q0toknxf     
      failure_domain_beta_kubernetes_io_region: sh         
      failure_domain_beta_kubernetes_io_zone: "200003"     
      instance: 172.18.1.106                               
      job: kubelet                                         
      kubernetes_io_arch: amd64                            
      kubernetes_io_hostname: 172.18.1.106                 
      kubernetes_io_os: linux

上邊新生成的配置文件是Prometheus真正使用的配置文件,Sidecar經過Coordinator下發的target列表來生成配置,就可讓Prometheus有選擇性得進行採集。

抓取劫持

在上邊的配置生成中,咱們會將proxy注入到job的配置中,而且target的label中,scheme會被設置成http,因此Prometheus全部的抓取請求都會被代理到Sidecar,之因此要這麼作,是由於Sidecar須要維護每一個target新的series規模,用於Coordinator查閱後做爲target遷移的參考。

從上邊配置生成咱們能夠看到,有如下幾個額外的請求參數會被一併發送到Sidecar

  • hash:target的hash值,用於Sidecar識別是哪一個target的抓取請求,hash值由Coordinator根據target的label集合進行計算得到並傳遞給Sidecar。
  • jobName:是哪一個job下的抓取請求,用於Sidecar根據原配置文件中job的請求配置(如原proxy_url,證書等)對抓取目標發起真正的請求。
  • scheme:這裏的scheme是target經過relabel操做以後最終獲得的協議值,雖然在job配置文件中已經有scheme字段,但Prometheus配置文件依舊支持經過relabel指定某個target的請求協議。在上述生成新配置過程當中,咱們將真實的scheme保存到這個參數裏,而後將scheme所有設置成http。

有了上述幾個參數,Sidecar就能夠對抓取目標發起正確的請求,並獲得監控數據,在統計的target此次抓取的series規模後,Sidecar會將監控數據拷貝一份給Prometheus。

API代理

因爲Sidecar的存在,部分發往Prometheus的API請求須要被特殊處理,包括

  • /-/reload:因爲Prometheus真正使用的配置文件由Sidecar生成,針對該接口,須要由Sidecar去處理並在處理成功後調用Prometheus的/-/reload接口。
  • /api/v1/status/config:該接口須要由Sidecar處理並把原配置文件返回。
  • 其餘接口直接發往Prometheus。

全局數據視圖

因爲咱們將採集目標分散到了不一樣分片中,致使每一個分片的數據都只是全局數據的一部分,因此咱們須要使用額外的組件來將全部數據進行彙總並去重(多副本的狀況下),獲得全局數據視圖。

以thanos爲例

thanos是一個很是好的方案,經過加入thanos組件,能夠很方便得獲得kvass集羣的全局數據視圖。固然咱們也能夠經過加入remote writer配置來使用其餘TSDB方案,例如influxdb,M3等等。

使用例子

這一節咱們經過一個部署例子,來直觀感覺一下Kvass的效果,相關yaml文件能夠在這裏找到https://github.com/tkestack/k...
讀者能夠將項目clone到本地,並進入examples。

git clone https://github.com/tkestack/kvass.git
cd kvass/examples

部署數據生成器

咱們提供了一個metrics數據生成器,能夠指定生成必定數量的series,在本例子中,咱們將部署6個metrics生成器副本,每一個會生成10045 series (其中45 series爲golang的metrics)。

kubectl create -f  metrics.yaml

部署kvass

如今咱們部署基於Kvass的Prometheus集羣,用以採集這6個metrics生成器的指標。

首先咱們部署rbac相關配置

kubectl create -f kvass-rbac.yaml

接着部署一個Prometheus config文件,這個文件就是咱們的原始配置,咱們在這個配置文件中,使用kubernetes_sd來作服務發現

kubectl create -f config.yaml

配置以下

global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: custom
scrape_configs:
- job_name: 'metrics-test'
  kubernetes_sd_configs:
    - role: pod
  relabel_configs:
  - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
    regex: metrics
    action: keep
  - source_labels: [__meta_kubernetes_pod_ip]
    action: replace
    regex: (.*)
    replacement: ${1}:9091
    target_label: __address__
  - source_labels:
    - __meta_kubernetes_pod_name
    target_label: pod

如今咱們來部署Kvass coordinator

kubectl create -f coordinator.yaml

咱們在Coordinator的啓動參數中設置每一個分片的最大head series數目不超過30000

--shard.max-series=30000

咱們如今就能夠部署帶有Kvass sidecar的Prometheus了,這裏咱們只部署單個副本

kubectl create -f prometheus-rep-0.yaml

部署thanos-query

爲了獲得全局數據,咱們須要部署一個thanos-query

kubectl create -f thanos-query.yaml

查看結果

根據上述計算,監控目標總計6個target, 60270 series,根據咱們設置每一個分片不能超過30000 series,則預期須要3個分片。

咱們發現,Coordinator成功將StatefulSet的副本數改爲了3。
14

咱們看下單個分片內存中的series數目,發現只有2個target的量
15

咱們再經過thanos-query來查看全局數據,發現數據是完整的(其中metrics0爲指標生成器生成的指標名)
16

17

雲原生監控

騰訊雲容器團隊在Kvass的設計思想上進一步優化,構建了高性能支持多集羣雲原生監控服務,產品目前已正式公測。

大集羣監控

這一節咱們就直接使用雲原生監控服務來監控一個規模較大的真實集羣,測試一下Kvass監控大集羣的能力。
18

集羣規模

咱們關聯的集羣規模大體以下

  • 1060個節點
  • 64000+ Pod
  • 96000+ container

採集配置

咱們直接使用雲原生監控服務在關聯集羣默認添加的採集配置,目前已包含了社區主流的監控指標:

  • kube-state-metrics
  • node-exporer
  • kubelet
  • cadvisor
  • kube-apiserver
  • kube-scheduler
  • kube-controler-manager

19

20

測試結果

21

  • 總計3400+target, 2700+萬series
  • 總計擴容了17個分片
  • 每一個分片series穩定在200w如下
  • 每一個分片消耗內存在6-10G左右

雲原生監控所提供的默認Grafana面板也能正常拉取
22

targets列表也能正常拉取
23

多集羣監控

值得一提的是,雲原生監控服務不只支持監控單個大規模集羣,還能夠用同個實例監控多個集羣,並支持採集和告警模板功能,可一鍵將採集告警模板下發至各地域各個集羣,完全告別了每一個集羣重複添加配置的問題。
24

總結

本文從問題分析,設計目標,原理剖析,使用案例等方面詳細介紹了一種開源Prometheus集羣化技術,可在不修改Prometheus代碼的前提下使其支持橫向擴縮容,從而監控單機Prometheus沒法監控的大規模集羣。

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!
相關文章
相關標籤/搜索