prometheus主动服务发现机制
prometheus主动服务发现机制
简介
- prometheus是基于pull模式抓取监控数据,首先要能够发现需要监控的目标对象target,那么prometheus如何获取数据的监控,目前有2种方式,静态手工配置和动态服务发现配置。
-
- 对于小型的系统环境来说,可以通过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-exporterjob: nodes
接着,将这两个文件放到当前的targets目录下(先创建targets目录),最后,修改prometheus.yml 配置文件,增加如下内容:
- job_name: 'node_service_discovery'file_sd_configs:- files:- targets/*.jsonrefresh_interval: 60s- files:- targets/*.yamlrefresh_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: replacesource_labels: [ '__address__' ]regex: "(.*):(.*)"replacement: $1target_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' locally1.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-boot
,team=appgroup
,project=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
dogroup_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=1rm -rf service-$service_ip-$service_port.json
done
}#注销服务
function deregister_service()
{
cat $1 | while read line
doservice_ip=$(echo $line | awk '{print $3}')service_port=$(echo $line | awk '{print $4}')#let service_port=redis_port+10000consul_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
doservice_ip=$(echo $line | awk '{print $3}')service_port=$(echo $line | awk '{print $4}')#let service_port=redis_port+10000consul_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" ]];thenecho "不存在服务$consul_service_id"elseret2=$(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" ]];thenecho -e "\033[5;34m服务$consul_service_id已注册,但服务状态异常 <---\033[0m"elseecho "服务$consul_service_id已注册,且服务状态正常"fifi
done
}function main()
{
if [[ $# -ne 2 ]];thenecho -e "must two parameters:\nfirst exec_command(check_service or register_service or deregister_service)\nsecond filename"exit
elsecase $1 incheck_service)shift 1check_service $1;;register_service)shift 1register_service $1;;deregister_service)shift 1deregister_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已注册,且服务状态正常