如何在Ubuntu14.04上查询Prometheus第2部分

来自菜鸟教程
跳转至:导航、​搜索

Prometheus 联合创作者 Julius Volz 的文章

介绍

Prometheus 是一个开源监控系统和时间序列数据库。 在 How To Query Prometheus on Ubuntu 14.04 Part 1 中,我们设置了三个演示服务实例,将综合指标暴露给 Prometheus 服务器。 使用这些指标,我们随后学习了如何使用 Prometheus 查询语言来选择和过滤时间序列,如何聚合维度,以及如何计算速率和导数。

在本教程的第二部分,我们将在第一部分的设置基础上学习更高级的查询技术和模式。 在本教程之后,您将了解如何应用基于值的过滤、集合操作、直方图等。

先决条件

本教程基于 如何在 Ubuntu 14.04 第 1 部分 上查询 Prometheus 中概述的设置。 至少,您需要按照该教程中的第 1 步和第 2 步来设置 Prometheus 服务器和三个受监控的演示服务实例。 但是,我们还将建立在第一部分中解释的查询语言技术的基础上,因此建议完全完成它。

第 1 步 — 按值过滤并使用阈值

在本节中,我们将学习如何根据值过滤返回的时间序列。

基于值的过滤最常见的用途是简单的数字警报阈值。 例如,我们可能希望找到总 500 状态请求率高于每秒 0.2 次的 HTTP 路径(过去 15 分钟的平均值)。 为此,我们只需查询所有 500 状态请求率,然后在表达式末尾附加一个 > 0.2 过滤器运算符:

rate(demo_api_request_duration_seconds_count{status="500",job="demo"}[15m]) > 0.2

Console 视图中,结果应如下所示:

但是,与二进制算术一样,Prometheus 不仅支持按单个标量数进行过滤。 您还可以根据另一组序列过滤一组时间序列。 同样,元素通过它们的标签集进行匹配,并且过滤器运算符应用于匹配的元素之间。 只有左侧的元素与右侧的元素 通过过滤器匹配的元素才会成为输出的一部分。 on(<labels>), group_left(<labels>), group_right(<labels>) 子句在这里的工作方式与算术运算符中的相同。

例如,我们可以为任何 jobinstancemethodpath 组合选择 500 状态速率, 200-状态率至少比 500-状态率高 50 倍,如下所示:

    rate(demo_api_request_duration_seconds_count{status="500",job="demo"}[5m]) * 50
> on(job, instance, method, path)
    rate(demo_api_request_duration_seconds_count{status="200",job="demo"}[5m])

这将如下所示:

除了 >,Prometheus 还支持常用的 >=<=<!=== 比较运算符用于过滤。

我们现在知道如何基于单个数值或基于具有匹配标签的另一组时间序列值来过滤一组时间序列。

第 2 步 — 使用集合运算符

在本节中,您将学习如何使用 Prometheus 的集合运算符将时间序列集合相互关联。

通常,您希望根据另一组过滤一组时间序列。 为此,Prometheus 提供了 and 集合运算符。 对于运算符左侧的每个系列,它会尝试在右侧找到具有相同标签的系列。 如果找到匹配项,则左侧系列将成为输出的一部分。 如果右侧不存在匹配的系列,则从输出中省略该系列。

例如,您可能希望选择 90% 延迟高于 50 毫秒 (0.05 秒) 的任何 HTTP 端点,但仅适用于每秒接收多个请求的维度组合。 我们将在此处使用 histogram_quantile() 函数进行百分位数计算。 我们将在下一节中解释这个函数的工作原理。 目前,只需要计算每个子维度的第 90 个百分位延迟即可。 要过滤由此产生的不良延迟并仅保留每秒接收超过一个请求的延迟,我们可以查询:

    histogram_quantile(0.9, rate(demo_api_request_duration_seconds_bucket{job="demo"}[5m])) > 0.05
and
    rate(demo_api_request_duration_seconds_count{job="demo"}[5m]) > 1

有时您不想取交集,而是想从两组时间序列中建立联合。 Prometheus 为此提供了 or 集合运算符。 它会产生操作左侧的系列,以及右侧没有左侧匹配标签集的任何系列。 例如,要列出低于 10 或高于 30 的所有请求率,请查询:

    rate(demo_api_request_duration_seconds_count{job="demo"}[5m]) < 10
or
    rate(demo_api_request_duration_seconds_count{job="demo"}[5m]) > 30

结果将在图表中如下所示:

如您所见,在图中使用值过滤器和集合操作可能会导致时间序列在同一个图中出现和消失,具体取决于它们在图中的任何时间步是否匹配过滤器。 通常,仅建议将这种过滤器逻辑用于警报规则。

您现在知道如何根据标记的时间序列构建交集和联合。

第 3 步 — 使用直方图

