一、什么是服務發現?
我們在每個節點上面都運行了 node-exporter,如果我們通過一個 Service 來將數據收集到一起用靜態配置的方式配置到 Prometheus 去中,就只會顯示一條數據,我們得自己在指標數據中去過濾每個節點的數據,當然我們也可以手動的把所有節點用靜態的方式配置到 Prometheus 中去,但是以后要新增或者去掉節點的時候就還得手動去配置,那么有沒有一種方式可以讓 Prometheus 去自動發現我們節點的 node-exporter 程序,並且按節點進行分組呢?這就是 Prometheus 里面非常重要的「服務發現」功能。
配置參考鏈接:https://prometheus.io/docs/prometheus/latest/configuration/configuration/
Prometheus支持多種服務發現機制:文件、DNS、Consul、Kubernetes、OpenStack、EC2等。基於服務發現的過程並不復雜,通過第三方提供的接口,Prometheus查詢到需要監控的Target列表,然后輪訓這些Target獲取監控數據,下面主要介紹Kubernetes服務發現機制。
目前,在Kubernetes下,Prometheus 通過與 Kubernetes API 集成主要支持5種服務發現模式:Node、Service、Pod、Endpoints、Ingress。不同的服務發現模式適用於不同的場景,例如:node適用於與主機相關的監控資源,如節點中運行的Kubernetes 組件狀態、節點上運行的容器狀態等;service 和 ingress 適用於通過黑盒監控的場景,如對服務的可用性以及服務質量的監控;endpoints 和 pod 均可用於獲取 Pod 實例的監控數據,如監控用戶或者管理員部署的支持 Prometheus 的應用。
官方文檔如下:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config
二、角色類型
1. node
node
角色可以發現集群中每個node節點的地址端口,默認為Kubelet的HTTP端口。目標地址默認為Kubernetes節點對象的第一個現有地址,地址類型順序為NodeInternalIP、NodeExternalIP、NodeLegacyHostIP和NodeHostName。
可用標簽
__meta_kubernetes_node_name
: node節點的名稱__meta_kubernetes_node_label_<labelname>
: k8s中node節點的標簽.<labelname>
代表標簽名稱__meta_kubernetes_node_labelpresent_<labelname>
: 標簽存在則為true.<labelname>
代表標簽名稱__meta_kubernetes_node_annotation_<annotationname>
: k8s中node節點的注解.<annotationname>
代表注解名稱__meta_kubernetes_node_annotationpresent_<annotationname>
: 注解存在則為true.<annotationname>
代表注解名稱-
__meta_kubernetes_node_address_<address_type>
: 不同類型的node節點地址,例如:_meta_kubernetes_node_address_Hostname="test-k8s-node1"
_meta_kubernetes_node_address_InternalIP="10.0.0.11"
instance
: 從apiserver獲取到的節點名稱
2. service
service
角色可以發現每個service的ip和port,將其作為target。這對於黑盒監控(blackbox)很有用
可用標簽
__meta_kubernetes_namespace
: service所在的命名空間__meta_kubernetes_service_annotation_<annotationname>
: k8s中service的注解__meta_kubernetes_service_annotationpresent_<annotationname>
: 注解存在則為true__meta_kubernetes_service_cluster_ip
: k8s中service的clusterIP__meta_kubernetes_service_external_name
: k8s中service的external_name__meta_kubernetes_service_label_<labelname>
: k8s中service的標簽__meta_kubernetes_service_labelpresent_<labelname>
: 標簽存在則為true__meta_kubernetes_service_name
: k8s中service的名稱__meta_kubernetes_service_port_name
: k8s中service的端口__meta_kubernetes_service_port_protocol
: k8s中service的端口協議__meta_kubernetes_service_type
: k8s中service的類型
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手動添加端口。
__meta_kubernetes_namespace
: pod所在的命名空間__meta_kubernetes_pod_name
: pod的名稱__meta_kubernetes_pod_ip
: pod的ip__meta_kubernetes_pod_label_<labelname>
: pod的標簽__meta_kubernetes_pod_labelpresent_<labelname>
: 標簽存在則為true__meta_kubernetes_pod_annotation_<annotationname>
: pod的注解__meta_kubernetes_pod_annotationpresent_<annotationname>
: 注解存在則為true__meta_kubernetes_pod_container_init
: 如果容器是InitContainer,則為true__meta_kubernetes_pod_container_name
: 容器的名稱__meta_kubernetes_pod_container_port_name
: 容器的端口名稱__meta_kubernetes_pod_container_port_number
: 容器的端口號__meta_kubernetes_pod_container_port_protocol
: 容器的端口協議__meta_kubernetes_pod_ready
: pod的就緒狀態,true或false。__meta_kubernetes_pod_phase
: pod的生命周期狀態.Pending
,Running
,Succeeded
,Failed
orUnknown
__meta_kubernetes_pod_node_name
: pod所在node節點名稱__meta_kubernetes_pod_host_ip
: pod所在node節點ip__meta_kubernetes_pod_uid
: pod的uid__meta_kubernetes_pod_controller_kind
: pod控制器的類型ReplicaSet
,DaemonSet
,Job
,StatefulSet
...__meta_kubernetes_pod_controller_name
: pod控制器的名稱
4. endpoints
endpoints
角色可以從ep列表中發現target。對於每個ep地址和端口都會發現target。如果端點由Pod支持,則該Pod的所有其他容器端口(未綁定到端點端口)也將作為目標。
可用標簽
__meta_kubernetes_namespace
: ep對象所在的命名空間__meta_kubernetes_endpoints_name
: ep的名稱-
直接從ep對象的列表中獲取的所有target,下面的標簽將會被附加上
__meta_kubernetes_endpoint_hostname
: ep的主機名__meta_kubernetes_endpoint_node_name
: ep的node節點名__meta_kubernetes_endpoint_ready
: ep的就緒狀態,true或false。__meta_kubernetes_endpoint_port_name
: ep的端口名稱__meta_kubernetes_endpoint_port_protocol
: ep的端口協議__meta_kubernetes_endpoint_address_target_kind
: ep對象的目標類型,比如Pod__meta_kubernetes_endpoint_address_target_name
: ep對象的目標名稱,比如pod名稱
- 如果ep是屬於service的話,則會附加
service
角色的所有標簽 - 對於ep的后端節點是pod,則會附加
pod
角色的所有標簽(即上邊介紹的pod角色可用標簽)
比如我么手動創建一個ep,這個ep關聯到一個pod,則prometheus的標簽中會包含這個pod
角色的所有標簽
5. ingress
ingress
角色發現ingress的每個路徑的target。這通常對黑盒監控很有用。該地址將設置為ingress中指定的host。
可用標簽
__meta_kubernetes_namespace
: ingress所在的命名空間__meta_kubernetes_ingress_name
: ingress的名稱__meta_kubernetes_ingress_label_<labelname>
: ingress的標簽__meta_kubernetes_ingress_labelpresent_<labelname>
: 標簽存在則為true__meta_kubernetes_ingress_annotation_<annotationname>
: ingress的注解__meta_kubernetes_ingress_annotationpresent_<annotationname>
: 注解存在則為true__meta_kubernetes_ingress_scheme
: ingress的協議,如果設置了tls則是https,默認http__meta_kubernetes_ingress_path
: ingress中指定的的路徑。默認為/
三、規則解析
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 # 下面的含義是源標簽__meta_kubernetes_namespace等如果其值為default;kubernetes;https標簽順序和值要對應。換句話說就是 # 當__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name三者對應的 # 值為default、kubernetes、https則進行保留,而且該endpoints對應的地址為api server的地址。 # # __meta_kubernetes_namespace 端點對象的命名空間,在不同對象上這個標簽的含義不同,在角色是endpoints中這個是端點對象的名稱空間 # __meta_kubernetes_service_name 端點對象的服務名稱 # __meta_kubernetes_endpoint_port_name 端點的端口名稱 # # kubernetes默認在default名稱空間有一個叫做kubernetes的service,所以這個service的有3個設置對應的就是下面三個標簽 # __meta_kubernetes_namespace 值為default # __meta_kubernetes_service_name 值為kubernetes # __meta_kubernetes_endpoint_port_name 值為https 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、創建發現規則
這里以我們收集pod為例,我們定義的 Prometheus 的配置如下: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
關於 additionalScrapeConfigs 屬性的具體介紹,我們可以使用 kubectl explain
命令來了解詳細信息:
$ kubectl explain prometheus.spec.additionalScrapeConfigs KIND: Prometheus VERSION: monitoring.coreos.com/v1 RESOURCE: additionalScrapeConfigs <Object> DESCRIPTION: AdditionalScrapeConfigs allows specifying a key of a Secret containing additional Prometheus scrape configurations. Scrape configurations specified are appended to the configurations generated by the Prometheus Operator. Job configurations specified must have the form as specified in the official Prometheus documentation: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config. As scrape configs are appended, the user is responsible to make sure it is valid. Note that using this feature may expose the possibility to break upgrades of Prometheus. It is advised to review Prometheus release notes to ensure that no incompatible scrape configs are going to break Prometheus after the upgrade. FIELDS: key <string> -required- The key of the secret to select from. Must be a valid secret key. name <string> Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid? optional <boolean> Specify whether the Secret or its key must be defined
添加完成后,直接更新 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 這個監控任務了。
參考:
https://www.scriptjc.com/article/1256
https://cloud.tencent.com/developer/article/1802679
https://github.com/prometheus/prometheus/blob/release-2.23/documentation/examples/prometheus-kubernetes.yml
https://prometheus.io/docs/prometheus/latest/configuration/configuration/
https://www.cnblogs.com/pythonPath/p/12505457.html