Prometheus配置/查询语句等简介

一. Server端 安装&配置&使用

1. 安装

1.1 二进制包

https://prometheus.io/download/
这个网页提供了各种组件的二进制包, 如果有的组件没有列出来,就在github上查找相应的repository.
还有一些独立维护的exporters: https://prometheus.io/docs/instrumenting/exporters/

对于 prometheus server 而言,只需要下载安装包,解压即可,例如

# 这里以2.1.0为例
wget https://github.com/prometheus/prometheus/releases/download/v2.1.0/prometheus-2.1.0.linux-amd64.tar.gz

1.2 从源码安装(略)

1.3 使用Docker

这是官方推荐的

# 获取镜像
docker pull prom/prometheus 

# 运行镜像,使用的配置自然是一个sample configuration
docker run -p 9090:9090 prom/prometheus 

# 使用自定义的配置文件
docker run -p 9090:9090 -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus 

或者可以将配置文件固化到image中,
Dockerfile内容

FROM prom/prometheus
ADD prometheus.yml /etc/prometheus/
docker build -t my-prometheus .
docker run -p 9090:9090 my-prometheus

还推荐将数据存储卷挂载到外部,以便当docker镜像升级时数据不丢失

2. 使用

version="2.1.0"
dirName="prometheus-${version}.linux-amd64"
tarName="${dirName}.tar.gz"
locPath="/home" 

curl -LJO https://github.com/prometheus/prometheus/releases/download/v${version}/${tarName}
# curl -LJO http://10.0.209.140/${tarName}

tar xf "${tarName}" -C ${locPath}
cd ${locPath}/${dirName}
./prometheus -config.file=prometheus.yml 

默认情况下,默认情况下使用的配置文件为 ./prometheus.yml

默认情况下,存储数据到 ./data/ 目录,可以使用 -storage.local.path 更改

现在你应该可以浏览 http://10.0.209.140:9090

当然 你应该想先修改下配置文件再启动

现在先Ctrl+C 停掉 prometheus 进程

vi ./prometheus.yml

键入如下内容,这个配置是一个HelloWorld配置,其内容是prometheus监控自己的状态
关于配置更多的内容,请参看配置部分的文档

3. 配置

Prometheus 通过命令行参数配置不变的参数(例如存储位置、保持多少数据在内存中等等).

配置文件用于配置scrape job及其实例的相关内容.

查看Prometheus的命令行参数

# 帮助命令会同时给出各个参数的默认值 
prometheus -h

# 查看帮助输出,可以看到
-config.file "prometheus.yml"
-storage.local.path "data"
-web.listen-address ":9090" 


默认的配置文件为 ./prometheus.yml
默认的存储位置为 ./data/
默认的web端口为 9090

配置文件可以在运行时动态reload:

#方式1: 
kill -HUP ${prometheus_pid} 

#方式2: 
# 需要 --web.enable-lifecycle 参数为true
curl -X POST http://10.0.209.140:9090/-/reload

配置文件

以 YAML 格式编写.

关于YAML格式的简单说明 : http://www.jianshu.com/p/2583a81ebfd0

配置文件的内容

主要有以下几部分:

  • 一部分是 global , 用于配置其他配置上下文中合法的参数,作为默认参数.
  • 一部分是 rule_files, 用于设置规则文件所在位置(相对于当前工作目录?)
  • 一部分是 scrape_configs: 用于设置多个不同的 scrape_config 对象
  • 一部分是 alerting:
  • 一部分是 remote_write 和 remote_read

配置文件示例 https://github.com/prometheus/prometheus/blob/master/config/testdata/conf.good.yml

例如:

# 全局默认设置
global: 
  # 刮取 target 的间隔,默认为 1m
  scrape_interval: 15s

  # 刮取 request的超时时间,default = 10s
  scrape_timeout: 10s 

  # 评估rules的间隔 default = 1m
  evaluation_interval: 15s 

  # The labels to add to any time series or alerts when communicating with  external systems (federation, remote storage, Alertmanager).
  external_labels:
    labelname1: labelvalue1
    labelname2: labelvalue2
    # ...

# 设置规则文件所在位置(相对于当前工作目录) ,可以使用通配符 
rule_files:
- "first.rules"
- "my/*.rules"

# A list of scrape configurations.
scrape_configs:
# 关于scrape的配置,后面单独讲
- <scrape_config1>  
- <scrape_config2>
# <scrape_config> section 指定了一组targets和描述如何刮取它们的参数. 
# 通常情况下,一个 <scrape_config> 指定一个 job (也就是相同类型的实例的集合). (在高级用法下,这可能会发生变化) 
# <scrape_config> 中的 target 可以静态配置(通过 static_configs 参数指定),或使用支持的服务发现机制动态发现.
# 另外, relabel_configs 允许在刮取之前 对任何target及其labels进行高级的修改

  # 必须设置,设置自动附加的 job labels,在所有的 <scrape_config> 中必须唯一
- job_name: xxx 

  # 设置刮取 target 的间隔,非必须,默认值为global中的设置
  scrape_interval: 5s 

  # 刮取 request的超时时间,非必须,默认值为global中的设置
  scrape_timeout: 10s 

  # 刮取target时使用的HTTP路径,非必须,默认值为 /metrics
  metrics_path: /metrics


  # 当client提供的metrics中的label和服务端自动添加的labels名称发生冲突时的行为,
  # 如果 honor_labels : true 则保留client端设置的label值,否则client设置的label名被重命名为 exported_<ori_label> ,然后 使用服务端自动增加的label的值.  
  # 非必须,默认值为false
  honor_labels: false 

  # 设置使用的schema,可选值有 http | https ,非必须,默认为http 
  scheme: http 

  # 可选的URL参数,非必须
  params: [ <string>: [<string>, ...] ] 

  # 设置认证部分
  basic_auth: 
    username: <string>  
    password: <secret>   

  # 设置TLS
  tls_config: [ <tls_config> ] 

  proxy_url: <string>

  # azure服务发现配置 
  azure_sd_configs: [ - <azure_sd_config> ... ] 
  dns_sd_configs: [ - <dns_sd_config> ... ]
  ec2_sd_configs: [ - <ec2_sd_config> ... ]
  openstack_sd_configs: [ - <openstack_sd_config> ... ]
  marathon_sd_configs: [ - <marathon_sd_config> ... ]
  # ... 还有其他许多服务发现配置 

  # consul服务发现配置( sd=service discovery)
  consul_sd_configs: [ - <consul_sd_config> ... ] 
  - server: http://10.0.209.149:8500
    datacenter: <string> # 可选
    scheme: <string> # 可选
    username: <string> # 可选
    password: <secret> # 可选
    services:
    - service1
    - service2

  static_configs: [ - <static_config> ... ]
    targets:
    - host1:port1
    - host2:port2

    labels: [ <labelname>: <labelvalue> ... ]

    relabel_configs: [ - <relabel_config> ... ]

    metric_relabel_configs: [ - <relabel_config> ... ]

# Alerting specifies settings related to the Alertmanager.
alerting:
  alert_relabel_configs:
    # 关于 relabel_config 的配置,后面单独讲
  - <relabel_config1> 
  - <relabel_config2>  
  # ...

  alertmanagers:
  # 关于 alertmanager_config 的配置,后面单独讲
  - <alertmanager_config1> 
  - <alertmanager_config2> 
  # ... 


# Settings related to the experimental remote write feature.
remote_write:
# 关于 remote_write_config 的配置,后面单独讲
- <remote_write_config1> 
- <remote_write_config2>
# ... 

