admin 管理员组

文章数量: 887021

开源监控Prometheus介绍,安装,配置,使用详解

介绍

什么是Prometheus?

Prometheus是一个开源监控系统,前身是SoundCloud的告警插件。从2012年开始,Prometheus逐渐被大量公司使用。该项目的社区也便跃起来,收到越来越的贡献。在2016年继Kurberntes之后,Prometheus加入了Cloud Native Computing Foundation。

特征

  1. 多维度数据模型
  2. 不依赖分布式存储,单个服务器节点是自主的
  3. 以HTTP方式,通过pull模型拉去时间序列数据
  4. 灵活的查询语言
  5. 也通过中间网关支持push模型
  6. 通过服务发现或者静态配置,来发现目标服务对象
  7. 支持多种多样的图表和界面展示,prometheus和grafana是部署监控的最佳实践

组件

Prometheus生态包括了很多组件,它们中的一些是可选的:

  1. 主服务Prometheus Server负责抓取和存储时间序列数据
  2. 客户库负责检测应用程序代码
  3. 支持短生命周期的PUSH网关
  4. 基于Rails/SQL仪表盘构建器的GUI
  5. 多种导出工具,可以支持Prometheus存储数据转化为HAProxy、StatsD、Graphite等工具所需要的
  6. 数据存储格式
  7. 警告管理器
  8. 命令行查询工具
  9. 其他各种支撑工具

多数Prometheus组件是Go语言写的,这使得这些组件很容易编译和部署。

构架

下面这张图说明了Prometheus的整体架构,以及生态中的一些组件作用:

Prometheus服务,可以直接通过目标拉取数据,或者间接地通过中间网关拉取数据。它在本地存储抓取的所有数据,并通过一定规则进行清理和整理数据,并把得到的结果存储到新的时间序列中,PromQL和其他API可视化地展示收集的数据

适用场景

Prometheus在记录纯数字时间序列方面表现非常好。它既适用于面向服务器等硬件指标的监控,也适用于高动态的面向服务架构的监控。对于现在流行的微服务,Prometheus的多维度数据收集和数据筛选查询语言也是非常的强大。

Prometheus是为服务的可靠性而设计的,当服务出现故障时,它可以使你快速定位和诊断问题。它的搭建过程对硬件和服务没有很强的依赖关系。

不适用场景

Prometheus,它的价值在于可靠性,甚至在很恶劣的环境下,你都可以随时访问它和查看系统服务各种指标的统计信息。 如果你对统计数据需要100%的精确,它并不适用,例如:它不适用于实时计费系统


安装

使用预编译二进制文件
我们为Prometheus大多数的官方组件,提供了预编译二进制文件。可用版本下载列表
源码安装
如果要从源码安装Prometheus的官方组件,可以查看各个项目源码目录下的Makefile
注意点:在web上的文档指向最新的稳定版(不包括预发布版)。下一个版本指向master分支还没有发布的版本

Docker安装

Docker安装
所有Prometheus服务的Docker镜像在官方组织prom下,都是可用的

在Docker上运行Prometheus服务,只需要简单地执行docker run -p 9090:9090 prom/prometheus命令行即可。这条命令会启动Prometheus服务,使用的是默认配置文件,并对外界暴露9090端口

Prometheus镜像使用docker中的volumn卷存储实际度量指标。在生产环境上使用容器卷模式, 可以在Prometheus更新和升级时轻松管理Prometheus数据, 这种使用docker volumn卷方式存储数据,是被docker官方强烈推荐的.

通过几个选项,可以达到使用自己的配置的目的。下面有两个例子。

卷&绑定挂载
在运行Prometheus服务的主机上,做一个本地到docker容器的配置文件关系映射。

docker run -p 9090:9090 -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

或者为这个配置文件使用一个独立的volumn

docker run -p 9090:9090 -v /prometheus-data
prom/prometheus -config.file=/prometheus-data/prometheus.yml

自定义镜像

为了避免在主机上与docker映射配置文件,我们可以直接将配置文件拷贝到docker镜像中。如果Prometheus配置是静态的,并且在所有服务器上的配置相同,这种把配置文件直接拷贝到镜像中的方式是非常好的。

例如:利用Dockerfile创建一个Prometheus配置目录, Dockerfile应该这样写:

FROM prom/prometheus
ADD prometheus.yml /etc/prometheus/

然后编译和运行它:

docker build -t my-prometheus .
docker run -p 9090:9090 my-prometheus

一个更高级的选项是可以通过一些工具动态地渲染配置,甚至后台定期地更新配置

使用配置管理系统
如果你喜欢使用配置管理系统,你可能对下面地第三方库感兴趣:
Ansible:

  • griggheo/ansible-prometheus
  • William-Yeh/ansible-prometheus

Chef:

  • rayrod2030/chef-prometheus

SaltStack:

  • bechtoldt/saltstack-prometheus-formula

启动

入门教程

本篇是一篇hello,world风格的入门指南,使用一个简单的例子,向大家演示怎么样安装、配置和使用Prometheus。你可以下载和本地运行Prometheus服务,通过配置文件收集Prometheus服务自己产生的数据,并在这些收集数据的基础上,进行查询、制定规则和图表化显示所关心的数据

下载和运行Prometheus

tar xvfz prometheus-*.tar.gz

cd prometheus-*

在运行Prometheus服务之前,我们需要指定一个该服务运行所需要的配置文件

配置Prometheus服务监控本身
Prometheus通过Http方式拉取目标机上的度量指标。Prometheus服务也暴露自己运行所产生的数据,它能够抓取和监控自己的健康状况。

实际上,Prometheus服务收集自己运行所产生的时间序列数据,是没有什么意义的。但是它是一个非常好的入门级教程。保存一下的Prometheus配置到文件中,并自定义命名该文件名,如:prometheus.yml

global:scrape_interval:     15s # By default, scrape targets every 15 seconds.# Attach these labels to any time series or alerts when communicating with# external systems (federation, remote storage, Alertmanager).external_labels:monitor: 'codelab-monitor'# A scrape configuration containing exactly one endpoint to scrape:
# Here its Prometheus itself.
scrape_configs:# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.- job_name: 'prometheus'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5sstatic_configs:- targets: ['localhost:9090']

一个完整的配置选项,可以查看文件文档

启动Prometheus服务

cd到Prometheus服务目录,并指定刚刚自定义好的配置文件,并启动Prometheus服务, 如下所示:

start Prometheus.

By default, Prometheus stores its database in ./data (flag -storage.local.path).

./prometheus -config.file={$dir}/prometheus.yml # $dir = absolutely/relative path

Prometheus服务启动成功后,然后再打开浏览器在页面上数据http://localhost:9090. 服务运行几秒后,会开始收集自身的时间序列数据

你也可以通过在浏览器输入http://localhost:9090/metrics, 直接查看Prometheus服务收集到的自身数据

Prometheus服务执行的操作系统线程数量由GOMAXPROCS环境变量控制。从Go 1.5开始,默认值是可用的CPUs数量

盲目地设置GOMAXPROCS到一个比较高德值,有可能会适得其反。见Go FAQs

注意:Prometheus服务默认需要3GB的内存代销。如果你的机器内存比较小, 你可以调整Prometheus服务使用更少的内存。详细见内存使用文档

使用表达式浏览器

我们试着查看一些Prometheus服务自身产生的数据。为了使用Prometheus内置表达式浏览器,可以在浏览器中数据http://localhost:9090/graph, 选择"Console"视图,同一层级还有"Graph"tab。

如果你可以从http://localhost:9090/metrics查看到收集的度量指标数据,那么其中有一个指标数据名称为prometheus_target_interval_length_seconds(两次抓取数据之间的时间差)可以被提取出来,可以在表达式控制框中输入:

prometheus_target_interval_length_seconds

它应该会返回带有prometheus_target_interval_length_seconds度量指标的许多时间序列数据,只是带有不能标签, 这些标签有不同的延迟百分比和目标群组之间的间隔。

如果我们仅仅对p99延迟感兴趣,我们使用下面的查询表达式收集该信息

prometheus_target_interval_length_seconds{quantile=“0.99”}

为了统计时间序列数据记录的总数量,你可以写:

count(prometheus_target_interval_length_seconds)
更多的表达式语言,详见表达式语言文档

使用图形界面

使用http://localhost:9090/graph链接,查看图表"Graph"。
例如:输入下面的表达式,绘制在Prometheus服务中每秒存储的速率.

