Prometheus 是一套开源的完整监控解决方案,对传统监控系统的测试和告警模型进行了彻底的颠覆,形成了基于中央化的规则计算、统一分析和告警的新模型。 Prometheus 采用 Pull 模型通过 HTTP 协议去采集指标,只要应用能够提供 HTTP 接口就可以接入到监控系统,相比于私有协议或二进制协议来说开发、简单。本文总结梳理了在使用 Prometheus 时候的关键概念和用法,关于 Prometheus Exporter 编写方法可以参考 这篇文章

相比于传统监控系统, Prometheus 具有以下优点:

  • 易于管理:只有一个单独的二进制文件,不存在任何的第三方依赖,采用 Pull 的方式拉取数据
  • 强大的数据模型:每一条时间序列由指标名称(Metrics Name)以及一组标签(Labels)唯一标识
  • 强大的查询语言 PromQL:内置了一个强大的数据查询语言 PromQL,可以实现多种查询、聚合
  • 高性能:单实例可以处理数以百万的监控指标、每秒处理数十万的数据点
  • 易扩展:支持 sharding 和联邦集群,实现多数据中心
  • 易集成:支持多种语言的 SDK 进行应用程序数据埋点,社区有丰富插件
  • 可视化:自带 Prometheus UI,可以进行查询与展示,Grafana 也完整支持 Prometheus。
  • 开放性:使用 sdk 采集的数据可以被其他监控系统使用,不一定非要用 Prometheus

系统架构

Prometheus 可以采用 Pull 模型从 Exporter 拉取数据,或者间接从 Push Gateway 拉取数据,然后通过一定规则进行清理和整理数据,并把得到的结果存储到新的时间序列中。采集到的数据有两个去向,一个是报警,另一个是可视化。PromQL 和其他 API 可视化地展示收集的数据,并通过 Alertmanager 提供报警能力。

注意,Prometheus 一般是通过 Pull 模型拉取数据,这里的 PushGateway 主要是面向 Short-lived jobs,由于这类 jobs 存在时间较短,可能在 Prometheus 来 Pull 之前就消失了。因此,这种 jobs 可以直接向 PushGateway 端推送它们的 metrics,然后 Prometheus 再从 PushGateway 拉取数据。

部署使用

ConfigMap

为了能够容 Prometheus Server 能够从 Node Exporter 等 Exporter 获取到监控数据,需要修改 Prometheus 配置文件,添加 scrape_configs 配置抓取的数据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |
    global:
      scrape_interval:     15s
      evaluation_interval: 15s
    scrape_configs:
      - job_name: 'prometheus'
        static_configs:
        - targets: ['localhost:9090']
     # 采集node exporter监控数据
     - job_name: 'node'
       static_configs:
       - targets: ['localhost:9100']    

Prometheus 配置文件主要分为以下几个部分,在本文的后面会详细介绍其中的关键部分配置,更多内容可以参考 官方文档 。其中 scrape_config 部分指定了一系列需要抓取的 Target 和参数,一般来说,一个 scrape 配置指定了一个 Job,这部分多个scrape_config 以一个列表的形式呈现。Target 可以通过 static_configs 来静态配置,也可以通过 Prometheus 支持的各种服务发现机制来动态配置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
global:
  # How frequently to scrape targets by default.
  [ scrape_interval: <duration> | default = 1m ]

  # How long until a scrape request times out.
  [ scrape_timeout: <duration> | default = 10s ]

  # How frequently to evaluate rules.
  [ evaluation_interval: <duration> | default = 1m ]

  # The labels to add to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
    [ <labelname>: <labelvalue> ... ]

  # File to which PromQL queries are logged.
  # Reloading the configuration will reopen the file.
  [ query_log_file: <string> ]

# Rule files specifies a list of globs. Rules and alerts are read from
# all matching files.
rule_files:
  [ - <filepath_glob> ... ]

# A list of scrape configurations.
scrape_configs:
  [ - <scrape_config> ... ]

# Alerting specifies settings related to the Alertmanager.
alerting:
  alert_relabel_configs:
    [ - <relabel_config> ... ]
  alertmanagers:
    [ - <alertmanager_config> ... ]

# Settings related to the remote write feature.
remote_write:
  [ - <remote_write> ... ]

# Settings related to the remote read feature.
remote_read:
  [ - <remote_read> ... ]

Deployment

当 ConfigMap 资源创建成功后,我们就可以通过 Volume 挂载的方式,将 Prometheus 的配置文件挂载到容器中。 这里我们通过 Deployment 部署 Prometheus Server 实例,创建 prometheus-deployment.yml文件,并写入以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    name: prometheus
  name: prometheus
  namespace: monitoring
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - name: prometheus
        image: prom/prometheus:v2.19.0
        command:
        - /bin/prometheus
        args:
        - --config.file=/etc/config/prometheus.yml
        ports:
        - containerPort: 9090
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/config
          name: prometheus-config
      volumes:
      - name: prometheus-config
        configMap:
          name: prometheus-config

注意,这里通过命令行配置的 --config.file=/etc/config/prometheus.yml指定了 Prometheus 的配置文件,也就是我们在上面创建的 ConfigMap。

Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: v1
kind: Service
metadata:
  name: prometheus
  namespace: monitoring
  labels:
    name: prometheus
spec:
  ports:
  - name: prometheus
    protocol: TCP
    port: 9090
    targetPort: 9090
  selector:
    app: prometheus

Ingress

由于之前配置了 Traefik,所以这里的 Prometheus 服务不使用 NodePort 方式暴露,在本地设置好 prometheus.houmin 的 host 之后,在浏览器中访问:http://prometheus.houmin:<TraefikPort>/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: prometheus
  namespace: monitoring
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: prometheus.houmin
    http:
      paths:
      - path: /
        backend:
          serviceName: prometheus
          servicePort: prometheus

数据模型

通过 Node Exporter 暴露的 HTTP 服务,Prometheus 可以采集到当前主机所有监控指标的样本数据。例如:

# HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",mode="idle"} 362812.7890625
# HELP node_load1 1m load average.
# TYPE node_load1 gauge
node_load1 3.0703125

其中非#开头的每一行表示当前 Node Exporter 采集到的一个监控样本:node_cpu 和 node_load1 表明了当前指标的名称、大括号中的标签则反映了当前样本的一些特征和维度、浮点数则是该监控样本的具体值。

Sample

Prometheus 会将所有采集到的样本数据以时间序列(time-series)的方式保存在内存数据库中,并且定时保存到硬盘上。time-series 是按照时间戳和值的序列顺序存放的,我们称之为 vector。 每条 time-series 通过指标名称(metrics name)和一组标签集(labelset)命名。如下所示,可以将 time-series 理解为一个以时间为 Y 轴的数字矩阵:

1
2
3
4
5
6
7
  ^
  │   . . . . . . . . . . . . . . . . .   . .   node_cpu{cpu="cpu0",mode="idle"}
  │     . . . . . . . . . . . . . . . . . . .   node_cpu{cpu="cpu0",mode="system"}
  │     . . . . . . . . . .   . . . . . . . .   node_load1{}
  │     . . . . . . . . . . . . . . . .   . .
  v
    <------------------ 时间 ---------------->

time-series 中的每一个点称为一个 samplesample 由以下三部分组成:

  • metric:metric name 和描述当前样本特征的 labelsets
  • timestamp:一个精确到毫秒的时间戳
  • value: 一个 float64 的浮点型数据表示当前样本的值
1
2
3
4
5
6
7
8
9
<--------------- metric ---------------------><-timestamp -><-value->
http_request_total{status="200", method="GET"}@1434417560938 => 94355
http_request_total{status="200", method="GET"}@1434417561287 => 94334

http_request_total{status="404", method="GET"}@1434417560938 => 38473
http_request_total{status="404", method="GET"}@1434417561287 => 38544

http_request_total{status="200", method="POST"}@1434417560938 => 4748
http_request_total{status="200", method="POST"}@1434417561287 => 4785

Metric

在形式上,所有的 Metric 都通过如下格式标示:

1
<metric name>{<label name>=<label value>, ...}