# Settings related to the experimental remote read feature.
remote_read:
# 关于 remote_read_config 的配置,后面单独讲
- <remote_read_config1> 
- <remote_read_config2>   
# ... 

3.1. DEFINING RECORDING RULES

配置rules

prometheus 支持两种类型的规则:

  • 记录规则
  • 警报规则.

可以对它们进行配置, 然后定期 evaluated(评估?执行?)

如果想要在Prometheus中包含规则, 那就创建一个规则文件,其内容包含了必要的 rule statements,
然后通过Prometheus配置中的rule_files字段加载规则文件.
规则文件是 YAML 格式

在 Prometheus 运行时可以 reload rule files, 只需要给 Prometheus 进程发送 SIGHUP 信号.
当然只有在所有的规则文件都格式良好时才会成功.

要想快速检查规则文件是否存在语法错误,可以使用 Prometheus 提供的工具:

go get github.com/prometheus/prometheus/cmd/promtool
promtool check-rules /path/to/example.rules

make 时遭遇到了错误
package math/bits: unrecognized import path "math/bits" (import path does not begin with hostname)
这个不知道什么原因
找了一个曲线救国的办法: 使用docker

docker run --name tmpProm --rm prom/prometheus 
docker cp tmpProm:/bin/promtool  /bin/promtool 

其实Prometheus发行版的tar包中已经包含了这个工具

  • 如果检查成功 则返回状态码为 0
  • 如果存在语法错误,则返回状态码为 1
  • 如果命令行参数错误,则返回状态码为 2

Recording rules

允许您预先计算经常需要的或计算上昂贵的表达式,并将其结果保存为新的 time series。
这对于仪表板尤其有用,每次刷新时它都需要重复查询相同的表达式。

rule files 的格式是:

groups:
  [ - <rule_group> ]

<rule_group> 格式:

name: <string>
[ interval: <duration> | default = global.evaluation_interval ]
rules:
  [ - <rule> ... ]

<rule> 格式:
recording <rule>的格式是:

record: <string>
expr: <string>
labels:
  [ <labelname>: <labelvalue> ]

alerting <rule>的格式是:

alert: <string>
expr: <string>
[ for: <duration> | default = 0s ]
labels:
  [ <labelname>: <tmpl_string> ]
annotations:
  [ <labelname>: <tmpl_string> ]

recording rules 例子 :

groups:
  - name: example
    rules:
    - record: job:http_inprogress_requests:sum
      expr: sum(http_inprogress_requests) by (job)

alerting rules 例子 :

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

Recording rules 按照 evaluation_interval 字段设置的间隔定期 evaluated.

4. 关于存储:

Prometheus 包含一个 local on-disk time series database, 也可以与远程存储系统集成。

On-disk layout

摄取的样本每两小时聚合一个块。
每个块都包含一个目录,其中包含一个或多个块文件,该文件包含该时间窗口的所有时间序列样本,以及元数据文件和索引文件(将度量名称和标签索引到块文件中的系列文件)。
当前传入采样的块保存在内存中,尚未完全保留。
它通过提前写入日志(WAL)来防止崩溃,当Prometheus服务器在崩溃后重新启动时可以重播(replayed)。
当通过API删除series时,删除记录将存储在单独的逻辑删除文件中(而不是立即从块文件中删除数据)。

请注意,本地存储的局限性在于它未被群集或复制。
因此,在面对磁盘或节点中断时,它不是任意可扩展或持久的,因此应该被视为更多近期数据的短暂滑动窗口。
但是,如果您的耐用性要求不严格,那么您可能仍然可以在本地存储中存储长达数年的数据。

大体上,Prometheus每个采样只使用大约1-2个字节。
因此,要规划Prometheus服务器的容量,可以使用粗略的公式:

needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample

内存的使用

Prometheus 在内存中保存当前使用的所有chunks.
另外,它还尽可能多地在内存中持有最近使用过的chunks.
你必须告诉 Prometheus 它可以使用多少内存来进行 caching.
使用 storage.local.target-heap-size 设置 heap size 的大小(单位 bytes).
需要注意的是 Prometheus 实际使用的物理内存是 Go runtime 和 操作系统 的复杂交互的结果,并且很难精确预言.
根据经验, you should have at least 50% headroom in physical memory over the configured heap size.
也就是说你应该设置 storage.local.target-heap-size 的值为 物理内存的 2/3 .
storage.local.target-heap-size 的默认值是2G, 也就是说 Prometheus 预期会使用3G物理内存, 根据你的物理内存的大小调整该参数的值

磁盘空间的使用

Prometheus 存储其数据到 ./data (relative to the working directory) 中(通过参数 storage.local.path 指定),
参数 storage.local.retention (数据的保存时长) 允许配置数据的保存时长.


二. 查询语言

https://prometheus.io/docs/prometheus/latest/querying/basics/

1. 基础