rate(prometheus_local_storage_chunk_ops_total[1m])

启动一些样本目标机

我们更感兴趣的是Prometheus服务抓取其他目标机的数据采样,并非自己的时间序列数据。Go客户库有一个例子,它会产生一些自己造的RPC延迟。启动三个带有不同的延时版本。

首先需要确保你有Go的环境

下载Go的Prometheus客户端,并运行下面三个服务:

 # Fetch the client library code and compile example.
git clone .git
cd client_golang/examples/random
go get -d
go build# Start 3 example targets in separate terminals:
./random -listen-address=:8080
./random -listen-address=:8081
./random -listen-address=:8082

你现在应该浏览器输入http://localhost:8080/metrics, http://localhost:8081/metrics, and http://localhost:8082/metrics, 会看到这些服务所产生的度量指标数据.

配置Prometheus服务,监听样本目标实例

现在我们将配置Prometheus服务,收集这三个例子的度量指标数据。我们把这三个服务实例命名为一个任务称为example-random, 并把8080端口服务和8081端口服务作为生产目标group,8082端口成为canary group。 为了在Prometheus服务中建模这个,我们需要添加两个群组到这个任务中,增加一些标签到不同的目标群组中。在这个例子中,我们会增加group="production"标签到带个目标组中,另外一个则是group=“canary”

为了达到这个目的,在prometheus.yml配置文件中,增加下面任务定义到scrape_config区域中, 并重启Prometheus服务:

scrape_configs:- job_name:       'example-random'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5sstatic_configs:- targets: ['localhost:8080', 'localhost:8081']labels:group: 'production'- targets: ['localhost:8082']labels:group: 'canary'

去表达式浏览器中验证Prometheus服务是否能统计到这两个群组的目标机度量数据,如:rpc_durations_seconds度量指标

为聚集到抓取的数据,设置规则并写入到新的时间序列中

当计算ad-hoc时,如果在累计到上千个时间序列数据的查询,可能会变慢。为了使这种多时间序列数据点查询更有效率,我们允许通过使用配置的记录规则,把预先记录表达式实时收集的数据存入到新的持久时间序列中。该例子中,如果我们对每秒RPCs数量(rpc_durations_seconds_count)的5分钟窗口流入的统计数量感兴趣的话。我们可以下面的表达式:

avg(rate(rpc_durations_seconds_count)[5m]) by (job, service)

试着使用图形化这个表达式

为了存储这个表达式所统计到的数据,我们可以使用新的度量指标,如job_service:rpc_durations_seconds_count:avg_rate5m, 创建一个配置规则文件,并把该文件保存为prometheus.rules:

job_service:rpc_durations_seconds_count:avg_rate5m = avg(rate(rpc_durations_seconds_count[5m])) by (job, service)

为了使Prometheus服务使用这个新的规则,在prometheus.yml配置文件的global配置区域添加一个rule_files语句。这个配置应该向下面这样写:

global:scrape_interval:     15s # By default, scrape targets every 15 seconds.evaluation_interval: 15s # Evaluate rules every 15 seconds.# Attach these extra labels to all timeseries collected by this Prometheus instance.external_labels:monitor: 'codelab-monitor'rule_files:- 'prometheus.rules'scrape_configs:- job_name: 'prometheus'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5sstatic_configs:- targets: ['localhost:9090']- job_name:       'example-random'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5sstatic_configs:- targets: ['localhost:8080', 'localhost:8081']labels:group: 'production'- targets: ['localhost:8082']labels:group: 'canary'

指定这个新的配置文件,并重启Prometheus服务。验证新的时间序列度量指标job_service:rpc_durations_seconds_count:avg_rate5m是否能够在Console控制框中查找出时间序列数据

常见问题

一般问题

  1. Prometheus是什么?

Prometheus是一款高活跃生态系统的开源系统监控和警告工具包。详见概览

  1. Prometheus与其他的监控系统比较

详见比较

  1. Prometheus有什么依赖?

Prometheus服务独立运行,没有其他依赖

  1. Prometheus有高可用的保证吗?

是的,在多台服务器上运行相同的Prometheus服务,相同的报警会由警告管理器删除
警告管理器当前不能保证高可用,但高可用是目标

  1. 我被告知Prometheus"不能水平扩展"

事实上,有许多方式可以扩展Prometheus。 阅读Robust Percetion的博客关于Prometheus的扩展

在通常的设置中,不可能使用inode。 有一个可能的缺点:如果你想删除Prometheus的存储目录,你会注意到,一些文件系统在删除文件时非常慢。

  1. 为什么Prometheus服务器组件不支持TLS或身份验证? 我可以添加这些吗?

虽然TLS和身份验证是经常请求的功能,但我们有意不在Prometheus的任何服务器端组件中实现它们。 有这么多不同的选项和参数(仅限TLS的10多个选项),我们决定专注于建立最佳的监控系统,而不是在每个服务器组件中支持完全通用的TLS和身份验证解决方案。

如果您需要TLS或身份验证,我们建议将反向代理放在Prometheus前面。 参见例如使用Nginx添加对Prometheus的基本认证。

请注意,这仅适用于入站连接。 Prometheus确实支持删除TLS-和auth启用的目标,以及其他创建出站连接的Prometheus组件具有类似的支持。

概念

数据模型

Prometheus从根本上存储的所有数据都是时间序列: 具有时间戳的数据流只属于单个度量指标和该度量指标下的多个标签维度。除了存储时间序列数据外,Prometheus也可以利用查询表达式存储5分钟的返回结果中的时间序列数据

metrics和labels(度量指标名称和标签)

每一个时间序列数据由metric度量指标名称和它的标签labels键值对集合唯一确定。

这个metric度量指标名称指定监控目标系统的测量特征(如:http_requests_total- 接收http请求的总计数). metric度量指标命名ASCII字母、数字、下划线和冒号,他必须配正则表达式[a-zA-Z_:][a-zA-Z0-9_:]*。

标签开启了Prometheus的多维数据模型:对于相同的度量名称,通过不同标签列表的结合, 会形成特定的度量维度实例。(例如:所有包含度量名称为/api/tracks的http请求,打上method=POST的标签,则形成了具体的http请求)。这个查询语言在这些度量和标签列表的基础上进行过滤和聚合。改变任何度量上的任何标签值,则会形成新的时间序列图

标签label名称可以包含ASCII字母、数字和下划线。它们必须匹配正则表达式[a-zA-Z_][a-zA-Z0-9_]*。带有_下划线的标签名称被保留内部使用。

标签labels值包含任意的Unicode码。

具体详见metrics和labels命名最佳实践。

有序的采样值

有序的采样值形成了实际的时间序列数据列表。每个采样值包括:

  • 一个64位的浮点值
  • 一个精确到毫秒级的时间戳
  • 一个样本数据集是针对一个指定的时间序列在一定时间范围的数据收集。这个时间序列是由<metric_name>{<label_name>=<label_value>, …}
    ‘‘小结:指定度量名称和度量指标下的相关标签值,则确定了所关心的目标数据,随着时间推移形成一个个点,在图表上实时绘制动态变化的线条’’

Notation(符号)
表示一个度量指标和一组键值对标签,需要使用以下符号:

[metric name]{[label name]=[label value], …}

例如,度量指标名称是api_http_requests_total, 标签为method=“POST”, handler="/messages" 的示例如下所示:

api_http_requests_total{method=“POST”, handler="/messages"}

这些命名和OpenTSDB使用方法是一样的

度量指标模型

metrics类型

Prometheus客户库提供了四个核心的metrics类型。这四种类型目前仅在客户库和wire协议中区分。Prometheus服务还没有充分利用这些类型。不久的将来就会发生改变。

Counter(计数器)

counter 是一个累计度量指标,它是一个只能递增的数值。计数器主要用于统计服务的请求数、任务完成数和错误出现的次数等等。计数器是一个递增的值。反例:统计goroutines的数量。计数器的使用方式在下面的各个客户端例子中:

客户端使用计数器的文档

  • Go
  • Java
  • Python
  • Ruby

Gauge(测量器)

gauge是一个度量指标,它表示一个既可以递增, 又可以递减的值。

测量器主要测量类似于温度、当前内存使用量等,也可以统计当前服务运行随时增加或者减少的Goroutines数量

客户端使用计量器的文档:

  • Go
  • Java
  • Python
  • Ruby

Histogram(柱状图)

