Prometheus服務發現之kubernetes_sd_config

2023-03-29 06:02:13

一、為什麼要使用Prometheus服務發現

之前我們講過通過設定prometheus-operator的CRD ServiceMonitor來達到K8S叢集相關元件和微服務的監控的目的,可以在ServiceMonitor的組態檔中以手動方式通過match lable和想要監控的Service進行匹配(這裡相當於是手動進行服務註冊和服務發現的作用,也可以將這種模式稱為靜態服務發現),以此來完成對想要監控的服務和元件進行監控,但這種方式進行監控設定,只能手工一個一個的增加,如果在k8s叢集規模較大的情況下,或者是叢集後面又增加了節點或者元件資訊,這種方式就會很麻煩也不現實,於是引出了今天的主題-Prometheus動態服務發現機制,下面來讓我們瞭解一下Prometheus是如何動態實現服務發現的。

二、什麼是Prometheus服務發現

從上面的介紹大家已經知道,prometheus獲取資料來源target的方式主要有兩種模式,一種是靜態設定,一種是動態服務發現設定,promethues的靜態服務發現static_configs或者是ServiceMonitor 通過標籤匹配Service:每當有一個新的目標範例需要監控,都需要手動修改組態檔設定目標target或者修改ServiceMonitor CRD組態檔.
那麼面對現如今動不動就成百上千臺節點的叢集來說,靜態服務發現這種純手動設定很顯然是不切實際的,然而現在的叢集往往都有一個很重要的功能叫做服務發現,例如在現在常用的微服務SpringCloud架構的中,Eureka元件作為服務註冊和發現的中心,在Kubernetes這類容器管理平臺中,Kubernetes也具有服務發現的功能,它們都掌握並管理著所有的容器或者是服務的相關資訊,於是Prometheus通過這個中間的代理人(服務發現和註冊中心)來獲取叢集當中監控目標的資訊,從而巧妙地實現了prometheus的動態服務發現。

三、Prometheus常用的集幾種動態服務發現

prometheus目前支援的動態服務發現有很多種,常用的主要分為以下幾種:
1. promethues基於k8s的服務發現kubernetes_sd_configs
2. promethues基於consul的服務發現consul_sd_config
3. promethues基於Eureka的服務發現eureka_sd_config
還有基於DNS等等的就不一一列舉。
下面主要講解promethues基於的k8s服務發現kubernetes_sd_configs

四、詳解Prometheus服務發現之kubernetes_sd_configs

目前,在Kubernetes下,Prometheus 通過與 Kubernetes API 整合主要支援5種服務發現模式又叫角色role:Node、Service、Pod、Endpoints、Ingress。不同的服務發現模式適用於不同的場景,例如:node適用於與主機相關的監控資源,如節點中執行的Kubernetes 元件狀態、節點上執行的容器狀態等;service 和 ingress 適用於通過黑盒監控的場景,如對服務的可用性以及服務質量的監控;endpoints 和 pod 均可用於獲取 Pod 範例的監控資料,如監控使用者或者管理員部署的支援 Prometheus 的應用。

下面貼出在Prometheus官網對這五種role的詳細說明:

1. node
node角色可以發現叢集中每個node節點的地址埠,預設為Kubelet的HTTP埠。目標地址預設為Kubernetes節點物件的第一個現有地址,地址型別順序為NodeInternalIP、NodeExternalIP、NodeLegacyHostIP和NodeHostName。

作用:監控K8S的node節點的伺服器相關的指標資料。


2. service
service角色可以發現每個service的ip和port,將其作為target。這對於黑盒監控(blackbox)很有用。


3. pod
pod角色可以發現所有pod並將其中的pod ip作為target。如果有多個埠或者多個容器,將生成多個target(例如:80,443這兩個埠,pod ip為10.0.244.22,則將10.0.244.22:80,10.0.244.22:443分別作為抓取的target)。
如果容器沒有指定的埠,則會為每個容器建立一個無埠target,以便通過relabel手動新增埠。


4. endpoints
endpoints角色可以從ep(endpoints)列表中發現所有targets。


- 如果ep是屬於service的話,則會附加service角色的所有標籤
- 對於ep的後端節點是pod,則會附加pod角色的所有標籤(即上邊介紹的pod角色可用標籤)
比如我麼手動建立一個ep,這個ep關聯到一個pod,則prometheus的標籤中會包含這個pod角色的所有標籤

