Prometheus运维十三 Prometheus服务发现

海阔凭鱼跃,天高任鸟飞

1.Prometheus服务发现

当我们使用各种服务的exporter分别对系统,数据和HTTP服务进行监控指标采集,对于所有监控指标对应的Target的运行状态和资源使用情况,都是用Prometheus的静态配置功能static_configs来手动添加主机IP和端口,然后重载服务让Prometheus发现。

如果是比较少的服务器线上或者测试环境下,这种手动配置是非常快捷简单的方法。但是如果服务器节点达到了一定数量例如成百上千台节点组成的大型集群或者k8s容器编排集群,手动添加非常的繁琐,低效,增加运维成本。

为此,Prometheus设计了一台服务发现的功能。类似于zabbix的自动发现和自动注册功能。

Prometheus服务发现能够自动检测分类,并且可以识别新节点和变更的节点。也就是说,可以在容器或者云平台中,自动发现并监控节点或者更新节点,动态的进行数据采集和处理。

目前Prometheus已经支持了很多常见的自动发现服务,比如:consul ec2 gce serverset_sd_config openstack kubernetes等。

static_configs: 静态服务发现
dns_sd_configs: DNS 服务发现
file_sd_configs: 文件服务发现
consul_sd_configs: Consul 服务发现
serverset_sd_configs: Serverset 服务发现
nerve_sd_configs: Nerve 服务发现
marathon_sd_configs: Marathon 服务发现
kubernetes_sd_configs: Kubernetes 服务发现
gce_sd_configs: GCE 服务发现
ec2_sd_configs: EC2 服务发现
openstack_sd_configs: OpenStack 服务发现
azure_sd_configs: Azure 服务发现
triton_sd_configs: Triton 服务发现

常用的就是 sd_config,DNS,kubernetes,consul。

promethues的静态静态服务发现static_configs:每当有一个新的目标实例需要监控,都需要手动配置目标target。
promethues的consul服务发现consul_sd_configs:Prometheus 一直监视consul服务,当发现在consul中注册的服务有变化,prometheus就会自动监控到所有注册到consul中的目标资源。
promethues的k8s服务发现kubernetes_sd_configs:Prometheus与Kubernetes的API进行交互,动态的发现Kubernetes中部署的所有可监控的目标资源。

为什么要用自动发现?
在基于云(IaaS或者CaaS)的基础设施环境中用户可以像使用水、电一样按需使用各种资源(计算、网络、存储)。按需使用就意味着资源的动态性,这些资源可以随着需求规模的变化而变化。例如在AWS中就提供了专门的AutoScall服务,可以根据用户定义的规则动态地创建或者销毁EC2实例,从而使用户部署在AWS上的应用可以自动的适应访问规模的变化。

这种按需的资源使用方式对于监控系统而言就意味着没有了一个固定的监控目标,所有的监控对象(基础设施、应用、服务)都在动态的变化。对于Nagias这类基于Push模式传统监控软件就意味着必须在每一个节点上安装相应的Agent程序,并且通过配置指向中心的Nagias服务,受监控的资源与中心监控服务器之间是一个强耦合的关系,要么直接将Agent构建到基础设施镜像当中,要么使用一些自动化配置管理工具(如Ansible、Chef)动态的配置这些节点。当然实际场景下除了基础设施的监控需求以外,我们还需要监控在云上部署的应用,中间件等等各种各样的服务。要搭建起这样一套中心化的监控系统实施成本和难度是显而易见的。

对于Prometheus这一类基于Pull模式的监控系统,显然也无法继续使用的static_configs的方式静态的定义监控目标。对于Prometheus而言其解决方案就是引入一个中间的代理人(服务注册中心),这个代理人掌握着当前所有监控目标的访问信息,Prometheus只需要向这个代理人询问有哪些监控目标即可, 这种模式被称为服务发现。
在这里插入图片描述
在不同的场景下,会有不同的东西扮演代理人(服务发现与注册中心)这一角色。

比如在AWS公有云平台或者OpenStack的私有云平台中,由于这些平台自身掌握着所有资源的信息,此时这些云平台自身就扮演了代理人的角色。

Prometheus通过使用平台提供的API就可以找到所有需要监控的云主机。

在Kubernetes这类容器管理平台中,Kubernetes掌握并管理着所有的容器以及服务信息,那此时Prometheus只需要与Kubernetes打交道就可以找到所有需要监控的容器以及服务对象。

Prometheus还可以直接与一些开源的服务发现工具进行集成,例如在微服务架构的应用程序中,经常会使用到例如Consul这样的服务发现注册软件,Promethues也可以与其集成从而动态的发现需要监控的应用服务实例。除了与这些平台级的公有云、私有云、容器云以及专门的服务发现注册中心集成以外,Prometheus还支持基于DNS以及文件的方式动态发现监控目标,从而大大的减少了在云原生,微服务以及云模式下监控实施难度。
在这里插入图片描述
相较于Push模式,Pull模式的优点可以简单总结为以下几点:
1.只要Exporter运行,可以在任何地方搭建监控系统;
2.可以更容易的查看监控目标实例的健康状态,并且可以快速定位故障;
3.更利于构建DevOps文化的团队;
4.松耦合的架构模式更适合于云原生的部署环境。