在本节中,我们将学习如何解释直方图指标以及如何从中计算分位数(百分位数的广义形式)。

Prometheus 支持直方图指标,它允许服务记录一系列值的分布。 直方图通常跟踪请求延迟或响应大小等测量值,但可以从根本上跟踪根据某种分布在幅度上波动的任何值。 Prometheus 直方图 sample 数据在客户端,这意味着它们使用许多可配置的(例如 延迟)桶,然后将这些桶公开为单独的时间序列。

在内部,直方图被实现为一组时间序列,每个时间序列代表给定存储桶的计数(例如 “10 毫秒以下的请求”、“25 毫秒以下的请求”、“50 毫秒以下的请求”等)。 桶计数器是累积的,这意味着较大值的桶包括所有较低值桶的计数。 在作为直方图一部分的每个时间序列上,相应的存储桶由特殊的 le(小于或等于)标签指示。 这会为您已在跟踪的任何现有维度添加一个额外维度。

例如,我们的演示服务导出了一个直方图 demo_api_request_duration_seconds_bucket,它跟踪 API 请求持续时间的分布。 由于该直方图每个跟踪的子维度导出 26 个桶,因此该指标有很多时间序列。 让我们首先从一个实例中查看仅针对一种类型的请求的原始直方图:

demo_api_request_duration_seconds_bucket{instance="localhost:8080",method="POST",path="/api/bar",status="200",job="demo"}

您应该看到 26 个系列,每个系列代表一个观察桶,由 le 标签标识:

直方图可以帮助您回答诸如“我有多少请求需要超过 100 毫秒才能完成?”之类的问题。 (假设直方图配置了一个具有 100 毫秒边界的存储桶)。 另一方面,您经常想回答一个相关问题,例如“我的查询完成 99% of 的延迟是多少?”。 如果您的直方图桶足够细粒度,您可以使用 histogram_quantile() 函数进行计算。 此函数需要一个直方图度量(一组带有 le 桶标签的系列)作为其输入并输出相应的分位数。 与范围从第 0 到第 100 个百分位的百分位数相比,histogram_quantile() 函数期望作为输入的目标分位数规范范围从 01(因此第 90 个百分位对应于 0.9 的分位数)。

例如,我们可以尝试计算所有维度的 90% API 延迟,如下所示:

# BAD!
histogram_quantile(0.9, demo_api_request_duration_seconds_bucket{job="demo"})

这不是很有用或可靠。 当单个服务实例重新启动时,存储桶计数器会重置,您通常希望查看“现在”的延迟时间(例如,在过去 5 分钟内测量),而不是在指标的整个时间段内。 您可以通过将 rate() 函数应用于底层直方图桶计数器来实现此目的,该函数既处理计数器重置,也只考虑每个桶在指定时间窗口内的增长率。

计算过去 5 分钟内 90% 的 API 延迟,如下所示:

# GOOD!
histogram_quantile(0.9, rate(demo_api_request_duration_seconds_bucket{job="demo"}[5m]))

这好多了,看起来像这样:

但是,这会显示 每个 子维度(jobinstancepathmethod 和 [ X126X])。 同样,我们可能对所有这些维度都不感兴趣,并且希望将其中一些维度聚合起来。 幸运的是,Prometheus 的 sum 聚合运算符可以与 histogram_quantile() 函数组合在一起,使我们能够在查询期间对维度进行聚合!

以下查询计算第 90 个百分位延迟,但仅按 jobinstancepath 维度拆分结果:

histogram_quantile(
  0.9,
  sum without(status, method) (
    rate(demo_api_request_duration_seconds_bucket{job="demo"}[5m])
  )
)

注意:在应用 histogram_quantile() 函数之前,始终在任何聚合中保留 le 存储桶标签。 这确保它仍然可以对桶组进行操作并从中计算分位数。


该图现在将如下所示:

从直方图计算分位数总是会引入一些统计误差。 此错误取决于您的存储桶大小、观测值的分布以及您要计算的目标分位数。 要了解更多信息,请阅读 Prometheus 文档中的 分位数估计错误

您现在知道如何解释直方图指标以及如何根据它们计算不同时间范围的分位数,同时还可以动态聚合某些维度。

第 4 步 — 使用时间戳指标

在本节中,我们将学习如何使用包含时间戳的指标。

Prometheus 生态系统中的组件经常暴露时间戳。 例如,这可能是批处理作业最后一次成功完成、配置文件最后一次成功重新加载或机器启动时。 按照惯例,时间表示为自 1970 年 1 月 1 日 UTC 以来的 Unix 时间戳 以秒为单位。

例如,演示服务公开了上次模拟批处理作业成功的时间:

demo_batch_last_success_timestamp_seconds{job="demo"}

此批处理作业模拟为每分钟运行一次,但在 25% of 所有尝试中均失败。 在失败的情况下,demo_batch_last_success_timestamp_seconds 指标保持其最后一个值,直到发生另一次成功运行。