histogram,是柱状图,在Prometheus系统中的查询语言中,有三种作用:

  1. 对每个采样点进行统计,打到各个分类值中(bucket)
  2. 对每个采样点值累计和(sum)
  3. 对采样点的次数累计和(count)

度量指标名称: [basename]的柱状图, 上面三类的作用度量指标名称

  • [basename]_bucket{le=“上边界”}, 这个值为小于等于上边界的所有采样点数量
  • [basename]_sum
  • [basename]_count
    小结:所以如果定义一个度量类型为Histogram,则Prometheus系统会自动生成三个对应的指标
    使用histogram_quantile()函数, 计算直方图或者是直方图聚合计算的分位数阈值。 一个直方图计算Apdex值也是合适的, 当在buckets上操作时,记住直方图是累计的。详见直方图和总结

客户库的直方图使用文档:

  • Go
  • Java
  • Python
  • Ruby

[Summary]总结

类似histogram柱状图,summary是采样点分位图统计,(通常的使用场景:请求持续时间和响应大小)。 它也有三种作用:

对于每个采样点进行统计,并形成分位图。(如:正态分布一样,统计低于60分不及格的同学比例,统计低于80分的同学比例,统计低于95分的同学比例)

  • 统计班上所有同学的总成绩(sum)
  • 统计班上同学的考试总人数(count)
  • 带有度量指标的[basename]的summary 在抓取时间序列数据展示。

观察时间的φ-quantiles (0 ≤ φ ≤ 1), 显示为[basename]{分位数="[φ]"}

  • [basename]_sum, 是指所有观察值的总和
  • [basename]_count, 是指已观察到的事件计数值

详见histogram和summaries
有关summaries的客户端使用文档:

  • Go
  • Java
  • Python
  • Ruby

任务与实例

Jobs和Instances(任务和实例)

就Prometheus而言,pull拉取采样点的端点服务称之为instance。多个这样pull拉取采样点的instance, 则构成了一个job

例如, 一个被称作api-server的任务有四个相同的实例。

  • job: api-server
    • instance 1:1.2.3.4:5670
    • instance 2:1.2.3.4:5671
    • instance 3:5.6.7.8:5670
    • instance 4:5.6.7.8:5671

自动化生成的标签和时间序列
当Prometheus拉取一个目标, 会自动地把两个标签添加到度量名称的标签列表中,分别是:

  • job: 目标所属的配置任务名称api-server。
  • instance: 采样点所在服务: host:port
    如果以上两个标签二者之一存在于采样点中,这个取决于honor_labels配置选项。详见文档

对于每个采样点所在服务instance,Prometheus都会存储以下的度量指标采样点:

  • up{job="[job-name]", instance=“instance-id”}: up值=1,表示采样点所在服务健康; 否则,网络不通, 或者服务挂掉了
  • scrape_duration_seconds{job="[job-name]", instance="[instance-id]"}: 尝试获取目前采样点的时间开销
  • scrape_samples_post_metric_relabeling{job="", instance=""}: 表示度量指标的标签变化后,标签没有变化的度量指标数量。
  • scrape_samples_scraped{job="", instance=""}: 这个采样点目标暴露的样本点数量

备注:我查了下scrape_samples_post_metric_relabeling 和 scrape_samples_scraped的值好像是一样的。还是这两个值没有理解

up度量指标对服务健康的监控是非常有用的。


查询

Prometheus提供一个函数式的表达式语言,可以使用户实时地查找和聚合时间序列数据。表达式计算结果可以在图表中展示,也可以在Prometheus表达式浏览器中以表格形式展示,或者作为数据源, 以HTTP API的方式提供给外部系统使用。

examples

这个文档仅供参考, 这里先举几个容易上手的例子。

表达式语言数据类型

在Prometheus的表达式语言中,任何表达式或者子表达式都可以归为四种类型:

  • instant vector 瞬时向量 - 它是指在同一时刻,抓取的所有度量指标数据。这些度量指标数据的key都是相同的,也即相同的时间戳。
  • range vector 范围向量 - 它是指在任何一个时间范围内,抓取的所有度量指标数据。
  • scalar 标量 - 一个简单的浮点值
  • string 字符串 - 一个当前没有被使用的简单字符串
    依赖于使用场景(例如:图表 vs. 表格),根据用户所写的表达式,仅仅只有一部分类型才适用这种表达式。例如:瞬时向量类型是唯一可以直接在图表中使用的。

Literals

字符串

字符串可以用单引号、双引号或者反引号表示

PromQL遵循与Go相同的转义规则。在单引号,双引号中,反斜杠成为了转义字符,后面可以跟着a, b, f, n, r, t, v或者\。 可以使用八进制(\nnn)或者十六进制(\xnn, \unnnn和\Unnnnnnnn)提供特定字符。

在反引号内不处理转义字符。与Go不同,Prom不会丢弃反引号中的换行符。例如:

“this is a string”
‘these are unescaped: \n \ \t’
these are not unescaped: \n ’ " \t"’

浮点数

标量浮点值可以直接写成形式-[.(digits)]。

-2.43

时间序列选择器
即时向量选择器
瞬时向量选择器可以对一组时间序列数据进行筛选,并给出结果中的每个结果键值对(时间戳-样本值): 最简单的形式是,只有一个度量名称被指定。在一个瞬时向量中这个结果包含有这个度量指标名称的所有样本数据键值对。

下面这个例子选择所有时间序列度量名称为http_requests_total的样本数据:

http_requests_total

通过在度量指标后面增加{}一组标签可以进一步地过滤这些时间序列数据。

下面这个例子选择了度量指标名称为http_requests_total,且一组标签为 job=prometheus, group=canary:

http_requests_total{job=“prometheus”,group=“canary”}

可以采用不匹配的标签值也是可以的,或者用正则表达式不匹配标签。标签匹配操作如下所示:

  • =: 精确地匹配标签给定的值
  • !=: 不等于给定的标签值
  • =~: 正则表达匹配给定的标签值
  • !=: 给定的标签值不符合正则表达式
    例如:度量指标名称为http_requests_total,正则表达式匹配标签environment为staging, testing, development的值,且http请求方法不等于GET。

http_requests_total{environment=~“staging|testing|development”, method!=“GET”}

匹配空标签值的标签匹配器也可以选择没有设置任何标签的所有时间序列数据。正则表达式完全匹配。

向量选择器必须指定一个度量指标名称或者至少不能为空字符串的标签值。以下表达式是非法的:

{job=~".*"} #Bad!

上面这个例子既没有度量指标名称,标签选择器也可以正则匹配空标签值,所以不符合向量选择器的条件

相反地,下面这些表达式是有效的,第一个一定有一个字符。第二个有一个有用的标签method

{job=~".+"} # Good!
{job=~".*", method=“get”} # Good!

标签匹配器能够被应用到度量指标名称,使用__name__标签筛选度量指标名称。例如:表达式http_requests_total等价于{name=“http_requests_total”}。 其他的匹配器,如:= ( !=, =~, !~)都可以使用。下面的表达式选择了度量指标名称以job:开头的时间序列数据:

{name=~"^job:.*"} #

范围向量选择器
范围向量类似瞬时向量, 不同在于,它们从当前实例选择样本范围区间。在语法上,时间长度被追加在向量选择器尾部的方括号[]中,用以指定对于每个样本范围区间中的每个元素应该抓取的时间范围样本区间。

时间长度有一个数值决定,后面可以跟下面的单位:

  • s - seconds
  • m - minutes
  • h - hours
  • d - days
  • w - weeks
  • y - years
    在下面这个例子中, 选择过去5分钟内,度量指标名称为http_requests_total, 标签为job="prometheus"的时间序列数据:

http_requests_total{job=“prometheus”}[5m]

偏移修饰符
这个offset偏移修饰符允许在查询中改变单个瞬时向量和范围向量中的时间偏移

例如,下面的表达式返回相对于当前时间的前5分钟时的时刻, 度量指标名称为http_requests_total的时间序列数据:

http_requests_total offset 5m

注意:offset偏移修饰符必须直接跟在选择器后面,例如:

sum(http_requests_total{method=“GET”} offset 5m) // GOOD.

然而,下面这种情况是不正确的

sum(http_requests_total{method=“GET”}) offset 5m // INVALID.

offset偏移修饰符在范围向量上和瞬时向量用法一样的。下面这个返回了相对于当前时间的前一周时,过去5分钟的度量指标名称为http_requests_total的速率:

rate(http_requests_total[5m] offset 1w)

操作符

Prometheus支持二元和聚合操作符。详见表达式语言操作符

函数

Prometheus提供了一些函数列表操作时间序列数据。详见表达式语言函数

陷阱

插值和陈旧

当运行查询后,独立于当前时刻被选中的时间序列数据所对应的时间戳,这个时间戳主要用来进行聚合操作,包括sum, avg等,大多数聚合的时间序列数据所对应的时间戳没有对齐。由于它们的独立性,我们需要在这些时间戳中选择一个时间戳,并已这个时间戳为基准,获取小于且最接近这个时间戳的时间序列数据。

如果5分钟内,没有获取到任何的时间序列数据,则这个时间戳不会存在。那么在图表中看到的数据都是在当前时刻5分钟前的数据。

注意:差值和陈旧处理可能会发生变化。详见://github.com/prometheus/prometheus/issues/581

避免慢查询和高负载

如果一个查询需要操作非常大的数据量,图表绘制很可能会超时,或者服务器负载过高。因此,在对未知数据构建查询时,始终需要在Prometheus表达式浏览器的表格视图中构建查询,直到结果是看起来合理的(最多为数百个,而不是数千个)。只有当你已经充分过滤或者聚合数据时,才切换到图表模式。如果表达式的查询结果仍然需要很长时间才能绘制出来,则需要通过记录规则重新清洗数据。

api_http_requests_total这样简单的度量指标名称选择器,可以扩展到具有不同标签的数千个时间序列中,这对于Prometheus的查询语言是非常重要的。还要记住,聚合操作即使输出的结果集非常少,但是它会在服务器上产生负载。这类似于关系型数据库查询可一个字段的总和,总是非常缓慢。

操作符

二元操作符
Prometheus的查询语言支持基本的逻辑运算和算术运算。对于两个瞬时向量, 匹配行为可以被改变。

算术二元运算符
在Prometheus系统中支持下面的二元算术操作符:

  • +加法
  • -减法
  • *乘法
  • /除法
  • % 模
  • ^ 幂等

二元运算操作符支持scalar/scalar(标量/标量)、vector/scalar(向量/标量)、和vector/vector(向量/向量)之间的操作。

在两个标量之间进行操作符运算,得到的结果也是标量

在向量和标量之间,这个操作符会作用于这个向量的每个样本值上。例如:如果一个时间序列瞬时向量除以2,操作结果也是一个新的瞬时向量,且度量指标名称不变, 它是原度量指标瞬时向量的每个样本值除以2.

在两个向量之间,一个二元算术操作符作用在左边瞬时向量的每个样本值,且该样本值与操作符右边能匹配上的样本值计算,向量匹配。结果写入到一个没有度量指标名称的瞬时向量。

比较二元操作符
在Prometheus系统中,比较二元操作符有:

  • == 等于
  • != 不等于
  • > 大于
  • < 小于
  • >= 大于等于
  • <= 小于等于

比较二元操作符被应用于scalar/scalar(标量/标量)、vector/scalar(向量/标量),和vector/vector(向量/向量)。比较操作符得到的结果是bool布尔类型值,返回1或者0值。

在两个标量之间的比较运算,bool结果写入到另一个结果标量中。

瞬时向量和标量之间的比较运算,这个操作符会应用到某个当前时刻的每个时间序列数据上,如果一个时间序列数据值与这个标量比较结果是false,则这个时间序列数据被丢弃掉,如果是true, 则这个时间序列数据被保留在结果中。

在两个瞬时向量之间的比较运算,左边度量指标数据中的每个时间序列数据,与右边度量指标中的每个时间序列数据匹配,没有匹配上的,或者计算结果为false的,都被丢弃,不在结果中显示。否则将保留左边的度量指标和标签的样本数据写入瞬时向量。

逻辑/集合二元操作符
逻辑/集合二元操作符只能作用在即时向量, 包括:

  • and 交集
  • or 并集
  • unless 补集
    vector1 and vector2 的逻辑/集合二元操作符,规则:vector1瞬时向量中的每个样本数据与vector2向量中的所有样本数据进行标签匹配,不匹配的,全部丢弃。运算结果是保留左边的度量指标名称和值。

vector1 or vector2的逻辑/集合二元操作符,规则: 保留vector1向量中的每一个元素,对于vector2向量元素,则不匹配vector1向量的任何元素,则追加到结果元素中。

vector1 unless vector2的逻辑/集合二元操作符,又称差积。规则:包含在vector1中的元素,但是该元素不在vector2向量所有元素列表中,则写入到结果集中

向量匹配

向量之间的匹配是指右边向量中的每一个元素,在左边向量中也存在。这里有两种基本匹配行为特征:

一对一,找到这个操作符的两边向量元素的相同元素。默认情况下,操作符的格式是vector1 [operate] vector2。如果它们有相同的标签和值,则表示相匹配。ingoring关键字是指,向量匹配时,可以忽略指定标签。on关键字是指,在指定标签上进行匹配。格式如下所示

[vector expr] [bin-op] ignoring([label list]) [vector expr]
[vector expr] [bin-op] on([lable list]) [vector expr]

例如样本数据:

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=“404”} 21

method:http_requests:rate5m{method=“get”} 600
method:http_requests:rate5m{method=“delete”} 34
method:http_requests:rate5m{method=“post”} 120

查询例子:

method_code:http_errors:rate5m{code=“500”} / ignoring(code) method:http_requests:rate5m

两个向量之间的除法操作运算的向量结果是,每一个向量样本http请求方法标签值是500,且在过去5分钟的运算值。如果没有忽略code="500"的标签,这里不能匹配到向量样本数据。两个向量的请求方法是put和delete的样本数据不会出现在结果列表中

{method=“get”} 0.04 // 24 / 600
{method=“post”} 0.05 // 6 / 120

多对一和一对多的匹配,是指向量元素中的一个样本数据匹配标签到了多个样本数据标签。这里必须直接指定两个修饰符group_left或者group_right, 左或者右决定了哪边的向量具有较高的子集