1.2 基于文件系统服务发现

Prometheus支持的众多服务发现的实现方式中,基于文件的服务发现是最通用的方式。这种方式不需要依赖于任何的平台或者第三方服务。对于Prometheus而言也不可能支持所有的平台或者环境。通过基于文件的服务发现方式下,Prometheus会定时从文件中读取最新的Target信息,因此,你可以通过任意的方式将监控Target的信息写入即可。

可以通过JSON或者YAML格式的文件,定义所有的监控目标。例如,在下面的JSON文件中分别定义了3个采集任务,以及每个任务对应的Target列表:

YAML格式:

- targets: ['localhost:8080']
  labels:
    env: 'localhsot'
    job: 'cadvisor'
- targets: ['localhost:8080']
  labels:
    env: 'prod'
    job: 'mysqld'
- targets: ['localhost:8080']
  labels:
    env: 'prod'
    job: 'node'

JSON格式:

[
  {
    "targets": [ "localhost:8080"],
    "labels": {
      "env": "localhost",
      "job": "cadvisor"
    }
  },
  {
    "targets": [ "localhost:9104" ],
    "labels": {
      "env": "prod",
      "job": "mysqld"
    }
  },
  {
    "targets": [ "localhost:9100"],
    "labels": {
      "env": "prod",
      "job": "node"
    }
  }
]

同时还可以通过为这些实例添加一些额外的标签信息,例如使用env标签标示当前节点所在的环境,这样从这些实例中采集到的样本信息将包含这些标签信息,从而可以通过该标签按照环境对数据进行统计。

创建Prometheus配置文件/etc/prometheus/prometheus-file-sd.yml,并添加以下内容:

global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s
scrape_configs:
- job_name: 'file_ds'
  file_sd_configs:
  - files:
    - *.json
    - *.yml

这里定义了一个基于file_sd_configs的监控采集任务,其中模式的任务名称为file_ds。在JSON文件中可以使用job标签覆盖默认的job名称,此时启动Prometheus服务:

$ prometheus --config.file=/etc/prometheus/prometheus-file-sd.yml --storage.tsdb.path=/data/prometheus
#或
$ systemctl restart Prometheus

在Prometheus UI的Targets下就可以看到当前从test.json文件中动态获取到的Target实例信息以及监控任务的采集状态,同时在Labels列下会包含用户添加的自定义标签:
在这里插入图片描述
Prometheus默认每5m重新读取一次文件内容,当需要修改时,可以通过refresh_interval进行设置,例如:

- job_name: 'file_ds'
  file_sd_configs:
  - refresh_interval: 1m
    files:
    - *.json
    - *.yml

通过这种方式,Prometheus会自动的周期性读取文件中的内容。当文件中定义的内容发生变化时,不需要对Prometheus进行任何的重启操作。

1.2 基于Consul的服务发现

Consul是由HashiCorp开发的一个支持多数据中心的分布式服务发现和键值对存储服务的开源软件,被大量应用于基于微服务的软件架构当中。

官网地址:https://www.consul.io/downloads.html
Consul与Prometheus同样使用Go语言进行开发,解压下载的软件包并将命令行工具放到系统PATH路径即可。
本地可以使用开发者模式在本地快速启动一个单节点的Consul环境:

$ consul agent -dev
==> Starting Consul agent...
==> Consul agent running!
           Version: 'v1.0.7'
           Node ID: 'd7b590ba-e2f8-3a82-e8a8-2a911bdf67d5'
         Node name: 'localhost'
        Datacenter: 'dc1' (Segment: '<all>')
            Server: true (Bootstrap: false)
       Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600)
      Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
           Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false

启动成功后,在一个新的terminal窗口中运行consul members可以查看当前集群中的所有节点:

$ consul members
Node       Address         Status  Type    Build  Protocol  DC   Segment
localhost  127.0.0.1:8301  alive   server  1.0.7  2         dc1  <all>

用户还可以通过HTTP API的方式查看当前集群中的节点信息:

$ curl localhost:8500/v1/catalog/nodes
[
    {
        "ID": "d7b590ba-e2f8-3a82-e8a8-2a911bdf67d5",
        "Node": "localhost",
        "Address": "127.0.0.1",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "127.0.0.1",
            "wan": "127.0.0.1"
        },
        "Meta": {
            "consul-network-segment": ""
        },
        "CreateIndex": 5,
        "ModifyIndex": 6
    }
]

Consul还提供了内置的DNS服务,可以通过Consul的DNS服务的方式访问其中的节点:

$ dig @127.0.0.1 -p 8600 localhost.node.consul

; <<>> DiG 9.9.7-P3 <<>> @127.0.0.1 -p 8600 localhost.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50684
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;localhost.node.consul.        IN    A

;; ANSWER SECTION:
localhost.node.consul.    0    IN    A    127.0.0.1