其在 Prometheus 源码种表示的数据结构如下:

1
2
3
4
5
type Metric LabelSet

type LabelSet map[LabelName]LabelValue
type LabelName string
type LabelValue string
  • Metric Name 可以反映被监控样本的含义,比如,http_request_total - 表示当前系统接收到的 HTTP 请求总量

  • Label 反映了当前样本的特征维度,通过这些维度 Prometheus 可以对样本数据进行过滤,聚合等。

    • 其中以__作为前缀的标签,是系统保留的关键字,只能在系统内部使用。标签的值则可以包含任何 Unicode 编码的字符。在 Prometheus 的底层实现中指标名称实际上是以__name__=<metric name>的形式保存在数据库中的,因此以下两种方式均表示的同一条 time-series:

      1
      
      api_http_requests_total{method="POST", handler="/messages"}
      

      等同于:

      1
      
      {__name__="api_http_requests_total",method="POST", handler="/messages"}
      

Prometheus 定义了 4 中不同的指标类型:

Counter 只增不减的计数器

计数器,如 http_requests_total请求总数

Gauge 可增可减的仪表盘

当前状态,如 kube_pod_status_ready当前 pod 可用数

Histogram 直方图

在大多数情况下人们都倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统 API 调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些 WEB 页面的响应时间落到中位数的情况,而这种现象被称为 长尾问题

为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10ms 之间的请求数有多少而 10~20ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram 和 Summary 都是为了能够解决这样问题的存在,通过 Histogram 和 Summary 类型的监控指标,我们可以快速了解监控样本的分布情况。

Histogram 由下面四个部分组成,主要用于表示一段时间范围内对数据进行采样(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。

1
2
3
4
- <basename>_bucket{le="<upper inclusive bound>"}
- <basename>_bucket{le="+Inf"}
- <basename>_sum
- <basename>_count

在 Prometheus Server 自身返回的样本数据中,我们还能找到类型为 Histogram 的监控指标 prometheus_tsdb_compaction_chunk_range_bucket

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# HELP prometheus_tsdb_compaction_chunk_range Final time range of chunks on their first compaction
# TYPE prometheus_tsdb_compaction_chunk_range histogram
prometheus_tsdb_compaction_chunk_range_bucket{le="100"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="6400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="25600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="102400"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="409600"} 0
prometheus_tsdb_compaction_chunk_range_bucket{le="1.6384e+06"} 260
prometheus_tsdb_compaction_chunk_range_bucket{le="6.5536e+06"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="2.62144e+07"} 780
prometheus_tsdb_compaction_chunk_range_bucket{le="+Inf"} 780
prometheus_tsdb_compaction_chunk_range_sum 1.1540798e+09
prometheus_tsdb_compaction_chunk_range_count 780

Summary 摘要

Summary 和 Histogram 类似,由下面 3 个部分组成,主要用于表示一段时间内数据采样结果(通常是请求持续时间或响应大小),它直接存储了 quantile 分位值 数据,而不是像 Histogram 需要根据统计区间计算分位值:

- <basename>{quantile="<φ>"}
- <basename>_sum
- <basename>_count

例如,指标 prometheus_tsdb_wal_fsync_duration_seconds 的指标类型为 Summary。 它记录了 Prometheus Server 中 wal_fsync 处理的处理时间,通过访问 Prometheus Server/metrics 地址,可以获取到以下监控样本数据:

1
2
3
4
5
6
7
# HELP prometheus_tsdb_wal_fsync_duration_seconds Duration of WAL fsync.
# TYPE prometheus_tsdb_wal_fsync_duration_seconds summary
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.5"} 0.012352463
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.9"} 0.014458005
prometheus_tsdb_wal_fsync_duration_seconds{quantile="0.99"} 0.017316173
prometheus_tsdb_wal_fsync_duration_seconds_sum 2.888716127000002
prometheus_tsdb_wal_fsync_duration_seconds_count 216

从上面的样本中可以得知当前 Prometheus Server 进行 wal_fsync 操作的总次数为 216 次,耗时 2.888716127000002s。其中中位数(quantile=0.5)的耗时为 0.012352463,9 分位数(quantile=0.9)的耗时为 0.014458005s。

PromQL 查询

PromQL 是 Prometheus 内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。并且被广泛应用在 Prometheus 的日常应用当中,包括对数据查询、可视化、告警处理当中。表达式的结果可以在浏览器中显示为图形,也可以显示为表格数据,或者由外部系统通过 HTTP API 调用。通过 PromQL 用户可以非常方便地查询监控数据,或者利用表达式进行告警配置。比如集群中网络使用:

sum by (name) (rate(container_network_receive_bytes_total{image!=""}[1m]))

下面将介绍 PromQL 的具体语法和使用。

查询时间序列

当我们直接使用监控指标名称查询时,可以查询该指标下的所有时间序列。如:

1
http_requests_total

等同于:

http_requests_total{}

该表达式会返回指标名称为 http_requests_total 的所有时间序列:

1
2
http_requests_total{code="200",handler="alerts",instance="localhost:9090",job="prometheus",method="get"}=(20889@1518096812.326)
http_requests_total{code="200",handler="graph",instance="localhost:9090",job="prometheus",method="get"}=(21287@1518096812.326)

PromQL 还支持用户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式:

  • 完全匹配
  • 正则匹配

PromQL 支持使用=!=两种完全匹配模式:

  • 通过使用label=value可以选择那些标签满足表达式定义的时间序列;
  • 反之使用label!=value则可以根据标签匹配排除时间序列;

例如,如果我们只需要查询所有 http_requests_total 时间序列中满足标签 instancelocalhost:9090 的时间序列,则可以使用如下表达式:

1
http_requests_total{instance="localhost:9090"}

反之使用instance!="localhost:9090"则可以排除这些时间序列:

1
http_requests_total{instance!="localhost:9090"}

除了使用完全匹配的方式对时间序列进行过滤以外,PromQL 还可以支持使用正则表达式作为匹配条件,多个表达式之间使用|进行分离:

  • 使用label=~regx表示选择那些标签符合正则表达式定义的时间序列;
  • 反之使用label!~regx进行排除;

例如,如果想查询多个环节下的时间序列序列可以使用如下表达式:

1
http_requests_total{environment=~"staging|testing|development",method!="GET"}

范围查询

Prometheus 中根据是瞬时还是时间范围,可以分为:

  • 瞬时向量表达式:直接通过类似于 PromQL 表达式http_requests_total查询时间序列时,返回值中只会包含该时间序列中的最新的一个样本值
  • 区间向量表达式:如果我们想过去一段时间范围内的样本数据时,通过时间范围选择器[]进行定义时间选择的范围

例如,通过以下表达式可以选择最近 5 分钟内的所有样本数据:

1
http_requests_total{}[5m]

该表达式将会返回查询到的时间序列中最近 5 分钟的所有样本数据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
http_requests_total{code="200",handler="alerts",instance="localhost:9090",job="prometheus",method="get"}=[
    1@1518096812.326
    1@1518096817.326
    1@1518096822.326
    1@1518096827.326
    1@1518096832.326
    1@1518096837.325
]
http_requests_total{code="200",handler="graph",instance="localhost:9090",job="prometheus",method="get"}=[
    4@1518096812.326
    4@1518096817.326
    4@1518096822.326
    4@1518096827.326
    4@1518096832.326
    4@1518096837.325
]

通过区间向量表达式查询到的结果我们称为区间向量

除了使用 m 表示分钟以外,PromQL 的时间范围选择器支持其它时间单位:

  • s - 秒
  • m - 分钟
  • h - 小时
  • d - 天
  • w - 周
  • y - 年

时间位移操作

在瞬时向量表达式或者区间向量表达式中,都是以当前时间为基准:

1
2
http_request_total{} # 瞬时向量表达式,选择当前最新的数据
http_request_total{}[5m] # 区间向量表达式,选择以当前时间为基准,5分钟内的数据

而如果我们想查询,5 分钟前的瞬时样本数据,或昨天一天的区间内的样本数据呢? 这个时候我们就可以使用位移操作,位移操作的关键字为offset

可以使用 offset 时间位移操作:

1
2
http_request_total{} offset 5m
http_request_total{}[1d] offset 1d

请注意,偏移量修饰符始终需要跟随选择器,即以下是正确的:

1
sum(http_requests_total{method="GET"} offset 5m) // GOOD.

下面是错误的:

1
sum(http_requests_total{method="GET"}) offset 5m // INVALID.

操作符

使用 PromQL 除了能够方便的按照查询和过滤时间序列以外,PromQL 还支持丰富的操作符,用户可以使用这些操作符对进一步的对事件序列进行二次加工。这些操作符包括:数学运算符逻辑运算符布尔运算符等等。

数学运算

例如,我们可以通过指标 node_memory_free_bytes_total 获取当前主机可用的内存空间大小,其样本单位为 Bytes。这是如果客户端要求使用 MB 作为单位响应数据,那只需要将查询到的时间序列的样本值进行单位换算即可:

1
node_memory_free_bytes_total / (1024 * 1024)

node_memory_free_bytes_total 表达式会查询出所有满足表达式条件的时间序列,在上一小节中我们称该表达式为瞬时向量表达式,而返回的结果成为瞬时向量。当瞬时向量与标量之间进行数学运算时,数学运算符会依次作用域瞬时向量中的每一个样本值,从而得到一组新的时间序列。

如果是瞬时向量与瞬时向量之间进行数学运算时,过程会相对复杂一点。 例如,如果我们想根据 node_disk_bytes_writtennode_disk_bytes_read 获取主机磁盘 IO 的总量,可以使用如下表达式:

1
node_disk_bytes_written + node_disk_bytes_read

那这个表达式是如何工作的呢?依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。同时新的时间序列将不会包含指标名称。 该表达式返回结果的示例如下所示:

1
2
{device="sda",instance="localhost:9100",job="node_exporter"}=>1634967552@1518146427.807 + 864551424@1518146427.807
{device="sdb",instance="localhost:9100",job="node_exporter"}=>0@1518146427.807 + 1744384@1518146427.807

PromQL 支持的所有数学运算符如下所示:

  • + (加法)
  • - (减法)
  • * (乘法)
  • / (除法)
  • % (求余)
  • ^ (幂运算)

布尔运算

在 PromQL 通过标签匹配模式,用户可以根据时间序列的特征维度对其进行查询。而布尔运算则支持用户根据时间序列中样本的值,对时间序列进行过滤。

例如,通过数学运算符我们可以很方便的计算出,当前所有主机节点的内存使用率:

1
(node_memory_bytes_total - node_memory_free_bytes_total) / node_memory_bytes_total

而系统管理员在排查问题的时候可能只想知道当前内存使用率超过 95%的主机呢?通过使用布尔运算符可以方便的获取到该结果:

1
(node_memory_bytes_total - node_memory_free_bytes_total) / node_memory_bytes_total > 0.95
  • 瞬时向量与标量进行布尔运算时,PromQL 依次比较向量中的所有时间序列样本的值,如果比较结果为 true 则保留,反之丢弃。
  • 瞬时向量与瞬时向量直接进行布尔运算时,同样遵循默认的匹配模式:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行相应的操作,如果没找到匹配元素,则直接丢弃。

目前,Prometheus 支持以下布尔运算符如下:

  • == (相等)
  • != (不相等)
  • > (大于)
  • < (小于)
  • >= (大于等于)
  • <= (小于等于)

集合运算

使用瞬时向量表达式能够获取到一个包含多个时间序列的集合,我们称为瞬时向量。 通过集合运算,可以在两个瞬时向量与瞬时向量之间进行相应的集合操作。目前,Prometheus 支持以下集合运算符:

  • and (并且)
  • or (或者)
  • unless (排除)

vector1 and vector2 会产生一个由 vector1 的元素组成的新的向量。该向量包含 vector1 中完全匹配 vector2 中的元素组成。

vector1 or vector2 会产生一个新的向量,该向量包含 vector1 中所有的样本数据,以及 vector2 中没有与 vector1 匹配到的样本数据。

vector1 unless vector2 会产生一个新的向量,新向量中的元素由 vector1 中没有与 vector2 匹配的元素组成。

操作符优先级

对于复杂类型的表达式,需要了解运算操作的运行优先级。例如,查询主机的 CPU 使用率,可以使用表达式:

1
100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )

其中 irate 是 PromQL 中的内置函数,用于计算区间向量中时间序列每秒的即时增长率。关于内置函数的部分,会在后面详细介绍。

在 PromQL 操作符中优先级由高到低依次为:

  1. ^
  2. *, /, %
  3. +, -
  4. ==, !=, <=, <, >=, >
  5. and, unless
  6. or

匹配模式(联合查询)

向量与向量之间进行运算操作时会基于默认的匹配规则:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。在 PromQL 中有两种典型的匹配模式:

  • 一对一(one-to-one)
  • 多对一(many-to-one)或一对多(one-to-many)
一对一匹配

一对一匹配模式会从操作符两边表达式获取的瞬时向量依次比较并找到唯一匹配(标签完全一致)的样本值。默认情况下,使用表达式:

1
vector1 <operator> vector2

在操作符两边表达式标签不一致的情况下,可以使用 on(label list) 或者 ignoring(label list) 来修改标签的匹配行为:

  • 使用 ignoreing 可以在匹配时忽略某些标签
  • 使用 on 则用于将匹配行为限定在某些标签之内
1
2
<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr>

例如当存在样本:

1
2
3
4
5
6
7
8
9
method_code:http_errors:rate5m{method="get", code="500"}  24
method_code:http_errors:rate5m{method="get", code="404"}  30
method_code:http_errors:rate5m{method="put", code="501"}  3
method_code:http_errors:rate5m{method="post", code="500"} 6
method_code:http_errors:rate5m{method="post", code="404"} 21

method:http_requests:rate5m{method="get"}  600
method:http_requests:rate5m{method="del"}  34
method:http_requests:rate5m{method="post"} 120

使用 PromQL 表达式:

1
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m

该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在所有请求中的比例。如果没有使用 ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。

因此结果如下:

1
2
{method="get"}  0.04            //  24 / 600
{method="post"} 0.05            //   6 / 120

同时由于 method 为 put 和 del 的样本找不到匹配项,因此不会出现在结果当中。

多对一和一对多

多对一和一对多两种匹配模式指的是一侧的每一个向量元素可以与多侧的多个元素匹配的情况。在这种情况下,必须使用 group 修饰符:group_left 或者group_righ t 来确定哪一个向量具有更高的基数(充当“多”的角色)。

1
2
3
4
<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr>

多对一和一对多两种模式一定是出现在操作符两侧表达式返回的向量标签不一致的情况。因此需要使用 ignoringon 修饰符来排除或者限定匹配的标签列表。例如,使用表达式:

1
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m

该表达式中,左向量method_code:http_errors:rate5m包含两个标签 method 和 code。而右向量method:http_requests:rate5m中只包含一个标签 method,因此匹配时需要使用 ignoring 限定匹配的标签为 code。 在限定匹配标签后,右向量中的元素可能匹配到多个左向量中的元素 因此该表达式的匹配模式为多对一,需要使用 group 修饰符 group_left 指定左向量具有更好的基数。

最终的运算结果如下:

1
2
3
4
{method="get", code="500"}  0.04            //  24 / 600
{method="get", code="404"}  0.05            //  30 / 600
{method="post", code="500"} 0.05            //   6 / 120
{method="post", code="404"} 0.175           //  21 / 120

提醒:group 修饰符只能在比较和数学运算符中使用。在逻辑运算 and,unless 和 or 才注意操作中默认与右向量中的所有元素进行匹配。

聚合操作

一般来说,如果描述样本特征的 label 在并非唯一的情况下,通过 PromQL 查询数据,会返回多条满足这些特征维度的时间序列。而 PromQL 提供的聚合操作可以用来对这些时间序列进行处理,形成一条新的时间序列:

1
2
3
4
5
6
7
8
# 查询系统所有http请求的总量
sum(http_request_total)

