{D4} - Prometheus的Relabeling机制

写这篇文章的目的是解释Prometheus relabeling的价值,以及它在整个prometheus数据流不同阶段的重要性。

在面向大型互联网公司、金融行业等大规模业务监控的场景,原生的 Prometheus 单实例模式无法直接满足需求,需要一种面向生产环境的集群化高可用方案来进行支撑。

Prometheus 常见的集群化高可用思路包括两种:

  • Prometheus 联邦集群方案,是基于Prometheus单体模式的一种补充。可以让一系列Prometheus的单体分别采集不同的目标,然后将数据统一汇总到中央的Prometheus集群服务中。在这种该方案中,中央的 Prometheus 仍然为单体模式,实际的存储能力仍然会受到单机的限制。所以在构建联邦模式时,需要根据数据量,对第一层的 Prometheus 所采集到的数据进行一些聚合计算,将减少后的数据传输到中央 Prometheus 中。故这种方案只能适用于最终需要分析的数据量偏少的场景。同时该方案本身的维护成本颇高,需要对集群中的每一个 Prometheus 分片采集配置、预计算配置进行管理,如要访问除中央 Prometheus 集群中的数据,还需记录每一个 Prometheus 分片的访问路由。
  • Prometheus 远端存储方案远端存储可以提供相比单体 Prometheus,突破了单机的资源限制,能够提供更大规模和更长时间的存储能力。通过这种方式,整体集群的管理会更加简单,只需要对 Prometheus 设置分片采集,统一将数据导入远端存储即可,配置管理成本也会降低许多。

基于以上分析,可以判断出在大规模的业务监控场景下,一种基于远端存储的Prometheus 方案是必须的。但对于远端存储,仍然有如下问题需要解决:

远端存储本身仍然有容量的上限限制,针对数据存储时长、性能等要求,并不可能无限扩容。存储的数据量越大,查询分析的压力也就会越大,一些涉及聚合计算曲线较多的查询、预计算规则(Record Rule)、报警规则(Alert Rule)所带来的大量查询,仍然会将整个集群拖垮。

我们从两个角度,来构建出一套解决方案:

  • 减少指标量级:借鉴 Prometheus 联邦的思路,从采集层做预聚合,来减少指标量级,这个需要从业务角度来进行分析,如何针对交易量类型的指标,缩减指标量级。

  • 提升架构性能:从架构实现角度,如何解决单纯远端存储无法解决的大规模数据分析和报警检测的需求。

对于中小型公司来说提升计算架构性能可能成本相对过高,今天我们先重点讨论第一种方案,通过重新标记(指标降维和重组)减少Prometheus指标级的使用。

在正式进入正题之前,我们先来回顾下Prometheus标签

之前的讲解中其实我们已经多次提到标签这个概念,这里我们在回顾下。标签是一组键值对,允许我们描述和组织在 Prometheus 指标中实际测量的内容。键值标签对的每个唯一组合都在 Prometheus 中存储为一个新的时间序列,因此标签对于理解数据的基数至关重要,应避免使用无界值集作为标签。

例如,在测量HTTP延迟时,我们可能会使用标签来记录 HTTP 路径和返回的状态以及哪个服务器负责请求。

http_requests_time{code="200", path="/api/v1/query", instance="10.0.0.2"}

内建/隐藏标签

以双下划线__开头的标签属于特殊的标签(before relabeling),它们在重新标记后会被删除,标记对象的来源最初可以附加这些隐藏的标签,以提供关于标记对象的额外元数据,这些特殊的标签可以在 relabeling 阶段被用来对target对象的标签进行修改,对于抓取指标,其中就包含一些隐藏的标签,可以用来控制目标应该如何被抓取。

下面是我们可以使用的一些特殊标签是:

标签名称 描述
__name__ 抓取的指标名称
__address__ 主机:抓取目标的端口
__scheme__ 抓取目标的 URI 方案
__metrics_path__ 抓取目标的指标端点
__param_<name> 是传递给目标的第一个 URL 参数的值
__scrape_interval__ 目标的抓取间隔
__scrape_timeout__ 目标的超时时间
__meta_* 服务发现机制设置的特殊标签, 如:__meta_kubernetes_pod_name__meta_kubernetes_pod_ready
__tmp 用于在丢弃标签值之前临时存储标签值的特殊前缀

Relabeling概述

Relabeling 是一个强大的工具,它允许你通过重写标签集来分类和过滤PrometheusTarget/目标Metric/指标