ignoring(

这个group带标签的修饰符标签列表包含了“一对多”中的“一”一侧的额外标签。对于on标签只能是这些列表中的一个。结果向量中的每一个时间序列数据都是唯一的。

group修饰符只能被用在比较操作符和算术运算符。

查询例子:

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

在这个例子中,左向量的标签数量多于左边向量的标签数量,所以我们使用group_left。右边向量的时间序列元素匹配左边的所有相同method标签:

{method=“get”, code=“500”} 0.04 // 24 /600
{method=“get”, code=“404”} 0.05 // 30 /600
{method=“post”, code=“500”} 0.05 // 6 /600
{method=“post”, code=“404”} 0.175 // 21 /600

多对一和一对多匹配应该更多地被谨慎使用。经常使用ignoring(<labels>)输出想要的结果。

聚合操作符

Prometheus支持下面的内置聚合操作符。这些聚合操作符被用于聚合单个即时向量的所有时间序列列表,把聚合的结果值存入到新的向量中。

  • sum (在维度上求和)
  • max (在维度上求最大值)
  • min (在维度上求最小值)
  • avg (在维度上求平均值)
  • stddev (求标准差)
  • stdvar (求方差)
  • count (统计向量元素的个数)
  • count_values (统计相同数据值的元素数量)
  • bottomk (样本值第k个最小值)
  • topk (样本值第k个最大值)
  • quantile (统计分位数)

这些操作符被用于聚合所有标签维度,或者通过without或者by子句来保留不同的维度。

([parameter,] ) [without | by (

parameter只能用于count_values, quantile, topk和bottomk。without移除结果向量中的标签集合,其他标签被保留输出。by关键字的作用正好相反,即使它们的标签值在向量的所有元素之间。keep_common子句允许保留额外的标签(在元素之间相同,但不在by子句中的标签)

count_values对每个唯一的样本值输出一个时间序列。每个时间序列都附加一个标签。这个标签的名字有聚合参数指定,同时这个标签值是唯一的样本值。每一个时间序列值是结果样本值出现的次数。

topk和bottomk与其他输入样本子集聚合不同,返回的结果中包括原始标签。by和without仅仅用在输入向量的桶中

例如:
如果度量指标名称http_requests_total包含由group, application, instance的标签组成的时间序列数据,我们可以通过以下方式计算去除instance标签的http请求总数

sum(http_requests_total) without (instance)

如果我们对所有应用程序的http请求总数,我们可以简单地写下:

sum(http_requests_total)

统计每个编译版本的二进制文件数量,我们可以如下写:

count_values(“version”, build_version)

通过所有实例,获取http请求第5个最大值,我们可以简单地写下:

topk(5, http_requests_total)

二元运算符优先级

在Prometheus系统中,二元运算符优先级从高到低:

  • ^
  • *, /, %
  • +, -
  • ==, !=, <=, <, >=, >
  • and, unless
  • or

函数

函数列表

一些函数有默认的参数,例如:year(v=vector(time()) instant-vector)。v是参数值,instant-vector是参数类型。vector(time())是默认值。
abs()
abs(v instant-vector)返回输入向量的所有样本的绝对值。

absent()
absent(v instant-vector),如果赋值给它的向量具有样本数据,则返回空向量;如果传递的瞬时向量参数没有样本数据,则返回不带度量指标名称且带有标签的样本值为1的结果

当监控度量指标时,如果获取到的样本数据是空的, 使用absent方法对告警是非常有用的

absent(nonexistent{job=“myjob”}) # => key: value = {job=“myjob”}: 1

absent(nonexistent{job=“myjob”, instance=~".*"}) # => {job=“myjob”} 1
so smart !

absent(sum(nonexistent{job=“myjob”})) # => key:value {}: 0

ceil()
ceil(v instant-vector) 是一个向上舍入为最接近的整数。

changes()
changes(v range-vector) 输入一个范围向量, 返回这个范围向量内每个样本数据值变化的次数。

clamp_max()
clamp_max(v instant-vector, max scalar)函数,输入一个瞬时向量和最大值,样本数据值若大于max,则改为max,否则不变

clamp_min()
clamp_min(v instant-vector)函数,输入一个瞬时向量和最大值,样本数据值小于min,则改为min。否则不变

count_saclar()
count_scalar(v instant-vector) 函数, 输入一个瞬时向量,返回key:value=“scalar”: 样本个数。而count()函数,输入一个瞬时向量,返回key:value=向量:样本个数,其中结果中的向量允许通过by条件分组。

day_of_month()
day_of_month(v=vector(time()) instant-vector)函数,返回被给定UTC时间所在月的第几天。返回值范围:1~31。

day_of_week()
day_of_week(v=vector(time()) instant-vector)函数,返回被给定UTC时间所在周的第几天。返回值范围:0~6. 0表示星期天。

days_in_month()
days_in_month(v=vector(time()) instant-vector)函数,返回当月一共有多少天。返回值范围:28~31.

delta()
delta(v range-vector)函数,计算一个范围向量v的第一个元素和最后一个元素之间的差值。返回值:key:value=度量指标:差值

下面这个表达式例子,返回过去两小时的CPU温度差:

delta(cpu_temp_celsius{host=“zeus”}[2h])
delta函数返回值类型只能是gauges。

deriv()
deriv(v range-vector)函数,计算一个范围向量v中各个时间序列二阶导数,使用简单线性回归

deriv二阶导数返回值类型只能是gauges。

drop_common_labels()
drop_common_labels(instant-vector)函数,输入一个瞬时向量,返回值是key:value=度量指标:样本值,其中度量指标是去掉了具有相同标签。
例如:http_requests_total{code=“200”, host=“127.0.0.1:9090”, method=“get”} : 4, http_requests_total{code=“200”, host=“127.0.0.1:9090”, method=“post”} : 5, 返回值: http_requests_total{method=“get”} : 4, http_requests_total{code=“200”, method=“post”} : 5

exp()
exp(v instant-vector)函数,输入一个瞬时向量, 返回各个样本值的e指数值,即为e^N次方。特殊情况如下所示:

Exp(+inf) = +Inf
Exp(NaN) = NaN

floor()
floor(v instant-vector)函数,与ceil()函数相反。 4.3 为 4 。

histogram_quantile()
histogram_quatile(φ float, b instant-vector) 函数计算b向量的φ-直方图 (0 ≤ φ ≤ 1) 。参考中文文献[/]

holt_winters()
holt_winters(v range-vector, sf scalar, tf scalar)函数基于范围向量v,生成事件序列数据平滑值。平滑因子sf越低, 对老数据越重要。趋势因子tf越高,越多的数据趋势应该被重视。0< sf, tf <=1。 holt_winters仅用于gauges

hour()
hour(v=vector(time()) instant-vector)函数返回被给定UTC时间的当前第几个小时,时间范围:0~23。

idelta()
idelta(v range-vector)函数,输入一个范围向量,返回key: value = 度量指标: 每最后两个样本值差值。

increase()
increase(v range-vector)函数, 输入一个范围向量,返回:key:value = 度量指标:last值-first值,自动调整单调性,如:服务实例重启,则计数器重置。与delta()不同之处在于delta是求差值,而increase返回最后一个减第一个值,可为正为负。

下面的表达式例子,返回过去5分钟,连续两个时间序列数据样本值的http请求增加值。

increase(http_requests_total{job=“api-server”}[5m])

increase的返回值类型只能是counters,主要作用是增加图表和数据的可读性,使用rate记录规则的使用率,以便持续跟踪数据样本值的变化。

irate
irate(v range-vector)函数, 输入:范围向量,输出:key: value = 度量指标: (last值-last前一个值)/时间戳差值。它是基于最后两个数据点,自动调整单调性, 如:服务实例重启,则计数器重置。

下面表达式针对范围向量中的每个时间序列数据,返回两个最新数据点过去5分钟的HTTP请求速率。

irate(http_requests_total{job=“api-server”}[5m])

irate只能用于绘制快速移动的计数器。因为速率的简单更改可以重置FOR子句,利用警报和缓慢移动的计数器,完全由罕见的尖峰组成的图形很难阅读。

label_replace()
对于v中的每个时间序列,label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) 将正则表达式与标签值src_label匹配。如果匹配,则返回时间序列,标签值dst_label被替换的扩展替换。$1替换为第一个匹配子组,$2替换为第二个等。如果正则表达式不匹配,则时间序列不会更改。

另一种更容易的理解是:label_replace函数,输入:瞬时向量,输出:key: value = 度量指标: 值(要替换的内容:首先,针对src_label标签,对该标签值进行regex正则表达式匹配。如果不能匹配的度量指标,则不发生任何改变;否则,如果匹配,则把dst_label标签的标签纸替换为replacement
下面这个例子返回一个向量值a带有foo标签:
label_replace(up{job=“api-server”, serice=“a:c”}, “foo”, “$1”, “service”, “(.):.”)

ln()
ln(v instance-vector)计算瞬时向量v中所有样本数据的自然对数。特殊例子:

ln(+Inf) = +Inf
ln(0) = -Inf
ln(x<0) = NaN
ln(NaN) = NaN

log2()
log2(v instant-vector)函数计算瞬时向量v中所有样本数据的二进制对数。

log10()
log10(v instant-vector)函数计算瞬时向量v中所有样本数据的10进制对数。相当于ln()

minute()
minute(v=vector(time()) instant-vector)函数返回给定UTC时间当前小时的第多少分钟。结果范围:0~59。

month()
month(v=vector(time()) instant-vector)函数返回给定UTC时间当前属于第几个月,结果范围:0~12。

predict_linear()
predict_linear(v range-vector, t scalar)预测函数,输入:范围向量和从现在起t秒后,输出:不带有度量指标,只有标签列表的结果值。

例如:predict_linear(http_requests_total{code=“200”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”}[5m], 5)
结果:
{code=“200”,handler=“query_range”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 1
{code=“200”,handler=“prometheus”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 4283.449995397104
{code=“200”,handler=“static”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 22.99999999999999
{code=“200”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 130.90381188596754
{code=“200”,handler=“graph”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 2
{code=“200”,handler=“label_values”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 2

rate()
rate(v range-vector)函数, 输入:范围向量,输出:key: value = 不带有度量指标,且只有标签列表:(last值-first值)/时间差s

rate(http_requests_total[5m])

结果:
{code=“200”,handler=“label_values”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“query_range”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“prometheus”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0.2
{code=“200”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0.003389830508474576
{code=“422”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“static”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“graph”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“400”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0

rate()函数返回值类型只能用counters, 当用图表显示增长缓慢的样本数据时,这个函数是非常合适的。

注意:当rate函数和聚合方式联合使用时,一般先使用rate函数,再使用聚合操作, 否则,当服务实例重启后,rate无法检测到counter重置。

resets()
resets()函数, 输入:一个范围向量,输出:key-value=没有度量指标,且有标签列表[在这个范围向量中每个度量指标被重置的次数]。在两个连续样本数据值下降,也可以理解为counter被重置。
示例:

resets(http_requests_total[5m])
结果:
{code=“200”,handler=“label_values”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“query_range”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“prometheus”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“422”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“static”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“200”,handler=“graph”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0
{code=“400”,handler=“query”,instance=“120.77.65.193:9090”,job=“prometheus”,method=“get”} 0

resets只能和counters一起使用。

round()
round(v instant-vector, to_nearest 1= scalar)函数,与ceil和floor函数类似,输入:瞬时向量,输出:指定整数级的四舍五入值, 如果不指定,则是1以内的四舍五入。

scalar()
scalar(v instant-vector)函数, 输入:瞬时向量,输出:key: value = “scalar”, 样本值[如果度量指标样本数量大于1或者等于0, 则样本值为NaN, 否则,样本值本身]

sort()
sort(v instant-vector)函数,输入:瞬时向量,输出:key: value = 度量指标:样本值[升序排列]

sort_desc()
sort(v instant-vector函数,输入:瞬时向量,输出:key: value = 度量指标:样本值[降序排列]

sqrt()
sqrt(v instant-vector)函数,输入:瞬时向量,输出:key: value = 度量指标: 样本值的平方根

time()
time()函数,返回从1970-01-01到现在的秒数,注意:它不是直接返回当前时间,而是时间戳

vector()
vector(s scalar)函数,返回:key: value= {}, 传入参数值

year()
year(v=vector(time()) instant-vector), 返回年份。

_over_time()
下面的函数列表允许传入一个范围向量,返回一个带有聚合的瞬时向量:

  • avg_over_time(range-vector): 范围向量内每个度量指标的平均值。
  • min_over_time(range-vector): 范围向量内每个度量指标的最小值。
  • max_over_time(range-vector): 范围向量内每个度量指标的最大值。
  • sum_over_time(range-vector): 范围向量内每个度量指标的求和值。
  • count_over_time(range-vector): 范围向量内每个度量指标的样本数据个数。
  • quantile_over_time(scalar, range-vector): 范围向量内每个度量指标的样本数据值分位数,φ-quantile (0 ≤ φ ≤ 1)
  • stddev_over_time(range-vector): 范围向量内每个度量指标的总体标准偏差。
  • stdvar_over_time(range-vector): 范围向量内每个度量指标的总体标准方差。

举例

查询例子

简单的时间序列选择

返回度量指标名称是http_requests_total的所有时间序列样本数据:

http_requests_total

返回度量指标名称是http_requests_total, 标签分别是job=“apiserver, handler=”/api/comments的所有时间序列样本数据:

http_requests_total{job=“apiserver”, hanlder="/api/comments"}

返回度量指标名称是http_requests_total, 标签分别是job=“apiserver, handler=”/api/comments,且是5分钟内的所有时间序列样本数据:

http_requests_total{job=“apiserver”, handler="/api/comments"}[5m]

注意:一个范围向量表达式结果不能直接在Graph图表中,但是可以在"console"视图中展示。

使用正则表达式,你可以通过特定模式匹配标签为job的特定任务名,获取这些任务的时间序列。在下面这个例子中, 所有任务名称以server结尾。

http_requests_total{job=~“server$”}

返回度量指标名称是http_requests_total, 且http返回码不以4开头的所有时间序列数据:

http_requests_total{status!~"^4…$"}

使用函数,操作符等

返回度量指标名称http_requests_total,且过去5分钟的所有时间序列数据值速率。

rate(http_requests_total[5m])

假设度量名称是http_requests_total,且过去5分钟的所有时间序列数据的速率和,速率的维度是job

sum(rate(http_requests_total)[5m]) by (job)

如果我们有相同维度标签,我们可以使用二元操作符计算样本数据,返回值:key: value=标签列表:计算样本值。例如,下面这个表达式返回每一个实例剩余内存,单位是M, 如果不同,则需要使用ignoring(label_lists),如果多对一,则采用group_left, 如果是一对多,则采用group_right。

(instance_memory_limit_byte - instant_memory_usage_bytes) / 1024 / 1024

相同表达式,求和可以采用下面表达式:

sum( instance_memory_limit_bytes - instance_memory_usage_bytes) by (app, proc) / 1024 / 1024

如果相同集群调度器任务,显示CPU使用率度量指标的话,如下所示:

instance_cpu_time_ns{app=“lion”, pro=“web”, rev=“34d0f99”, env=“prod”, job=“cluster-manager”}
instance_cpu_time_ns{app=“elephant”, proc=“worker”, rev=“34d0f99”, env=“prod”, job=“cluster-manager”}
instance_cpu_time_ns{app=“turtle”, proc=“api”, rev=“4d3a513”, env=“prod”, job=“cluster-manager”}

我们可以获取最高的3个CPU使用率,按照标签列表app和proc分组

topk(3, sum(rate(instance_cpu_time_ns[5m])) by(app, proc))

假设一个服务实例只有一个时间序列数据,那么我们通过下面表达式,可以统计出每个应用的实例数量:

count(instance_cpu_time_ns) by (app)

记录规则

定义recording rules

配置规则

Prometheus支持可以配置,然后定期执行的两种规则: recording rules(记录规则)和alerting rules警告规则。为了在Prometheus系统中包括规则,我们需要创建一个包含规则语句的文件,并通过在Prometheus配置的rule_fields字段加载这个记录规则文件。

这些规则文件可以通过像Prometheus服务发送SIGNUP信号量,实时重载记录规则。如果所有的记录规则有正确的格式和语法,则这些变化能够生效。

语法检查规则
在没有启动Prometheus服务之前,想快速知道一个规则文件是否正确,可以通过安装和运行Prometheus的promtool命令行工具检验:

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

当记录规则文件是有效的,则这个检查会打印出解析到规则的文本表示,并以返回值0退出程序。

如果有任何语法错误的话,则这个命令行会打印出一个错误信息到标准输出,并以返回值1退出程序。无效的输入参数,则以返回值2退出程序。

记录规则

记录规则允许你预先计算经常需要的,或者计算复杂度高的表达式,并将结果保存为一组新的时间序列数据。查询预计算结果通常比需要时进行计算表达式快得多。对于dashboard是非常有用的,因为dashboard需要实时刷新查询表达式的结果。

为了增加一个新记录规则,增加下面的记录规则到你的规则文件中:

<new time series name>[{<label overrides>}] = <expression to record>

例子:
计算每个job的http请求总数,保存到新的度量指标中>

job:http_inprogree_requests:sum = sum(http_inprogress_requests) by (job)

放弃老标签,写入新标签的结果时间序列数据:

new_time_series{label_to_change=“new_value”, label_to_drop=""} = old_time_series

记录规则的执行周期有Prometheus的配置文件中的evaluate_interval指定。规则语句的右侧表达式一旦被执行,则新的时间戳key为当前时间,value为右边表达式的样本值,新的度量指标名称和标签列表为左边名称。

HTTP API访问

在Prometheus服务上/api/v1版本api是稳定版。

格式概述

这个API返回是JSON格式。每个请求成功的返回值都是以2xx开头的编码。

到达API处理的无效请求,返回一个JSON错误对象,并返回下面的错误码:

  • 400 Bad Request。当参数错误或者丢失时。
  • 422 Unprocessable Entity。当一个表达式不能被执行时。
  • 503 Service Unavailable。当查询超时或者中断时。
    在请求到达API之前,其他非2xx的错误码可能会被返回。

JSON返回格式如下所示:

"status": "success" | "error",
"data": <data>,// 如果status是"error", 这个数据字段还会包括下面的数据
"errorType": "<string>",
"error": "<string>"
}

输入时间戳可以被RFC3339格式或者Unix时间戳提供。输出时间戳以Unix时间戳的方式呈现。

查询参数名称可以用[]中括号重复次数。
<series_selector>占位符提供像http_requests_total或者http_requests_total{method=~"^GET|POST$"}的Prometheus时间序列选择器,并需要在URL中编码传输。

占位符涉及到[0-9]-[smhdwy]。例如:5m表示5分钟的持续时间。

表达式查询

查询语言表达式可以在瞬时向量或者范围向量中执行。

Instant queries(即时查询)
瞬时向量的http restful api查询:

GET /api/v1/query

URL查询参数:

  • query=: Prometheus表达式查询字符串。
  • time=<rfc3339 | uninx_timestamp>: 执行时间戳,可选项。
  • timeout=: 执行超时时间设置,默认由-query.timeout标志设置

如果time缺省,则用当前服务器时间表示执行时刻。

这个查询结果的data部分有下面格式:

{"resultType": "matrix" | "vector" | "scalar" | "string","result": <value>
}

是一个查询结果数据,依赖于这个resultType格式, 不同的结果类型,则会有不同的结果数据格式。见表达式查询结果格式。

下面例子执行了在时刻是2015-07-01T20:10:51.781Z的up表达式:

$ curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'
{"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" ]}]}
}

范围查询
下面评估了一个范围时间的查询表达式:

GET /api/v1/query_range

URL查询参数

  • query=: Prometheus表达式查询字符串。
  • start=<rfc3339 | unix_timestamp>: 开始时间戳。
  • end=<rfc3339 | unix_timestamp>: 结束时间戳。
  • step=: 查询时间步长,范围时间内每step秒执行一次。

下面查询结果格式的data部分:

{"resultType": "matrix","result": <value>
}

对于占位符的格式,详见范围向量结果格式。

下面例子执行了在时刻是2015-07-01T20:10:51.781Z的up表达式:

$ curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'
{"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" ]}]}
}

范围查询

下面评估了一个范围时间的查询表达式:

GET /api/v1/query_range

URL查询参数

  • query=: Prometheus表达式查询字符串。
  • start=<rfc3339 | unix_timestamp>: 开始时间戳。
  • end=<rfc3339 | unix_timestamp>: 结束时间戳。
  • step=: 查询时间步长,范围时间内每step秒执行一次。

下面查询结果格式的data部分:

{"resultType": "matrix","result": <value>
}

对于占位符的格式,详见范围向量结果格式。

下面例子评估的查询条件up,且30s范围的查询,步长是15s。

$ 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'
{"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" ]]}]}
}