5. ingress
ingress角色發現ingress的每個路徑的target。這通常對黑盒監控很有用。該地址將設定為ingress中指定的host。

Prometheus-additional.yaml組態檔規則詳解

為解決服務發現的問題,kube-prometheus 為我們提供了一個額外的抓取設定來解決這個問題,我們可以通過新增額外的設定來進行服務發現進行自動監控。我們可以在 kube-prometheus 當中去自動發現並監控具有 prometheus.io/scrape=true 這個 annotations 的 Service。
其中通過 kubernetes_sd_configs 支援監控其各種資源。kubernetes SD 設定允許從 kubernetes REST API 接受蒐集指標,且總是和叢集保持同步狀態,任何一種 role 型別都能夠設定來發現我們想要的物件。

規則設定使用 yaml 格式,下面是檔案中一級設定項。自動發現 k8s Metrics 介面是通過 scrape_configs 來實現的:

#全域性設定
global:

#規則設定主要是設定報警規則
rule_files:

#抓取設定,主要設定抓取使用者端相關
scrape_configs:

#報警設定
alerting:

#用於遠端儲存寫設定
remote_write:

#用於遠端讀設定
remote_read:

舉例說明:

# Kubernetes的API SERVER會暴露API服務,Promethues整合了對Kubernetes的自動發現,它有5種模式:Node、Service
# 、Pod、Endpoints、ingress,下面是Prometheus官方給出的對Kubernetes服務發現的範例。這裡你會看到大量的relabel_configs,
# 其實你就是把所有的relabel_configs去掉一樣可以對kubernetes做服務發現。relabel_configs僅僅是對採集過來的指標做二次處理,比如
# 要什麼不要什麼以及替換什麼等等。而以__meta_開頭的這些後設資料標籤都是範例中包含的,而relabel則是動態的修改、覆蓋、新增刪除這些標籤
# 或者這些標籤對應的值。而且以__開頭的標籤通常是系統內部使用的,因此這些標籤不會被寫入樣本資料中,如果我們要收集這些東西那麼則要進行
# relabel操作。當然reabel操作也不僅限於操作__開頭的標籤。
#
# action的行為:
# replace:預設行為,不設定action的話就採用這種行為,它會根據regex來去匹配source_labels標籤上的值,並將並將匹配到的值寫入target_label中
# labelmap:它會根據regex去匹配標籤名稱,並將匹配到的內容作為新標籤的名稱,其值作為新標籤的值
# keep:僅收集匹配到regex的源標籤,而會丟棄沒有匹配到的所有標籤,用於選擇
# drop:丟棄匹配到regex的源標籤,而會收集沒有匹配到的所有標籤,用於排除
# labeldrop:使用regex匹配標籤,符合regex規則的標籤將從target範例中移除,其實也就是不收集不儲存
# labelkeep:使用regex匹配標籤,僅收集符合regex規則的標籤,不符合的不收集

global:
  # 間隔時間
  scrape_interval: 30s
  # 超時時間
  scrape_timeout: 10s
  # 另一個獨立的規則週期,對告警規則做定期計算
  evaluation_interval: 30s
  # 外部系統標籤
  external_labels:
	prometheus: monitoring/k8s
	prometheus_replica: prometheus-k8s-1

# 抓取伺服器端點,整個這個任務都是用來發現node-exporter和kube-state-metrics-service的,這裡用的是endpoints角色,這是通過這兩者的service來發現
# 的後端endpoints。另外需要說明的是如果滿足採集條件,那麼在service、POD中定義的labels也會被採集進去
scrape_configs: 
  # 定義job名稱,是一個拉取單元 