Prometheus 提供了一种 函数表达式语言 , 这让用户可以实时 select and aggregate time series data.
表达式的结果可以作为graph展示, 也可以在Prometheus's expression browser中以扁平的数据查看, 或通过 HTTP API 被外部系统消费(consumed).
( 其实都是通过 HTTP API 获取数据 , 包括 Prometheus's expression browser )

表达式语言的数据类型:

在 Prometheus 的表达式语言中,一个表达式或子表达式(sub-expression)可以评估为4种类型之一:
( 这些数据类型 可以在 web页里的console中 查看其值来加深理解 )

  • (1) Instant vector(即时向量): 一组时间序列(time series),这些时间序列包含一个单独的sample(样本数据),共享相同的时间戳.
    例如 http_requests_totalhttp_requests_total{job="prometheus",group="canary"} 或者 {job="prometheus",group="canary"} 或者 http_requests_total{environment=~"staging|testing|development",method!="GET"}
    label的值可以匹配正则表达式:

    • (a) = 等号 表示 精确匹配
    • (b) != 表示 不等于提供的字符串
    • (c) =~ 表示 正则匹配
    • (d) !~ 表示 正则不匹配

    正则表达式不能匹配空字符串(必须至少有一个label不匹配空字符串),
    例如
    {job=~".*"} 这是不合法的.
    {job=~".+"} 这是合法的.
    {job=~".*",method="get"} 这也是合法的.

    正则表达式也可以用于匹配 metric name ,使用内部的 __name__ label
    {__name__=~"^job:.*"}

  • (2) Range vector(范围向量): 一组时间序列(time series),这些时间序列包含一段时间范围内的数据点. 例如: 表示时间的单位有 s m h d w y
    http_requests_total{job="prometheus"}[5m]

  • (3) Scalar(标量): 一个简单的浮点数字值

  • (4) String(字符串): 一个简单的字符串值. 当前未使用

根据使用情况(例如画图或者展示表达式的输出时),只有某些表达式的结果是合法的.
例如 返回 Instant vector 类型的表达式 是可以画图的唯一类型

字符串字面值可以使用 单引号、双引号和反引号``括起来.

PromQL 遵循 Go语言 的escaping rules. 在单引号和双引号中使用 \ 作为escape符号,其后面可以跟 \a \b \f \n \r \t \v \\ . 或者使用八进制(\nnn)或十六进制(\xnn,\unnnn,\Unnnnnnnn).

使用反引号``括起来的字符串没有转义.
但和Go不同的是, Prometheus不会丢弃backticks中的换行符.
例如: `these are not unescaped: \n ' " \t`

浮点数字面值例子: -2.43

操作符:

Prometheus 支持许多 binary and aggregation 操作符,后面再讲(什么是 binary and aggregation 操作符 ? 二元和聚合的操作符)

函数:

Prometheus 支持多种操作数据的函数. 后面再讲

Gotchas(got you?)(陷阱?)

Interpolation and staleness(插值和陈旧):

当运行查询时,时间戳是独立于实际的当前时间序列数据 选择的采样数据.
这主要是为了支持聚合(sum,avg等等)场景,这种场景下多个被聚合的时间序列在时间上并不完全对齐. 因为它们彼此独立, Prometheus 需要在那些时间戳上指定一个值,指定的这个值一般就是简单的使用在这个时间戳之前的最新的样本值.

但是如果5分钟(默认值)之内没有样本值, 那这个时间点上就没有值. 也就是说这意味着这个时间序列在这个时间点从graph上"消失"了.

注意: 这种处理方式将来可能会改变.
查看 https://github.com/prometheus/prometheus/issues/398https://github.com/prometheus/prometheus/issues/581

避免缓慢的查询和过载

如果查询需要对大量数据进行操作, 对它进行绘图可能会 超时或超载.
因此,当对未知数据构造查询时, 首先要在 Prometheus's expression browser 中调试 ,直到结果集合看起来合理了(最多数百个时间序列,而不是上千个).
只有当您充分筛选或汇总数据时,才能切换到图形模式。
如果该表达式仍然花费太长时间来绘制ad-hoc,可以通过 recording rule 对其 pre-record .

这和 PromQL 有关,使用一个bare metric name selector(例如 api_http_requests_total)可能会选择到数以千计的不同labels的time series.
还要记住,在多个时间序列上聚合的表达式将在服务器上产生负载,即使输出只是少量的时间序列.
这类似于在关系数据库中对列的所有值求和的速度很慢,即使输出值只是一个数字.

2. 操作符

PromQL 支持基本的逻辑和算术运算符。
对于两个即时向量之间的操作, matching behavior 可以修改
(
can be modified. 什么意思? 看了后面会理解,但是这里先说两句
操作符应用于两个即时向量(只能应用于即时向量)时,一般来说总是先进行匹配,而后才进行操作符运算,匹配规则是尝试针对左侧向量中的每个元素 在右侧向量中查找对应的匹配元素.
匹配不到的元素将不作为结果的一部分,但是这种匹配规则可以通过增加一些关键字被修改,后面有详细说明
)

二元算术运算符

+ - * / % ^(power)
可以应用于 标量/标量 、 向量/标量 、 向量/向量 中.

二元算术运算符 应用于 即时向量/标量 时

运算符将应用于向量中的每个样本值.
结果向量的metric name被丢弃.
例如:

employee_age_bucket_bucket{le="30"}  +2 

返回的是

{instance="10.0.86.71:8080",job="prometheus",le="30"} 3002

可以看到结果中的metric name被丢弃了.

二元算术运算符 应用于 应用于 即时向量/即时向量 时

运算符将应用于左侧向量中的元素及其在右侧向量中的匹配到的元素.
运算结果被传播到结果向量中,并且度量名称被丢弃.
那些在右侧向量中没有匹配条目的条目 不是结果的一部分。
例如:

employee_age_bucket_bucket{le=~"20|30|40"} + employee_age_bucket_bucket{le=~"30|40|50"}

返回的是

{instance="10.0.86.71:8080",job="prometheus",le="30"} 6000
{instance="10.0.86.71:8080",job="prometheus",le="40"} 8000

可以看到 左侧向量中的元素中只有与右侧匹配的元素参与了计算,并且结果中的metric name被丢弃了.

比较运算符

== != > >= < <=
可以应用于 标量/标量 、 向量/标量 、 向量/向量 中.

默认情况下, 这些运算符用于filter.
但是如果在运算符后提供了 bool修饰符 该行为会发生改变,此时它们会返回 0或1 而不是 filtering

比较运算符 应用于 标量/标量 时

必须提供 bool修饰符 , 并且该运算符会产生一个 0或1 的标量值.
例如:

3 > 2
# 报错 "comparisons between scalars must use BOOL modifier"

3 > bool 2 
# 返回 scalar 1

1 > bool 2 
# 返回 scalar 0 

比较运算符 应用于 即时向量/标量 时

运算符将应用于向量中的每个样本值,并且 运算结果为 0 的元素会被丢弃.
如果提供了 bool修饰符 , 向量中的值会变为 0或1.
例如:

employee_age_bucket_bucket > 1 
# 结果是 value <=1 的time series被丢了

employee_age_bucket_bucket > bool 1 
# 结果是 time series 的值变为了0或1

比较运算符 应用于 即时向量/即时向量 时

运算符的默认行为仍然是过滤.
向量中那些 在另一侧没有匹配元素的元素 以及 匹配到的元素间的运算结果为 0 的都会被丢弃, 同时左侧向量中的其他的元素被生成到结果向量中(元素的值不变).
如果提供了 bool修饰符 , 那些没有匹配到的元素 该丢弃还是丢弃, 而运算结果为0的元素不会被丢弃,只是值都变成了 0或者1.
例如:

employee_age_bucket_bucket{le=~"20|30|40"} >=  employee_age_bucket_bucket{le=~"30|40|50"}

返回的是

employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="30"} 3000
employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="40"} 4000

又例如:

employee_age_bucket_bucket{le=~"20|30|40"} > bool employee_age_bucket_bucket{le=~"30|40|50"}

返回的是

employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="30"} 0
employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="40"} 0

Logical/set binary operators( 逻辑/集合 二元操作符 )

这些操作符只应用于 即时向量/即时向量 中: and or unless

vector1 and vector2 的结果是

vector1 中的labels在vector2中存在的元素被保留,其他的被丢弃.
例如:

employee_age_bucket_bucket{le=~"20|30|40"} and employee_age_bucket_bucket{le=~"30|40|50"}

返回的

employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="30"} 3000
employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="40"} 4000

vector1 or vector2 的结果是

vector1中的所有元素和值,以及vector2中没有在vector1中匹配到的元素.

vector1 unless vector2 的结果是

和 and 正相反, 返回的是 vector1 中的labels在vector2中存在的元素被丢弃,其他的被保留.
例如:

employee_age_bucket_bucket{le=~"20|30|40"} unless employee_age_bucket_bucket{le=~"30|40|50"}

返回的

employee_age_bucket_bucket{instance="10.0.86.71:8080",job="prometheus",le="20"} 1000

Vector matching(向量匹配)行为

在两个向量之间的运算(好像不包括 and or unless 这种集合对集合的运算,它们属于 many-to-many?)总是尝试针对左侧向量中的每个元素 在右侧向量中查找对应的匹配元素.
这种匹配行为 有2种基础类型 :

(1) one-to-one :

匹配唯一的完全相同的label标签组和label对应的值都相同的元素
意思就是 左侧向量 中的元素的标签 在 右侧向量 中最多只有一个元素的标签与之对应,同时反之亦然.
这是 vector1 <operator> vector2 的默认行为.

关键字 ignoring 允许你在匹配时忽略某些标签,
关键字 on 允许将匹配的标签组减少为提供的列表,

语法格式为:

<vector expr> <bin-op> ignoring|on(<label list>) <vector expr>

例如:

employee_age_bucket_bucket{le=~"20|30|40"} > bool ignoring(job,instance) employee_age_bucket_bucket{le=~"30|40|50"}