;; Query time: 5 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sun Apr 15 22:10:56 CST 2018
;; MSG SIZE  rcvd: 66

在Consul当中服务可以通过服务定义文件或者是HTTP API的方式进行注册。这里使用服务定义文件的方式将本地运行的node_exporter通过服务的方式注册到Consul当中。

创建配置目录:

sudo mkdir /etc/consul.d
echo '{"service": {"name": "node_exporter", "tags": ["exporter"], "port": 9100}}' \
    | sudo tee /etc/consul.d/node_exporter.json

重新启动Consul服务,并且声明服务定义文件所在目录:

$ consul agent -dev -config-dir=/etc/consul.d
==> Starting Consul agent...
    2018/04/15 22:23:47 [DEBUG] agent: Service "node_exporter" in sync

一旦服务注册成功之后,用户就可以通过DNS或HTTP API的方式查询服务信息。默认情况下,所有的服务都可以使用NAME.service.consul域名的方式进行访问。

例如,可以使用node_exporter.service.consul域名查询node_exporter服务的信息:

$ dig @127.0.0.1 -p 8600 node_exporter.service.consul

;; QUESTION SECTION:
;node_exporter.service.consul.    IN    A

;; ANSWER SECTION:
node_exporter.service.consul. 0    IN    A    127.0.0.1

如上所示DNS记录会返回当前可用的node_exporter服务实例的IP地址信息。
除了使用DNS的方式以外,Consul还支持用户使用HTTP API的形式获取服务列表:

$ curl http://localhost:8500/v1/catalog/service/node_exporter
[
    {
        "ID": "e561b376-2c1b-653d-61a0-1d844bce06a7",
        "Node": "localhost",
        "Address": "127.0.0.1",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "127.0.0.1",
            "wan": "127.0.0.1"
        },
        "NodeMeta": {
            "consul-network-segment": ""
        },
        "ServiceID": "node_exporter",
        "ServiceName": "node_exporter",
        "ServiceTags": [
            "exporter"
        ],
        "ServiceAddress": "",
        "ServiceMeta": {},
        "ServicePort": 9100,
        "ServiceEnableTagOverride": false,
        "CreateIndex": 6,
        "ModifyIndex": 6
    }
]

Consul也提供了一个Web UI可以查看Consul中所有服务以及节点的状态:
在这里插入图片描述
Consul还提供了更多的API用于支持对服务的生命周期管理(添加、删除、修改等)

集成Prometheus
Consul作为一个通用的服务发现和注册中心,记录并且管理了环境中所有服务的信息。
Prometheus通过与Consul的交互可以获取到相应Exporter实例的访问信息。在Prometheus的配置文件当可以通过以下方式与Consul进行集成:

- job_name: node_exporter
    metrics_path: /metrics
    scheme: http
    consul_sd_configs:
      - server: localhost:8500
        services:
          - node_exporter

在consul_sd_configs定义当中通过server定义了Consul服务的访问地址,services则定义了当前需要发现哪些类型服务实例的信息,这里限定了只获取node_exporter的服务实例信息。

1.3 基于k8s的服务发现

官方文档:https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config
官方配置文档:
https://github.com/prometheus/prometheus/blob/release-2.21/documentation/examples/prometheus-kubernetes.yml

Kubernetes sd配置允许从Kubernetes的 REST API 检索抓取目标, 并始终与集群状态保持同步。

role可以将以下类型之一配置为发现目标:
node
该node角色为每个集群节点发现一个目标,其地址默认为Kubelet的HTTP端口。目标地址默认为的地址类型顺序Kubernetes节点对象的第一个现有地址NodeInternalIP,NodeExternalIP, NodeLegacyHostIP,和NodeHostName。
可用的元标签:

__meta_kubernetes_node_name:节点对象的名称。
_meta_kubernetes_node_label:节点对象中的每个标签。
_meta_kubernetes_node_labelpresent:true对于节点对象中的每个标签。
_meta_kubernetes_node_annotation:来自节点对象的每个注释。
_meta_kubernetes_node_annotationpresent:true用于节点对象的每个注释。
_meta_kubernetes_node_address:每个节点地址类型的第一个地址(如果存在)。
此外,该instance节点的标签将设置为从API服务器检索到的节点名称。

service
该service角色发现每一个服务端口为每个服务的目标。通常,这对于监视服务的黑盒很有用。该地址将设置为服务的Kubernetes DNS名称以及相应的服务端口。
可用的元标签:

__meta_kubernetes_namespace:服务对象的名称空间。
_meta_kubernetes_service_annotation:服务对象的每个注释。
_meta_kubernetes_service_annotationpresent:对于服务对象的每个注释为“ true”。
__meta_kubernetes_service_cluster_ip:服务的群集IP地址。(不适用于“外部名称”类型的服务)
__meta_kubernetes_service_external_name:服务的DNS名称。(适用于外部名称类型的服务)
_meta_kubernetes_service_label:服务对象中的每个标签。
_meta_kubernetes_service_labelpresent:true用于服务对象的每个标签。
__meta_kubernetes_service_name:服务对象的名称。
__meta_kubernetes_service_port_name:目标服务端口的名称。
__meta_kubernetes_service_port_protocol:目标服务端口的协议。
__meta_kubernetes_service_type:服务的类型。