查询元数据

通过标签匹配器找到度量指标列表
下面例子返回了度量指标列表 且不返回时间序列数据值。

GET /api/v1/series

URL查询参数:

  • match[]=<series_selector>: 选择器是series_selector。这个参数个数必须大于等于1.
  • start=<rfc3339 | unix_timestamp>: 开始时间戳。
  • end=<rfc3339 | unix_timestamp>: 结束时间戳。

返回结果的data部分,是由key-value键值对的对象列表组成的。

下面这个例子返回时间序列数据, 选择器是up或者process_start_time_seconds{job=“prometheus”}

$ curl -g 'http://localhost:9090/api/v1/series?match[]=up&match[]=process_start_time_seconds{job="prometheus"}'
{"status" : "success","data" : [{"__name__" : "up","job" : "prometheus","instance" : "localhost:9090"},{"__name__" : "up","job" : "node","instance" : "localhost:9091"},{"__name__" : "process_start_time_seconds","job" : "prometheus","instance" : "localhost:9090"}]
}

查询标签值

下面这个例子,返回了带有指定标签的标签值列表

GET /api/v1/label/<label_name>/values

这个返回JSON结果的data部分是带有label_name=job的值列表:

$ curl http://localhost:9090/api/v1/label/job/values
{"status" : "success","data" : ["node","prometheus"]
}

删除时间序列

下面的例子,是从Prometheus服务中删除匹配的度量指标和标签列表:

DELETE /api/v1/series

URL查询参数

match[]=<series_selector>: 删除符合series_selector匹配器的时间序列数据。参数个数必须大于等于1.
返回JSON数据中的data部分有以下的格式

{
“numDeleted”:
}

下面的例子删除符合度量指标名称是up或者时间序列为process_start_time_seconds{job=“prometheus”}:

$ curl -XDELETE -g 'http://localhost:9090/api/v1/series?match[]=up&match[]=process_start_time_seconds{job="prometheus"}'
{"status" : "success","data" : {"numDeleted" : 3}
}

表达式查询结果格式

表达式查询结果格式
表达式查询结果,在data部分的result部分中,返回下面的数据。<sample_value>占位符有数值样本值。JSON不支持特殊浮点值,例如:NaN, Inf和-Inf。因此样本值返回结果是字符串,不是原生的数值。

范围向量

范围向量返回的result类型是一个matrix矩阵。下面返回的结果是result部分的数据格式:

[{"metric": { "<label_name>": "<label_value>", ... },"values": [ [ <unix_time>, "<sample_value>" ], ... ]},...
]
瞬时向量

瞬时向量的result类型是vector。下面是result部分的数据格式

[{"metric": { "<label_name>": "<label_value>", ... },"value": [ <unix_time>, "<sample_value>" ]},...
]
Scalars标量

标量查询返回result类型是scalar。下面是result部分的数据格式:

[ <unix_time>, “<scalar_value>” ]

字符串

字符串的result类型是string。下面是result部分的数据格式:

[ <unix_time>, “<string_value>” ]


启动

这是个类似"hello,world"的试验,教大家怎样快速安装、配置和简单地搭建一个DEMO。你会下载和本地化运行Prometheus服务,并写一个配置文件,监控Prometheus服务本身和一个简单的应用,然后配合使用query、rules和图表展示采样点数据

下载和运行Prometheus

最新下载页, 然后提取和运行它,so easy:

tar zxvf prometheus-*.tar.gz
cd prometheus-*

在开始启动Prometheus之前,我们要配置它

配置Prometheus监控自身

Prometheus从目标机上通过http方式拉取采样点数据, 它也可以拉取自身服务数据并监控自身的健康状况

当然Prometheus服务拉取自身服务采样数据,并没有多大的用处,但是它是一个好的DEMO。保存下面的Prometheus配置,并命名为:prometheus.yml:


global:scrape_interval:     15s # 默认情况下,每15s拉取一次目标采样点数据。# 我们可以附加一些指定标签到采样点度量标签列表中, 用于和第三方系统进行通信, 包括:federation, remote storage, Alertmanagerexternal_labels:monitor: 'codelab-monitor'# 下面就是拉取自身服务采样点数据配置
scrape_configs:# job名称会增加到拉取到的所有采样点上,同时还有一个instance目标服务的host:port标签也会增加到采样点上- job_name: 'prometheus'# 覆盖global的采样点,拉取时间间隔5sscrape_interval: 5sstatic_configs:- targets: ['localhost:9090']

对于一个完整的配置选项,请见配置文档

启动Prometheus

指定启动Prometheus的配置文件,然后运行

./prometheus --config.file=prometheus.yml

这样Prometheus服务应该起来了。你可以在浏览器上输入:http://localhost:9090, 就可以看到Prometheus的监控界面

你也可以通过输入http://localhost:9090/metrics,直接拉取到所有最新的采样点数据集

使用expression browser(暂翻译:浏览器上输入表达式)
为了使用Prometheus内置浏览器表达式,导航到http://localhost:9090/graph,并选择带有"Graph"的"Console".

在拉取到的度量采样点数据中, 有一个metric叫prometheus_target_interval_length_seconds, 两次拉取实际的时间间隔,在表达式的console中输入:

prometheus_target_interval_length_seconds

这个应该会返回很多不同的倒排时间序列数据,这些度量名称都是prometheus_target_interval_length_seconds, 但是带有不同的标签列表值,这些标签列表值指定了不同的延迟百分比和目标组间隔

如果我们仅仅对99%的延迟感兴趣,则我们可以使用下面的查询去清洗信息:

prometheus_target_interval_length_seconds{quantile=“0.99”}

为了统计返回时间序列数据个数,你可以写:

count(prometheus_target_interval_length_seconds)

有关更多的表达式语言,请见表达式语言文档

使用graph interface

见图表表达式,导航到http://localhost:9090/graph, 然后使用"Graph" tab

例如,进入下面表达式,绘图最近1分钟产生chunks的速率:

rate(prometheus_tsdb_head_chunks_created_total[1m])

启动其他一些采样目标
Go客户端包括了一个例子,三个服务只见的RPC调用延迟

首先你必须有Go的开发环境,然后才能跑下面的DEMO, 下载Prometheus的Go客户端,运行三个服务:

git clone .git
cd client_golang/examples/random
go get -d
go build
## 启动三个服务
./random -listen-address=:8080
./random -listen-address=:8081
./random -listen-address=:8082

现在你在浏览器输入:http://localhost:8080/metrics, http://localhost:8081/metrics, http://localhost:8082/metrics, 能看到所有采集到的采样点数据

配置Prometheus去监控这三个目标服务

现在我们将会配置Prometheus,拉取三个目标服务的采样点。我们把这三个目标服务组成一个job, 叫example-radom. 然而,想象成,前两个服务是生产环境服务,后者是测试环境服务。我们可以通过group标签分组,在这个例子中,我们通过group="production"标签和group="test"来区分生产和测试

scrape_configs:- job_name:       'example-random'scrape_interval: 5sstatic_configs:- targets: ['localhost:8080', 'localhost:8081']labels:group: 'production'- targets: ['localhost:8082']labels:group: 'test'

进入浏览器,输入rpc_duration_seconds, 验证Prometheus所拉取到的采样点中每个点都有group标签,且这个标签只有两个值production, test

聚集到的采样点数据配置规则

上面的例子没有什么问题, 但是当采样点海量时,计算成了瓶颈。查询、聚合成千上万的采样点变得越来越慢。为了提高性能,Prometheus允许你通过配置文件设置规则,对表达式预先记录为全新的持续时间序列。让我们继续看RPCs的延迟速率(rpc_durations_seconds_count), 如果存在很多实例,我们只需要对特定的job和service进行时间窗口为5分钟的速率计算,我们可以写成这样:

avg(rate(rpc_durations_seconds_count[5m])) by (job, service)

为了记录这个计算结果,我们命名一个新的度量:job_service:rpc_durations_seconds_count:avg_rate5m, 创建一个记录规则文件,并保存为prometheus.rules.yml:


groups:
- name: examplerules:- record: job_service:rpc_durations_seconds_count:avg_rate5mexpr: avg(rate(rpc_durations_seconds_count[5m])) by (job, service)

然后再在Prometheus配置文件中,添加rule_files语句到global配置区域, 最后配置文件应该看起来是这样的:

global:scrape_interval:     15s # By default, scrape targets every 15 seconds.evaluation_interval: 15s # Evaluate rules every 15 seconds.# Attach these extra labels to all timeseries collected by this Prometheus instance.external_labels:monitor: 'codelab-monitor'rule_files:- 'prometheus.rules.yml'scrape_configs:- job_name: 'prometheus'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5sstatic_configs:- targets: ['localhost:9090']- job_name:       'example-random'# Override the global default and scrape targets from this job every 5 seconds.scrape_interval: 5sstatic_configs:- targets: ['localhost:8080', 'localhost:8081']labels:group: 'production'- targets: ['localhost:8082']labels:group: 'test'

然后重启Prometheus服务,并指定最新的配置文件,查询并验证job_service:rpc_durations_seconds_count:avg_rate5m度量指标

可视化

Grafana

Grafana支持Prometheus可视化

Grafana支持Prometheus查询。从Grafana 2.5.0 (2015-10-28)开始Prometheus可以作为它的数据源。

下面的例子:Prometheus查询在Grafana Dashboard界面的图表展示

Grafana安装
如果要Grafana的完整安装教程,详见Grafana官方文档
在Linux安装Grafana,如下所示:

# Download and unpack Grafana from binary tar (adjust version as appropriate).
curl -L -O .5.0.linux-x64.tar.gz
tar zxf grafana-2.5.0.linux-x64.tar.gz# Start Grafana.
cd grafana-2.5.0/
./bin/grafana-server web

使用方法

默认情况下,Grafana服务端口http://localhost:3000。默认登录用户名和密码“admin/admin”。

创建一个Prometheus数据源
为了创建一个Prometheus数据源Data source:

点击Grafana的logo,打开工具栏。
在工具栏中,点击"Data Source"菜单。
点击"Add New"。
数据源Type选择“Prometheus”。
设置Prometheus服务访问地址(例如:http://localhost:9090)。
调整其他想要的设置(例如:关闭代理访问)。
点击“Add”按钮,保存这个新数据源。
下面显示了一个Prometheus数据源配置例子:

创建一个Prometheus Graph图表
下面是添加一个新的Grafana的标准方法:

点击图表Graph的title,它在图表上方中间。然后点击“Edit”。
在“Metrics”tab下面,选择你的Prometheus数据源(下面右边)。
在“Query”字段中输入你想查询的Prometheus表达式,同时使用“Metrics”字段通过自动补全查找度量指标。
为了格式化时间序列的图例名称,使用“Legend format”图例格式输入。例如,为了仅仅显示这个标签为method和status的查询结果,你可以使用图例格式{{method{} - {{status}}。
调节其他的Graph设置,知道你有一个工作图表。
下面显示了一个Prometheus图表配置:

从Grafana.net导入预构建的dashboard

Grafana.net维护一个共享仪板表,它们能够被下载,并在Grafana服务中使用。使用Grafana.net的“Filter”选项去浏览来自Prometheus数据源的dashboards

你当前必须手动编辑下载下来的JSON文件和更改datasource: 选择Prometheus服务作为Grafana的数据源,使用“Dashboard”->“Home”->"Import"选项去导入编辑好的dashboard文件到你的Grafana中。

控制模板

控制模板允许使用Go语言模板创建任意的console。这些由Prometheus服务提供

Getting started

Prometheus提供了一系列的控制模板来帮助您。这些可以在Prometheus服务上的console/index.html.example中找到,如果Prometheus服务正在删除带有标签job="node"的Node Exporter, 则会显示NodeExporter控制台

这个例子控制台包括5部分:

  • 在顶部的导航栏
  • 左边的一个菜单
  • 底部的时间控制
  • 在中心的主内容,通常是图表
  • 右边的表格

这个导航栏是链接到其他系统,例如Prometheus其他方面的文档,以及其他任何使你明白的。该菜单用于在同一个Prometheus服务中导航,它可以快速在另一个tar中打开一个控制台。这些都是在console_libraries/menu.lib中配置。

时间控制台允许持久性和图表范围的改变。控制台URLs能够被分享,并且在其他的控制台中显示相同的图表。

主要内容通常是图表。这里有一个可配置的JavaScript图表库,它可以处理来自Prometheus服务的请求,并通过Rickshaw来渲染

最后,在右边的表格可以用笔图表更紧凑的形式显示统计信息。

例子控制台

这是一个最基本的控制台。它显示任务的数量,其中CPU平均使用率、以及右侧表中的平均内存使用率。主要内容具有每秒查询数据。

{{template "head" .}}{{template "prom_right_table_head"}}
<tr><th>MyJob</th><th>{{ template "prom_query_drilldown" (args "sum(up{job='myjob'})") }}/ {{ template "prom_query_drilldown" (args "count(up{job='myjob'})") }}</th>
</tr>
<tr><td>CPU</td><td>{{ template "prom_query_drilldown" (args"avg by(job)(rate(process_cpu_seconds_total{job='myjob'}[5m]))""s/s" "humanizeNoSmallPrefix") }}</td>
</tr>
<tr><td>Memory</td><td>{{ template "prom_query_drilldown" (args"avg by(job)(process_resident_memory_bytes{job='myjob'})""B" "humanize1024") }}</td>
</tr>
{{template "prom_right_table_tail"}}{{template "prom_content_head" .}}
<h1>MyJob</h1><h3>Queries</h3>
<div id="queryGraph"></div>
<script>
new PromConsole.Graph({node: document.querySelector("#queryGraph"),expr: "sum(rate(http_query_count{job='myjob'}[5m]))",name: "Queries",yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,yUnits: "/s",yTitle: "Queries"
})
</script>{{template "prom_content_tail" .}}{{template "tail"}}

模板部分不翻译了,建议用Grafana

工具

客户端

客户端库

在你能够监控你的服务器之前,你需要通过Prometheus客户端库把监控的代码放在被监控的服务代码中。下面实现了Prometheus的度量指标类型metric types。

选择你需要的客户端语言,在你的服务实例上通过HTTP端口提供内部度量指标

  • Go
  • Java or Scala
  • Python
  • Ruby
    当Prometheus获取实例的HTTP端点时,客户库发送所有跟踪的度量指标数据到服务器上。

如果没有可用的客户端语言版本,或者你想要避免依赖,你也可以实现一个支持的导入格式到度量指标数据中。

在实现一个新的Prometheus客户端库时,请遵循客户端指南。注意,这个文档在仍然在更新中。同时也请关注开发邮件列表。我们非常乐意地给出合适的意见或者建议。

本文标签: 开源监控Prometheus介绍 安装 配置 使用详解