prometheus主动服务发现机制

简介

  • prometheus是基于pull模式抓取监控数据,首先要能够发现需要监控的目标对象target,那么prometheus如何获取数据的监控,目前有2种方式,静态手工配置和动态服务发现配置。
    1. 对于小型的系统环境来说,可以通过static_config静态指定各个Target便能解决这个问题,这个就是最简单的手工配置
  • 但是,prometheus最开始设是一个面向云原生应用程序的,云原生,容器场景下按需的资源使用方式,对于监控系统而言就意味着没有一个固定的监控目标,所有的监控对象(基础设施,应用,服务)都在动态的变化,因此,对于有较强的动态性云环境中,静态配置显然难以适用,于是就出现了动态服务发现机制
  • 为了适应静态和动态发现机制,prometheus引入了一个中间代理(服务注册中心),这个代理人掌握着当前所有的当前监控目标访问信息,prometheus只需要引入代理人询问有那些目标即可,这种模式被称作为服务发现(service discovery )

在这里插入图片描述

如图: SD模块是专门负责发现需要监控的target信息,prometheus去SD模块订阅该信息,有target信息会推送到Prometheus,然后Prometheus拿到target信息后通过pull http 协议去拉取该指标的数据。

  • kubernetes_sd_configs: kubernetes服务发现,让prometheus动态发现kubernetes中被监控的目标。
  • static_configs: 静态服务发现,基于prometheus配置文件指定监控目标
  • dns_sd_configs: DNS服务发现监控目标
  • consul_sd_configs: Consul服务发现,基于consul服务动态发现监控目标
  • file_sd_configs: 基于指定的文件事先服务发现,prometheus定时读取文件如果发生变化则加载新目标

1、静态配置的局限性

静态服务发现机制,配置简单,但是监控目标是写死在了配置文件中,如果要新增、修改、删除监控节点时,需要每次都去修改配置文件,然后再通知prometheus重新加载,这太麻烦了。于是出现了动态服务发现

2、基于文件的服务发现

我们下面有更好的代替,这里这个服务发现先简单的了解明白工作原理

通过定义一组资源“子”配置文件,yaml格式文件里面只存储需要采集的targets信息,此种方式可以被prometheus实时动态获取,而不需要重启prometheus服务。

首先,定义一个JSON格式文件的服务发现文件:

[root@prometheus-server targets]# more node.json 
[
  {
    "targets": [ "172.16.213.90:9100" ],
    "labels": {
      "env": "dev_gameserver"
    }
  }
]

或者也可以定义一个YAML格式文件的服务发现文件:

[root@prometheus-server targets]# more node.yaml 
- targets:
  - "172.16.213.29:9100"
  labels:
    app: node-exporter
    job: nodes

