服务发现及打标
一、服务发现
在之前,我们去配置一个node_exporter都是自己直接静态配置在prometheus.yml配置文件当中的,通过定义job_name定义,如果节点发生变化,比如新增节点,那我们就不得不编辑prometheus.yml配置文件,并重载prometheus配置才能完成,这些都需要手动去进行,很显然,对于一个小规模的环境下是可以的;
但是在遇到大规模的监控环境情况下,一个一个手动的去配置,很显然是不可取的,尤其考虑到现在动态容器环境当中,每一个容器都可能是我们的需要抓取的端点,容器很轻量化,随时创建又随时可能会被销毁,在这种常见当中,手动配置prometheus.yml配置文件来添加target,很显然不是一个理想的选择;
static_configs这种静态配置只是Prometheus支持的众多配置target一种而已,它还支持在动态环境当中的发现机制,因此Prometheus为此还专门设计来一组服务发现机制,以便于能够基于服务注册中心自动发现、检测、分类可以监控的各Target,以便更新发生了变动的Target;
上述的SD实际上就是Service Discovery,可以认为它就是Prometheus的注册中心,整个系统当中,每个可以被监控的Target,要想被纳入到Prometheus的监控体系当中,那么他们都应该将自己的相关信息注册到SD当中,Prometheus就通过从SD读取各个节点的相关信息,然后Prometheus会自动将其加载到Prometheus监控系统中来,而不需要用户静态的配置;
当然,既然有注册中心,那么肯定有故障检测,每一个服务在启动时会将自己注册到注册中心上,而且会周期性的去更新自己的注册信息,如果不更新了,注册中心就会将那些过期的注册条目删除,从而Prometheus也就会知道这个Target离线了,然后Prometheus也会将这个Target从自己的监控系统中移除;
在Prometheus当中,最为简单的服务发现配置就是文件,说白了就是一个文件里面指明了哪些是Target,所以Prometheus启动起来之后,会加载这个文件,并周期性的刷新这个文件,我们只要改了这个文件,都会被Prometheus识别并加载到当前Prometheus监控系统当中,这是基于文件的服务发现,它只不过比传统的方式进步了而已,无需人为的重启Prometheus服务;
除此之外,基于DNS也可以进行服务发现,同时也可以基于API的服务发现,但不管是哪一种这种服务发现的功能,应该都比纯静态配置要有效得多得多,更何况对于一个主机来讲,除了Node_Exporter之外,如果还运行了一个MySQL,那可能我们还需要运行一个MySQL_Exporter;
二、抓取的生命周期
指标抓取生命周期,也就意味着Prometheus为了完成指标抓取会经过哪些步骤
- 第一步要先做服务发现,因为这个指标一定属于某个Target,所以要经由服务发现这个步骤,将Target添加到Prometheus监控对象中;
- 第二步,对于对应的监控对象来讲,需要配置这个监控对象独有的配置,比如指标抓取周期等;
- 第三,我们可以实现对这个对应的Target进行重新打标,当标签不符合我们的期望时,我们可以添加标签、移除标签等,
- 第四,数据抓取;
- 第五,对于抓取过来的数据,我们还可以对Metrics指标进行重新打标;
服务发现返回一个目标列表,其中包含一组称为元数据的标签,这些标签以__meta__为前缀,此外服务发现还会根据目标的配置来设置其他标签,这些标签带有__的前缀和后缀,包括
__scheme__、__address__和__metrics_path__。他们分别保存有Target支持使用协议、Target的地址及指标的URL路径;
对于发现的各个目标,Prometheus提供来可以重新标记(relabel)目标的机会,它定义在job配置段的relabel_config配置中,常用语实现过滤目标或者将来自服务发现的元数据标签中的信息附加到指标的标签上;
而对于抓取来的指标,在保存之前,还允许用于对指标重新打标和过滤,它定义在job配置端的metric_relabel_config配置中,常用于删除不必要的指标、从指标中删除敏感或不需要的标签和添加、编辑或修改标签值或标签格式;
每个标签通常都有一个默认值。例如,__metrics_path__默认为/metrics,__scheme__默认为http。此外,如果路径中存在任何URL参数,则它们的前缀会设置为__param_*。
配置标签会在抓取的生命周期中重复利用以生成其他标签。例如,指标上instance标签的默认内容是__address__标签的内容。
# 覆盖标签内容
- job_name: "docker"
scheme: https
metrics_path: /moremetrics
static_configs:
- targets: ["192.168.0.232:9091"]
# 通过targets查看时,元属性就变成了: https://192.168.0.232:9091/moremetrics
三、服务发现
Prometheus可以集成到多种多种不同的开源服务发现工具上,以动态发现需要监控的目标,如Consul、Eureka、Zookeeper、Serverset或者Airbnb Nerve等,同时Prometheus也可以很好的集成到目前主流的Kubernetes容器编排平台上,通过其API Server动态发现各类被监控的对象,如Pod、Server、Endpoint、Ingress和Node对象,同时Prometheus基于DNS或者文件的动态发现机制;
3.1、基于文件的服务发现
基于文件的服务发现是仅仅略优于静态配置的服务发现的方式,它不依赖于任何平台或第三方服务,因而也是最为简单和通用的实现方式,Prometheus Server可以定期从指定文件中加载Target信息;
文件可以使用JSON和YAML格式,它含有定义的Target列表,以及可选的标签信息,下面的配置,能将Prometheus默认的静态配置转为基于文件的服务发现时所需要的配置;
- targets:
- localhost:9090
labels:
app: prometheus
job: prometheus
- targets:
- node1.cce.com:9100
- node2.cce.com:9100
labels:
app: node-expoter
job: node
服务发现还需要在主配置当中启用服务发现,并指定需要加载的服务发现的文件列表以及服务发现间隔时间;
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- target:
rule_files:
scrape_configs:
- job_name: 'prometheus'
file_sd_configs:
- files:
- targets/prometheus*.yaml # 指定要加载的文件列表,支持通配符
refresh_interval: 2m # 每个2分钟重新加载一次文件中定义的Target,默认为5m
- job_name: 'nodes'
....
3.2、基于DNS服务发现
基于DNS服务发现指的是,在DNS服务器上,将每一个被监控对象,给它生成一个SRV记录就行了,正常情况下基于DNS的服务发现就是Prometheus针对一组DNS域名进行定期查询,以发现待监控的目标;
查询时所使用的DNS服务器由/etc/resolv.conf文件指定,Prometheus的基于DNS的服务发现还要依赖于A、AAAA、和SRA记录,且仅支持该类方法,尚不支持RFC6763中的高级DNS服务发现方式;
基于DNS服务发现的方式,默认提供几个元数据标签,_meta_dns_name、_meta_dns_srv_record_target、_meta_dns_srv_record_port;
# 示例:_prometheus._tcp.magedu.com
# dns_sd_config # 配置段
# A list of DNS domain names to be queried.
names:
[ - <string> ]
# The type of DNS query to perform. One of SRV, A, or AAAA.
[ type: <string> | default = 'SRV' ]
# The port number used if the query type is not SRV.
[ port: <int>]
# The time after which the provided names are refreshed.
[ refresh_interval: <duration> | default = 30s ]
3.3、基于Kubernetes API的服务发现
Prometheus基于Kubernetes API支持,将APIServer中的Node、Service、Endpoint、Pod和Ingress等资源类型下相应的各类资源对象视作target,并持续监控相关的资源变动;
需要注意的是Node、Service、Endpoint、Pod和Ingress资源分别由各自的服务发现机制进行定义,发现Node是noderole,这每一个资源发现称之为一个role,这个role和Kubernetes的RBAC中的role是两回事,这是Prometheus自己内部用来分别发现不同的资源类型时使用的一个发现器,发现Service的则是service-role;
同时,我们还可以基于Kubernetes集群的DaemonSet在每一个Kubernetes节点上部署一个node-exporter来监控节点,并且Prometheus也能自动发现;
3.3.1、Node资源发现
Prometheus针对Kubernetes的node role会将Kubernetes集群中的每一个节点(安装了kubelet的节点)视作一个Target,node role会依次检索节点规范上的NodeInternalIP、NodeInternalIPs、NodeExternalIP、NodeLegacyHost和NodeHostName字段,并将发现的第一个地址作为目标地址(__address__);
同时,Prometheus针对Kubernetes的node role默认情况下会有如下几个元数据标签;
__meta.kubernetes_node_name # 节点名称
__meta.kubernetes_node_label_<labelname> # 节点的资源的标签
__meta.kubernetes_node_labelpresent_<labelname> # 节点资源标签是否存在
__meta.kubernetes_node_annotation_<annotation> # 节点的注解
__meta.kubernetes_node_annotationpresen_<annotation> # 节点资源注解是否存在
__meta.kubernetes_node_address_<address_type> # 节点地址是什么类型的地址,是NodeExternalIP还是NodeInternalIP等;
**注意:**节点上instance标签的取值是来自于__meta.kubernetes_node_name而不是__address__的值;
3.3.2、Pod资源发现
Pod资源发现是通过pod role来发现的,Pod和Node不同在于,Node只监听一个端口,而Pod可能会监听多个端口,所以,Prometheus的pod role负责发现Kubernetes集群中的每一个Pod资源,并且将其容器暴露为Target,它会将Pod声明的每一个端口,都当作一个Target;
并且,如果遇到一些没有端口的容器,还会将其添加为"无端口"类型的Target,以便于用户通过relabel机制进行手动添加端口;
每一个通过pod role发现的Target都会有如下几个元数据标签;
__meta_kubernetes_namespace: The namespace of the pod object.
__meta_kubernetes_pod_name: The name of the pod object.
__meta_kubernetes_pod_ip: The pod IP of the pod object.
__meta_kubernetes_pod_label_<labelname>: Each label from the pod object.
__meta_kubernetes_pod_labelpresent_<labelname>: truefor each label from the pod object.
__meta_kubernetes_pod_annotation_<annotationname>: Each annotation from the pod object.
__meta_kubernetes_pod_annotationpresent_<annotationname>: true for each annotation from the pod object.
__meta_kubernetes_pod_container_init: true if the container is an InitContainer
__meta_kubernetes_pod_container_name: Name of the container the target address points to.
__meta_kubernetes_pod_container_port_name: Name of the container port.
__meta_kubernetes_pod_container_port_number: Number of the container port.
__meta_kubernetes_pod_container_port_protocol: Protocol of the container port.
__meta_kubernetes_pod_ready: Set to true or false for the pod's ready state.
__meta_kubernetes_pod_phase: Set to Pending, Running, Succeeded, Failed or Unknown in the lifecycle.
__meta_kubernetes_pod_node_name: The name of the node the pod is scheduled onto.
__meta_kubernetes_pod_host_ip: The current host IP of the pod object.
__meta_kubernetes_pod_uid: The UID of the pod object.
__meta_kubernetes_pod_controller_kind: Object kind of the pod controller.
__meta_kubernetes_pod_controller_name: Name of the pod controller.
3.3.3、Service资源发现
对于Kubernetes的Serivce而言,Service上的每一个端口,都会被Prometheus视为一个Target,Target地址为Service的DNS名称及相应的端口;
每一个通过service role发现的Target都会有如下几个元数据标签;
__meta_kubernetes_namespace: The namespace of the service object.
__meta_kubernetes_service_annotation_<annotationname>: Each annotation from the service object.
__meta_kubernetes_service_annotationpresent_<annotationname>: "true" for each annotation of the service object.
__meta_kubernetes_service_cluster_ip: The cluster IP address of the service. (Does not apply to services of type ExternalName)
__meta_kubernetes_service_external_name: The DNS name of the service. (Applies to services of type ExternalName)
__meta_kubernetes_service_label_<labelname>: Each label from the service object.
__meta_kubernetes_service_labelpresent_<labelname>: true for each label of the service object.
__meta_kubernetes_service_name: The name of the service object.
__meta_kubernetes_service_port_name: Name of the service port for the target.
__meta_kubernetes_service_port_protocol: Protocol of the service port for the target.
__meta_kubernetes_service_type: The type of the service.
3.3.4、Endpoint资源发现
对于Kubernetes的Endpoint而言,Prometheus从各个Endpoint发现目标,每个Endpoint可能不止一个端口,而每个Endpoint的端口都视作一个Target;
需要注意的是,如果Endpoint的后端工作负载是Pod,则会将该Pod上,其他未绑定到该Endpoint的端口,同样视作一个单独的目标,
每一个通过endpoint role发现的Target都会有如下几个元数据标签;
__meta_kubernetes_namespace: The namespace of the endpoints object.
__meta_kubernetes_endpoints_name: The names of the endpoints object.
# 对于通过附加在Endpoint资源上端口发现的各target,还有如下meta标签
__meta_kubernetes_endpoint_hostname: Hostname of the endpoint.
__meta_kubernetes_endpoint_node_name: Name of the node hosting the endpoint.
__meta_kubernetes_endpoint_ready: Set to true or false for the endpoint's ready state.
__meta_kubernetes_endpoint_port_name: Name of the endpoint port.
__meta_kubernetes_endpoint_port_protocol: Protocol of the endpoint port.
__meta_kubernetes_endpoint_address_target_kind: Kind of the endpoint address target.
__meta_kubernetes_endpoint_address_target_name: Name of the endpoint address target.
# 如果发现的Endpoint资源属于某个Service,则相关Service的元标签也会添加到该Endpoint上;
# 如果发现的Endpoint资源的后段端点是Pod,则相关Pod的元标签也会添加到该Endpoint上;
3.3.5、Ingress资源发现
对于Kubernetes的Ingress而言,Prometheus会将Ingress的每个path作为一个Target,每一个通过endpoint role发现的Target都会有如下几个元数据标签;
__meta_kubernetes_namespace: The namespace of the ingress object.
__meta_kubernetes_ingress_name: The name of the ingress object.
__meta_kubernetes_ingress_label_<labelname>: Each label from the ingress object.
__meta_kubernetes_ingress_labelpresent_<labelname>: true for each label from the ingress object.
__meta_kubernetes_ingress_annotation_<annotationname>: Each annotation from the ingress object.
__meta_kubernetes_ingress_annotationpresent_<annotationname>: true for each annotation from the ingress object.
__meta_kubernetes_ingress_scheme: Protocol scheme of ingress, https if TLS config is set. Defaults to http.
__meta_kubernetes_ingress_path: Path from ingress spec. Defaults to /.
3.3.6、基于Kubernetes的服务发现配置
Prometheus基于Kubernetes的服务发现的配置参数是比较复杂的,如下;
# The information to access the Kubernetes API.
# The API server addresses. If left empty, Prometheus is assumed to run inside
# of the cluster and will discover API servers automatically and use the pod's
# CA certificate and bearer token file at /var/run/secrets/kubernetes.io/serviceaccount/.
[ api_server: <host> ]
# The Kubernetes role of entities that should be discovered.
# One of endpoints, service, pod, node, or ingress.
role: <string>
# Optional authentication information used to authenticate to the API server.
# Note that `basic_auth` and `authorization` options are
# mutually exclusive.
# password and password_file are mutually exclusive.
# Optional HTTP basic authentication information.
basic_auth:
[ username: <string> ]
[ password: <secret> ]
[ password_file: <string> ]
# Optional the `Authorization` header configuration.
authorization:
# Sets the authentication type.
[ type: <string> | default: Bearer ]
# Sets the credentials. It is mutually exclusive with
# `credentials_file`.
[ credentials: <secret> ]
# Sets the credentials with the credentials read from the configured file.
# It is mutually exclusive with `credentials`.
[ credentials_file: <filename> ]
# Optional OAuth 2.0 configuration.
# Cannot be used at the same time as basic_auth or authorization.
oauth2:
[ <oauth2> ]
# Optional proxy URL.
[ proxy_url: <string> ]
# Configure whether HTTP requests follow HTTP 3xx redirects.
[ follow_redirects: <bool> | default = true ]
# TLS configuration.
tls_config:
[ <tls_config> ]
# Optional namespace discovery. If omitted, all namespaces are used.
namespaces:
names:
[ - <string> ]
四、标签
标签提供了时间序列的维度。它们可以定义目标,并为时间序列提供上下文。但最重要的是,结合指标名称,它们构成了时间序列的标识,如果它们改变了,那么时间序列的标识也会跟着改变。
更改或添加标签会创建新的时间序列,这意味着应该明智地使用标签并尽可能保持不变,如果修改会使得后续的监控变得极为复杂,如警报,警报依赖于指标的标签来进行操作
4.1、Target重新打标
在正常服务发现的过程中,一旦基于服务发现发现之后,我们要配置这个Target同时也可以在对这个Target进行指标抓取之前对Target自己的标签做重新标记,重新标记完成之后就可以抓取了,抓取完成之后对抓取过来的指标数据,也可以做重新标记;
**重新标记,**即重新打标,所谓重新打标有集中操作,删除标签、修改标签值、更改标签名等等;
对Target进行重新打标是在数据抓取之前的,在每个数据抓取配置中,可以定义多该relabel步骤,他们将按照定义的顺序依次执行对于发现的每个Target,Prometheus默认会执行几个操作,
- 将job标签的值设定为,job_name的值
- 将__address__的标签值,设定为该Target的套接字地址
- instance的标签值为__address__的值
- 将__schme__标签的值设定为抓取该Target指标时使用的协议
- 将__metrics_path__标签的的设定为抓取该Target上的指标时使用的路径
- 将__param__标签的值设定为传递URL参数中第一个名称为的值;
重新打标期间,还可以使用该Target以上的“__meta_”开头的元标签,另外重新标记完成之后,该Target以“__”开头的所有标签都会移除,所以relabel的过程中需要临时存储标签,则要使用"__tmp"标签名为前缀进行保存,以避免同Prometheus的内建标签冲突;
# 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: <int> ]
# 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 ] # 此配置对标签做何种操作,默认为将source_targets的值替换target_label
<relabel_config>字段用于定义重新标记,其可用取之如下
替换标签值
replace:首先将source_labels中指定的各标签值进行串联,而后将regex字段中的正则表达式对源标签值进行匹配判定,若匹配,则将target_label字段中指定的标签值替换为replacement字段中的值;
replacement可按需引用保存在regex中的某个“分组模式”匹配到的值,默认保存整个regex匹配的内容;
进行值替换时,replacement字段中指定标签的值也支持以分组格式进行引用;
hashmod:将target_label的值设置为一个hash值,该hash值由modules字段指定的hash模对source_labels上各标签的串联值进行hash计算生成的;
删除指标(该处的每个指标名称对应一个Target)
keep:regex不能匹配到target上的source_labels上各标签的串联值时,则删除该target;
drop:regex能匹配到target上的source_labels上各标签的串联值时,则删除该target;
创建或删除标签
labelmap:将regex对所有的标签名进行匹配判定,而后将匹配到的标签的值赋给replacement字段指定的标签吗之上,通常用于取出匹配的标签名的一部分生成新的标签;
labeldrop:将regex对所有的标签名进行匹配判定,能够匹配到的标签将从该Target的标签集中删除;
labelkeep:将regex对所有的标签名进行匹配判定,不能够匹配到的标签将从该Target的标签集中删除;
# 需要注意的是,要确保labeldrop或labelkeep操作后,余下的标签集依然能唯一标示该指标;
4.2、标签分类
- 拓扑标签(topological label): 通过其物理或逻辑组成来切割服务组件, 如每个指标都天然带有两个拓扑标签:job和instance
- 模式标签(schematic label): url、error_code或user之类的东西,它们允许你将拓扑中同一级别的
时间序列匹配在一起,例如创建数据间的比率
4.3、标签添加、替换
4.3.1、标签添加
-
标签添加1
- job_name: 'zookeeper_exporter_targets' static_configs: - targets: ["x.x.x.x:9143"] labels: service: "cluster"
-
标签添加2
# 下列示例,将regex指定的模式对Target上的所有标签进行匹配判定,对于匹配到的标签名,它将以该标签名中匹配的部分为前缀,并手动指定"_name"为后缀,生成新的标签名,而新标签的值,与其源标签值相同,并删除源标签; - job_name: 'nodes' file_sd_configs: - files: - targets/node*.yaml refresh_interval: 30s relabel_configs: - regex: "^(job|app).*" replacement: "${1}_name" action: labelmap # - regex: "^(job|app)$" # 当存在注释时,添加的app原标签不会变删除 # action: labeldrop
4.3.2、替换
# 下面示例,将三个源标签的值进行串联后,由指定的正则表达式进行模式匹配,而后由replacement引用模式匹配的结果,并加一改造后,将其赋值给endpoint标签;
- job_name: 'nodes'
file_sd_configs:
- files:
- targets/node*.yaml
refresh_interval: 30s
relabel_configs:
- source_labels:
- __scheme__
- __address__
- __metrics_path__
regex: "(http|https)(.*)"
separator: ""
target_label: "endpoint"
replacement: "${1}://${2}"
action: replace
cat targets/node-exporter.yaml
- targets:
- 192.168.0.236:9100
labels:
job: node_exporter
4.3.3、删除Target
如下示例,通过正则表达式匹配Target,删除名称node名称大于1的target;
- job_name: 'nodes'
file_sd_configs:
- files:
- targets/node*.yaml
refresh_interval: 30s
relabel_configs:
- source_labels:
- __address__
regex: "^node[2-9].*"
action: drop # 与 labeldrop没啥区别
4.4、Metrics重新打标
Metrics重新打标其实和Target重新打标的执行逻辑是一样的,所不同的地方仅在于Metrics是对抓取的到Metric进行重新打标,而对Target打标是对Target进行重新打标的,同样的逻辑,可以删除指标,可以修改指标名称,可以创建新的指标标签并给它赋予一个值,这个和Target重新打标是一摸一样的,操作语法也都一样,示例如下;
# 使用正则表达式来匹配需要删除的Metrics名
metric_relabel_configs:
- source_labels:
- __name__
regex: '(go_gc_duration_seconds|go_info)' # 匹配要删除的标签名称,如果有多个正则,用分号隔开`;`
action: drop
# 将所有Metric的instance标签值复制给cce标签
metric_relabel_configs:
- source_labels:
- instance
regex: '(.*)'
replacement: $1
target_label: cce
action: replace
# 删除标签
metric_relabel_configs:
- source_labels:
regex: 'instance'
action: labeldrop
5、prometheus规划
prometheus的性能很难估计,它很大程度上取决于配置、所收集的时间序列的数量以及服务器上规则的复杂性,一般容器规划关注两个问题: 内存和磁盘
5.1、内存
使用粗略的经验法则是将每秒采集的样本乘以样本的大小,可以使用 rate(prometheus_tsdb_head_samples_appended_total[1m])
来查看样本收集率,这显示的是最后一分钟添加到数据库的每秒样本率,如果想知道收集的指标数量,则可以使用sum(count by (__name__) ({__name__=~".+"}))
rate(prometheus_tsdb_head_samples_appended_total[1m]):{instance="localhost:9090", job="prometheus"}
35.045223227182824
sum(count by (__name__) ({__name__=~".+"})) :
# 使用=~运算符和.+正则表达来匹配所有指标
{} 526 # 所有指标的总数,每个样本的大小通常为1到2个字节,按照2个字节算,假设在12小时内每秒收集100000个样本,那么算出的结果
10,0000 * 2bytes * 43200 seconds, 那么就需要 8.64G内存
也可以通过process_resident_memory_bytes
来查看当前prometheus进程的内存情况
5.2、磁盘
磁盘使用量受存储的时间序列数量和这些时间序列的保留时间限制。默认情况下,指标会在本地时间序列数据库中存储15天,数据库位置和保留时间由命令行选项控制
--storage.tsdb.path
: 默认数据目录位于运行prometheus的目录中,用于控制TSDB的位置--storage.tsdb.retention
: 控制时序的保留期,默认15天
对于每秒10万个样本的示例,如果按每个样本在磁盘上占用1到2个字节算,那么保留15天的时间序列意味着需要大约259GB的磁盘