# 按照mode计算主机CPU的平均使用时间
avg(node_cpu) by (mode)

# 按照主机查询各个主机的CPU使用率
sum(sum(irate(node_cpu{mode!='idle'}[5m])) / sum(irate(node_cpu[5m]))) by (instance)

Prometheus 还提供了下列内置的聚合操作符,这些操作符作用域瞬时向量。可以将瞬时表达式返回的样本数据进行聚合,形成一个具有较少样本值的新的时间序列:

  • sum (求和)
  • min (最小值)
  • max (最大值)
  • avg (平均值)
  • stddev (标准差)
  • stdvar (标准方差)
  • count (计数)
  • count_values (对 value 进行计数)
  • bottomk (后 n 条时序)
  • topk (前 n 条时序)
  • quantile (分位数)

使用聚合操作的语法如下:

1
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]

其中只有count_values, quantile, topk, bottomk支持参数(parameter)。这些操作符被用于聚合所有标签维度,或者通过 without 或者 by 子语句来保留不同的维度。

  • without用于从计算结果中移除列举的标签,而保留其它标签
  • by 则正好相反,结果向量中只保留列出的标签,其余标签则移除

通过 without 和 by 可以按照样本的问题对数据进行聚合。例如:

1
sum(http_requests_total) without (instance)

等价于

1
sum(http_requests_total) by (code,handler,job,method)

如果只需要计算整个应用的 HTTP 请求总量,可以直接使用表达式:

sum(http_requests_total)

count_values 用于时间序列中每一个样本值出现的次数。count_values 会为每一个唯一的样本值输出一个时间序列,并且每一个时间序列包含一个额外的标签。例如:

count_values("count", http_requests_total)

topk 和 bottomk 则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。

获取 HTTP 请求数前 5 位的时序样本数据,可以使用表达式:

topk(5, http_requests_total)

quantile 用于计算当前样本数据值的分布情况quantile(φ, express)其中 0 ≤ φ ≤ 1

例如,当 φ 为 0.5 时,即表示找到当前样本数据中的中位数:

1
quantile(0.5, http_requests_total)

内置函数

Prometheus 还提供了其它大量的内置函数,可以对时序数据进行丰富的处理。本小节将带来读者了解一些常用的内置函数以及相关的使用场景和用法。

计算 Counter 指标增长率

我们知道 Counter 类型的监控指标其特点是只增不减,在没有发生重置(如服务器重启,应用重启)的情况下其样本值应该是不断增大的。为了能够更直观的表示样本数据的变化剧烈情况,需要计算样本的增长速率。

如下图所示,样本增长率反映出了样本变化的剧烈程度:

通过增长率表示样本的变化情况
通过增长率表示样本的变化情况

increase(v range-vector) 函数是 PromQL 中提供的众多内置函数之一。其中参数 v 是一个区间向量,increase 函数获取区间向量中的第一个和最后一个样本并返回其增长量。因此,可以通过以下表达式 Counter 类型指标的增长率:

1
increase(node_cpu[2m]) / 120

这里通过 node_cpu[2m] 获取时间序列最近两分钟的所有样本,increase 计算出最近两分钟的增长量,最后除以时间 120 秒得到 node_cpu 样本在最近两分钟的平均增长率。并且这个值也近似于主机节点最近两分钟内的平均 CPU 使用率。

除了使用 increase 函数以外,PromQL 中还直接内置了rate(v range-vector)函数,rate 函数可以直接计算区间向量 v 在时间窗口内平均增长速率。因此,通过以下表达式可以得到与 increase 函数相同的结果:

1
rate(node_cpu[2m])

需要注意的是使用 rate 或者 increase 函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在 2 分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致 CPU 占用 100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

为了解决该问题,PromQL 提供了另外一个灵敏度更高的函数 irate(v range-vector)。irate 同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate 函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过 irate 函数绘制的图标能够更好的反应样本数据的瞬时变化状态。

1
irate(node_cpu[2m])

irate 函数相比于 rate 函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate 的这种灵敏度反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用 rate 函数。

预测 Gauge 指标变化趋势

在一般情况下,系统管理员为了确保业务的持续可用运行,会针对服务器的资源设置相应的告警阈值。例如,当磁盘空间只剩 512MB 时向相关人员发送告警通知。 这种基于阈值的告警模式对于当资源用量是平滑增长的情况下是能够有效的工作的。 但是如果资源不是平滑变化的呢? 比如有些某些业务增长,存储空间的增长速率提升了高几倍。这时,如果基于原有阈值去触发告警,当系统管理员接收到告警以后可能还没来得及去处理问题,系统就已经不可用了。 因此阈值通常来说不是固定的,需要定期进行调整才能保证该告警阈值能够发挥去作用。 那么还有没有更好的方法吗?

PromQL 中内置的 predict_linear(v range-vector, t scalar) 函数可以帮助系统管理员更好的处理此类情况,predict_linear 函数可以预测时间序列 v 在 t 秒后的值。它基于简单线性回归的方式,对时间窗口内的样本数据进行统计,从而可以对时间序列的变化趋势做出预测。例如,基于 2 小时的样本数据,来预测主机可用磁盘空间的是否在 4 个小时候被占满,可以使用如下表达式:

predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0

统计 Histogram 指标的分位数

前面我们介绍了 Prometheus 的四种监控指标类型,其中 Histogram 和 Summary 都可以用于统计和分析数据的分布情况。区别在于 Summary 是直接在客户端计算了数据分布的分位数情况。而 Histogram 的分位数计算需要通过 histogram_quantile(φ float, b instant-vector) 函数进行计算。其中 φ(0<φ<1)表示需要计算的分位数,如果需要计算中位数 φ 取值为 0.5,以此类推即可。

以指标 http_request_duration_seconds_bucket 为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# HELP http_request_duration_seconds request duration histogram
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.5"} 0
http_request_duration_seconds_bucket{le="1"} 1
http_request_duration_seconds_bucket{le="2"} 2
http_request_duration_seconds_bucket{le="3"} 3
http_request_duration_seconds_bucket{le="5"} 3
http_request_duration_seconds_bucket{le="+Inf"} 3
http_request_duration_seconds_sum 6
http_request_duration_seconds_count 3

当计算 9 分位数时,使用如下表达式:

histogram_quantile(0.5, http_request_duration_seconds_bucket)

通过对 Histogram 类型的监控指标,用户可以轻松获取样本数据的分布情况。同时分位数的计算,也可以非常方便的用于评判当前监控指标的服务水平。

获取分布直方图的中位数
获取分布直方图的中位数

需要注意的是通过 histogram_quantile 计算的分位数,并非为精确值,而是通过 http_request_duration_seconds_buckethttp_request_duration_seconds_sum 近似计算的结果。

动态标签替换

一般来说来说,使用 PromQL 查询到时间序列后,可视化工具会根据时间序列的标签来渲染图表。例如通过 up 指标可以获取到当前所有运行的 Exporter 实例以及其状态:

1
2
3
up{instance="localhost:8080",job="cadvisor"}    1
up{instance="localhost:9090",job="prometheus"}    1
up{instance="localhost:9100",job="node"}    1

这是可视化工具渲染图标时可能根据,instance 和 job 的值进行渲染,为了能够让客户端的图标更具有可读性,可以通过 label_replace 标签为时间序列添加额外的标签。label_replace 的具体参数如下:

label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)

该函数会依次对 v 中的每一条时间序列进行处理,通过 regex 匹配 src_label 的值,并将匹配部分 relacement 写入到 dst_label 标签中。如下所示:

label_replace(up, "host", "$1", "instance",  "(.*):.*")

函数处理后,时间序列将包含一个 host 标签,host 标签的值为 Exporter 实例的 IP 地址:

1
2
3
up{host="localhost",instance="localhost:8080",job="cadvisor"}    1
up{host="localhost",instance="localhost:9090",job="prometheus"}    1
up{host="localhost",instance="localhost:9100",job="node"} 1

除了 label_replace 以外,Prometheus 还提供了 label_join 函数,该函数可以将时间序列中 v 多个标签 src_label 的值,通过 separator 作为连接符写入到一个新的标签 dst_label 中:

label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...)

label_replace 和 label_join 函数提供了对时间序列标签的自定义能力,从而能够更好的于客户端或者可视化工具配合。

其它内置函数

除了上文介绍的这些内置函数以外,PromQL 还提供了大量的其它内置函数。这些内置函数包括一些常用的数学计算、日期等等。这里就不一一细讲,感兴趣的读者可以通过阅读 Prometheus 的官方文档,了解这些函数的使用方式。

API 访问

Prometheus 当前稳定的 HTTP API 可以通过 /api/v1 访问,根据瞬时数据查询和区间范围查询请求字段可分为:

  • /api/v1/query
  • /api/v1/query_range

错误状态码:

  • 404 Bad Request:当参数错误或者缺失时。
  • 422 Unprocessable Entity 当表达式无法执行时。
  • 503 Service Unavailiable 当请求超时或者被中断时。

所有的 API 请求均使用以下的 JSON 格式:

1
2
3
4
5
6
7
8
{
  "status": "success" | "error",
  "data": <data>,

  // 为error时,有如下报错信息
  "errorType": "<string>",
  "error": "<string>"
}

瞬时数据查询

URL 请求参数:

  • query=:PromQL 表达式。
  • time=:用于指定用于计算 PromQL 的时间戳。可选参数,默认情况下使用当前系统时间。
  • timeout=:超时设置。可选参数,默认情况下使用-query,timeout 的全局设置。
1
$ curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'

返回:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
   "status" : "success",
   "data" : {
      "resultType" : "vector",
      "result" : [
         {
            "metric" : {
               "__name__" : "up",
               "job" : "prometheus",
               "instance" : "localhost:9090"
            },
            "value": [ 1435781451.781, "1" ]
         },
         {
            "metric" : {
               "__name__" : "up",
               "job" : "node",
               "instance" : "localhost:9100"
            },
            "value" : [ 1435781451.781, "0" ]
         }
      ]
   }
}

区间范围查询

URL 请求参数:

  • query=: PromQL 表达式。
  • start=: 起始时间。
  • end=: 结束时间。
  • step=: 查询步长。
  • timeout=: 超时设置。可选参数,默认情况下使用-query,timeout 的全局设置。
1
$ curl 'http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s'

返回:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
   "status" : "success",
   "data" : {
      "resultType" : "matrix",
      "result" : [
         {
            "metric" : {
               "__name__" : "up",
               "job" : "prometheus",
               "instance" : "localhost:9090"
            },
            "values" : [
               [ 1435781430.781, "1" ],
               [ 1435781445.781, "1" ],
               [ 1435781460.781, "1" ]
            ]
         },
         {
            "metric" : {
               "__name__" : "up",
               "job" : "node",
               "instance" : "localhost:9091"
            },
            "values" : [
               [ 1435781430.781, "0" ],
               [ 1435781445.781, "0" ],
               [ 1435781460.781, "1" ]
            ]
         }
      ]
   }
}

服务发现

与传统的监控系统需要向中心监控服务 Push 监控数据不同,Prometheus 基于 Pull 模型从 Target 拉取监控的数据。传统的 Push 模型中,每个监控 Target 需要知道中心监控服务的地址,然后才可以推送。而 Prometheus 不需要给每个监控 Target 配置 Prometheus 服务的地址,这种开放性极大地减少了监控 Target 对于监控系统的绑定。

然而,为了支持 Pull 模型,Prometheus 还是得知道每个监控 Target 的地址,这就是服务发现机制。在 Kubernetes 这种容器平台中,Kubernetes 掌管着所有容器与服务的信息,这时候 Prometheus 只需要和 Kubernetes 打交道即可。在其他的场景,也可以有 服务发现和注册中心 来扮演代理人的角色。Prometheus 集成了各种服务发现机制,可以动态配置想要抓取的 Target。

服务发现
服务发现

scrape_config 部分指定了一系列需要抓取的 Target 和参数,一般来说,一个 scrape 配置指定了一个 Job,这部分多个scrape_config 以一个列表的形式呈现。Target 可以通过 static_configs 来静态配置,也可以通过 Prometheus 支持的各种服务发现机制来动态配置。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# The job name assigned to scraped metrics by default.
job_name: <job_name>

# How frequently to scrape targets from this job.
[ scrape_interval: <duration> | default = <global_config.scrape_interval> ]

# Per-scrape timeout when scraping this job.
[ scrape_timeout: <duration> | default = <global_config.scrape_timeout> ]

# The HTTP resource path on which to fetch metrics from targets.
[ metrics_path: <path> | default = /metrics ]

# honor_labels controls how Prometheus handles conflicts between labels that are
# already present in scraped data and labels that Prometheus would attach
# server-side ("job" and "instance" labels, manually configured target
# labels, and labels generated by service discovery implementations).
#
# If honor_labels is set to "true", label conflicts are resolved by keeping label
# values from the scraped data and ignoring the conflicting server-side labels.
#
# If honor_labels is set to "false", label conflicts are resolved by renaming
# conflicting labels in the scraped data to "exported_<original-label>" (for
# example "exported_instance", "exported_job") and then attaching server-side
# labels.
#
# Setting honor_labels to "true" is useful for use cases such as federation and
# scraping the Pushgateway, where all labels specified in the target should be
# preserved.
#
# Note that any globally configured "external_labels" are unaffected by this
# setting. In communication with external systems, they are always applied only
# when a time series does not have a given label yet and are ignored otherwise.
[ honor_labels: <boolean> | default = false ]

# honor_timestamps controls whether Prometheus respects the timestamps present
# in scraped data.
#
# If honor_timestamps is set to "true", the timestamps of the metrics exposed
# by the target will be used.
#
# If honor_timestamps is set to "false", the timestamps of the metrics exposed
# by the target will be ignored.
[ honor_timestamps: <boolean> | default = true ]

# Configures the protocol scheme used for requests.
[ scheme: <scheme> | default = http ]

# Optional HTTP URL parameters.
params:
  [ <string>: [<string>, ...] ]

# Sets the `Authorization` header on every scrape request with the
# configured username and password.
# password and password_file are mutually exclusive.
basic_auth:
  [ username: <string> ]
  [ password: <secret> ]
  [ password_file: <string> ]

# Sets the `Authorization` header on every scrape request with
# the configured bearer token. It is mutually exclusive with `bearer_token_file`.
[ bearer_token: <secret> ]

# Sets the `Authorization` header on every scrape request with the bearer token
# read from the configured file. It is mutually exclusive with `bearer_token`.
[ bearer_token_file: <filename> ]

# Configures the scrape request's TLS settings.
tls_config:
  [ <tls_config> ]

# Optional proxy URL.
[ proxy_url: <string> ]

# List of Azure service discovery configurations.
azure_sd_configs:
  [ - <azure_sd_config> ... ]

# List of Consul service discovery configurations.
consul_sd_configs:
  [ - <consul_sd_config> ... ]

# List of DigitalOcean service discovery configurations.
digitalocean_sd_configs:
  [ - <digitalocean_sd_config> ... ]

# List of Docker Swarm service discovery configurations.
dockerswarm_sd_configs:
  [ - <dockerswarm_sd_config> ... ]

# List of DNS service discovery configurations.
dns_sd_configs:
  [ - <dns_sd_config> ... ]

# List of EC2 service discovery configurations.
ec2_sd_configs:
  [ - <ec2_sd_config> ... ]

# List of Eureka service discovery configurations.
eureka_sd_configs:
  [ - <eureka_sd_config> ... ]

# List of file service discovery configurations.
file_sd_configs:
  [ - <file_sd_config> ... ]

# List of GCE service discovery configurations.
gce_sd_configs:
  [ - <gce_sd_config> ... ]

# List of Hetzner service discovery configurations.
hetzner_sd_configs:
  [ - <hetzner_sd_config> ... ]

# List of Kubernetes service discovery configurations.
kubernetes_sd_configs:
  [ - <kubernetes_sd_config> ... ]

# List of Marathon service discovery configurations.
marathon_sd_configs:
  [ - <marathon_sd_config> ... ]

# List of AirBnB's Nerve service discovery configurations.
nerve_sd_configs:
  [ - <nerve_sd_config> ... ]

# List of OpenStack service discovery configurations.
openstack_sd_configs:
  [ - <openstack_sd_config> ... ]

# List of Zookeeper Serverset service discovery configurations.
serverset_sd_configs:
  [ - <serverset_sd_config> ... ]

# List of Triton service discovery configurations.
triton_sd_configs:
  [ - <triton_sd_config> ... ]

# List of labeled statically configured targets for this job.
static_configs:
  [ - <static_config> ... ]

# List of target relabel configurations.
relabel_configs:
  [ - <relabel_config> ... ]

# List of metric relabel configurations.
metric_relabel_configs:
  [ - <relabel_config> ... ]

# Per-scrape limit on number of scraped samples that will be accepted.
# If more than this number of samples are present after metric relabeling
# the entire scrape will be treated as failed. 0 means no limit.
[ sample_limit: <int> | default = 0 ]

# Per-scrape config limit on number of unique targets that will be
# accepted. If more than this number of targets are present after target
# relabeling, Prometheus will mark the targets as failed without scraping them.
# 0 means no limit. This is an experimental feature, this behaviour could
# change in the future.
[ target_limit: <int> | default = 0 ]

基于 k8s 的服务发现

Kubernetes SD configurations 允许从 Kubernetes 的 REST API 来动态发现需要监控的 target 信息,并且保持与集群状态同步。