返回的是 ( 注意1: 和之前 不加 ignoring 返回内容的区别是 labels 不一样了) ( 注意2: 如果是 ignoring(le), 那么匹配行为就不是 one-to-one 了,会报错 )

employee_age_bucket_bucket{le="30"} 0
employee_age_bucket_bucket{le="40"} 0

又例如

employee_age_bucket_bucket{le=~"20|30|40"} > bool on(le) employee_age_bucket_bucket{le=~"30|40|50"}

返回的是 (只有 labels 没有 metric name,为什么没有 metric name? 因为 on list 中没有 name , 如果加上则 metric name 就会出现)

{le="30"}  0
{le="40"}  0

(2) many-to-one 和 one-to-many

这是指 某一侧的向量中的元素的可以与 另一侧的向量中的多个元素相匹配(元素匹配的意思是指它们的labels相同).
此时必须使用修饰符 group_left (左侧的多个元素对应右侧的单个元素时) 或 group_right (左侧的单个元素对应右侧的多个元素时) 来显式地表达这一请求.
语法格式为:

<vector expr> <bin-op> ignoring|on(<label list>) group_left|group_right(<label list>) <vector expr>

group修饰符中提供的 label list 包含了 "one"-side 中的 额外的 labels,这些labels会包含在结果的标签组中.
同时 on或ignoring 提供的 label list 和 group修饰符提供的 labels 不能重复,结果向量中的每个时间序列都必须是唯一可识别的.

grouping修饰符只能用于数学和比较运算符,不能用于 and,unless,or 运算符.

例如

employee_age_bucket_bucket{le=~"30|40"} / ignoring(le) group_left employee_age_bucket_count

返回 ( 没有 metric name )

{instance="10.0.86.71:8080",job="prometheus",le="30"} 0.28
{instance="10.0.86.71:8080",job="prometheus",le="40"} 0.76

聚合运算符:

Prometheus 支持一些内建的聚合运算符,这些运算符可以用于汇总单个的即时向量的元素, 生成一个由被聚合的值组成的新向量.
这些聚合运算符 包括:

  • sum min max avg count
  • count_values(按元素的值分组,分别计算具有相同的值的元素的数量)
  • stddev (计算标准偏差(standard deviation))
  • stdvar (计算标准方差(standard variance ))
  • bottomk/topk (最小/大的k个元素)
  • quantile (计算 0-1 之间的百分比数量的样本的最大值)

这些操作符可以用于聚合 all label 维度,也可以聚合 预留的 distinct labels 维度,通过使用 without 或 or 子句.

其标准语法是

<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)] [keep_common]
  • parameter 只有部分运算符用到( count_values, quantile, topk and bottomk ).
  • without 列出的labels会从结果向量中移除.
  • by 列出的labels会在结果向量中保留,其他labels则会被移除
  • keep_common 子句允许在结果向量中保持那些额外的labels(必须是唯一的),
    使用without时不能使用 keep_common , 这应该很容易理解.

例如:
sum(http_requests_total) 会返回所有的样本值的和. 相当于group null
sum(http_requests_total) without (instance) 会按照除了instance label之外,以其他相同labels分组的各分组中的样本值的和

运算符优先级

算数运算符(^幂操作符优先级最高) > 比较运算符 > 逻辑运算符

一般来说相同优先级的运算符是从左到右运算的,

但是 幂运算操作符有点特殊
例子: 2^3^2 等价于 2^(3^2)

总之, 自己写的时候多用 () , 看别人写的时候不确定顺序就再查一下

3. 函数


3.1 数学函数等

  • abs(v instant-vector) 返回向量中所有样本值的绝对值

  • ceil(v instant-vector) 返回向量中所有样本值的向上取整数

  • floor(v instant-vector) 返回向量中所有样本值的向下取整数

  • round(v instant-vector, to_nearest=1 scalar) 返回向量中所有样本值的最接近的整数,
    to_nearest是可选的,默认为1,表示样本返回的是最接近1的整数倍的值, 可以指定任意的值(也可以是小数),表示样本返回的是最接近它的整数倍的值

  • exp(v instant-vector) 计算指数值. 即 e的value次方.
    value值够大时会返回 +Inf.
    特殊情况为:
    Exp(+Inf)=+Inf
    Exp(NaN)=NaN

  • ln(v instant-vector) 计算ln(value)值.
    value值够大时会返回 +Inf.
    特殊情况为:
    ln(0)=-Inf
    ln(x<0)=NaN
    ln(NaN)=NaN

  • log2,log10 类似于ln,

  • sqrt

  • scalar(v instant-vector) 的参数是一个单元素的即时向量,它返回其唯一的time series的值作为一个标量. 如果参数不合法则返回 NaN

  • vector(s scalar) 返回一个空labels的、值为s的向量

  • sort(v instant-vector) 对向量按元素的值进行升序排序

  • sort_desc(v instant-vector) 对向量按元素的值进行降序排序

  • count_scalar(v instant-vector) 返回的是一个标量值,
    和 count() 运算符不一样,count()返回的是 一个向量.

  • day_of_month(v=vector(time()) instant-vector) 返回向量中每个元素值代表的时间的day_of_month,
    如果不提供参数则时间当前时间

  • day_of_month,days_in_month,day_of_week(0-6),hour,minute,month,year

  • time() 返回从1970-1-1起至今的秒数. 注意它一般不表示实际的当前时间,而是表达式被evaluated时的时间

  • histogram_quantile(φ float, b instant-vector) 从 bucket 类型的向量中 计算 φ (0 ≤ φ ≤ 1) 百分比的样本的最大值.
    所谓 bucket 类型就是 labels 中必须要有 le 这个label 来表示 不同bucket区间 的样本数量.
    没有 le 标签的 time series 会被忽略.
    可以使用 rate() 函数来指定 计算的时间窗口. (关于rate函数的描述见后面)
    例如:
    histogram_quantile(0.5,rate(employee_age_bucket_bucket[10m]))
    返回
    {instance="10.0.86.71:8080",job="prometheus"} 35.714285714285715
    这表示 最近10分钟之内 50%的样本 最大值为 35.71...
    这个计算结果是 每组labels 组合一个time series , 如果要聚合的话, 使用 sum() 聚合操作符.
    如下所示:
    histogram_quantile(0.5,sum(rate(employee_age_bucket_bucket[10m])) by (job,le))
    如果要聚合所有的label,则如下:
    histogram_quantile(0.5,sum(rate(employee_age_bucket_bucket[10m])) by (le))

    注意1: histogram_quantile这个函数是根据假定每个区间内的样本分布是线性分布来计算结果值的(也就是说它的结果未必准确). 最高的bucket必须是 le="+Inf" (否则就返回NaN).
    注意2: 如果 b 含有少于 2 个 buckets , 那么会返回 NaN , 如果 φ<0 会返回 -Inf . 如果 φ > 1 会返回 +Inf

  • absent(v instant-vector) 如果参数向量有任何的元素就返回一个空的向量,否则返回一个单元素的向量,其值为1,其labels为你指定的labels.
    也就是说对于一个给定的metric name 和 label组合 不存在 time series 时,你可以得到一个 单元素的向量.
    例如: absent(non_exists_metric) 返回 {} 1 , 其 labels 为空
    例如: absent(anyMetric{anyLabel:"anyValue") 返回 {anyLabel:"anyValue"} 1 , 其 labels 为给定的labels组合
    例如: absent(employee_age_bucket_bucket{job="myjob",instance=~".*"}) 返回 {job="myjob"} 1 , 正在匹配的instance不作为返回labels中的一部分

  • clamp_max(v instant-vector, max scalar) clamp(钳住)即时向量中的每个元素的值,不让它们的最大值超越指定的最大值.

  • clamp_min(v instant-vector, min scalar) clamp(钳住)即时向量中的每个元素的值,不让它们的最小值超越指定的最小值.

  • drop_common_labels(instant-vector) 会丢弃一些labels,这些被丢弃的labels是所有的time series中都存在且labels值都相同的label

  • label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) 对于每个time series, 它会使用正则表达式匹配源标签的值,如果匹配到则增加一个目标标签,且其值由replacement表示.
    例如: label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*") 会返回一个向量,每个time series都会增加一个 foo 标签,其值为 a .