接着,将这两个文件放到当前的targets目录下(先创建targets目录),最后,修改prometheus.yml 配置文件,增加如下内容:

  - job_name: 'node_service_discovery'
    file_sd_configs:
    - files:
      - targets/*.json
      refresh_interval: 60s
    - files:
      - targets/*.yaml
      refresh_interval: 60s

配置文件说明:

  • file_sd_configs,指定prometheus基于文件的服务发现配置。
  • files,自定义的和prometheus程序同级目录的targets目录,要被自动加载的所有.json格式、yaml格式的文件。当然也可以单独指定某一个JSON格式的文件。
  • refresh_interval: 60m,自定义刷新间隔时间为60秒。

3、Prometheus中的标签重写机制

(1)、Prometheus中标签的概念

在Prometheus所有的Target监控实例中,都包含一些默认的Metadata标签信息。可以通过Prometheus UI的Targets页面中查看这些实例的Metadata标签的内容

在这里插入图片描述

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

上面这些标签将会告诉Prometheus如何从Target实例中获取监控数据。除了这些默认的标签以外,我们还可以为Target添加自定义的标签。

一般来说,Target以__作为前置的标签是在系统内部使用的,因此这些标签不会被写入到样本数据中。不过这里有一些例外,比如,指标上的instance标签的默认值就来自于_address__标签的值。这里实际上是发生了一次标签的重写处理。

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

(2)、relabel_configs的使用

relabel_configs常用的几个参数如下:

  • source_labels: 源标签,没有经过relabel处理之前的名字
  • target_labels: 通过action处理之后的新标签名字
  • regex: 正则表达式,匹配源标签
  • replacement: 通过分组替换后标签(target_label)对应的值
  • action:执行的动作。支持: replace,drop,keep,labelmap,labeldrop,labelkeep,hashmod

下面我简单列举一下action里面每个 relabel_action 的作用:

relabel_action 参数含义
replace 根据 regex 的配置,匹配 source_labels 标签的值,并且将匹配到的值写入到 target_label 当中,如果有多个匹配组,则可以使用 ${1}, ${2} 确定写入的内容。如果没匹配到任何内容,则不对 target_label 进行重新, 默认为 replace。
keep 丢弃 source_labels 的值中没有匹配到 regex 正则表达式内容的 Target 实例
drop 丢弃 source_labels 的值中匹配到 regex 正则表达式内容的 Target 实例
hashmod 将 target_label 设置为关联的 source_label 的哈希模块
labelmap 根据 regex 去匹配 Target 实例所有标签的名称(注意是名称),并且将捕获到的内容作为新的标签名称,regex 匹配到标签的的值,作为新标签的值
labeldrop 对 Target 标签进行过滤,会移除匹配过滤条件的所有标签
labelkeep 对 Target 标签进行过滤,会移除不匹配过滤条件的所有标签

例子1:给target增加标签

修改prometheus.yml,增加如下内容:

  - job_name: 'prometheus'
    static_configs:
    - targets: [ 'localhost:9090']
      labels:
        env: 'prod'
        __hostname__: 'localhost'

然后执行:# curl -X POST http://172.16.213.35:9090/-/reload

此操作会在原来的标签基础之上又增加了两个标签。
注意,内部标签默认是不显示的,只有把鼠标移动到Labels那里才会显示。同时,要注意__开头的标签是不会写到metrics指标里面的,因为这属于系统内置标签。env这种没有__作为前缀的,是可以写到metrics指标中的。

例子2:将target的初始标签里面的值替换到新的标签中

修改prometheus.yml,增加如下内容:

  - job_name: 'prometheus'
    static_configs:
    - targets: [ 'localhost:9090']
    relabel_configs:
      - source_labels: [ '__address__' ]
        target_label:  'addr'
      - source_labels: [ '__metrics_path__' ]
        target_label:  'path'

然后执行: # curl -X POST http://172.16.213.35:9090/-/reload

上面直接替换了两个标签,如果只替换一个标签,就不用写两个source_lables了,还有一点要注意,目标标签不能用__标签名 __

如果要替换成指定值,可做如下修改:

    relabel_configs:
      - source_labels: [ '__address__' ]
        target_label:  'addr'
        replacement: 'localhost'

上面就是将__address__替换成addr,然后再让addr=localhost。

如果要使用正则匹配替换,可做如下修改:

   relabel_configs:
      - source_labels: [ '__address__' ]
        target_label:  'addr'
        regex: "(.*):(.*)"
        replacement: $1

上面就是将localhost:9090通过正则截成两段,然后将第一段交给replacement去替换addr的值。

当然也可以使用下面的写法:

   relabel_configs:
    - action: replace
      source_labels: [ '__address__' ]
      regex: "(.*):(.*)"
      replacement: $1
      target_label:  'addr'

例子3:使用labelmap标签名替换

labelmap: 根据 regex 去匹配 Target 实例所有标签的名称(注意是名称),并且将捕获到的内容作为为新的标签名称,regex 匹配到标签的的值作为新标签的值.当然这个新的标签也会加到样本数据中。

 relabel_configs:
      - source_labels: [ '__metrics_path__' ]
        regex: __metrics_(.+)__   #这相当于将path截取出来作为新的标签名称
        action: labelmap

或者下面的方式

 relabel_configs:
      - regex:  __metrics_(.+)__
        action: labelmap

最终会新增一个path="/metrics"的新标签。

下面是这个博客的重点哦

基于consul的服务发现

Consul是基于Go语言开发的开源工具,提供服务注册,服务发现,健康检查,Key/Value存储,多数据中心和分布式一致性保证等一系列功能。

之前我们呢通过prometheus实现监控,当新增加一个Target时,需要变更服务器上的配置文件,即使使用了file_sd_configs配置,也需要登录服务器修改对应的json文件会十分的麻烦,不过prometheus官方支持多种自动服务发现的类型,其中就支持consul

  • 安装consul
    • 这里我们使用docker的方式快速简单的启动和运行。
  • Consul安装很方便,官网(https://www.consul.io/downloads) 提供各个系统版本二进制安装包,解压安装即可,同时也可以通过 Docker 来快速安装。这里我在172.16.213.26主机上,执行如下命令docker方式安装:
[root@docker-110 ~]#  docker run -d -p 8500:8500 \
> --restart=always \
> -v /data/consul/data:/consul/data \
> --name=consul \
> hashicorp/consul:1.15.2 agent \
> -server \
> -bootstrap \
> -ui \
> -node=dc1 \
> -client='0.0.0.0'
Unable to find image 'hashicorp/consul:1.15.2' locally

1.15.2: Pulling from hashicorp/consul
f56be85fc22e: Pull complete 
d7d6b9886c5f: Pull complete 
996f67d6a296: Pull complete 
6368458efd23: Pull complete 
716242560a20: Pull complete 
4f4fb700ef54: Pull complete 
6b40a7781f0c: Pull complete 
Digest: sha256:c2169f3bb18dd947ae8eb5f6766896695c71fb439f050a3343e0007d895615b8
Status: Downloaded newer image for hashicorp/consul:1.15.2
4d25f24d12c2d6c651b2980efbca4597c80a4c1bbd2170adb1a8402985db1b45

  • agent :表示启动Agent进程

  • server: 表示启动Consul Server 模式

  • client:表示启动Consul Client模式

  • bootstrap: 表示这个节点时server_Leader,每一个数据中心只能运行一台服务器,技术角度上讲Leader 是通过 Raft 算法选举的,但是集群第一次启动时需要一个引导 Leader,在引导群集后,建议不要使用此标志。

  • ui: 表示启动 Web UI 管理器,默认开放端口 8500,所以上面使用 Docker 命令把 8500 端口对外开放。

  • node: 节点的名称,集群中必须是唯一的,默认是该节点的主机名。

  • client: consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0

这里就可以访问呢10.0.0.110:8500

在这里插入图片描述

默认情况下这里就只有一个consul本身的services服务

通过api注册服务到Consul

  • Consul服务注册提供了两种注册方法:一种是定义配置文件服务注册方法,即在配置文件中定义服务来进行注册;一种是HTTP API服务注册方法,即在启动服务后,自身通过调用API进行自我注册。

  • 这里介绍第二种,consul主要是添加和删除命令,都是使用接口调用。先创建test.json文件,内容如下:

    {
            "id": "docker-110",
            "name": "docker-110-10.0.0.110",
            "address": "10.0.0.110",
            "port": 9100,
            "tags": ["test"],
            "checks": [{
                    "http": "http://10.0.0.110:9100/metrics",
                    "interval": "5s"
            }]
    }
    

注意这里的test.json时在你敲命令的当前目录

curl --request PUT --data @test.json http://10.0.0.110:8500/v1/agent/service/register

执行完毕后,刷新一下 Consul Web 控制台页面,可以看到成功注册到 Consul 中。

在这里插入图片描述

要删除这个服务,可执行如下命令:

$ curl --request PUT  http://172.16.213.26:8500/v1/agent/service/deregister/node-exporter

注意,url最后的node-exporter是注册对应的ID,删除哪个服务,就指定哪个ID。

为了报红

原因是他的这个访问不了

在这里插入图片描述

因此我们知道时目标主机的node_exporter没有起来

在这里插入图片描述

  • 我们刷新后看看

在这里插入图片描述

  • 获取的node节点信息

在这里插入图片描述

配置prometheus实现服务自动发现

现在 Consul 服务已经启动完毕,并成功注册了一个服务,接下来,我们需要配置 Prometheus 来使用 Consul 自动服务发现,目的就是能够将上边添加的服务自动发现到 Prometheus 的 Targets 中,增加 prometheus.yml 配置如下:

  - job_name: 'consul-prometheus'
    consul_sd_configs:
      - server: '10.0.0.110:8500'
        services: [] 

说明一下:

  • consul_sd_configs:表示使用 Consul 服务发现类型
  • server: 为 Consul 的服务地址,这里跟上边要对应上。
  • services: []:这个表示匹配consul中所有的service。

配置完毕后,重启 Prometheus 服务,此时可以通过 Prometheus UI 页面的 Targets 下查看是否配置成功。

可以看到,在 Targets 中能够成功的自动发现 Consul 中的 Services 信息,后期需要添加新的 Targets 时,只需要通过 API 往 Consul 中注册服务即可,Prometheus 就能自动发现该服务,非常方便。

(4)、使用relabel_configs自定义标签

修改刚才的test.json文件,内容如下:

{
  "ID": "node-exporter",
  "Name": "node-exporter-172.16.213.28",
  "Tags": [
    "test"
  ],
  "Address": "172.16.213.28",
  "Port": 9100,
  "Meta": {
    "app": "spring-boot",
    "team": "appgroup",
    "project": "bigdataserver"
  },
  "EnableTagOverride": false,
  "Check": {
    "HTTP": "http://172.16.213.28:9100/metrics",
    "Interval": "10s"
  },
  "Weights": {
    "Passing": 10,
    "Warning": 1
  }
}

参数含义:

  • replace-existing-checks:在重新注册服务时替换现有的检查。
  • Name:指定服务名称,必选。
  • ID:指定服务ID,如果未指定,默认为服务名称。
  • Tags:指定标记,用于筛选。
  • Address:指定服务的地址。
  • Port:指定服务的端口。
  • Meta:指定 KV 元数据。
  • Weights:指定服务的权重。
  • Check:指定健康检测。

说明:

该 Json 文件为要注册的服务信息,同时往 Meta 信息中添加了 app=spring-bootteam=appgroupproject=bigdataserver 三组自定义标签,目的就是为了方便告警分组使用。执行如下命令进行注册:

 $ curl --request PUT --data @test.json http://172.12.213.26:8500/v1/agent/service/register?replace-existing-checks=1

注册完毕,通过 Consul Web 管理页面可以查看到已注册成功,并且包含了 Meta 信息。

然后修改 prometheus.yml 配置如下:

- job_name: 'consul-prometheus'
  consul_sd_configs:
    - server: '172.16.213.26:8500'
      services: []  
  relabel_configs:
    - source_labels: [__meta_consul_tags]
      regex: .*test.*
      action: keep
    - regex: __meta_consul_service_metadata_(.+)
      action: labelmap

(5)、使用relabel_configs将自动发现的服务进行分类

重新修改test.json文件,此类服务实现node-exporter自动注册,内容如下:

{
  "ID": "node-exporter",
  "Name": "node-exporter-172.16.213.28",
  "Tags": [
    "node-exporter"
  ],
  "Address": "172.16.213.28",
  "Port": 9100,
  "Meta": {
    "app": "spring-boot",
    "team": "appgroup",
    "project": "bigdataserver"
  },
  "EnableTagOverride": false,
  "Check": {
    "HTTP": "http://172.16.213.28:9100/metrics",
    "Interval": "10s"
  },
  "Weights": {
    "Passing": 10,
    "Warning": 1
  }
}

然后更新注册服务:

$ curl --request PUT --data @test.json http://172.16.213.26:8500/v1/agent/service/register?replace-existing-checks=1

新增另一个文件docker.json,用于docker类服务的自动发现,内容如下:

{
  "ID": "cadvisor-exporter",
  "Name": "cadvisor-exporter-172.16.213.29",
  "Tags": [
    "cadvisor-exporter"
  ],
  "Address": "172.16.213.29",
  "Port": 8080,
  "Meta": {
    "app": "docker",
    "team": "cloudgroup",
    "project": "docker-service"
  },
  "EnableTagOverride": false,
  "Check": {
    "HTTP": "http://172.16.213.29:8080/metrics",
    "Interval": "10s"
  },
  "Weights": {
    "Passing": 10,
    "Warning": 1
  }
}

继续注册服务到consul:

$ curl --request PUT --data @docker.json http://172.16.213.26:8500/v1/agent/service/register?replace-existing-checks=1

最后,我们修改 prometheus.yml 配置如下:

  - job_name: 'consul-node-exporter'
    consul_sd_configs:
      - server: '172.16.213.26:8500'
        services: []  
    relabel_configs:
      - source_labels: [__meta_consul_tags]
        regex: .*node-exporter.*
        action: keep
      - regex: __meta_consul_service_metadata_(.+)
        action: labelmap
 
  - job_name: 'consul-cadvisor-exproter'
    consul_sd_configs:
      - server: '172.16.213.26:8500'
        services: []
    relabel_configs:
      - source_labels: [__meta_consul_tags]
        regex: .*cadvisor-exporter.*
        action: keep
      - regex: __meta_consul_service_metadata_(.+)
        action: labelmap

这里通过relabel_configs 中配置的 Tags 来做匹配区分,对注册的服务进行分类,重启 Prometheus 服务,可以看到服务已经按照类型分类了,方便查看。

虽然prometheus 使用consul的服务发现做监控,不对prometheus 做修改了,但是,我们每次还要手动去注册服务,感觉很麻烦,于是呢,来个终极方法,分享一个脚本,自动去consul注册服务,自动检测服务状态,还能卸载consul里面的服务:

#!/bin/bash
 
#usage:
#param1:check_service or register_service or deregister_service
#param2: an exist file,and the file content format is as follows,pay attention to remove the first #
#group_name cluster_name service_ip service_port
 
 
#注册服务
function register_service()
{
cat $1 | while read line
do
 
    group_name=$(echo $line | awk '{print $1}')
    cluster_name=$(echo $line | awk '{print $2}')
    service_ip=$(echo $line | awk '{print $3}')
    service_port=$(echo $line | awk '{print $4}')
     
cat >service-$service_ip-$service_port.json<<EOF
{
    "id":"service-${service_ip}-${service_port}",
    "name":"service-${service_ip}-${service_port}",
    "address":"${service_ip}",
    "port":${service_port},
    "tags":["$cluster_name"],
    "meta":{"group":"$group_name","cluster":"$cluster_name","instance_name":"$service_ip:$service_port"},
    "checks":[{
        "http":"http://${service_ip}:$service_port/metrics",
        "interval":"5s"
    }],
    "Weights": {
        "Passing": 10,
        "Warning": 1
    },
    "EnableTagOverride": false
}
EOF
curl --request PUT --data @service-$service_ip-$service_port.json \
http://${consul_host}:${consul_http_api_port}/v1/agent/service/register?replace-existing-checks=1
 
rm -rf service-$service_ip-$service_port.json
done
}
 
 
#注销服务
function deregister_service()
{
cat $1 | while read line
do
    service_ip=$(echo $line | awk '{print $3}')
    service_port=$(echo $line | awk '{print $4}')
    #let service_port=redis_port+10000
    consul_service_id="service-$service_ip-$service_port"
    curl --request PUT  http://${consul_host}:${consul_http_api_port}/v1/agent/service/deregister/${consul_service_id}
done
}
 
#检查服务
check_service()
{
cat $1 | while read line
do
    service_ip=$(echo $line | awk '{print $3}')
    service_port=$(echo $line | awk '{print $4}')
    #let service_port=redis_port+10000
    consul_service_id="service-$service_ip-$service_port"
    ret1=$(curl -s http://${consul_host}:${consul_http_api_port}/v1/agent/service/$consul_service_id?pretty)
    if [[ $ret1 =~ "unknown proxy service ID" ]];then
             echo "不存在服务$consul_service_id"
    else
         ret2=$(curl -s http://${consul_host}:${consul_http_api_port}/v1/health/service/service-$service_ip-$service_port?pretty \
         | grep "$service_ip:$service_port/metrics: 200 OK")
         if [[ -z "$ret2" ]];then
            echo -e "\033[5;34m服务$consul_service_id已注册,但服务状态异常  <---\033[0m"
         else
            echo "服务$consul_service_id已注册,且服务状态正常"
         fi
    fi
done
}
 
function main()
{
if [[ $# -ne 2 ]];then
    echo -e "must two parameters:\nfirst exec_command(check_service or register_service or deregister_service)\nsecond filename"
    exit
else
    case $1 in
    check_service)
        shift 1
        check_service $1
        ;;
    register_service)
        shift 1
        register_service $1
        ;;
    deregister_service)
        shift 1
        deregister_service $1
        ;;
    *)
        echo "无效的指令"
    esac
fi
}
 
consul_host="172.16.213.26"
consul_http_api_port=8500
main $1 $2

将上面脚本命名为consul-register.sh,脚本执行方式:

[root@docker-server consul]# ./consul-register.sh 
must two parameters:
first exec_command(check_service or register_service or deregister_service)
second filename

第一个参数可从check_service or register_service or deregister_service选择,对应检测服务、注册服务、注销服务。

第二个参数对应一个文件,文件名随便,内容格式为:

group_name cluster_name service_ip service_port

例如,consul-service文件内容如下:

dbgroup  mysql-exports 172.16.213.27 9104

每个部分,用空格分开。下面是一个执行例子:

[root@docker-server consul]# ./consul-register.sh   check_service consul-service 
服务service-172.16.213.27-9104已注册,且服务状态正常
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