下面是 kubernetes_sd_config 的配置模板,在 这里 可以看到 Prometheus 给出的示例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 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`, `bearer_token` and `bearer_token_file` 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 bearer token authentication information.
[ bearer_token: <secret> ]

# Optional bearer token file authentication information.
[ bearer_token_file: <filename> ]

# Optional proxy URL.
[ proxy_url: <string> ]

# TLS configuration.
tls_config:
  [ <tls_config> ]

# Optional namespace discovery. If omitted, all namespaces are used.
namespaces:
  names:
    [ - <string> ]

# Optional label and field selectors to limit the discovery process to a subset of available resources.
# See https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
# and https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ to learn more about the possible
# filters that can be used. Endpoints role supports pod, service and endpoints selectors, other roles
# only support selectors matching the role itself (e.g. node role can only contain node selectors).

# Note: When making decision about using field/label selector make sure that this
# is the best approach - it will prevent Prometheus from reusing single list/watch
# for all scrape configs. This might result in a bigger load on the Kubernetes API,
# because per each selector combination there will be additional LIST/WATCH. On the other hand,
# if you just want to monitor small subset of pods in large cluster it's recommended to use selectors.
# Decision, if selectors should be used or not depends on the particular situation.
[ selectors:
  [ - role: <string>
    [ label: <string> ]
    [ field: <string> ] ]]

Role 的类型可以被配置为以下几种:

  • node
  • pod
  • service
  • Ingress
  • endpoint

node

Prometheus 会自动将获取到 Node 的信息作为 meta labels 写到 Target 数据中:

实例的Metadata信息
实例的Metadata信息

Node 可用的 meta labels 有:

1
2
3
4
5
6
__meta_kubernetes_node_name: The name of the node object.
__meta_kubernetes_node_label_<labelname>: Each label from the node object.
__meta_kubernetes_node_labelpresent_<labelname>: true for each label from the node object.
__meta_kubernetes_node_annotation_<annotationname>: Each annotation from the node object.
__meta_kubernetes_node_annotationpresent_<annotationname>: true for each annotation from the node object.
__meta_kubernetes_node_address_<address_type>: The first address for each node address type, if it exists.

下面是配置示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
scrape_configs:
# Scrape config for nodes (kubelet).
#
# Rather than connecting directly to the node, the scrape is proxied though the
# Kubernetes apiserver.  This means it will work if Prometheus is running out of
# cluster, or can't connect to nodes for some other reason (e.g. because of
# firewalling).
- job_name: 'kubernetes-nodes'

  # Default to scraping over https. If required, just disable this or change to
  # `http`.
  scheme: https

  # This TLS & bearer token file config is used to connect to the actual scrape
  # endpoints for cluster components. This is separate to discovery auth
  # configuration because discovery & scraping are two separate concerns in
  # Prometheus. The discovery auth config is automatic if Prometheus runs inside
  # the cluster. Otherwise, more config options have to be provided within the
  # <kubernetes_sd_config>.
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    # If your node certificates are self-signed or use a different CA to the
    # master CA, then disable certificate verification below. Note that
    # certificate verification is an integral part of a secure infrastructure
    # so this should only be disabled in a controlled environment. You can
    # disable certificate verification by uncommenting the line below.
    #
    # insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

  kubernetes_sd_configs:
  - role: node

  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)

cAdvisor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
scrape_configs:
# Scrape config for Kubelet cAdvisor.
#
# This is required for Kubernetes 1.7.3 and later, where cAdvisor metrics
# (those whose names begin with 'container_') have been removed from the
# Kubelet metrics endpoint.  This job scrapes the cAdvisor endpoint to
# retrieve those metrics.
#
# In Kubernetes 1.7.0-1.7.2, these metrics are only exposed on the cAdvisor
# HTTP endpoint; use the "/metrics" endpoint on the 4194 port of nodes. In
# that case (and ensure cAdvisor's HTTP server hasn't been disabled with the
# --cadvisor-port=0 Kubelet flag).
#
# This job is not necessary and should be removed in Kubernetes 1.6 and
# earlier versions, or it will cause the metrics to be scraped twice.
- job_name: 'kubernetes-cadvisor'

  # Default to scraping over https. If required, just disable this or change to
  # `http`.
  scheme: https

  # Starting Kubernetes 1.7.3 the cAdvisor metrics are under /metrics/cadvisor.
  # Kubernetes CIS Benchmark recommends against enabling the insecure HTTP
  # servers of Kubernetes, therefore the cAdvisor metrics on the secure handler
  # are used.
  metrics_path: /metrics/cadvisor

  # This TLS & bearer token file config is used to connect to the actual scrape
  # endpoints for cluster components. This is separate to discovery auth
  # configuration because discovery & scraping are two separate concerns in
  # Prometheus. The discovery auth config is automatic if Prometheus runs inside
  # the cluster. Otherwise, more config options have to be provided within the
  # <kubernetes_sd_config>.
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    # If your node certificates are self-signed or use a different CA to the
    # master CA, then disable certificate verification below. Note that
    # certificate verification is an integral part of a secure infrastructure
    # so this should only be disabled in a controlled environment. You can
    # disable certificate verification by uncommenting the line below.
    #
    # insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

  kubernetes_sd_configs:
  - role: node

  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)

service

可用的 meata label

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
__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.

配置示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
scrape_configs:

- job_name: 'kubernetes-service-endpoints'

  kubernetes_sd_configs:
  - role: endpoints

  relabel_configs:
  # Example relabel to scrape only endpoints that have
  # "example.io/should_be_scraped = true" annotation.
  #  - source_labels: [__meta_kubernetes_service_annotation_example_io_should_be_scraped]
  #    action: keep
  #    regex: true
  #
  # Example relabel to customize metric path based on endpoints
  # "example.io/metric_path = <metric path>" annotation.
  #  - source_labels: [__meta_kubernetes_service_annotation_example_io_metric_path]
  #    action: replace
  #    target_label: __metrics_path__
  #    regex: (.+)
  #
  # Example relabel to scrape only single, desired port for the service based
  # on endpoints "example.io/scrape_port = <port>" annotation.
  #  - source_labels: [__address__, __meta_kubernetes_service_annotation_example_io_scrape_port]
  #    action: replace
  #    regex: ([^:]+)(?::\d+)?;(\d+)
  #    replacement: $1:$2
  #    target_label: __address__
  #
  # Example relabel to configure scrape scheme for all service scrape targets
  # based on endpoints "example.io/scrape_scheme = <scheme>" annotation.
  #  - source_labels: [__meta_kubernetes_service_annotation_example_io_scrape_scheme]
  #    action: replace
  #    target_label: __scheme__
  #    regex: (https?)
  - action: labelmap
    regex: __meta_kubernetes_service_label_(.+)
  - source_labels: [__meta_kubernetes_namespace]
    action: replace
    target_label: kubernetes_namespace
  - source_labels: [__meta_kubernetes_service_name]
    action: replace
    target_label: kubernetes_name
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
scrape_configs:

- job_name: 'kubernetes-services'

  metrics_path: /probe
  params:
    module: [http_2xx]

  kubernetes_sd_configs:
  - role: service

  relabel_configs:
  # Example relabel to probe only some services that have "example.io/should_be_probed = true" annotation
  #  - source_labels: [__meta_kubernetes_service_annotation_example_io_should_be_probed]
  #    action: keep
  #    regex: true
  - source_labels: [__address__]
    target_label: __param_target
  - target_label: __address__
    replacement: blackbox-exporter.example.com:9115
  - source_labels: [__param_target]
    target_label: instance
  - action: labelmap
    regex: __meta_kubernetes_service_label_(.+)
  - source_labels: [__meta_kubernetes_namespace]
    target_label: kubernetes_namespace
  - source_labels: [__meta_kubernetes_service_name]
    target_label: kubernetes_name

pod

可用的 mata labels

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
__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.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
scrape_configs:
# Example scrape config for pods
#
# The relabeling allows the actual pod scrape to be configured
# for all the declared ports (or port-free target if none is declared)
# or only some ports.
- job_name: 'kubernetes-pods'

  kubernetes_sd_configs:
  - role: pod

  relabel_configs:
  # Example relabel to scrape only pods that have
  # "example.io/should_be_scraped = true" annotation.
  #  - source_labels: [__meta_kubernetes_pod_annotation_example_io_should_be_scraped]
  #    action: keep
  #    regex: true
  #
  # Example relabel to customize metric path based on pod
  # "example.io/metric_path = <metric path>" annotation.
  #  - source_labels: [__meta_kubernetes_pod_annotation_example_io_metric_path]
  #    action: replace
  #    target_label: __metrics_path__
  #    regex: (.+)
  #
  # Example relabel to scrape only single, desired port for the pod
  # based on pod "example.io/scrape_port = <port>" annotation.
  #  - source_labels: [__address__, __meta_kubernetes_pod_annotation_example_io_scrape_port]
  #    action: replace
  #    regex: ([^:]+)(?::\d+)?;(\d+)
  #    replacement: $1:$2
  #    target_label: __address__
  - action: labelmap
    regex: __meta_kubernetes_pod_label_(.+)
  - source_labels: [__meta_kubernetes_namespace]
    action: replace
    target_label: kubernetes_namespace
  - source_labels: [__meta_kubernetes_pod_name]
    action: replace
    target_label: kubernetes_pod_name

可以通过 selectors 来对 pod 进行选择,比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
scrape_configs:
  - job_name: 'kubernetes-pods'
    metrics_path: /metrics
    kubernetes_sd_configs:
    - role: pod
      namespaces:
        names:
          - default
      selectors:
        - role: pod
          label: "app:abc-123"

endpoints

可用的 meta label

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
- __meta_kubernetes_namespace: The namespace of the endpoints object.
- __meta_kubernetes_endpoints_name: The names of the endpoints object.
- For all targets discovered directly from the endpoints list (those not additionally inferred from underlying pods), the following labels are attached:
  - __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.
- If the endpoints belong to a service, all labels of the role: service discovery are attached.
- For all targets backed by a pod, all labels of the role: pod discovery are attached.

配置示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
scrape_configs:
- job_name: 'kubernetes-apiservers'

  kubernetes_sd_configs:
  - role: endpoints

  # Default to scraping over https. If required, just disable this or change to
  # `http`.
  scheme: https

  # This TLS & bearer token file config is used to connect to the actual scrape
  # endpoints for cluster components. This is separate to discovery auth
  # configuration because discovery & scraping are two separate concerns in
  # Prometheus. The discovery auth config is automatic if Prometheus runs inside
  # the cluster. Otherwise, more config options have to be provided within the
  # <kubernetes_sd_config>.
  tls_config:
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    # If your node certificates are self-signed or use a different CA to the
    # master CA, then disable certificate verification below. Note that
    # certificate verification is an integral part of a secure infrastructure
    # so this should only be disabled in a controlled environment. You can
    # disable certificate verification by uncommenting the line below.
    #
    # insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

  # Keep only the default/kubernetes service endpoints for the https port. This
  # will add targets for each API server which Kubernetes adds an endpoint to
  # the default/kubernetes service.
  relabel_configs:
  - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
    action: keep
    regex: default;kubernetes;https

ingress

可用的 mata label

1
2
3
4
5
6
7
8
__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 /.

配置示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
scrape_configs:
- job_name: 'kubernetes-ingresses'

  metrics_path: /probe
  params:
    module: [http_2xx]

  kubernetes_sd_configs:
  - role: ingress

  relabel_configs:
  # Example relabel to probe only some ingresses that have "example.io/should_be_probed = true" annotation
  #  - source_labels: [__meta_kubernetes_ingress_annotation_example_io_should_be_probed]
  #    action: keep
  #    regex: true
  - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path]
    regex: (.+);(.+);(.+)
    replacement: ${1}://${2}${3}
    target_label: __param_target
  - target_label: __address__
    replacement: blackbox-exporter.example.com:9115
  - source_labels: [__param_target]
    target_label: instance
  - action: labelmap
    regex: __meta_kubernetes_ingress_label_(.+)
  - source_labels: [__meta_kubernetes_namespace]
    target_label: kubernetes_namespace
  - source_labels: [__meta_kubernetes_ingress_name]
    target_label: kubernetes_name

Relabeling 机制

Relabeling 是一个可以在数据被抓取之前动态重写 target 里面 label 的强大工具。这种发生在采集样本数据之前,对 Target 实例的标签进行重写的机制在 Prometheus 被称为 Relabeling,具体是通过配置 relabel_configs 来实现:

Relabeling作用时机
Relabeling作用时机

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 实例采集上来的样本数据如下所示:

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

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

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

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

1
2
3
4
5
6
7
8
9
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 数据中心,因此我们希望从这些实例中采集到的监控样本中也可以包含这样一个标签,例如:

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

这样可以方便的根据 dc 标签的值,根据不同的数据中心聚合分析各自的数据。在这个例子中,通过从 Target 实例中获取 __meta_consul_dc 的值,并且重写所有从该实例获取的样本中。

一般来说,Target 以__作为前置的标签是在系统内部使用的,因此这些标签不会被写入到样本数据中。在 Prometheus 所有的 Target 实例中,都包含一些默认的 Metadata 标签信息。可以通过 Prometheus UI 的 Targets 页面中查看这些实例的 Metadata 标签的内容:

实例的Metadata信息
实例的Metadata信息

默认情况下,当 Prometheus 加载 Target 实例完成后,这些 Target 时候都会包含一些默认的标签:

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

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
__address__="192.168.99.100:10250"
__meta_kubernetes_node_address_Hostname="minikube"
__meta_kubernetes_node_address_InternalIP="192.168.99.100"
__meta_kubernetes_node_annotation_alpha_kubernetes_io_provided_node_ip="192.168.99.100"
__meta_kubernetes_node_annotation_node_alpha_kubernetes_io_ttl="0"
__meta_kubernetes_node_annotation_volumes_kubernetes_io_controller_managed_attach_detach="true"
__meta_kubernetes_node_label_beta_kubernetes_io_arch="amd64"
__meta_kubernetes_node_label_beta_kubernetes_io_os="linux"
__meta_kubernetes_node_label_kubernetes_io_hostname="minikube"
__meta_kubernetes_node_name="minikube"
__metrics_path__="/metrics"
__scheme__="https"
instance="minikube"
job="kubernetes-nodes"

完整的 relabel_config 配置如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 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 ]

<relabel_action> 决定了 relabel 时候采取的动作:

  • replace: Match regex against the concatenated source_labels. Then, set target_label to replacement, with match group references (${1}, ${2}, …) in replacement substituted by their value. If regex does not match, no replacement takes place.
  • keep: Drop targets for which regex does not match the concatenated source_labels.
  • drop: Drop targets for which regex matches the concatenated source_labels.
  • hashmod: Set target_label to the modulus of a hash of the concatenated source_labels.
  • labelmap: Match regex against all label names. Then copy the values of the matching labels to label names given by replacement with match group references (${1}, ${2}, …) in replacement substituted by their value.
  • labeldrop: Match regex against all label names. Any label that matches will be removed from the set of labels.
  • labelkeep: Match regex against all label names. Any label that does not match will be removed from the set of labels.

Replace

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

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

LabelMap

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

1
2
3
4
5
6
- job_name: 'kubernetes-nodes'
  kubernetes_sd_configs:
  - role: node
  relabel_configs:
  - action: labelmap
    regex: __meta_kubernetes_node_label_(.+)

Keep/Drop

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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 用于排除。

LabelKeep/LabelDrop

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

1
2
3
relabel_configs:
  - regex: label_should_drop_(.+)
    action: labeldrop

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

告警处理

告警能力在 Prometheus 的架构中被划分成两个独立的部分:

  • 通过在 Prometheus 中定义 AlertRule,Prometheus 会周期性的对告警规则进行计算,如果满足告警触发条件就会向 Alertmanager 发送告警信息
  • AlertManager 收到来自 Prometheus Server 的报警规则后,对这些告警信息进行处理,并路由到正确的通知方

Prometheus告警处理
Prometheus告警处理

Alertmanager 除了提供基本的告警通知能力以外,还主要提供了如:分组、抑制以及静默等告警特性:

Alertmanager特性
Alertmanager特性

告警规则

一条典型的告警规则如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
groups:
- name: example
  rules:
  - alert: HighErrorRate
    expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5
    for: 10m
    labels:
      severity: page
    annotations:
      summary: High request latency
      description: description info

在告警规则文件中,我们可以将一组相关的规则设置定义在一个 group 下。在每一个 group 中我们可以定义多个告警规则。一条告警规则主要由以下几部分组成:

  • alert:告警规则的名称。
  • expr:基于 PromQL 表达式告警触发条件,用于计算是否有时间序列满足该条件。
  • for:评估等待时间,可选参数。用于表示只有当触发条件持续一段时间后才发送告警。在等待期间新产生告警的状态为 pending。
  • labels:自定义标签,允许用户指定要附加到告警上的一组附加标签。
  • annotations:用于指定一组附加信息,比如用于描述告警详细信息的文字等,annotations 的内容在告警产生时会一同作为参数发送到 Alertmanager。

为了能够让 Prometheus 能够启用定义的告警规则,我们需要在 Prometheus 全局配置文件中通过rule_files指定一组告警规则文件的访问路径,Prometheus 启动后会自动扫描这些路径下规则文件中定义的内容,并且根据这些规则计算是否向外部发送通知:

1
2
rule_files:
  [ - <filepath_glob> ... ]

默认情况下 Prometheus 会每分钟对这些告警规则进行计算,如果用户想定义自己的告警计算周期,则可以通过evaluation_interval来覆盖默认的计算周期:

1
2
global:
  [ evaluation_interval: <duration> | default = 1m ]

模板化

一般来说,在告警规则文件的 annotations 中使用summary描述告警的概要信息,description用于描述告警的详细信息。同时 Alertmanager 的 UI 也会根据这两个标签值,显示告警信息。为了让告警信息具有更好的可读性,Prometheus 支持模板化 label 和 annotations 的中标签的值。

通过$labels.<labelname>变量可以访问当前告警实例中指定标签的值。$value 则可以获取当前 PromQL 表达式计算的样本值。

1
2
3
4
# To insert a firing element's label values:
{{ $labels.<labelname> }}
# To insert the numeric expression value of the firing element:
{{ $value }}

例如,可以通过模板化优化 summary 以及 description 的内容的可读性:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
groups:
- name: example
  rules:
  # Alert for any instance that is unreachable for >5 minutes.
  - alert: InstanceDown
    expr: up == 0
    for: 5m
    labels:
      severity: page
    annotations:
      summary: "Instance {{ $labels.instance }} down"
      description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."

  # Alert for any instance that has a median request latency >1s.
  - alert: APIHighRequestLatency
    expr: api_http_request_latencies_second{quantile="0.5"} > 1
    for: 10m
    annotations:
      summary: "High request latency on {{ $labels.instance }}"
      description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"

配置 Alert Rules,修改 Prometheus 配置文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
  namespace: monitoring
data:
  prometheus.yml: |
    global:
      scrape_interval:     15s
      evaluation_interval: 15s
    rule_files:
    - /etc/config/recording_rules.yml
    - /etc/config/alerting_rules.yml
    - /etc/config/rules
    - /etc/config/alerts
    scrape_configs:
      - job_name: 'prometheus'
        static_configs:
        - targets: ['localhost:9090']
      - job_name: 'node'
        static_configs:
        - targets: ['node-exporter:9100']
      - job_name: 'container'
        static_configs:
        - targets: ['cadvisor:8080']    
  alerting_rules.yml: |
    groups:
    - name: hostStatsAlert
      rules:
      - alert: hostMemUsageAlert
        expr: (sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes + node_memory_Buffers_bytes+node_memory_Cached_bytes)) / sum(node_memory_MemTotal_bytes) > 0.55
        for: 1m
        labels:
          severity: page
        annotations:
            summary: "Instance {{ $labels.instance }} MEM usgae high"
            description: "{{ $labels.instance }} MEM usage above 85% (current value: {{ $value }})"    
  alerts: |
        {}
  recording_rules.yml: |
        {}
  rules: |
        {}

重启 Prometheus 后访问 Prometheus UI 可以查看当前以加载的规则文件:

切换到 Alerts 标签可以查看当前告警的活动状态。

现在只是在 Prometheus Web 页面查看警告,通过配置 Alert Manager 可以实现连接钉钉/Slack 等平台报警。

聚合规则

通过 PromQL 可以实时对 Prometheus 中采集到的样本数据进行查询,聚合以及其它各种运算操作。而在某些 PromQL 较为复杂且计算量较大时,直接使用 PromQL 可能会导致 Prometheus 响应超时的情况。这时需要一种能够类似于后台批处理的机制能够在后台完成这些复杂运算的计算,对于使用者而言只需要查询这些运算结果即可。Prometheus 通过 Recoding Rule 规则支持这种后台计算的方式,可以实现对复杂查询的性能优化,提高查询效率。

在 Prometheus 配置文件中,通过 rule_files 定义 recoding rule 规则文件的访问路径。

1
2
rule_files:
  [ - <filepath_glob> ... ]

每一个规则文件通过以下格式进行定义:

1
2
groups:
  [ - <rule_group> ]

一个简单的规则文件可能是这个样子的:

1
2
3
4
5
groups:
  - name: example
    rules:
    - record: job:http_inprogress_requests:sum
      expr: sum(http_inprogress_requests) by (job)

rule_group 的具体配置项如下所示:

1
2
3
4
5
6
7
8
# The name of the group. Must be unique within a file.
name: <string>

# How often rules in the group are evaluated.
[ interval: <duration> | default = global.evaluation_interval ]

rules:
  [ - <rule> ... ]

与告警规则一致,一个 group 下可以包含多条规则 rule。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# The name of the time series to output to. Must be a valid metric name.
record: <string>

# The PromQL expression to evaluate. Every evaluation cycle this is
# evaluated at the current time, and the result recorded as a new set of
# time series with the metric name as given by 'record'.
expr: <string>

# Labels to add or overwrite before storing the result.
labels:
  [ <labelname>: <labelvalue> ]

根据规则中的定义,Prometheus 会在后台完成 expr 中定义的 PromQL 表达式计算,并且将计算结果保存到新的时间序列 record 中。同时还可以通过 labels 为这些样本添加额外的标签。

这些规则文件的计算频率与告警规则计算频率一致,都通过 global.evaluation_interval 定义:

1
2
global:
  [ evaluation_interval: <duration> | default = 1m ]

参考资料