3.2 范围向量函数

  • changes(v range-vector) 的参数是一个范围向量, 他返回一个即时向量
    labels不变,值是 元素的值发生变化的次数.
    例如: changes(requests_total[2d])
    返回 {instance="xx",job="yy",method="zz",requestpath="xx"} 3703

  • delta(v range-vector) 的参数是一个范围向量,返回一个即时向量
    它计算每个time series中的第一个值和最后一个值的差别,
    由于这个值 is extrapolated to (被外推到) 指定的整个时间范围,所以可能你会得到一个非整数值,即使样本值都是整数.
    这个函数一般只用在 gauge 类型的time series 上.

  • idelta(v range-vector) 的参数是一个范围向量, 返回一个即时向量
    它计算最新的2个样本值之间的差别.
    这个函数一般只用在 gauge 类型的time series 上.

  • deriv(v range-vector) 的参数是一个范围向量,返回一个即时向量.
    它计算每个time series的每秒的导数(derivative),它使用简单的现行回归计算这个导数(using simple linear regression).
    这个函数一般只用在 gauge 类型的time series 上

  • holt_winters(v range-vector, sf scalar, tf scalar) 的参数是一个范围向量
    它根据 范围向量 中的范围 产生一个平滑的值.
    平滑因子sf越小, 对旧数据的重视程度越高.
    趋势因子tf越大, 对数据的趋势的考虑就越多.
    sf和tf都必须在0和1之间.
    这个函数一般只用在 gauge 类型的time series 上 (没看懂)

  • increase(v range-vector) 的参数是一个范围向量.
    它计算指定范围内的增长值, 它会在单调性发生变化时(如由于目标重启引起的计数器复位)自动中断.
    由于这个值 is extrapolated to (被外推到) 指定的整个时间范围,所以可能你会得到一个非整数值,即使样本值都是整数.
    这个函数一般只用在 counter 类型的time series 上.

  • rate(v range-vector) 的参数是一个范围向量.
    它计算每秒的平均增长值.
    它会在单调性发生变化时(如由于目标重启引起的计数器复位)自动中断.
    这个函数一般只用在 counter 类型的time series 上.
    一般用于 alert .

  • irate(v range-vector) 的参数是一个范围向量.
    它计算每秒的平均增长值, 它基于的是最新的2个数据点,而不是第一个和最后一个值.

  • predict_linear(v range-vector, t scalar) 根据线性分布预测n秒后的值,基于给定的范围向量.

  • resets(v range-vector) 的参数是一个范围向量.
    对于每个 time series , 它都返回一个 counter resets的次数,
    两个连续样本之间的值的降低被认为是一次 reset.

<aggregation>_over_time(v range-vector)

一些聚合运算符有这种用法,它们会聚合每个time series的range,并返回一个即时向量:

  • avg_over_time(range-vector): 计算指定时间段内所有点的平均值
  • min_over_time, max_over_time, sum_over_time, count_over_time, stddev_over_time, stdvar_over_time

4. HTTP API

  • 当前文档的 HTTP API 都位于 /api/v1 下
  • API 的响应都是 JSON 格式的.
  • 成功的 API 响应的返回码都是 2xx
  • 失败的 API 响应的返回码: 400(表示参数缺失或不正确) 422(表示表达式不能被执行) 503(表示服务超时或中断)
  • 其他的 非2xx 的返回码 可能表示 没有请求到 API endpoint 返回的错误

返回的JSON格式为:

{
    "status":"success" | "error",
    "data":<data>,
    // 如果发生错误时
    "errorType":"<string>",
    "error":"<string>"
}
  • 输入的时间戳应该使用 RFC3339格式或unix时间戳格式(即秒数).
  • 输出的时间戳总是按照Unix时间戳格式
  • 输入参数 <series_selector> 需要 urlencoded,它就是: http_requests_total or http_requests_total{method=~"^GET|POST$"}
  • <duration> 使用 [0-9]+[smhdwy]

4.1 表达式查询

即时向量查询

GET /api/v1/query

参数1: query
query=<string>: Prometheus expression query string

参数2: time
time=<rfc3339 | unix_timestamp> : Evaluation timestamp

参数3: timeout
timeout=<duration>: Evaluation timeout. Optional. Defaults to and is capped by the value of the -query.timeout flag.

范围向量查询

GET /api/v1/query_range
参数:query,start,end,step,timeout

......


四. pushgateway

https://github.com/prometheus/pushgateway


五. 数据展示

https://prometheus.io/docs/visualization/browser/

1.

prometheus 提供的web页中的 /graph 页面主要用于临时查询和调试.
正经显示还是使用 Grafana 和 Console Templates

2. Grafana:

官方文档: http://docs.grafana.org/installation/rpm/

Grafana 支持查询 Prometheus .
从 Grafana 2.5.0(2015-10-28) 之后, 它开始包含 Prometheus data source.

安装

有多种方式 rpm,yum,docker,这里就介绍 tar 文件方式吧

# 下载tar文件
version="5.0.0"
dirName="grafana-${version}"
tarName="${dirName}.linux-x64.tar.gz"
locPath="/home"

curl -L -O https://s3-us-west-2.amazonaws.com/grafana-releases/release/${tarName}

tar zxf ${tarName} -C ${locPath}

# Start Grafana.
cd ${locPath}/${dirName}
./bin/grafana-server web
./bin/grafana-server -h / -v

使用 Docker 方式:

docker run -d -p 3000:3000 -v /grafanaData/var/lib/grafana:/var/lib/grafana -v /grafanaData/etc/grafana:/etc/grafana -v /grafanaData/var/log/grafana:/var/log/grafana -e "GF_SECURITY_ADMIN_PASSWORD=secret" grafana/grafana

配置文件:

默认的配置存储在 WORKING_DIR/conf/defaults.ini 自定义的配置存储在WORKING_DIR/conf/custom.ini
自定义的配置文件可以使用 --config 指定,
例如使用其他方式安装的 grafana 的自定义配置文件都使用 --config /etc/grafana/grafana.ini

配置文件中的选项都可以被 环境变量ENV 所覆盖: GF_SectionName_KeyName .
环境变量必须大写,并且 . 必须被替换为 _

grafana 和 alertmanager 一样,也是一个单独的产品,有兴趣的自行研究 http://docs.grafana.org/

默认情况下, Grafana 会监听 3000 端口.

默认情况下, Grafana 使用 admin / admin 登陆

界面操作不再赘述,不同版本的界面也会有差异,不过大概思路肯定是
先 创建 Prometheus data source
然后创建 Prometheus graph: 使用 表达式 创建 graph

还可以 从 https://grafana.com/dashboards 下载预设好的 datasource.
然后在 Grafana web页中 import, 导入时你必须手动指定使用的 Prometheus 源( 这个还没有测试成功, 研究的不够深入 )

3. Console Templates

大概 反正就是使用 go template 加一些自定义的函数 , 使用 {{ }} 括起来

4. Template reference & examples:


六. instrumenting

1. Client Libraries:

https://prometheus.io/docs/instrumenting/clientlibs/

在你可以监控你的服务之前,你需要增加 instrumentation 到它们的代码中.
(通过 Prometheus client libraries, 例如这里我可能要介绍Java client)

Java client使用说明地址: https://github.com/prometheus/client_java

mvn引入:

   <!-- The client -->
   <dependency>
     <groupId>io.prometheus</groupId>
     <artifactId>simpleclient</artifactId>
     <version>0.0.26</version>
   </dependency>
   <!-- Exposition servlet-->
   <dependency>
     <groupId>io.prometheus</groupId>
     <artifactId>simpleclient_servlet</artifactId>
     <version>0.0.26</version>
   </dependency>

Counter 使用:

import io.prometheus.client.Counter;
class YourClass {
     static final Counter requests = Counter.build().name("requests_total").help("Total requests.").register();
     void processRequest() {
       requests.inc();
       // Your code here.
     }
}

Gauge 使用:

请记住,Gauge上默认的 inc(), dec() and set() 方法 具有 线程安全性

class YourClass {
    static final Gauge inprogressRequests = Gauge.build().name("inprogress_requests").help("Inprogress requests.").register();
    void processRequest() { 
       inprogressRequest.inc();
       // Your code here.
       inprogressRequest.dec();
     }
}

Histogram 使用:

( histogram 的bucket 如何显示为整数?不必担心,Prometheus采集之后会自动显示为整数)

class YourClass {
     static final Histogram requestLatency = Histogram.build().name("requests_latency_seconds").help("Request latency in seconds.").register();
     // 设置 bucket 可以使用 Histogram.build().bucket(doubles...) 或使用指数或线性递增序列指定
     void processRequest(Request req) {
       Histogram.Timer requestTimer = requestLatency.startTimer();
       try {
         // Your code here.
       } finally {
         requestTimer.observeDuration();
     //关于这个observe也是迷惑了半天,如果我要统计 age 的分布,那我该如何统计呢? 其实很简单, 就是简单的
     HistogramInstance.observe(someIntOfAge);
     // 这个 requestTimer 纯粹是为了简化目的而开发的,如果不用 requestTimer ,那实现起来的方式大概应该是
     // 自己计算请求的用时,假设变量是 myRequestLatency ,然后
     HistogramInstance.observe(myRequestLatency); 
     // 然后 Histogram 会自动根据你设置的bucket进行归类统计
     // Summary 大同小异,不再赘述
     // 还有就是 Histgram 对应的metric name是其指定的name+"_"+bucket,即metric name会自动附加上 bucket
     // 而Summary却不会
       }
     }
}

使用Labels:

所有的 metrics 都可以使用labels , 使用Labels的方式大概如下:

class YourClass {
     static final Counter requests = Counter.build().name("requests_total").help("Total requests.").labelNames("method","path")register();
     void processRequest() {
       requests.labels("get","/myPath").inc();
       // labelNames和labels的数量和位置必须对应上
     }
}

注册 Metrics:

最佳方式就是上面介绍的,通过 static final class 变量

如何 export 给 Prometheus 呢?

通过 io.prometheus.client.exporter.MetricsServlet
也就是在web.xml 里增加相应的 Servlet ,并配置 访问路径

可以使用 name[]= 参数来过滤返回的time series,
例如: ?name[]=employee_age_summary&name[]=employee_age_summary_count

这只是一种 export metric 的方法,其实还有其他许多简单方便的方式

对于短暂的和批处理任务

就需要 Pushgateway 协助,
要如何 export 给 Pushgateway 呢?
( 当然了,这需要你依赖另外一个jar包 )

void executeBatchJob() throws Exception {
     CollectorRegistry registry = new CollectorRegistry();
     Gauge duration = Gauge.build().name("my_batch_job_duration_seconds").help("Duration of my batch job in seconds.").register(registry);
     Gauge.Timer durationTimer = duration.startTimer();
     try {
       // Your code here.
  
       // This is only added to the registry after success,
       // so that a previous success in the Pushgateway isn't overwritten on failure.
       Gauge lastSuccess = Gauge.build().name("my_batch_job_last_success").help("Last time my batch job succeeded, in unixtime.").register(registry);
       lastSuccess.setToCurrentTime();
     } finally {
       durationTimer.setDuration();
       PushGateway pg = new PushGateway("127.0.0.1:9091");
       pg.pushAdd(registry, "my_batch_job");
     }
}

2. exporters and integration

https://prometheus.io/docs/instrumenting/exporters/

有许多 库 和 servers 有助于将来自第三方的 existing mestrics 导出为 Prometheus metrics .
这对于不能直接使用 Prometheus metric的系统是非常有用的,
例如 HAProxy或Linux系统统计信息

有些 exporters 由 官方的 Prometheus GitHub 组织维护(https://github.com/prometheus) , 称为官方的exporter

我们鼓励创建更多的 exporters , 但是不知道它们是否有最好的体验.

我们设定的 exporter 的默认端口设定为:
https://github.com/prometheus/prometheus/wiki/Default-port-allocations

  • 9090 (Prometheus server 监听的端口)
  • 9091 - Pushgateway
  • 9092 - UNALLOCATED (to avoid collision with Kafka)
  • 9093 - Alertmanager

然后 exporters 从 9100 端口开始

  • 9100 - Node exporter
  • ...

JMX exporter

JMX exporter 可以 export 很多 基于JVM的应用,
例如 Kafka 和 Cassandra

数据库相关的exporter :

硬件相关的exporter:

消息系统的exporter:

存储相关的exporter:

HTTP相关的exporter:

  • Nginx

还有很多...

还有一些第三方的软件

还有一些第三方的软件直接 expose metric 给 prometheus , 所以不需要额外的exporter

这里讲一讲 node_exporter:

它用于 expose *NIX kernels 的 硬件和操作系统 相关的metrics,
它使用Go编写,并使用了 pluggable(可插拔的) metric collectors

如果要监控 windows的metric,推荐使用 WMI exporter

node exporter 默认会监控很多metrics,
可以使用 --collectors.enabled 参数指定要使用的 Collector.
一般简单的如果只想监控 cpu,meminfo,diskstats,filesystem,netdev, 可以使用参数:
--collectors.enabled="cpu,loadavg,meminfo,diskstats,filesystem,netdev"

如何获取和运行?

# 获取
go get github.com/prometheus/node_exporter
cd ${GOPATH-$HOME/go}/src/github.com/prometheus/node_exporter
make

# 获取方式也可以是
mkdir -p ${GOPATH-$HOME/go}/src/github.com/prometheus
git clone https://github.com/prometheus/node_exporter.git ${GOPATH-$HOME/go}/src/github.com/prometheus/node_exporter
go install github.com/prometheus/node_exporter

# 运行
${GOPATH-$HOME/go}/bin/node_exporter --collectors.enabled="cpu,meminfo,loadavg,filesystem"
${GOPATH-$HOME/go}/bin/node_exporter -h # 获取命令行参数帮助, 默认监听端口 9100

make 时可能会遇到错误
imports golang.org/x/tools/go/vcs: unrecognized import path "golang.org/x/tools/...

这是因为被盾了,有2种方法解决:
(1) 设置代理(略)
(2) 使用 github 上的替代品

git clone https://github.com/golang/tools.git ${GOPATH-$HOME/go}/src/golang.org/x/tools
# 然后再 make 

Node Exporter 常用查询语句 https://songjiayang.gitbooks.io/prometheus/content/exporter/nodeexporter_query.html

CPU 使用率

100 - (avg by (instance) (irate(node_cpu{instance="xxx", mode="idle"}[5m])) * 100)

CPU 各 mode 占比率

avg by (instance, mode) (irate(node_cpu{instance="xxx"}[5m])) * 100

机器平均负载

node_load1{instance="xxx"} // 1分钟负载
node_load5{instance="xxx"} // 5分钟负载
node_load15{instance="xxx"} // 15分钟负载

内存使用率

100 - ((node_memory_MemFree{instance="xxx"}+node_memory_Cached{instance="xxx"}+node_memory_Buffers{instance="xxx"})/node_memory_MemTotal) * 100

磁盘使用率

100 - node_filesystem_free{instance="xxx",fstype!~"rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*"} / node_filesystem_size{instance="xxx",fstype!~"rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*"} * 100

网络 IO

为什么使用 irate 而不是 rate ?
为什么除以 128 ?

// 上线带宽
sum by (instance) (irate(node_network_receive_bytes{device!~"bond.*?|lo"}[5m])/128)
// 下行带宽
sum by (instance) (irate(node_network_transmit_bytes{device!~"bond.*?|lo"}[5m])/128)

网卡出/入包

// 入包量
sum by (instance) (rate(node_network_receive_bytes{device!="lo"}[5m]))
// 出包量
sum by (instance) (rate(node_network_transmit_bytes{device!="lo"}[5m]))

mysqld_exporter

和 node_exporter 类似:

CREATE USER 'exporter'@'127.0.0.1' IDENTIFIED BY '12345qwert' WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'127.0.0.1' IDENTIFIED BY '12345qwert' ;
flush privileges;
exit;

export DATA_SOURCE_NAME='exporter:12345qwert@(127.0.0.1:3306)/'
echo export DATA_SOURCE_NAME=\'exporter:12345qwert@(127.0.0.1:3306)/\' >> /etc/bashrc

go get github.com/prometheus/node_exporter
cd ${GOPATH-$HOME/go}/src/github.com/prometheus/node_exporter
make
${GOPATH-$HOME/go}/bin/mysqld_exporter  

# 获取命令行参数帮助, 默认监听端口 9104
${GOPATH-$HOME/go}/bin/mysqld_exporter -h 

redis_exporter

和 node_exporter 类似:

git clone https://github.com/golang/crypto.git ${GOPATH-$HOME/go}/src/golang.org/x/crypto
git clone https://github.com/golang/sys.git ${GOPATH-$HOME/go}/src/golang.org/x/sys

go get github.com/oliver006/redis_exporter
cd ${GOPATH-$HOME/go}/src/github.com/oliver006/redis_exporter
make

${GOPATH-$HOME/go}/bin/redis_exporter -redis.addr '10.0.209.140:6379,10.0.209.141:6379,10.0.209.142:6379' -redis.password '!@#rjyjy,!@#rjyjy,!@#rjyjy' -redis.alias '10.0.209.140,10.0.209.141,10.0.209.142'

# 获取命令行参数帮助, 默认监听端口 9121
${GOPATH-$HOME/go}/bin/redis_exporter -h 

再说一说 container exporter:

运行cAdvisor即可, 目前只监控到 内存使用, 设置监听端口 19100

docker run --detach=true \
--name=cadvisor \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:rw \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=19100:8080 \
10.0.209.140:5000/cadvisor:20170927

3. EXPOSITION FORMATS(暴露格式)

prometheus 实现了 2种 不同的 wire formats 供客户端使用来 暴露 metric 给 prometheus server.
一种是 基于文本(text-based)的格式
一种是 更强大和更有效的 protocol-buffer 格式.

Prometheus servers and clients 使用 content negotation (内容协商) 来建立实际使用的格式.
服务器优先使用 protocol-buffer 格式, 但是如果 client不支持,它也可以使用 text-based 格式.

大部分用户应该使用这些已经实现了 exposition formats 的 client Libraries.

除了上面提到的两种格式之外, clients 可能还 expose 了一些 Prometheus 不理解的格式.
这些仅仅是为了方便人类阅读,方便调试.
强烈建议 client 至少支持一种人类可阅读的格式.
当HTTP请求头中的 Content-Type 不能被 client library理解时, client 应该返回人类可阅读的格式.
text format 一般被认为是人类可阅读的,所以这是一个很好的fallback候选方式(并且同时也可以也被 Prometheus 所理解).


七. Alerting

1. Overview

Prometheus 中的 Alerting 被分成两部分:

  • Prometheus servers 中的 alerting rules 发送 alerts 到 alertmanager,
  • 然后 alertmanager 接手管理这些 alerts, 包括 silencing, inhibition(抑制), aggregation 和 发送通知(通过 email,pagerduty 和 hipchat 等方式)

设置 alerting 和 notification 的主要步骤是:

  • (1) 配置 alertmanager
  • (2) 配置 Prometheus 与 alertmanager 交谈,通过 -alertmanager.url 命令行参数指定( 与在 Prometheus Configuration 中配置 alertmanager的区别?: https://prometheus.io/docs/operating/configuration )
  • (3) 在 Prometheus 中创建 alerting rules

2. AlertManager

https://github.com/prometheus/alertmanager

核心概念

Grouping

将类似特性的alerts进行分类, 从而只发一个 notification.
例如 你的一个服务有 很多个实例正在运行, 这时候网络出现问题了, 很多实例都连不上数据库了.
按照你的配置, 此时 Prometheus 会发送 alerts 给 alertmanager,
此时你肯定不想收到 很多条错误提示,但同时还想看看到底是哪些实例连不上数据库了.
Grouping (通过配置文件配置Grouping )可以做到这点, 它可以把很多 alerts 打包成一个 notification.

Inhibition: 抑制

就是如果某个alerts已经firing了,那么就抑制某些特定的alerts.
例如: 假设整个集群都无法访问了 的这样一个alert已经firing了, 那 alertmanager (通过配置文件配置Inhibition)可以 mute all other alerts . 这可以阻止你收到那些与问题无关的通知.

Silences:

是指直接简单的在给定时间内 mute alerts.
到来的 alert 首先会被检查是否匹配 silence 规则, 如果匹配则不会为 alert 发送 notification.
通过 web界面 配置 silence.

Client behaviour: 客户端行为

( alertmanager 的客户端就是给它发送alert的客户端? Prometheus 就是它的客户端? ).
alertmanager对它的客户端的行为有一些特殊要求.
这些一般只有高级场景时才用到, 就是不使用 Prometheus 时 如何给 alertmanager 发送 alert 信息.
这里就不过多介绍了
网址: https://prometheus.io/docs/alerting/clients/

安装

可以从 https://prometheus.io/download/ 下载预编译的包

也可以使用 docker 镜像

docker pull quay.io/prometheus/alertmanager

启动

# 配置文件默认为 ./alertmanager.yml
./alertmanaer -config.file=<your_file> 

# 查看命令行参数帮助
./alertmanaer -h 

配置

和 prometheus 类似

配置文件可以在运行时动态reload:

# 方式1:
kill -HUP ${prometheus_pid} 

# 方式2:
curl -X POST http://10.0.209.140:9093/-/reload

配置文件以 YAML 格式编写.

配置文件中的 global 部分, 用于配置其他配置上下文中合法的参数,作为默认参数.

配置文件示例

https://github.com/prometheus/alertmanager/blob/master/doc/examples/simple.yml

例如:

# 全局默认设置
global: 
   # 在一个alert被声明为resolved之后(而且没有更新)的ResolveTimeout,默认为 5m
   resolve_timeout: 5m  
   
   # 默认的 "SMTP FROM" header field
   smtp_from: <tmpl_string> 
   
   # 默认的 SMTP smarthost,用来发邮件
   smtp_smarthost:<string> 
   
   # 默认的SMTP认证信息
   smtp_auth_username: <string> 
   smtp_auth_password:<secret>
   smtp_auth_secret: <secret> ]
   smtp_auth_identity: <string> 
   smtp_require_tls: <bool> | default = true

   # 发送Slack通知使用的 API URL
   slack_api_url: <string>  

   # 还有一些其他的 url 不再列出
   pagerduty_url: <string> | default = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" 
 
# 从哪里读取 自定义的 通知模版定义,最后一个组件可以使用通配符,例如: 'templates/*.tmpl'
templates: [ - <filepath> ... ] 

# routing tree 的 root node
route:<route> 

# 接收 notification 的 receivers
receivers: [ - <receiver> ... ]
- name: <string>
  email_configs: [ - <email_config>, ... ]
  - to:  <tmpl_string>
    from: <tmpl_string> | default = global.smtp_from 
    ...

  other_likely_email_configs: [ - <someconfig> ... ]

# inhibition rules 的列表
inhibit_rules: [ - <inhibit_rule> ... ]

<route> block:

它定义了 routing tree 中的一个节点 及其 children. children会继承 parent node 的参数选项值
每个 alert 都会在配置的 top-level route 进入 routing tree,
这个 routing tree 必须可以匹配所有 alerts (也就是没有配置任何matchers).
然后它遍历 child nodes.
如果 continue 被设置为 false, 它会在第一个匹配的 child 之后就停止.
如果 匹配的节点 上的continute是true,那么 alert 会继续与其他的 siblings node 匹配.
如果 alert 不匹配 node 的任意一个 children node 或 该node可能就没有 child, 那么 alert如何被处理 则基于 当前node 的配置参数.

在线 Routing tree editor: https://prometheus.io/webtools/alerting/routing-tree-editor/

<route> block 的结构:

  # alert 是否被其他 sibling nodes 继续匹配
  continue: <boolean> | default = false
    
  group_by: [ - <labelname>, ... ]
  # 例如 group_by: ['alertname', 'cluster', 'service']

  # How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)
  group_wait: <duration> 

  # How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5min or more.)
  group_interval: <duration>  

  # How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).
  repeat_interval: <duration> 

  # 设置 receiver
  receiver:<string>

  # match 规则
  match: [ <labelname>: <labelvalue>, ... ]
  match_re: [ <labelname>: <regex>, ... ]
 
  # Zero or more child routes.
  routes: [ - <route> ... ]

amtool

amtool 是与 alertmanager api 交互的 cli tool , 它已经绑定在 alertmanager 里了
其用法如下

# 查询 alert
./amtool alert
./amtool -o extended alert
./amtool -o extended alert query alertname="Test_Alert"

# silence alert
./amtool silence add alertname=Test_Alert
./amtool silence add alertname="Test_Alert" instance=~".+0"
./amtool silence query
./amtool silence query instance=~".+0"
./amtool silence expire b3ede22e-ca14-4aa0-932c-ca2f3445f926
./amtool silence query instance=~".+0"
./amtool silence expire $(amtool silence -q query instance=~".+0")
./amtool silence query instance=~".+0"
./amtool silence expire $(amtool silence query -q)

amtool config

High Availability

3. Alerting rules:

Alerting rules 允许你根据 Prometheus expression 来定义 alert condition, 并把关于 firing alert 的通知发送到外部服务.
每当 alert expression 在给定时间点 产生于 一个或多个 vector 元素时, alert 将为这些元素的 label sets 计数.
(原文: 实在难以理解
Alerting rules allow you to define alert conditions based on Prometheus expression language expressions and to send notifications about firing alerts to an external service. Whenever the alert expression results in one or more vector elements at a given point in time, the alert counts as active for these elements' label sets. )

在 Prometheus 中定义 Alerting rules

和定义 recording rules 一样.
在 Prometheus 的配置文件中使用rule_files定义

rule_files:
- "first.rules"
- "my/*.rules"

语法

ALERT <alert name>
IF <expression>
[ FOR <duration> ]
[ LABELS <label set> ]
[ ANNOTATIONS <label set> ]

FOR 子句 表示从 表达式第一次可以产生 alert 开始,让 Prometheus 等待多长时间,并且在此期间 表达式一直可以产生 alert, alert 才会被firing.

LABELS 子句 表示 指定了一组额外的labels,附加到 alert 上.
任何已经存在的labels都会被 overwritten.
label value 可以被 templated( 也就是可以使用模版,即可以使用 {{ }} 这样的表达式)

ANNOTATIONS 子句 表示 另一组labels,这些labels不用于标识 alert instance,只用于存储更多的附加信息.
例如 summary,description.

templated 是什么意思?

The labels variable holds the label key/value pairs of an alert instance andvalue holds the evaluated value of an alert instance

template例子:
To insert a firing element's label values:

{{ $labels.<labelname> }}

To insert the numeric expression value of the firing element:

{{ $value }}

例子:

# Alert for any instance that is unreachable for >5 minutes.
ALERT InstanceDown
  IF 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 have a median request latency >1s.
ALERT APIHighRequestLatency
  IF api_http_request_latencies_second{quantile="0.5"} > 1
  FOR 1m
  ANNOTATIONS {
      summary = "High request latency on {{ $labels.instance }}",
     description = "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)",
  }

在运行时检查alerts

( Inspecting alerts during runtime )

要想手动检查哪些 alert are active ( pending or firing ) ,

可以访问 Prometheus web页中的 alert tab页( http://10.0.209.140:9090/alerts ), 这个页面会展示当前 active 的 label sets.

对于 pending and firing alerts

Prometheus 还存储了其他一些合成的labels,其格式为:

ALERTS{alertname="<alert name>", alertstate="pending|firing", <additional alert labels>}

sample的value=1 表示其状态是 active 的,
sample的value=0表示 alert 从active转变为inactive,
一旦变为 inactive, 那再次刷新时其time series就消失了.

4. Clients:

https://prometheus.io/docs/alerting/clients/
访问 /api/v1/alerts , 使用 POST 提交 一个 JSONArray 对象

5. Notification template reference: 及例子

发送给 receiver 的 notification 是通过 templates 构造的.
Alertmanager有默认的 templates, 但是也可以自定义.

为了避免混淆, 理解如下的概念很重要:
Alertmanager templates 和 Prometheus 中的 template 是不一样的,
虽然 Prometheus 中的 template 也包括了 alert rule 的 labels/annotations.

有点类似于 go template: 有时间的自己看看吧 https://prometheus.io/docs/alerting/notifications/

大概就是有一个 Data 数据结构,其数据结构中的 Key 有 Receiver,Status,Alerts 等,
其中 Alerts 的数据结构是 Alert 结构,
Alert结构中有 Status,Labels,Annotations等,
其中 Labels等 是 KV 结构,KV结构就比较简单了,可以理解为Java中的 Map,
关于 KV 的方法有 Names,Values,SortedPairs 等等



作者:坚持到底v2
链接:https://www.jianshu.com/p/695d9e0e8af4
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值