Prometheus之kubernetessd自动发现

一、什么是服务发现?

我们在每个节点上面都运行了 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的生命周期状态.PendingRunningSucceededFailed or Unknown
  • __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

原文地址:https://www.cnblogs.com/sfnz/p/15619240.html