首先我们大体上要明确俩个思路如何将指标量级降维和重组:

  • 要保留的数据:这涉及保留一组明确定义的重要指标和标签,并删除其他所有内容。要将指标和标签列入白名单,首先应该明确哪些核心指标和标签是希望保留的。要在Prometheus中启用白名单,需要配合relabeling action的keeplabelkeep来操作。

  • 可丢弃的数据:这涉及删除你需要明确定义的一组不重要指标,并保留其他所有指标。一旦确定了要删除的高基数指标和标签列表,拒绝名单就可能需要被使用。要在 Prometheus 中启用黑名单,需要配合relabeling action的droplabeldrop来操作。

这两种方法都是通过Prometheus的metric过滤和重新标记功能实现的relabel_config。这里你可以使用正则表达式过滤系列标签并保留或删除匹配的标签。

接下来,我们开始深入了解下Prometheus中的relabel的4种方式,应用范围和工作时段,以及它是如何relabel_config操作的。

relabel_config
在被prometheus抓取之前修改,针对的是target

metric_relabel_configs
在被prometheus存储之前修改,针对的是metric

alert_relabel_configs
在被发送到alertmanager之前,针对的是alert

write_relabel_configs
写到远程存储的样本

Relabeling (Life of Relabeling)

这个示例配置文件演示了每种relabel配置操作在Prometheus配置中的位置:

global:
...

rule_files:
...

scrape_configs:
  - job_name: sample_job_1
    kubernetes_sd_configs:
    - ...
    relabel_configs:           # <-
    - source_labels: [...]
       ...
    - source_labels: [...]
      ...
    metric_relabel_configs:    # <-
    - source_labels: [...]
      ...
    - source_labels: [...]
      ...
  - job_name: sample_job_2
    static_configs:
    - targets: [...]
    metric_relabel_configs:    # <-
    - source_labels: [...]
      ...

alerting:
  alert_relabel_configs:       # <-
    [ - <relabel_config> ... ]
  alertmanagers:
    [ - <alertmanager_config> ... ]

remote_write:
- url: ...
  write_relabel_configs:       # <-
  - source_labels: [...]
    ...
  - source_labels: [...]
    ...

Relabeling 规则

Prom Labs 的Relabeler工具在调试relabeling配置时可能会有所帮助,它可以让你直观地确认relabel配置实施的规则。

Relabeling 规则主要由以下的一些配置属性组成:

参数 解释
action 执行的relabeling动作,可选值包括 replacekeepdrophashmodlabelmaplabeldroplabelkeep,默认值为replace
separator 分隔符,一个字符串,用于在连接源标签source_labels时分隔它们,默认为;
source_labels 源标签,使用配置的分隔符串联的标签名称列表,并与提供的正则表达式进行匹配
target_label 目标标签,当使用replace或者hashmod动作时,应该被覆盖的标签名
regex 正则表达式,用于匹配串联的源标签,默认为(.*)
modulus 模数,串联的源标签哈希值的模,主要用于Prometheus水平分片
replacement 写在目标标签上,它可以参考由 regex 捕获的正则表达式捕获组,默认值为$1

关于relabel_config的action类型说明:

参数 解释
replace 设置或替换/覆盖标签值,是默认的action
keep 满足特定条件的target进行采集,其他的不采集
drop 满足特定条件的target不采集,其他的采集
labeldrop 对抓取的实例特定标签进行删除,其他的保留
labelkeep 对抓取的实例特定标签进行保留,其他标签删除
labelmap 将源标签的值映射到一组新的标签中去
hashmod 对服务的整体目标进行分片
# metric
my_counter_total{server="web01",project="dadou"} 192  1644075044000
my_counter_total{server="cache01",project="dadou"} 147  1644075044000

1.source_labels and separator

# relabel_config:
source_labels: [project, server]
separator: "@"

# after relabeling:
dadou@web01
dadou@cache01

2.replacement

如果提取的值与给定的正则表达式匹配,则replacement通过执行正则表达式替换并利用任何先前定义的捕获组来填充。

# relabel_config:
source_labels: [project, server]
separator: "@"
regex: "(.*)@(.*)"
replacement: "${2}+${1}"

# after relabeling:
web01+dadou
cache01+dadou

3.target_label

# relabel_config:
source_labels: [project, server]
separator: "@"
regex: "(.*)@(.*)"
replacement: "${2}/${1}"
target_label: "my_new_label"
action: "replace"

# after relabeling:
{my_new_label="web01/dadou"}
{my_new_label="cache01/dadou"}

4.keep/drop