pod
该pod角色发现所有吊舱并将其容器公开为目标。对于容器的每个声明的端口,将生成一个目标。如果容器没有指定的端口,则会为每个容器创建无端口目标,以通过重新标记手动添加端口。
可用的元标签:

__meta_kubernetes_namespace:pod对象的名称空间。
__meta_kubernetes_pod_name:pod对象的名称。
__meta_kubernetes_pod_ip:pod对象的pod IP。
_meta_kubernetes_pod_label:来自pod对象的每个标签。
_meta_kubernetes_pod_labelpresent:true用于pod对象中的每个标签。
_meta_kubernetes_pod_annotation:来自pod对象的每个注释。
_meta_kubernetes_pod_annotationpresent:true用于pod对象的每个注释。
__meta_kubernetes_pod_container_init:true如果容器是InitContainer
__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:设置true或false吊舱的准备状态。
__meta_kubernetes_pod_phase:在生命周期设置为Pending,Running,Succeeded,Failed或Unknown 。
__meta_kubernetes_pod_node_name:将Pod调度到的节点的名称。
__meta_kubernetes_pod_host_ip:pod对象的当前主机IP。
__meta_kubernetes_pod_uid:pod对象的UID。
__meta_kubernetes_pod_controller_kind:pod控制器的对象种类。
__meta_kubernetes_pod_controller_name:pod控制器的名称。

endpoints
该endpoints角色从列出的服务端点发现目标。对于每个端点地址,每个端口都发现一个目标。如果端点由Pod支持,则该Pod的所有其他容器端口(未绑定到端点端口)也将作为目标。
可用的元标签:

__meta_kubernetes_namespace:端点对象的名称空间。
__meta_kubernetes_endpoints_name:端点对象的名称。
对于直接从端点列表中发现的所有目标(未从基础容器中另外推断出的那些目标),将附加以下标签:
__meta_kubernetes_endpoint_hostname:端点的主机名。
__meta_kubernetes_endpoint_node_name:托管端点的节点的名称。
__meta_kubernetes_endpoint_ready:设置为true或false为端点的就绪状态。
__meta_kubernetes_endpoint_port_name:端点端口的名称。
__meta_kubernetes_endpoint_port_protocol:端点端口的协议。
__meta_kubernetes_endpoint_address_target_kind:端点地址目标的种类。
__meta_kubernetes_endpoint_address_target_name:端点地址目标的名称。
如果端点属于服务,role: service则会附加发现的所有标签。
对于由Pod支持的所有目标,role: pod将附加发现的所有标签

ingress
该ingress角色发现了一个目标,为每个进入的每个路径。这通常对黑盒监视入口很有用。该地址将设置为入口规范中指定的主机。
可用的元标签:

__meta_kubernetes_namespace:入口对象的名称空间。
__meta_kubernetes_ingress_name:入口对象的名称。
_meta_kubernetes_ingress_label:来自入口对象的每个标签。
_meta_kubernetes_ingress_labelpresent:true用于来自入口对象的每个标签。
_meta_kubernetes_ingress_annotation:来自入口对象的每个注释。
_meta_kubernetes_ingress_annotationpresent:true用于来自入口对象的每个注释。
__meta_kubernetes_ingress_scheme:https如果设置了TLS配置,则为入口的协议方案。默认为http。
__meta_kubernetes_ingress_path:来自入口规范的路径。默认为/。

k8s服务发现之endpoints模式
在上篇博文中,Prometheus的配置文件如下:
在这里插入图片描述

- job_name: 'kubernetes-apiserver'
  kubernetes_sd_configs:
  - role: endpoints    # 此处定义了服务发现模式为 endpoints模式
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
  - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
    action: keep
    regex: default;kubernetes;https

在上面的配置中,主要就是为了Prometheus自动监控api-server这个组件,Prometheus监控到的组件信息如下:
在这里插入图片描述
在上图中可以看到,很多Prometheus默认采集到的标签,那么我们在Prometheus的配置文件中指定了relabel_configs配置,如下:

relabel_configs:
  - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
    action: keep
    regex: default;kubernetes;https

relabel_configs可选配置如下:

source_labels: 表示源标签(没有经过relabel处理之前的标签名字),上述指定的源标签,都是我们在Prometheus的UI界面的红框中可以看到的(我上一个截图中标识出来的红框),prometheus加载target成功之后,在Target实例中,都包含一些Metadata标签信息,默认的标签有:
__address__:以: 格式显示目标targets的地址
__scheme__:采集的目标服务地址的Scheme形式,HTTP或者HTTPS
__metrics_path__:采集的目标服务的访问路径
action: action定义了relabel的动作,action支持多种,如下:
replace: 替换标签值,根据regex正则匹配到源标签的值,并把匹配的值写入到目的标签中。
keep: 满足regex正则条件的实例进行采集,把source_labels中没有匹配到regex正则内容的Target实例丢掉。
drop: 满足regex正则条件的实例不采集,把source_labels中匹配到regex正则内容的Target实例丢掉。
labeldrop: 对抓取到的符合过滤规则的target标签进行删除
labelkeep: 对抓取到的符合过滤规则的target标签进行保留
target_labels: 通过action处理之后的标签名字。
regex: 正则表达式,匹配源标签(在上述的配置中,regex: default;kubernetes;https表示__meta_kubernetes_namespace的值为default的,__meta_kubernetes_service_name的值为kubernetes的,并且__meta_kubernetes_endpoint_port_name值为https的,就进行keep动作(对满足regex正则条件的实例进行采集))
replacement:replacement指定的替换后的标签(target_label)对应的数值。
labelmap: 会根据regex的定义去匹配Target实例所有标签的名称,并且以匹配到的内容为新的标签名称,其值作为新标签的值。

那么,知道了各项配置的意思,现在试试,如果Prometheus中没有上面relabel_configs那些配置,会采集到什么样的数据呢?修改配置文件如下:

- job_name: 'kubernetes-apiserver'
  kubernetes_sd_configs:
  - role: endpoints
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

Prometheus数据采集页面如下:
在这里插入图片描述
可以看到,如果没有配置relabel_configs对标签进行过滤,那么会采集到很多其他信息,在上图中有coredns的及calico等组件的监控信息,由于那些目标并不没有相应的/metrics路径,故状态都为down。这就是relabel_configs的魅力。

k8s之node-exporter服务发现

# 摘自上篇博文中的配置
    - job_name: 'kubernetes-node'
      kubernetes_sd_configs:
      - role: node
      relabel_configs:
      - source_labels: [__address__]
        regex: '(.*):10250'
        replacement: '${1}:9100'
        target_label: __address__
        action: replace
      - action: labelmap
        regex: __meta_kubernetes_node_label_(.+)

上述就是自动发现node节点的配置,通过指定Kubernetes_sd_config的模式为node,prometheus就会自动从Kubernetes中发现所有的node节点并作为当前job监控的目标实例,发现的节点/metrics接口是默认的kubelet的HTTP接口,但是由于metrics监听的端口是9100而并不是默认的10250端口,所以这里就需要使用Prometheus提供的relabel_configs中的replace能力了。

relabel可以在Prometheus采集数据之前,通过Target实例的Metadata信息,动态重新写入Label的值,这里使用address标签替换10250端口为9100,才可以正确的采集到node信息。

还添加了一个action为labelmap,正则表达式是__meta_kubernetes_node(.+)的配置,这里的意思就是表达式中匹配的数据也添加到指标数据的Label标签中去。此配置采集的就是通过kubectl get nodes --show-labels命令采集到的标签。

过上面的配置,我们可以使Prometheus采集到集群中运行node-export程序的所有节点信息,如下:
在这里插入图片描述
为了展现配置relabel_configs的作用,现在将去掉端口替换的配置,修改后的配置如下:

- job_name: 'kubernetes-node'
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)

此时,Prometheus将无法采集到node节点的信息,因为使用了默认的10250端口去采集节点信息,如下:
在这里插入图片描述
现在来确认第二个action的配置作用,修改配置文件如下:

- job_name: 'kubernetes-node'
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - source_labels: [__address__]
    regex: '(.*):10250'
    replacement: '${1}:9100'
    target_label: __address__
    action: replace

此时,可以看到如下,可以正常采集到数据,但是标签少了很多东西(鼠标悬浮到标签上显示的是重写标签之前的标签):
在这里插入图片描述
由此可以看出,上面配置中的action: labelmap,就是通过正则将匹配到的元标签中的数据给删除,并且进行重写,上述配置的正则是将元标签中的__meta_kubernetes_node_label_进行了删除,保留了后面的信息作为标签名。

k8s之自动发现cadvisor采集数据

- job_name: 'kubernetes-node-cadvisor'
  kubernetes_sd_configs:
  - role:  node
  scheme: https
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)
  - target_label: __address__
    replacement: kubernetes.default.svc:443
  - source_labels: [__meta_kubernetes_node_name]
    regex: (.+)
    target_label: __metrics_path__
    replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor

tls_config配置的证书地址是每个Pod连接apiserver所使用的地址,基本上写死了,证书是我们Pod启动的时候kubelet给pod注入的一个证书,所有的pod启动的时候都会有一个ca证书注入进来,如要想要访问apiserver的信息,还需要配置一个token_file。

relabel_configs: 用新的前缀代替原label name前缀,没有replacement的话功能就是去掉label name前缀。

例如: 以下两句的功能就是将__meta_kubernetes_node_label_kubernetes_io_hostname变为kubernetes_io_hostname

- action: labelmap
  regex: __meta_kubernetes_node_label_(.+)

replacement 中的值将会覆盖target_label中指定的label name的值,如下,address的值会被替换为kubernetes.default.svc:443:

- target_label: __address__
  replacement: kubernetes.default.svc:443
# 获取__meta_kubernetes_node_name的值
- source_labels: [__meta_kubernetes_node_name]
# 匹配一个或多个任意字符,将上述source_labels的值生成变量
  regex: (.+)
  
# replacement中的值将会覆盖target_label中指定的label name的值
# 即__metrics_path__的值会被替换为/api/v1/nodes/${1}/proxy/metrics
# 其中${1}的值会被替换为__meta_kubernetes_node_name的值。
  target_label: __metrics_path__
  replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor

借鉴地址:https://lvjianzhao.gitee.io/

1.4 基于DNS的服务发现

对于一些环境,可能基于文件与consul服务发现已经无法满足的时候,可能就需要DNS来做服务发现了。在互联网架构中,我们使用主机节点或者Kubernetes集群通常是不对外暴露IP的,这就要求我们在一个内部局域网或者专用的网络中部署DNS服务器,使用DNS服务来完成内部网络中的域名解析工作。

Prometheus的DNS服务发现有俩种方法:
1.第一种是使用DNA A记录来做自动发现。
2.第二种方法是DNS SRV。
第一种显然没有没有SRV资源记录更为便捷,在这里就把俩种配置全部做一遍,对于取决用什么,根据你自己的环境来抉择。

DNS A记录发现配置,首先内网需要有一个DNS服务器,或者直接自行配置解析记录即可,这里使用的dnsmasq服务在内网测试

# 验证 test1 DNS记录
nslookup test1.example.com
Server:        127.0.0.53
Address:    127.0.0.53#53

Non-authoritative answer:
Name:    test1.example.com
Address: 192.168.1.221
# 验证 test2 DNS记录
nslookup test2.example.com
Server:        127.0.0.53
Address:    127.0.0.53#53

Non-authoritative answer:
Name:    test2.example.com
Address: 192.168.1.222

Prometheus配置

  # 基于DNS A记录发现
  - job_name: 'DNS-A'                  # job 名称
    metrics_path: "/metrics"            # 路径
    dns_sd_configs:
    - names: ["test1.example.com", "test2.example.com"]   # A记录
      type: A                                   # 解析类型
      port: 29100                     # 端口

重启Prometheus 在targets中可以看到dns-a记录
在这里插入图片描述
DNS SRV是DNS资源记录中的一种记录类型,用来指定服务器地址与端口,并且可以设置每个服务器的优先级和权重。访问到服务的时候,本地的DNS resolver 从DNS服务器获取一个地址列表,然后根据优先级和权重来选择一个地址作为本次请求的目标地址。

SRV的记录格式:

_service._proto.name. TTL class SRV priority weight port target

| 参数 | 说明 |
| :-----: | :----: |
| _service | 服务名称,前缀 _ 是为了防止与DNS 标签(域名)冲突|
| proto | 服务使用的通讯协议 通常是 tcp udp |
| name | 此记录有效域名 |
| TTL | 标准DNS class 字段 如 IN |
| priority | 记录优先级,数值越小,优先级越高。 [0-65535] |
| weight | 记录权重,数值越大,权重越高。[0-65535] |
| port | 服务使用端口 |
| target | 使用服务的主机地址名称 |

这里没有使用named,而是使用的dnsmasq来做的测试,添加SRV记录完成后,需要重启dnsmasq服务使其生效。

# 配置dns解析
cat /etc/dnsmasq.d/localdomain.conf
address=/test1.example.com/192.168.1.221
address=/test2.example.com/192.168.1.222
# 添加 SRV 记录
cat /etc/dnsmasq.conf
srv-host =_prometheus._tcp.example.com,test1.example.com,29100
srv-host =_prometheus._tcp.example.com,test2.example.com,29100

# 验证srv服务是否正确,192.168.1.123 是内部DNS服务器,
dig @192.168.1.123 +noall +answer SRV _prometheus._tcp.example.com
output...
_prometheus._tcp.example.com. 0    IN    SRV    0 0 9100 test1.example.com.
_prometheus._tcp.example.com. 0    IN    SRV    0 0 9100 test2.example.com.

Prometheus配置完成以后,重载Prometheus服务。

  - job_name: 'DNS-SRV'                # 名称
    metrics_path: "/metrics"            # 获取数据的路径
    dns_sd_configs:                     # 配置使用DNS解析
    - names: ['_prometheus._tcp.example.com']   # 配置SRV对应的解析地址

这个时候在targets中可以看到DNS自动发现的记录了。
在这里插入图片描述
这个时候,我们在新加一个记录,用来做自动发现。

# 添加test0解析
cat /etc/dnsmasq.d/localdomain.conf
address=/test1.example.com/192.168.1.221
address=/test2.example.com/192.168.1.222
address=/test0.example.com/192.168.1.220

# 添加 test0 SRV 记录
cat /etc/dnsmasq.conf
srv-host =_prometheus._tcp.example.com,test1.example.com,29100
srv-host =_prometheus._tcp.example.com,test2.example.com,29100
srv-host =_prometheus._tcp.example.com,test0.example.com,19100

# 验证dns SRV记录是否成功
dig @192.168.1.123 +noall +answer SRV _prometheus._tcp.example.com
_prometheus._tcp.example.com. 0    IN    SRV    0 0 19100 test0.example.com.
_prometheus._tcp.example.com. 0    IN    SRV    0 0 29100 test2.example.com.
_prometheus._tcp.example.com. 0    IN    SRV    0 0 29100 test1.example.com.

这个时候在去观察targets就发现已经可以自动发现test0了。
在这里插入图片描述

1.5 服务发现和Relabel

通过服务发现的方式,管理员可以在不重启Prometheus服务的情况下动态的发现需要监控的Target实例信息。
在这里插入图片描述
如上图所示,对于线上环境我们可能会划分为:dev, stage, prod不同的集群。每一个集群运行多个主机节点,每个服务器节点上运行一个Node Exporter实例。Node Exporter实例会自动注册到Consul中,而Prometheus则根据Consul返回的Node Exporter实例信息动态的维护Target列表,从而向这些Target轮询监控数据。

然而,如果我们可能还需要:
1.按照不同的环境dev, stage, prod聚合监控数据?
2.对于研发团队而言,我可能只关心dev环境的监控数据,如何处理?
3.如果为每一个团队单独搭建一个Prometheus Server。那么如何让不同团队的Prometheus Server采集不同的环境监控数据?

面对以上这些场景下的需求时,我们实际上是希望Prometheus Server能够按照某些规则(比如标签)从服务发现注册中心返回的Target实例中有选择性的采集某些Exporter实例的监控数据。

Prometheus的Relabeling机制

在Prometheus所有的Target实例中,都包含一些默认的Metadata标签信息。可以通过Prometheus UI的Targets页面中查看这些实例的Metadata标签的内容:
在这里插入图片描述
默认情况下,当Prometheus加载Target实例完成后,这些Target时候都会包含一些默认的标签:

__address__:当前Target实例的访问地址<host>:<port>
__scheme__:采集目标服务访问地址的HTTP Scheme,HTTP或者HTTPS
__metrics_path__:采集目标服务访问地址的访问路径
__param_<name>:采集任务目标服务的中包含的请求参数

上面这些标签将会告诉Prometheus如何从该Target实例中获取监控数据。除了这些默认的标签以外,我们还可以为Target添加自定义的标签.
例如,在“基于文件的服务发现”小节中的示例中,我们通过JSON配置文件,为Target实例添加了自定义标签env,如下所示该标签最终也会保存到从该实例采集的样本数据中:

node_cpu{cpu="cpu0",env="prod",instance="localhost:9100",job="node",mode="idle"}

一般来说,Target以__作为前置的标签是在系统内部使用的,因此这些标签不会被写入到样本数据中。不过这里有一些例外,例如,我们会发现所有通过Prometheus采集的样本数据中都会包含一个名为instance的标签,该标签的内容对应到Target实例的__address__。 这里实际上是发生了一次标签的重写处理。

这种发生在采集样本数据之前,对Target实例的标签进行重写的机制在Prometheus被称为Relabeling。
在这里插入图片描述
Prometheus允许用户在采集任务设置中通过relabel_configs来添加自定义的Relabeling过程。

使用replace/labelmap重写标签
Relabeling最基本的应用场景就是基于Target实例中包含的metadata标签,动态的添加或者覆盖标签。
例如,通过Consul动态发现的服务实例还会包含以下Metadata标签信息:

__meta_consul_address:consul地址
__meta_consul_dc:consul中服务所在的数据中心
__meta_consulmetadata:服务的metadata
__meta_consul_node:服务所在consul节点的信息
__meta_consul_service_address:服务访问地址
__meta_consul_service_id:服务ID
__meta_consul_service_port:服务端口
__meta_consul_service:服务名称
__meta_consul_tags:服务包含的标签信息

在默认情况下,从Node Exporter实例采集上来的样本数据如下所示:

node_cpu{cpu="cpu0",instance="localhost:9100",job="node",mode="idle"} 93970.8203125

们希望能有一个额外的标签dc可以表示该样本所属的数据中心:

node_cpu{cpu="cpu0",instance="localhost:9100",job="node",mode="idle", dc="dc1"} 93970.8203125

在每一个采集任务的配置中可以添加多个relabel_config配置,一个最简单的relabel配置如下:

scrape_configs:
  - job_name: node_exporter
    consul_sd_configs:
      - server: localhost:8500
        services:
          - node_exporter
    relabel_configs:
    - source_labels:  ["__meta_consul_dc"]
      target_label: "dc"

该采集任务通过Consul动态发现Node Exporter实例信息作为监控采集目标。
在上一小节中,我们知道通过Consul动态发现的监控Target都会包含一些额外的Metadata标签,比如标签__meta_consul_dc表明了当前实例所在的Consul数据中心,因此我们希望从这些实例中采集到的监控样本中也可以包含这样一个标签,例如:

node_cpu{cpu="cpu0",dc="dc1",instance="172.21.0.6:9100",job="consul_sd",mode="guest"}

