Prometheus除了存储数据外,还提供了一种强大的功能表达式语言 PromQL,容许用户实时选择和汇聚时间序列数据。node
表达式的结果能够在浏览器中显示为图形,也能够显示为表格数据,或者由外部系统经过 HTTP API 调用。经过PromQL用户能够很是方便地查询监控数据,或者利用表达式进行告警配置git
如:k8s中的node在线率:sum(kube_node_status_condition{condition="Ready", status="true"}) / sum(kube_node_info) *100 正则表达式
关于时间序列存储,能够参考:https://www.infoq.cn/article/...sql
Prometheus会将全部采集到的样本数据以时间序列(time-series)的方式保存在内存数据库TSDB中,而且定时保存到硬盘上。time-series是按照时间戳和值的序列顺序存放的,咱们称之为向量(vector)。每条time-series经过指标名称(metrics name)和一组标签集(labelset)命名。数据库
在time-series中的每个点称为一个样本(sample),样本由如下三部分组成:express
如某一时刻的node_cpu指标为459.71json
node_cpu{app="node-exporter",cpu="cpu0",instance="192.168.0.4:9100",job="kubernetes-service-endpoints",kubernetes_name="node-exporter",kubernetes_namespace="kube-system",mode="guest"} 459.71
Prometheus定义了4中不一样的指标类型(metric type):api
计数器,只增不减,如http_requests_total请求总数 例如,经过rate()函数获取HTTP请求量的增加率: rate(http_requests_total[5m])
当前状态,可增可减。如kube_pod_status_ready当前pod可用数 能够获取样本在一段时间返回内的变化状况,如: delta(kube_pod_status_ready[2h])
Histogram 由 <basename>_bucket{le="<upper inclusive bound>"},<basename>_bucket{le="+Inf"}, <basename>_sum,<basename>_count 组成,主要用于表示一段时间范围内对数据进行采样(一般是请求持续时间或响应大小),并可以对其指定区间以及总数进行统计,一般它采集的数据展现为直方图。 例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted, 表示 Prometheus 中每一个时序须要存储的 chunks 数量,咱们能够用它计算待持久化的数据的分位数。
Summary 和 Histogram 相似,由 <basename>{quantile="<φ>"},<basename>_sum,<basename>_count 组成,主要用于表示一段时间内数据采样结果(一般是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。 例如 Prometheus server 中 prometheus_target_interval_length_seconds。 Histogram 须要经过 <basename>_bucket 计算 quantile, 而 Summary 直接存储了 quantile 的值。
PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。浏览器
如http_requests_total指标bash
你能够经过附加一组标签,并用{}括起来,来进一步筛选这些时间序列。下面这个例子只选择有http_requests_total名称的、有prometheus工做标签的、有canary组标签的时间序列:
http_requests_total{job="prometheus",group="canary"}
若是条件为空,能够写为:http_requests_total{}
另外,也能够也能够将标签值反向匹配,或者对正则表达式匹配标签值。如操做符:
=:选择正好相等的字符串标签 !=:选择不相等的字符串标签 =~:选择匹配正则表达式的标签(或子标签) !=:选择不匹配正则表达式的标签(或子标签)
相似http_requests_total{job="prometheus",group="canary"}
的方式,获得的是瞬时值,若是想获得必定范围内的值,可使用范围查询
时间范围经过时间范围选择器[]进行定义。例如,经过如下表达式能够选择最近5分钟内的全部样本数据,如:http_request_total{}[5m]
除了分钟,支持的单位有:
如:查询http_requests_total在当前时刻的一周的速率:
rate(http_requests_total{} offset 1w)
偏移修饰符容许更改查询中单个即时向量和范围向量的时间偏移量,例如,如下表达式返回相对于当前查询时间5分钟前的http_requests_total值:
http_requests_total offset 5m
等价于
http_requests_total{job="prometheus"}[5m]
请注意,偏移量修饰符始终须要跟随选择器,即如下是正确的:
sum(http_requests_total{method="GET"} offset 5m) // GOOD.
下面是错误的:
sum(http_requests_total{method="GET"}) offset 5m // INVALID.
Prometheus 的查询语言支持基本的逻辑运算和算术运算
运算中用到的基础数据类型:
二元运算操做符支持 scalar/scalar(标量/标量)、vector/scalar(向量/标量)、和 vector/vector(向量/向量) 之间的操做。
在两个标量之间进行数学运算,获得的结果也是标量。
例如,若是咱们想根据 node_disk_bytes_written 和 node_disk_bytes_read 获取主机磁盘IO的总量,可使用以下表达式:
`node_disk_bytes_written + node_disk_bytes_read
`
或者node的内存数GB
node_memory_free_bytes_total / (1024 * 1024)
(大于)
= (大于等于)
如:获取http_requests_total请求总数是否超过10000,返回0和1,1则报警
http_requests_total > 10000 # 结果为 true 或 false http_requests_total > bool 10000 # 结果为 1 或 0
四则运算有优先级,promql的复杂运算也有优先级
例如,查询主机的CPU使用率,可使用表达式:
100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )
其中irate是PromQL中的内置函数,用于计算区间向量中时间序列每秒的即时增加率
在PromQL操做符中优先级由高到低依次为:
与数据库中的join相似,promsql有两种典型的匹配查询:
例如当存在样本:
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 表达式:
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在全部请求中的比例。若是没有使用 ignoring(code),操做符两边表达式返回的瞬时向量中将找不到任何一个标签彻底相同的匹配项。
所以结果以下:
{method="get"} 0.04 // 24 / 600
{method="post"} 0.05 // 6 / 120
同时因为 method 为 put 和 del 的样本找不到匹配项,所以不会出如今结果当中。
多对一模式
例如,使用表达式:
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 指定左向量具备更好的基数。
最终的运算结果以下:
{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 操做中默认与右向量中的全部元素进行匹配。
Prometheus 还提供了下列内置的聚合操做符,这些操做符做用域瞬时向量。能够将瞬时表达式返回的样本数据进行聚合,造成一个具备较少样本值的新的时间序列。
这些操做符被用于聚合全部标签维度,或者经过 without 或者 by 子语句来保留不一样的维度。
经过 without 和 by 能够按照样本的问题对数据进行聚合。
例如:
若是指标 http_requests_total 的时间序列的标签集为 application, instance, 和 group,咱们能够经过如下方式计算全部 instance 中每一个 application 和 group 的请求总量:
sum(http_requests_total) without (instance)
等价于
`
sum(http_requests_total) by (application, group)
`
若是只须要计算整个应用的 HTTP 请求总量,能够直接使用表达式:
`sum(http_requests_total)
`
count_values 用于时间序列中每个样本值出现的次数。count_values 会为每个惟一的样本值输出一个时间序列,而且每个时间序列包含一个额外的标签。
这个标签的名字由聚合参数指定,同时这个标签值是惟一的样本值。
例如要计算运行每一个构建版本的二进制文件的数量:
count_values("version", build_version) 返回结果以下: {count="641"} 1 {count="3226"} 2 {count="644"} 4
topk 和 bottomk
则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。
获取 HTTP 请求数前 5 位的时序样本数据,可使用表达式:
topk(5, http_requests_total)
quantile 用于计算当前样本数据值的分布状况 quantile(φ, express) ,其中 0 ≤ φ ≤ 1
例如,当 φ 为 0.5 时,即表示找到当前样本数据中的中位数: quantile(0.5, http_requests_total) 返回结果以下: {} 656
Prometheus 提供了其它大量的内置函数,能够对时序数据进行丰富的处理。如上文提到的irate
100 * (1 - avg (irate(node_cpu{mode='idle'}[5m])) by(job) )
经常使用的有:
两分钟内的平均CPU使用率:
rate(node_cpu[2m])
和
irate(node_cpu[2m])
须要注意的是使用rate或者increase函数去计算样本的平均增加速率,容易陷入“长尾问题”当中, 其没法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在2分钟的时间窗口内,可能在某一个因为访问量或者其它问题致使CPU占用100%的状况, 可是经过计算在时间窗口内的平均增加率却没法反应出该问题。 为了解决该问题,PromQL提供了另一个灵敏度更高的函数irate(v range-vector)。 irate一样用于计算区间向量的计算率,可是其反应出的是瞬时增加率。 irate函数是经过区间向量中最后两个两本数据来计算区间向量的增加速率。 这种方式能够避免在时间窗口范围内的“长尾问题”,而且体现出更好的灵敏度,经过irate函数绘制的图标可以更好的反应样本数据的瞬时变化状态。
irate函数相比于rate函数提供了更高的灵敏度,不过当须要分析长期趋势或者在告警规则中,irate的这种灵敏度反而容易形成干扰。
所以在长期趋势分析或者告警中更推荐使用rate函数。
完整的函数列表为:
Prometheus当前稳定的HTTP API能够经过/api/v1访问
错误状态码:
全部的API请求均使用如下的JSON格式:
{ "status": "success" | "error", "data": <data>, // 为error时,有以下报错信息 "errorType": "<string>", "error": "<string>" }
经过HTTP API咱们能够分别经过/api/v1/query和/api/v1/query_range查询PromQL表达式当前或者必定时间范围内的计算结果。
URL请求参数:
$ 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" ] } ] } }
URL请求参数:
$ 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" ] ] } ] } }
本文为容器监控实践系列文章,完整内容见:container-monitor-book