如果你绘制原始时间戳,它看起来有点像这样:

如您所见,原始时间戳值本身通常不是很有用。 相反,您通常想知道时间戳值的年龄。 一种常见的模式是从当前时间中减去度量中的时间戳,如 time() 函数所提供的:

time() - demo_batch_last_success_timestamp_seconds{job="demo"}

这会产生自上次成功运行批处理作业以来的时间(以秒为单位):

如果您想将此年龄从秒转换为小时,可以将结果除以 3600

(time() - demo_batch_last_success_timestamp_seconds{job="demo"}) / 3600

像这样的表达式对于绘图和警报都很有用。 像上面那样可视化时间戳年龄时,您会收到一个锯齿图,当批处理作业成功完成时,线条线性增加,并定期重置为 0。 如果锯齿尖峰变得太大,这表明批处理作业很长时间没有完成。 您还可以通过向表达式添加 > 阈值过滤器并在生成的时间序列上发出警报来对此发出警报(尽管我们不会在本教程中介绍警报规则)。

要简单地列出批处理作业在过去 1.5 分钟内未完成的实例,您可以运行以下查询:

time() - demo_batch_last_success_timestamp_seconds{job="demo"} > 1.5 * 60

您现在知道如何将原始时间戳指标转换为相对年龄,这对绘图和警报都有帮助。

第 5 步 - 排序和使用 topk / bottomk 函数

在此步骤中,您将学习如何对查询输出进行排序或仅选择一组系列中的最大值或最小值。

在表格 Console 视图中,按输出序列的值对输出序列进行排序通常很有用。 您可以通过使用 sort()(升序排序)和 sort_desc()(降序排序)函数来实现此目的。 例如,要显示按其值从最高到最低排序的每条路径请求率,您可以查询:

sort_desc(sum by(path) (rate(demo_api_request_duration_seconds_count{job="demo"}[5m])))

排序后的输出将如下所示:

或者您可能根本对显示所有系列都不感兴趣,而只对 K 最大或最小系列感兴趣。 为此,Prometheus 提供了 topk()bottomk() 函数。 它们每个都采用一个 K 值(您要选择多少个系列)和一个任意表达式,该表达式返回一组应该被过滤的时间序列。 例如,要仅显示每个路径和方法的前三个请求率,您可以查询:

topk(3, sum by(path, method) (rate(demo_api_request_duration_seconds_count{job="demo"}[5m])))

虽然排序在 Console 视图中仅 ' 有用,但 topk()bottomk() 在图形中也可能有用。 请注意,输出不会将顶部或底部 K 系列显示为整个图形时间范围内的平均值 - 相反,输出将针对图形上的每个分辨率步骤重新计算 K 顶部或底部输出系列。 因此,您的顶部或底部 K 系列实际上可以在图表的范围内变化,并且您的图表总共可能显示超过 K 系列。

我们现在学习了如何排序或只选择 K 个最大或最小的系列。

第 6 步 - 检查已报废实例的运行状况

在这一步中,我们将学习如何随着时间的推移检查实例的抓取健康状况。

为了使该部分更有趣,让我们终止三个后台演示服务实例中的第一个(侦听端口 8080 的那个):

pkill -f -- -listen-address=:8080

每当 Prometheus 抓取目标时,它都会存储一个合成样本,其度量名称为 up 以及抓取实例的 jobinstance 标签。 如果刮取成功,则样本的值设置为 1。 如果刮擦失败,则设置为 0。 因此,我们可以很容易地查询哪些实例当前是“up”或“down”:

up{job="demo"}

现在应该将一个实例显示为关闭:

要显示 only 向下实例,您可以过滤值 0

up{job="demo"} == 0

您现在应该只看到您终止的实例:

或者,要获取停机实例的总数:

count by(job) (up{job="demo"} == 0)

这将显示 1 的计数:

这些类型的查询对于基本的抓取健康警报很有用。

注意:当没有down实例时,此查询返回一个空结果,而不是单个输出序列,计数为0。 这是因为 count() 是一个聚合运算符,它期望一组维度时间序列作为其输入,并且可以根据 bywithout 子句对输出序列进行分组。 任何输出组只能基于现有的输入序列 - 如果根本没有输入序列,则不会产生输出。


您现在知道如何查询实例的健康状态。

结论

在本教程中,我们基于 How To Query Prometheus on Ubuntu 14.04 Part 1 的进展,涵盖了更高级的查询技术和模式。 我们学习了如何根据值过滤序列、从直方图中计算分位数、处理基于时间戳的指标等等。

虽然这些教程无法涵盖所有可能的查询用例,但我们希望示例查询对您在使用 Prometheus 构建实际查询、仪表板和警报时有用。 有关 Prometheus 查询语言的更多详细信息,请参阅 Prometheus 查询语言文档