这样可以方便的根据dc标签的值,根据不同的数据中心聚合分析各自的数据。

在这个例子中,通过从Target实例中获取__meta_consul_dc的值,并且重写所有从该实例获取的样本中。

完整的relabel_config配置如下所示:

# The source labels select values from existing labels. Their content is concatenated
# using the configured separator and matched against the configured regular expression
# for the replace, keep, and drop actions.
[ source_labels: '[' <labelname> [, ...] ']' ]

# Separator placed between concatenated source label values.
[ separator: <string> | default = ; ]

# Label to which the resulting value is written in a replace action.
# It is mandatory for replace actions. Regex capture groups are available.
[ target_label: <labelname> ]

# Regular expression against which the extracted value is matched.
[ regex: <regex> | default = (.*) ]

# Modulus to take of the hash of the source label values.
[ modulus: <uint64> ]

# Replacement value against which a regex replace is performed if the
# regular expression matches. Regex capture groups are available.
[ replacement: <string> | default = $1 ]

# Action to perform based on regex matching.
[ action: <relabel_action> | default = replace ]

其中action定义了当前relabel_config对Metadata标签的处理方式,默认的action行为为replace。
replace行为会根据regex的配置匹配source_labels标签的值(多个source_label的值会按照separator进行拼接),并且将匹配到的值写入到target_label当中,如果有多个匹配组,则可以使用${1}, ${2}确定写入的内容。
如果没匹配到任何内容则不对target_label进行重新。

repalce操作允许用户根据Target的Metadata标签重写或者写入新的标签键值对,在多环境的场景下,可以帮助用户添加与环境相关的特征维度,从而可以更好的对数据进行聚合。

除了使用replace以外,还可以定义action的配置为labelmap。与replace不同的是,labelmap会根据regex的定义去匹配Target实例所有标签的名称,并且以匹配到的内容为新的标签名称,其值作为新标签的值。
例如,在监控Kubernetes下所有的主机节点时,为将这些节点上定义的标签写入到样本中时,可以使用如下relabel_config配置:

- job_name: 'kubernetes-nodes'
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)

而使用labelkeep或者labeldrop则可以对Target标签进行过滤,仅保留符合过滤条件的标签,例如:

relabel_configs:
  - regex: label_should_drop_(.+)
    action: labeldrop

该配置会使用regex匹配当前Target实例的所有标签,并将符合regex规则的标签从Target实例中移除。labelkeep正好相反,会移除那些不匹配regex定义的所有标签。

使用keep/drop过滤Target实例

使用中心化的服务发现注册中心时,所有环境的Exporter实例都会注册到该服务发现注册中心中。而不同职能(开发、测试、运维)的人员可能只关心其中一部分的监控数据,他们可能各自部署的自己的Prometheus Server用于监控自己关心的指标数据,如果让这些Prometheus Server采集所有环境中的所有Exporter数据显然会存在大量的资源浪费。如何让这些不同的Prometheus Server采集各自关心的内容?答案还是Relabeling,relabel_config的action除了默认的replace以外,还支持keep/drop行为。

例如,如果我们只希望采集数据中心dc1中的Node Exporter实例的样本数据,那么可以使用如下配置:

scrape_configs:
  - job_name: node_exporter
    consul_sd_configs:
      - server: localhost:8500
        services:
          - node_exporter
    relabel_configs:
    - source_labels:  ["__meta_consul_dc"]
      regex: "dc1"
      action: keep

当action设置为keep时,Prometheus会丢弃source_labels的值中没有匹配到regex正则表达式内容的Target实例,而当action设置为drop时,则会丢弃那些source_labels的值匹配到regex正则表达式内容的Target实例。可以简单理解为keep用于选择,而drop用于排除。

使用hashmod计算source_labels的Hash值
当relabel_config设置为hashmod时,Prometheus会根据modulus的值作为系数,计算source_labels值的hash值。例如:

scrape_configs
- job_name: 'file_ds'
  relabel_configs:
    - source_labels: [__address__]
      modulus:       4
      target_label:  tmp_hash
      action:        hashmod
  file_sd_configs:
  - files:
    - targets.json

根据当前Target实例__address__的值以4作为系数,这样每个Target实例都会包含一个新的标签tmp_hash,并且该值的范围在1~4之间,查看Target实例的标签信息,可以看到如下的结果,每一个Target实例都包含了一个新的tmp_hash值:
在这里插入图片描述
需要注意的是,如果relabel的操作只是为了产生一个临时变量,以作为下一个relabel操作的输入,那么我们可以使用__tmp作为标签名的前缀,通过该前缀定义的标签就不会写入到Target或者采集到的样本的标签中。

参考文献:
https://yunlzheng.gitbook.io/prometheus-book/part-ii-prometheus-jin-jie/sd/why-need-service-discovery
https://prometheus.io/docs/practices/naming/
https://segmentfault.com/a/1190000023765950
https://lvjianzhao.gitee.io/


版权声明:本文为ZhanBiaoChina原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。