保留和删除操作允许我们根据我们的标签值是否与提供的正则表达式匹配来过滤掉目标和指标。

配置规则如下:

action: keep|drop
source_labels: [<source label name list>]
separator: <source label separator>
regex: <regular expression>

keep/drop操作同样按顺序执行如下步骤:

1.使用`separator`分隔符将`source_labels`中列出的标签值连接起来
2.测试`regex`中的正则表达式是否与上一步的连接字符串匹配
3.如果不匹配,该对象将从最终输出列表中删除,如果匹配,则保留或丢弃该对象

实际样例:

# relabel_config 1:dadou
source_labels: [project, server]
separator: "@"
regex: "dadou@web"
action: "drop"

# relabel_config 2:
source_labels: [project, server]
separator: "@"
regex: "dadou@(.*)"
action: keep

# relabel_config 3:
source_labels: [__name__]
regex: "my_custom_counter_total|my_custom_counter_sum|my_custom_gauge"
action: keep

# relabel_config 4:
source_labels: [__meta_kubernetes_namespace]
regex: "testing|staging"
action: drop

5.labelkeep/labeldrop

labelkeep和labeldrop操作允许过滤标签集本身。

# relabel_config 1:
regex: "project"
action: labeldrop

# relabel_config 2:
regex: "project|server|shard"
action: labelkeep

6.replace

# 设置一个固定的标签值
# relabel_config 1:
action: replace
replacement: production
target_label: env

# 替换标签的值
# relabel_config 2:
action: replace
source_labels: [__meta_kubernetes_pod_name,__meta_kubernetes_pod_container_port_number]
separator: ":"
target_label: address

7.hashmod

hashmod 操作提供了一种水平扩展Prometheus的机制。重新标记步骤以正整数N为模计算连接标签值的MD5哈希,得到范围[0, N-1]内的数字。

# metric
my_custom_metric{name="node",val="42"} 100

# relabel_config
action: hashmod
source_labels: [name, val]
separator: "-"
modulus: 8
target_label: __tmp_hashmod

# 连接的结果是字符串“node-42”,字符串模数 8 的 MD5 是5
$ python3
>>> import hashlib
>>> m = hashlib.md5(b"node-42")
>>> int(m.hexdigest(), 16) % 8
5
# 所以最终{__tmp=5}会被附加到指标的标签集中。
#
# 这最常用于在一组Prometheus实例中对多个目标进行分片。
# 以下规则可用于在8个Prometheus实例之间分配负载,
# 每个实例负责抓取最终在`[0, 7]` 范围内
# 产生特定值的目标子集,并忽略所有其他实例。

# relabel_config
action: keep
source_labels: [__tmp_hashmod]
regex: 5

8.labelmap

labelmap 最常用的使用场景就是从服务发现中获取一组隐藏的或临时的元数据标签,并将它们映射到新的目标标签中。和前面的一些 action 不同,labelmap 是对标签名而不是标签值进行重新匹配和操作,labelmap 按顺序执行以下步骤:

1.将regex中的正则表达式与所有标签名进行匹配
2.将匹配的标签名的任何匹配值复制到由`replacement`字符串决定的新的标签名中

配置规则如下:

action: labelmap
regex: <regular expression>
replacement: <replacement string>

这里我们使用Prometheus的Kubernetes SD的例子:

    __meta_kubernetes_node_name: The name of the node object.
    __meta_kubernetes_node_provider_id: The cloud provider's name for the node object.
    __meta_kubernetes_node_address_<address_type>: The first address for each node address type, if it exists.
…
    __meta_kubernetes_namespace: The namespace of the service object.
    __meta_kubernetes_service_external_name: The DNS name of the service. (Applies to services of type ExternalName)
    __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_pod_name: The name of the pod object.
    __meta_kubernetes_pod_ip: The pod IP of 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.

在应用重新标记步骤后,Prometheus 将删除以双下划线开头的标签,我们可以labelmap通过将它们映射到不同的名称来保留它们。

action: labelmap
regex: "__meta_kubernetes_(.*)"
replacement: "k8s_${1}"

最后,提供一些Prometheus中重新标记的常见场景

  • 当您想忽略一部分应用程序时,使用 relabel_config
  • 在多个Prometheus服务器之间拆分目标时,使用 relabel_config + hashmod
  • 当您想忽略高基数指标的子集时,使用 metric_relabel_config
  • 当向不同的端点发送不同的指标时,使用 write_relabel_config

好了,今天就到这里,Happy-995

参考或使用文档: