Prometheus 联合创作者 Julius Volz 的文章
介绍
Prometheus 是一个开源监控系统和时间序列数据库。 Prometheus 最重要的方面之一是它的多维数据模型以及随附的查询语言。 这种查询语言允许您对维度数据进行切片和切块,以临时方式回答操作问题、在仪表板中显示趋势或生成有关系统故障的警报。
在本教程中,我们将学习如何查询 Prometheus 1.3.1。 为了使用合适的示例数据,我们将设置三个相同的演示服务实例,用于导出各种综合指标。 然后,我们将设置一个 Prometheus 服务器来抓取和存储这些指标。 使用示例指标,我们将学习如何查询 Prometheus,从简单的查询开始,然后进入更高级的查询。
在本教程之后,您将了解如何根据维度选择和过滤时间序列,聚合和转换时间序列,以及如何在不同指标之间进行算术运算。 在后续教程 如何在 Ubuntu 14.04 第 2 部分 上查询 Prometheus,我们将在本教程的知识基础上涵盖更高级的查询用例。
先决条件
要遵循本教程,您将需要:
- 按照 Ubuntu 14.04 的 初始服务器设置指南设置一台 Ubuntu 14.04 服务器,包括 sudo 非 root 用户。
第 1 步 — 安装 Prometheus
在这一步中,我们将下载、配置和运行 Prometheus 服务器来抓取三个(尚未运行的)演示服务实例。
首先,下载 Prometheus:
wget https://github.com/prometheus/prometheus/releases/download/v1.3.1/prometheus-1.3.1.linux-amd64.tar.gz
提取压缩包:
tar xvfz prometheus-1.3.1.linux-amd64.tar.gz
在 ~/prometheus.yml
的主机文件系统上创建一个最小的 Prometheus 配置文件:
nano ~/prometheus.yml
将以下内容添加到文件中:
~/prometheus.yml
# Scrape the three demo service instances every 5 seconds. global: scrape_interval: 5s scrape_configs: - job_name: 'demo' static_configs: - targets: - 'localhost:8080' - 'localhost:8081' - 'localhost:8082'
保存并退出 nano。
此示例配置使 Prometheus 抓取演示实例。 Prometheus 使用拉模型,这就是为什么它需要配置为了解端点以从中提取指标。 演示实例尚未运行,但稍后将在端口 8080
、8081
和 8082
上运行。
使用 nohup
并作为后台进程启动 Prometheus:
nohup ./prometheus-1.3.1.linux-amd64/prometheus -storage.local.memory-chunks=10000 &
命令开头的 nohup
将输出发送到文件 ~/nohup.out
而不是 stdout
。 命令末尾的 &
将允许进程在后台继续运行,同时提示您返回其他命令。 要使进程回到前台(即回到终端的运行进程),请在同一终端使用命令 fg
。
如果一切顺利,在 ~/nohup.out
文件中,您应该会看到类似于以下内容的输出:
启动 Prometheus 的输出
time="2016-11-23T03:10:33Z" level=info msg="Starting prometheus (version=1.3.1, branch=master, revision=be476954e80349cb7ec3ba6a3247cd712189dfcb)" source="main.go:75" time="2016-11-23T03:10:33Z" level=info msg="Build context (go=go1.7.3, user=root@37f0aa346b26, date=20161104-20:24:03)" source="main.go:76" time="2016-11-23T03:10:33Z" level=info msg="Loading configuration file prometheus.yml" source="main.go:247" time="2016-11-23T03:10:33Z" level=info msg="Loading series map and head chunks..." source="storage.go:354" time="2016-11-23T03:10:33Z" level=info msg="0 series loaded." source="storage.go:359" time="2016-11-23T03:10:33Z" level=warning msg="No AlertManagers configured, not dispatching any alerts" source="notifier.go:176" time="2016-11-23T03:10:33Z" level=info msg="Starting target manager..." source="targetmanager.go:76" time="2016-11-23T03:10:33Z" level=info msg="Listening on :9090" source="web.go:240"
在另一个终端中,您可以使用命令 tail -f ~/nohup.out
监视此文件的内容。 当内容写入文件时,它将显示在终端上。
默认情况下,Prometheus 将从 prometheus.yml
(我们刚刚创建)加载其配置,并将其指标数据存储在当前工作目录的 ./data
中。
-storage.local.memory-chunks
标志将 Prometheus 的内存使用调整为主机系统非常少量的 RAM(仅 512MB)和本教程中存储的少量时间序列。
您现在应该可以通过 http://your_server_ip:9090/
访问您的 Prometheus 服务器。 通过前往 http://your_server_ip:9090/status
并在 Targets 部分中找到 demo
作业的三个目标端点,验证它是否已配置为从三个演示实例收集指标。 所有三个目标的 State 列应将目标的状态显示为 DOWN,因为演示实例尚未启动,因此无法抓取:
第 2 步 — 安装演示实例
在本节中,我们将安装并运行三个演示服务实例。
下载演示服务:
wget https://github.com/juliusv/prometheus_demo_service/releases/download/0.0.4/prometheus_demo_service-0.0.4.linux-amd64.tar.gz
提取它:
tar xvfz prometheus_demo_service-0.0.4.linux-amd64.tar.gz
在不同的端口上运行演示服务 3 次:
./prometheus_demo_service -listen-address=:8080 & ./prometheus_demo_service -listen-address=:8081 & ./prometheus_demo_service -listen-address=:8082 &
&
在后台启动演示服务。 他们不会记录任何内容,但会在各自端口上的 /metrics
HTTP 端点上公开 Prometheus 指标。
这些演示服务导出有关几个模拟子系统的综合指标。 这些都是:
- 公开请求计数和延迟的 HTTP API 服务器(以路径、方法和响应状态代码为键)
- 一个周期性的批处理作业,它公开其最后一次成功运行的时间戳和处理的字节数
- 关于 CPU 数量及其使用情况的综合指标
- 关于磁盘总大小及其使用情况的综合指标
各个指标将在后面部分的查询示例中介绍。
Prometheus 服务器现在应该会自动开始抓取您的三个演示实例。 前往 http://your_server_ip:9090/status
的 Prometheus 服务器状态页面,并确认 demo
作业的目标现在显示 UP 状态:
第 3 步 — 使用查询浏览器
在这一步中,我们将熟悉 Prometheus 的内置查询和图形 Web 界面。 虽然此界面非常适合临时数据探索和学习 Prometheus 的查询语言,但它不适合构建持久仪表板并且不支持高级可视化功能。 要构建仪表板,请参阅示例 如何将 Prometheus 仪表板添加到 Grafana 。
转到 Prometheus 服务器上的 http://your_server_ip:9090/graph
。 它应该如下所示:
如您所见,有两个选项卡:Graph 和 Console。 Prometheus 允许您以两种不同的模式查询数据:
- Console 选项卡允许您在当前时间评估查询表达式。 运行查询后,表格将显示每个结果时间序列的当前值(每个输出序列一个表格行)。
- Graph 选项卡允许您绘制指定时间范围内的查询表达式。
由于 Prometheus 可以扩展到数百万个时间序列,因此可以构建非常昂贵的查询(认为这类似于从 SQL 数据库中的大表中选择所有行)。 为避免查询超时或服务器过载,建议首先在 Console 视图中开始探索和构建查询,而不是立即绘制图表。 在单个时间点评估可能成本高昂的查询将使用比尝试在一段时间内绘制相同查询的图表要少得多的资源。
一旦你充分缩小了查询范围(根据它选择加载的序列、它需要执行的计算以及输出时间序列的数量),你可以切换到 Graph 选项卡以显示随着时间的推移评估表达式。 知道查询何时便宜到可以绘制图表并不是一门精确的科学,它取决于您的数据、延迟要求以及运行 Prometheus 服务器的机器的功率。 随着时间的推移,你会对此有所感觉。
由于我们的测试 Prometheus 服务器不会抓取大量数据,因此我们实际上无法在本教程中制定任何代价高昂的查询。 任何示例查询都可以在 Graph 和 Console 视图中查看,没有风险。
要减少或增加图形时间范围,请单击 - 或 + 按钮。 要移动图表的结束时间,请按 << 或者 >> 纽扣。 您可以通过激活 stacked 复选框来堆叠图形。 最后, 水库。 (s) input 允许您指定自定义查询解析(本教程中不需要)。
第 4 步 — 执行简单的时间序列查询
在开始查询之前,让我们快速回顾一下 Prometheus 的数据模型和术语。 Prometheus 从根本上将所有数据存储为时间序列。 每个时间序列都由一个指标名称以及一组 Prometheus 称为 labels 的键值对来标识。 指标名称表示正在测量的系统的整体方面(例如,自进程启动以来处理的 HTTP 请求数,http_requests_total
)。 标签用于区分度量的子维度,例如 HTTP 方法(例如 method="POST"
)或路径(例如 path="/api/foo"
)。 最后,一系列样本形成了一系列的实际数据。 每个样本都包含一个时间戳和一个值,其中时间戳具有毫秒精度,并且值始终是 64 位浮点值。
我们可以制定的最简单的查询返回具有给定度量名称的所有系列。 例如,演示服务导出一个指标 demo_api_request_duration_seconds_count
,它表示由虚拟服务处理的合成 API HTTP 请求的数量。 您可能想知道为什么指标名称包含字符串 duration_seconds
。 这是因为此计数器是名为 demo_api_request_duration_seconds
的较大直方图度量的一部分,该度量主要跟踪请求持续时间的分布,但也公开跟踪请求的总数(此处以 _count
为后缀)作为有用的副产品。
确保选中 Console 查询选项卡,在页面顶部的文本字段中输入以下查询,然后单击 Execute 按钮执行查询:
demo_api_request_duration_seconds_count
由于 Prometheus 正在监控三个服务实例,您应该会看到一个表格输出,其中包含 27 个具有此度量名称的结果时间序列,每个跟踪的服务实例、路径、HTTP 方法和 HTTP 状态代码一个。 除了服务实例自己设置的标签(method
、path
和status
),该系列将有适当的job
和instance
区分不同服务实例的标签。 Prometheus 在存储来自抓取目标的时间序列时会自动附加这些标签。 输出应如下所示:
右侧表格列中显示的数值是每个时间序列的当前值。 随意绘制此查询和后续查询的输出(单击 Graph 选项卡并再次单击 Execute),以查看值如何随着时间的推移而发展。
我们现在可以添加标签匹配器来根据标签限制返回的系列。 标签匹配器直接跟在大括号中的度量名称之后。 在最简单的形式中,它们过滤给定标签具有精确值的系列。 例如,此查询将仅显示任何 GET
请求的请求计数:
demo_api_request_duration_seconds_count{method="GET"}
匹配器可以使用逗号组合。 例如,我们可以另外过滤仅来自实例 localhost:8080
和作业 demo
的指标:
demo_api_request_duration_seconds_count{instance="localhost:8080",method="GET",job="demo"}
结果将如下所示:
当组合多个匹配器时,它们都需要匹配才能选择一个系列。 上面的表达式仅返回在端口 8080 上运行且 HTTP 方法为 GET
的服务实例的 API 请求计数。 我们还确保只选择属于 demo
作业的指标。
注:建议在选择时间序列时始终指定job
标签。 这可以确保您不会意外地从不同的工作中选择具有相同名称的指标(当然,除非这确实是您的目标!)。 虽然我们在本教程中只监控一项作业,但在以下大多数示例中,我们仍将按作业名称进行选择,以强调这种做法的重要性。
除了等式匹配,Prometheus 还支持非等式匹配(!=
)、正则表达式匹配(=~
)以及负正则表达式匹配(!~
)。 也可以完全省略指标名称,仅使用标签匹配器进行查询。 例如,要列出 path
标签以 /api
开头的所有系列(无论是哪个指标名称或作业),您可以运行以下查询:
{path=~"/api.*"}
上面的正则表达式需要以 .*
结尾,因为在 Prometheus 中正则表达式总是匹配一个完整的字符串。
生成的时间序列将是具有不同度量名称的序列的混合:
您现在知道如何通过指标名称以及标签值的组合来选择时间序列。
第 5 步 — 计算利率和其他衍生品
在本节中,我们将学习如何计算指标随时间变化的比率或增量。
Prometheus 中最常用的函数之一是 rate()
。 与直接在检测服务中计算事件率不同,在 Prometheus 中,通常使用原始计数器跟踪事件并让 Prometheus 服务器在查询期间临时计算事件率(这有许多优点,例如不会丢失率峰值之间的刮擦,以及能够在查询时选择动态平均窗口)。 当受监控的服务启动时,计数器从 0
开始,并在服务进程的生命周期内不断增加。 有时,当一个受监控的进程重新启动时,它的计数器会重置为 0
并从那里再次开始爬升。 绘制原始计数器通常不是很有用,因为您只会看到一条不断增加的线,偶尔会重置。 您可以通过绘制演示服务的 API 请求计数来查看:
demo_api_request_duration_seconds_count{job="demo"}
它看起来有点像这样:
为了使计数器有用,我们可以使用 rate()
函数来计算它们的 每秒 增长率。 我们需要通过在系列匹配器之后提供范围选择器(如 [5m]
)来告诉 rate()
在哪个时间窗口内平均速率。 例如,要计算上述计数器指标在过去五分钟内的平均每秒增加量,请绘制以下查询图:
rate(demo_api_request_duration_seconds_count{job="demo"}[5m])
结果现在更有用了:
rate()
是智能的,并且通过假设计数器值的任何减少都是重置来自动调整计数器重置。
rate()
的变体是 irate()
。 虽然 rate()
对给定时间窗口(本例中为五分钟)内所有样本的速率进行平均,但 irate()
只回顾过去的两个样本。 它仍然需要您指定一个时间窗口(如 [5m]
),以了解这两个样本的最大回溯时间。 irate()
将对速率变化做出更快的反应,因此通常建议在图表中使用。 相比之下,rate()
将提供更平滑的速率,建议用于警报表达式(因为短速率峰值将被抑制,不会在夜间唤醒您)。
使用 irate()
,上图看起来像这样,揭示了请求率的短暂间歇性下降:
rate()
和 irate()
始终计算 每秒 速率。 有时您会想知道 总量 计数器在一个时间窗口内增加了多少,但对于计数器重置仍然是正确的。 您可以使用 increase()
功能实现此目的。 例如,要计算过去一小时处理的请求总数,请查询:
increase(demo_api_request_duration_seconds_count{job="demo"}[1h])
除了计数器(只能增加),还有计量指标。 仪表是可以随时间上升或下降的值,例如温度或可用磁盘空间。 如果我们想计算仪表随时间的变化,我们不能使用 rate()
/irate()
/increase()
系列函数。 这些都面向计数器,因为它们将度量值的任何减少解释为计数器重置并对其进行补偿。 相反,我们可以使用 deriv()
函数,它基于线性回归计算量规的每秒导数。
例如,要根据过去 15 分钟的线性回归查看我们的演示服务导出的虚拟磁盘使用量增加或减少的速度(以每秒 MiB 为单位),我们可以查询:
deriv(demo_disk_usage_bytes{job="demo"}[15m])
结果应如下所示:
要了解有关计算仪表中的增量和趋势的更多信息,另请参阅 delta() 和 predict_linear() 函数。
我们现在知道如何计算具有不同平均行为的每秒速率,如何在速率计算中处理计数器重置,以及如何计算仪表的导数。
第 6 步 — 聚合时间序列
在本节中,我们将学习如何聚合单个系列。
Prometheus 收集具有高维细节的数据,这可能导致每个指标名称有许多系列。 但是,您通常并不关心所有维度,甚至可能有太多系列以合理的方式一次绘制它们。 解决方案是聚合某些维度并仅保留您关心的维度。 例如,演示服务通过 method
、path
和 status
跟踪 API HTTP 请求。 Prometheus 在从 Node Exporter 抓取该指标时为其添加了更多维度:instance
和 job
标签跟踪指标来自哪个进程。 现在,要查看所有维度的总请求率,我们可以使用 sum()
聚合运算符:
sum(rate(demo_api_request_duration_seconds_count{job="demo"}[5m]))
但是,这会聚合 all 维度并创建单个输出系列:
通常,您会希望在输出中保留 some 的维度。 为此,sum()
和其他聚合器支持 without(<label names>)
子句,该子句指定要聚合的维度。 还有一个替代的相反 by(<label names>)
子句允许您指定要保留的标签名称。 如果我们想知道所有三个服务实例和所有路径的总请求率总和,但按方法和状态代码拆分结果,我们可以查询:
sum without(method, status) (rate(demo_api_request_duration_seconds_count{job="demo"}[5m]))
这相当于:
sum by(instance, path, job) (rate(demo_api_request_duration_seconds_count{job="demo"}[5m]))
结果总和现在按 instance
、path
和 job
分组:
注意:在应用任何聚合之前,始终计算 rate()
、irate()
或 increase()
。 如果您先应用聚合,它将隐藏计数器重置,并且这些功能将无法再正常工作。
Prometheus 支持以下聚合运算符,每个聚合运算符都支持 by()
或 without()
子句来选择要保留的维度:
sum
:对聚合组中的所有值求和。min
:选择聚合组内所有值的最小值。max
:选择聚合组内所有值的最大值。avg
:计算聚合组内所有值的平均值(算术平均值)。stddev
:计算聚合组内所有值的标准差。stdvar
:计算聚合组内所有值的标准方差。count
:计算聚合组内的系列总数。
您现在已经学习了如何聚合一系列系列以及如何仅保留您关心的维度。
第 7 步 - 执行算术
在本节中,我们将学习如何在 Prometheus 中进行算术运算。
作为最简单的算术示例,您可以将 Prometheus 用作数字计算器。 例如,在 Console 视图中运行以下查询:
(4 + 7) * 3
您将获得 33
的单个标量输出值:
标量值是没有任何标签的简单数值。 为了使它更有用,Prometheus 允许您应用常见的算术运算符(+
、-
、*
、/
、%
)到整个时间序列向量。 例如,以下查询将模拟的上次批处理作业运行处理的字节数转换为 MiB:
demo_batch_last_run_processed_bytes{job="demo"} / 1024 / 1024
结果将以 MiB 显示:
对于这些类型的单位转换,通常使用简单的算术运算,尽管好的可视化工具(如 Grafana)也会为您处理转换。
Prometheus 的一个专长(也是 Prometheus 真正闪耀的地方!)是两组时间序列之间的二进制算术。 在两组序列之间使用二元运算符时,Prometheus 会自动匹配运算左侧和右侧具有相同标签集的元素,并将运算符应用于每个匹配对以生成输出序列。
例如,demo_api_request_duration_seconds_sum
指标告诉我们响应 HTTP 请求花费了多少秒,而 demo_api_request_duration_seconds_count
告诉我们有多少 many HTTP 请求。 两个指标具有相同的维度(method
、path
、status
、instance
、job
)。 要计算每个维度的平均请求延迟,我们可以简单地查询请求中花费的总时间除以请求总数的比率。
rate(demo_api_request_duration_seconds_sum{job="demo"}[5m]) / rate(demo_api_request_duration_seconds_count{job="demo"}[5m])
请注意,我们还在操作的每一侧包装了一个 rate()
函数,以仅考虑过去 5 分钟内发生的请求的延迟。 这也增加了对计数器重置的弹性。
生成的平均请求延迟图应如下所示:
但是当标签在两边不完全匹配时,我们该怎么办? 尤其是当我们在操作的两侧都有不同大小的时间序列集时,就会出现这种情况,因为一侧的维度比另一侧多。 例如,演示作业将在各种模式(idle
、user
、system
)下花费的虚构 CPU 时间导出为 demo_cpu_usage_seconds_total
和 mode
标签尺寸。 它还将虚构的 CPU 总数导出为 demo_num_cpus
(此指标没有额外的维度)。 如果您尝试将一个除以另一个以得出三种模式中每种模式的平均 CPU 使用率百分比,则查询将不会产生任何输出:
# BAD! # Multiply by 100 to get from a ratio to a percentage rate(demo_cpu_usage_seconds_total{job="demo"}[5m]) * 100 / demo_num_cpus{job="demo"}
在这些一对多或多对一的匹配中,我们需要告诉 Prometheus 使用哪个标签子集进行匹配,我们还需要指定如何处理额外的维度。 为了解决匹配问题,我们将 on(<label names>)
子句添加到指定要匹配的标签的二元运算符中。 为了根据较大一侧的额外维度的单个值展开和分组计算,我们添加了一个 group_left(<label names>)
或 group_right(<label names>)
子句,分别列出左侧或右侧的额外维度.
在这种情况下,正确的查询是:
# Multiply by 100 to get from a ratio to a percentage rate(demo_cpu_usage_seconds_total{job="demo"}[5m]) * 100 / on(job, instance) group_left(mode) demo_num_cpus{job="demo"}
结果应如下所示:
on(job, instance)
告诉操作员仅在其 job
和 instance
标签上从左右匹配系列(因此不在 mode
标签上,这右侧不存在),而 group_left(mode)
子句告诉操作员扇出并显示每个模式的 CPU 使用平均值。 这是一个多对一匹配的情况。 要进行反向(一对多)匹配,请以相同的方式使用 group_right(<label names>)
子句。
您现在知道如何在时间序列集之间使用算术,以及如何处理不同的维度。
结论
在本教程中,我们设置了一组演示服务实例并使用 Prometheus 对其进行监控。 然后,我们学习了如何对收集的数据应用各种查询技术来回答我们关心的问题。 您现在知道如何选择和过滤系列,如何聚合维度,以及如何计算比率或导数或进行算术运算。 您还学习了如何处理一般查询的构造以及如何避免 Prometheus 服务器过载。
要了解有关 Prometheus 查询语言的更多信息,包括如何从直方图中计算百分位数、如何处理基于时间戳的指标或如何查询服务实例健康状况,请继续阅读 如何在 Ubuntu 14.04 第 2 部分上查询 Prometheus 。