prometheus生产上易犯的错误
Mistake 1: Cardinality bombs
这是每个人在开始使用 Prometheus 时至少会遇到一次的经典陷阱。一旦您发现 Prometheus 基于标签的数据模型的有用性,您可能会想按各种有用的标签维度来拆分指标,直到您创建的时间序列超出 Prometheus 服务器可以处理的数量。
例如,您可能有一个 HTTP 服务器公开一个由 HTTP 方法分割的请求计数器:
http_requests_total{method="POST"}
http_requests_total{method="GET"}
http_requests_total{method="PUT"}
http_requests_total{method="DELETE"}
这完全没问题,只要您确保仅在method标签中存储有效的方法值即可。然而,也许现在您想到了通过请求用户的 ID 来有效地分割指标:
http_requests_total{method="POST",user_id="1"}
http_requests_total{method="POST",user_id="2"}
http_requests_total{method="POST",user_id="3"}
[…many more…]
http_requests_total{method="POST",user_id="16434313"}
http_requests_total{method="GET",user_id="1"}
http_requests_total{method="GET",user_id="2"}
http_requests_total{method="GET",user_id="3"}
[…many more…]
http_requests_total{method="GET",user_id="16434313"}
[…many more…]
假设您拥有多个用户,您现在将遭受基数爆炸,这将导致 Prometheus 服务器的内存使用量达到 OOM(内存不足)崩溃的程度。
因此请始终记住:指标上的每一个独特的标签组合都会自动创建一个时间序列,Prometheus 必须摄取、索引、存储和处理该时间序列。虽然对于可以放入单个标签中的值的数量没有绝对的硬性限制,但您需要保持在 Prometheus 服务器可以处理的时间序列的总预算之内。例如,大型 Prometheus 服务器可能能够处理多达几百万个时间序列,因此您需要选择标签的组合、它们所在的指标以及相应地公开这些指标的目标数量,以便您保持在该服务器的时间序列总预算范围内。
特别是,您需要避免存储无界的高基数值,例如:
- 公共 IP 地址或电子邮件地址
- 完整的 HTTP 路径,如果这些路径包含 ID 或其他无界基数信息
- 进程 ID(除非严格限制到特定集合)
有时,您可以通过减少标签值的基数来解决标签的高基数问题,而无需完全删除它,同时仍然保留最有用的信息。例如,对于表单的 HTTP 路径/api/users/739567637385/posts/28388445,您至少可以用最终标签值中的占位符替换路径的最高基数组件(本例中的用户和帖子 ID),以生成更通用的模式,例如生成/api/users/{user_id}/posts/{post_id}方式更少的不同标签值和时间序列。
Mistake 2: Aggregating away valuable labels in alerting expressions
在编写聚合整个服务的警报表达式时,一个常见的诱惑是聚合掉您不需要立即需要的任何标签来确定是否存在问题。例如,如果您想确定服务的总体错误率(跨所有标签维度)是否太高,您可以编写如下规则:
sum(rate(errors_total{job="my-job"}[5m])) > 10
默认情况下,sum()聚合器生成不带任何标签的单个输出系列。这不仅会删除您实际想要聚合的维度(例如实例、错误类型等),还会删除所有输入系列中常见的所有标签,这些标签稍后可能对路由或消除警报有用。警报管理器。特别是job标签(或等效的服务分组标签)是常见的路由标签,因为作业通常与负责处理其警报的团队相关联。因此,建议尽可能在聚合中保留此标签:
sum by(job) (rate(errors_total{job="my-job"}[5m])) > 10
更好的选择是通过使用聚合修饰符将聚合修饰符替换为排除列表方法来保留您不想显式删除的任何标签:by()without()
sum without(instance, type) (rate(errors_total{job="my-job"}[5m])) > 10
这样,输入系列上您未明确聚合的任何标签仍可在 Alertmanager 中用于警报聚合和路由,以及帮助您了解警报的来源。
Mistake 3: Using unscoped, “naked” selectors
编写 PromQL 查询(尤其是警报!)时,您需要格外小心,仅从要为其编写查询的作业或服务中选择数据。多个不相关的服务可能会公开相同的指标名称,甚至可能具有完全不同的语义含义。即使您最初编写查询时情况并非如此,明天可能会出现指标名称冲突的新服务,从而完全破坏您的警报规则或仪表板。
为了避免意外提取不相关作业的数据的情况,请务必使用标签匹配器来确定选择器的范围,该标签匹配器明确命名您所引用的作业或服务。通常,这就是job标签。
例如,不安全的“裸”选择器可能会从您不期望的其他作业中选择具有相同指标名称的数据:
rate(errors_total[5m]) > 10
更安全的范围选择器会将所选指标限制为来自作业的指标my-job:
rate(errors_total{job="my-job"}[5m]) > 10
通过这种方式,您还可以避免出现以下情况:一旦刮掉另一个产生指标名称冲突的服务,您的警报规则或仪表板就会开始出现异常行为。
Mistake 4: No for duration in alerting rules
警报规则中的字段for允许您指定输出时间序列需要在连续的规则评估周期中存在多长时间才能从待处理警报转变为触发警报。这本质上允许您向警报规则添加每个系列的时间容差。现在您可能想知道哪些警报应该有for持续时间,以及应该持续多长时间。在大多数警报中完全省略持续时间可能是个好主意吗for?
考虑一个警报规则,该规则使用up指标来查找无法成功抓取的目标,并省略可选for修饰符:
alert: InstanceDown
expr: up == 0
一次失败的抓取(这很容易发生)将导致此规则触发。通常,您会希望让警报规则不那么容易触发,并在通知人员之前至少等待几分钟以查看问题是否仍然存在:
alert: InstanceDown
expr: up == 0
for: 5m # An instance needs to be down / unreachable for 5 minutes before creating an alert for it.
对于已经内置了一些时间平均的警报表达式来说,不指定for持续时间甚至可能会出现问题。请考虑此警报的高错误率:
alert: HighErrorRate
expr: rate(my_errors_total{job="my-job"}[5m]) > 10
此规则将在第一次评估时触发,其中错误率较高( 5 分钟可用数据的平均值)。虽然 5 分钟平均值已经引入了一些鲁棒性,但请考虑当 Prometheus 服务器完全新鲜或一段时间没有收集数据时会发生什么:
- 5 分钟rate()窗口将仅考虑一些最近的样本,而不是实际平均超过五分钟的数据。
- 该规则现在可以为甚至还没有五分钟数据的系列立即生成警报。
引入for持续时间可以解决此问题:
alert: HighErrorRate
expr: rate(my_errors_total{job="my-job"}[5m]) > 10
for: 5m
对于大多数警报规则也可以提出类似的论点。因此,为了使您的警报规则更加强大,您几乎总是希望将for持续时间设置为至少几分钟。请记住,这也会导致警报反应时间变慢for,因此找到时间容差的平衡点很重要。
Mistake 5: Too short rate() windows
当在太短的时间窗口内计算速率时,您是否曾经因间隙或完全空的图表而感到沮丧,例如rate(my_counter[1m])?rate()和其他 PromQL 函数(如increase()、irate()、deriv())告诉您时间序列在给定时间窗口内上升或下降的速度,都需要在输入时间窗口下至少有两个样本才能告诉您该序列在之间的发展情况这两个样本。如果您将时间窗口设置得太小,则可能会面临窗口下只有 1 个或 0 个样本的风险,在这种情况下,输出将变为空。
例如,如果您采用rate()每 15 秒抓取一次的 20 秒范围内的计数器指标,则很有可能这 20 秒通常不会覆盖两个样本,因此您会得到一个间隙率:
采取极端的方式:如果将速率窗口减少到 16 秒,当两个相隔 15 秒的点碰巧落在任意对齐的 16 秒窗口下时,您只会偶尔获得输出点:
如果进一步减小窗口,您最终将根本没有机会覆盖两个样本,因此您会得到完全空的查询输出:
因此,您需要选择足够大的输入窗口 - 不仅仅是 2 倍的抓取间隔,而且您还希望在面对偶尔的抓取失败和不幸的窗口对齐时保持稳健。通常,选择速率窗口大小至少为抓取间隔的 4 倍是一个好习惯:
提示:使用Grafana时,您还可以使用$__rate_interval模板变量自动为您选择一个安全间隔。
Mistake 6: Using rate() and friends with the wrong metric type
虽然 PromQL 以其他一些方式进行静态类型化,但它无法直接告诉您是否将错误类型的指标传递到并非旨在处理该错误的函数中。最突出的例子是:
-
Using rate() with gauge metrics
rate()、irate()和函数increase()被设计为仅与计数器度量一起使用。计数器是跟踪累积计数的指标,这些计数只会增加(而不会减少),除非0跟踪过程重新启动时偶尔重置。为了在速率计算中尽可能抵消计数器重置,这些函数尝试将提供的时间窗口下样本值的任何减少解释为重置并对其进行补偿(另请参阅我们有关速率计算的详细文章)。这种计数器重置检测和补偿意味着您只能0从这些功能中获得积极的结果(或 )。如果您不小心传递了一个可以自然上升的计量指标如果下降(如内存使用情况),PromQL 将无法分辨出此错误,而只会返回不正确的输出值。这是因为每次您的仪表指标下降时,rate()都会将其解释为计数器重置,并会错误地“纠正”它。
-
Using deriv() with counter metrics文章来源:https://www.toymoban.com/news/detail-501932.html
您可以将该deriv()函数视为大致相当于rate(),但对于仪表而言。deriv()告诉您在输入时间窗口内测量的仪表指标每秒上升或下降的速度。虽然不常见,但与rate()这里相同的陷阱可能会以另一种方式击中您:由于该deriv()函数没有实现有关计数器重置的任何逻辑(仪表没有这些!),尝试计算deriv()计数器指标将给出每当在提供的窗口下重置计数器时,您都会得到错误的结果,有时甚至是负面的结果。文章来源地址https://www.toymoban.com/news/detail-501932.html
到了这里,关于【博客675】prometheus生产上易犯的错误的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!