- job_name: "kubernetes-endpoints"
  # 發現endpoints,它是從列出的伺服器端點發現目標,這個endpoints來自於Kubernetes中的service,每一個service都有對應的endpoints,這裡是一個列表
  # 可以是一個IP:PORT也可以是多個,這些IP:PORT就是service通過標籤選擇器選擇的POD的IP和埠。所以endpoints角色就是用來發現server對應的pod的IP的
  # kubernetes會有一個預設的service,通過找到這個service的endpoints就找到了api server的IP:PORT,那endpoints有很多,我怎麼知道哪個是api server呢
  # 這個就靠source_labels指定的標籤名稱了。
  kubernetes_sd_configs:
	# 角色為 endpoints
	- role: endpoints

  relabel_configs:
	# 重新打標僅抓取到的具有 "prometheus.io/scrape: true" 的annotation的端點,意思是說如果某個service具有prometheus.io/scrape = true annotation宣告則抓取
 # annotation本身也是鍵值結構,所以這裡的源標籤設定為鍵,而regex設定值,當值匹配到regex設定的內容時則執行keep動作也就是保留,其餘則丟棄.
 # node-exporter這個POD的service裡面就有一個叫做prometheus.io/scrape = true的annotations所以就找到了node-exporter這個POD
	- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
	  # 動作 刪除 regex 與串聯不匹配的目標 source_labels
	  action: keep
	  # 通過正式表示式匹配 true
	  regex: true
	# 重新設定scheme
 # 匹配源標籤__meta_kubernetes_service_annotation_prometheus_io_scheme也就是prometheus.io/scheme annotation
 # 如果源標籤的值匹配到regex則把值替換為__scheme__對應的值
	- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
	  action: replace
	  target_label: __scheme__
	  regex: (https?)
	# 匹配來自 pod annotationname prometheus.io/path 欄位
	- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
	  # 獲取POD的 annotation 中定義的"prometheus.io/path: XXX"定義的值,這個值就是你的程式暴露符合prometheus規範的metrics的地址
   # 如果你的metrics的地址不是 /metrics 的話,通過這個標籤說,那麼這裡就會把這個值賦值給 __metrics_path__這個變數,因為prometheus
	  # 是通過這個變數獲取路徑然後進行拼接出來一個完整的URL,並通過這個URL來獲取metrics值的,因為prometheus預設使用的就是 http(s)://X.X.X.X/metrics
	  # 這樣一個路徑來獲取的。
	  action: replace
	  # 匹配目標指標路徑
	  target_label: __metrics_path__
	  # 匹配全路徑
	  regex: (.+)
	# 匹配出 Pod ip地址和 Port
	- source_labels:
		[__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
	  action: replace
	  target_label: __address__
	  regex: ([^:]+)(?::d+)?;(d+)
	  replacement: $1:$2
	# 下面主要是為了給樣本新增額外資訊
	- action: labelmap
	  regex: __meta_kubernetes_service_label_(.+)
	# 元標籤 服務物件的名稱空間
	- source_labels: [__meta_kubernetes_namespace]
	  action: replace
	  target_label: kubernetes_namespace
	# service 物件的名稱
	- source_labels: [__meta_kubernetes_service_name]
	  action: replace
	  target_label: kubernetes_name
	# pod物件的名稱
	- source_labels: [__meta_kubernetes_pod_name]
	  action: replace
	  target_label: kubernetes_pod_name

1、建立prometheus-additional.yaml組態檔

新增 prometheus 在 Kubernetes 下的自動服務發現 prometheus-additional.yaml

- job_name: 'dev-kubernetes-endpoints'
  scrape_interval: 10s
  scrape_timeout: 10s
  metrics_path: (.*)/actuator/prometheus
  scheme: http
  relabel_configs:
  - action: keep
	regex: true
	source_labels:
	- __meta_kubernetes_pod_annotation_prometheus_io_scrape
  - action: replace
	regex: (.+)
	source_labels:
	- __meta_kubernetes_pod_annotation_prometheus_io_path
	target_label: __metrics_path__
  - action: replace
	regex: ([^:]+)(?::\d+)?;(\d+)
	replacement: $1:$2
	source_labels:
	- __address__
	- __meta_kubernetes_pod_annotation_prometheus_io_port
	target_label: __address__
  - action: labelmap
	regex: __meta_kubernetes_pod_label_(.+)
  - action: replace
	source_labels:
	- __meta_kubernetes_namespace
	target_label: kubernetes_namespace
  - action: replace
	source_labels:
	- __meta_kubernetes_pod_name
	target_label: kubernetes_pod_name
  kubernetes_sd_configs:
  - role: pod
	kubeconfig_file: ""
	follow_redirects: true
	namespaces:
	names: []

2、建立Secret 物件

將上面檔案直接儲存為 prometheus-additional.yaml,然後通過這個檔案建立一個對應的 Secret 物件:

$ kubectl create secret generic additional-configs --from-file=prometheus-additional.yaml -n monitoring
secret "additional-configs" created

3、建立資源物件

然後我們需要在宣告 prometheus 的資源物件檔案中通過 additionalScrapeConfigs 屬性新增上這個額外的設定:
「prometheus-prometheus.yaml」:

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  labels:
	app.kubernetes.io/component: prometheus
	app.kubernetes.io/name: prometheus
	app.kubernetes.io/part-of: kube-prometheus
	app.kubernetes.io/version: 2.29.1
	prometheus: k8s
  name: k8s
  namespace: monitoring
spec:
  retention: 7d
  alerting:
	alertmanagers:
	- apiVersion: v2
	  name: alertmanager-main
	  namespace: monitoring
	  port: web
  enableFeatures: []
  externalLabels: {}
  image: quay.io/prometheus/prometheus:v2.29.1
  nodeSelector:
	kubernetes.io/os: linux
  podMetadata:
	labels:
	  app.kubernetes.io/component: prometheus
	  app.kubernetes.io/name: prometheus
	  app.kubernetes.io/part-of: kube-prometheus
	  app.kubernetes.io/version: 2.29.1
  podMonitorNamespaceSelector: {}
  podMonitorSelector: {}
  probeNamespaceSelector: {}
  probeSelector: {}
  replicas: 2
  resources:
	requests:
	  memory: 400Mi
  ruleNamespaceSelector: {}
  ruleSelector: {}
  securityContext:
	fsGroup: 2000
	runAsNonRoot: true
	runAsUser: 1000
  serviceAccountName: prometheus-k8s
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  version: 2.29.1
  additionalScrapeConfigs:                 #以下為新增的設定項
	name: prometheus-additional-configs
	key: prometheus-additional-config.yaml

新增完成後,直接更新 prometheus 這個 CRD 資源物件即可:

kubectl apply -f prometheus-prometheus.yaml

過一段時間,重新整理 promethues 上的 config,將會檢視設定已經生效。

自動發現規則設定好後如何讓prometheus抓取pod內的metrics指標呢,抓取的路徑埠等資訊如何指定呢,這就要在應用deployments中的spec.template.metadata.annotations中指定了。設定如下:

annotations:
	prometheus.io/path: /actuator/prometheus
	prometheus.io/port: "7070"
	prometheus.io/scheme: http
	prometheus.io/scrape: "true"

定義好後prometheus即可抓取pod內的metrics指標資料了,在prometheus的targets頁面即可看到job名稱為 dev-kubernetes-endpoints 的target。

4、建立 RBAC 許可權

我們切換到 targets 頁面下面卻並沒有發現對應的監控任務,檢視 Prometheus 的 Pod 紀錄檔,發現很多錯誤紀錄檔出現,都是 xxx is forbidden,這說明是 RBAC 許可權的問題。

通過 prometheus 資源物件的設定可以知道 Prometheus 繫結了一個名為 prometheus-k8s 的 ServiceAccount 物件,而這個物件繫結的是一個名為 prometheus-k8s 的 ClusterRole:

建立 prometheus-clusterRole.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus-k8s
rules:
- apiGroups:
  - ""
  resources:
  - nodes/metrics
  verbs:
  - get
- nonResourceURLs:
  - /metrics
  verbs:
  - get

上面的許可權規則中我們可以看到明顯沒有對 Service 或者 Pod 的 list 許可權,所以報錯了,要解決這個問題,我們只需要新增上需要的許可權即可:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
	app.kubernetes.io/component: prometheus
	app.kubernetes.io/name: prometheus
	app.kubernetes.io/part-of: kube-prometheus
	app.kubernetes.io/version: 2.29.1
  name: prometheus-k8s
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  - services
  - endpoints
  - pods
  - nodes/proxy
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/metrics
  verbs:
  - get
- nonResourceURLs:
  - /metrics
  - /actuator/prometheus
  verbs:
  - get

更新上面的 ClusterRole 這個資源物件,然後重建下 Prometheus 的所有 Pod,正常就可以看到 targets 頁面下面有 dev-kubernetes-endpoints 這個監控任務了。
這裡抓取目標是因為 Service 中都有 prometheus.io/scrape=true 這個 annotation。至此,一個自動發現endpoint的設定就完成了,其他資源(service、pod、ingress、node同樣也可以通過自動發現的方式實現。