Prometheus最佳实践 监控
如何监控
一个简单地答案是监控所有。所有的library,子系统和服务,都应该有一些指标来大致告诉你,他们是否正常运行。
监控和你的代码应该是一个整体。监控代码和业务代码放在一起。这样子从告警追溯到代码会变得比较容易。
三种类型的服务
仅仅从监控的视角来看,服务可以大致分成三种类型:在线服务、离线处理、批量任务。它们之间是有一些重叠,不过每个服务都能基本归类于其中。
在线服务
在线服务是,用户或者其他系统在期待即刻的响应。举个例子,大部分数据库和HTTP请求都归于此类。在这类系统中,指标的关键在于,处理了多少请求、有多少失败和时延。正在处理中的请求也很有用。在线的服务系统应该在客户端和服务端两侧都进行监控。如果在两侧观测到的现象不一样,就是我们定位的有用信息。如果一个服务有很多个客户端,那么针对每个客户端单独跟踪是不现实的,客户端们必须依赖自己的统计信息。
查询的计数应该跟查询开始保持一致呢?还是查询的结束。推荐使用查询结束的时候,这个和时延、错误等指标都能对上,也易于编码。
离线处理
对于离线处理来说,没有人需要等待响应,批工作才是常见的。经常会有处理的多个阶段。对于每一个阶段,跟踪进来的item,有多少在进度中的,上次处理的时间点,上次输出了多少个tiem。如果是批量的,还应该跟踪批量的条数。
知道系统上次处理某物的时间对于检测它是否已停止很有用,但这是非常本地化的信息。 更好的方法是通过系统发送心跳:某些虚拟项会一直传递,并包括插入时的时间戳。 每个阶段都可以导出它所看到的最新心跳时间戳,让您知道item需要多长时间才能在系统中传播。 对于没有静默期且不进行任何处理的系统,可能不需要明确的心跳。
批量任务
离线处理和批处理作业之间存在模糊界限,因为离线处理可能在批处理作业中完成。批处理作业的特点是它们不连续运行,这使它们的采集变得困难。
批处理作业的关键指标是上一次成功。 跟踪作业的每个主要阶段花费了多长时间,整体运行时间以及作业最后一次完成(成功或失败)的时间也很有用。 这些都是Gauge,应将其推入PushGateway。 通常,还有一些特定于作业的总体统计信息对于跟踪很有用,例如处理的记录总数。
对于要花费几分钟才能运行的批处理作业,也可以使用基于拉取的监视来抓取它们,这很有用。 这样,您可以随时间跟踪与其他类型的作业相同的指标,例如与其他系统对话时的资源使用情况和延迟。 如果作业开始变慢,这可以帮助调试。
对于经常运行的批处理作业(例如,每隔15分钟更频繁),您应考虑将其转换为守护程序并将其作为脱机处理作业进行处理。
子系统
除了三种主要的服务类型外,系统还具有应监视的子部分。
library库
库应提供工具,而用户不需要其他配置。
如果它是用于访问进程外的某些资源(例如,网络,磁盘或IPC)的库,则应至少跟踪总体查询计数,错误(如果可能发生错误)和延迟。根据库的重型程度,跟踪库本身内部的错误和延迟以及您认为可能有用的任何常规统计信息。
一个库可能被应用程序的多个独立部分针对不同的资源使用,因此请注意在适当的情况下使用标签来区分使用。 例如,数据库连接池应区分正在与之通信的数据库,但是对于DNS客户端来说,无需区分用户。
日志
通常,对于每一行日志代码,您还应该增加一个计数器。 如果您发现有趣的日志消息,则希望能够看到它发生的频率和时间。
如果同一功能中有多个紧密相关的日志消息(例如,if或switch语句的不同分支),则有时可以合理地为所有日志消息增加一个计数器。
通常,导出应用程序整体记录的信息/错误/警告行的总数,并在发布过程中检查是否存在重大差异也很有用。
失败
失败的处理方式与日志记录类似。 每次发生故障时,都应增加一个计数器。 与日志记录不同,该错误可能还会冒出一个更通用的错误计数器,具体取决于代码的结构。
报告失败时,通常应使用其他指标来表示尝试的总数。 这使得故障率易于计算
线程池
对于任何类型的线程池,关键指标是排队的请求数,正在使用的线程数,线程总数,已处理的任务数以及花费的时间。 跟踪队列中等待的内容也很有用。
缓存
缓存的关键指标是查询总数,命中数,总体延迟,然后是缓存所在的任何联机服务系统的查询计数,错误和延迟。
收集器
在实现重要的自定义指标收集器时,建议暴露一个Gauge,以了解收集所花费的时间(以秒为单位),以及另一个Gague,以了解遇到的错误数。
这是可以将持续时间导出为Gauge而不是Summary或Histogram的两种情况之一,另一种是批处理作业的持续时间。 这是因为两者都代表有关特定Pull/Scrap的信息,而不是在时间维度跟踪多个持续的时间。
注意事项
进行监控时,需要注意一些事情,尤其是针对Prometheus的事项
使用Labels
很少有监视系统具有标签和表达语言的概念来利用它们,因此需要一点时间来习惯。
当您要添加/平均/求和多个指标时,它们通常应该是带有标签的一个指标,而不是多个指标。
例如,而不是http_responses_500_total和http_responses_403_total,而是创建一个名为http_responses_total的度量标准,该度量标准带有HTTP响应代码的代码标签。 然后,您可以将整个指标作为规则和图形中的一个进行处理。
根据经验,度量标准名称的任何部分都不能程序生成(请使用标签)。 一个例外是当从另一个监视/仪器系统透传度量标准时。
不要过度使用Labels
每个标签集都是一个附加的时间序列,其中包含RAM,CPU,磁盘和网络成本。通常,开销可以忽略不计,但是在具有大量度量标准和跨数百个服务器的数百个标签集的方案中,这可能会迅速加起来。
作为一般准则,请尝试将指标的基数保持在10以下,对于超过该指标的基数,旨在将它们限制在整个系统中。您的绝大多数指标都应没有标签。
如果您有一个基数超过100或有可能增长到更大的度量标准,请研究其他解决方案,例如减少维数或将分析从监视转移到通用处理系统。
为了让您更好地了解基础数字,让我们看一下node_exporter。 node_exporter公开每个已装入文件系统的指标。每个节点将在数十个时间序列中具有例如node_filesystem_avail的信息。如果您有10,000个节点,那么node_filesystem_avail的时间序列大约为100,000,这对于Prometheus来说是可以的。
如果现在要为每个用户添加配额,则在10,000个节点上有10,000个用户时,很快就会达到数百万的两位数。对于Prometheus的当前实现而言,这太多了。即使数量较少,也存在机会成本,因为您再也无法在此计算机上拥有其他可能更有用的指标。
如果不确定,请从没有标签开始,然后随着具体use case的出现逐渐添加更多标签。
Counter vs. gauge
要在计数器和仪表之间进行选择,有一个简单的经验法则:如果该值可以下降,则它是一个仪表。
计数器只能上升(并重置(例如,进程重新启动时))。 它们对于累积事件数或每个事件中的事物数量很有用。 例如,HTTP请求的总数,或HTTP请求中发送的字节总数。 原始计数器很少有用。 使用rate()函数获取它们正在增加的每秒速率。
仪表可以设置,上升和下降。 它们对于状态快照(例如正在进行的请求,可用/总内存或温度)非常有用。 您绝对不应该使用仪表的rate()。
时间戳而不是事件以来的时间
如果要跟踪某件事发生后的时间,请导出发生该事件的Unix时间戳-而不是某件事发生的时间。
导出时间戳后,您可以使用表达式 time() - my_timestamp_metric 来计算自事件以来的时间,从而消除了对更新逻辑的需求,并防止了后续无法更新这个值。
内部循环
总的来说,监控 带来的额外资源成本远远超过其为运营和开发带来的收益。
对于性能至关重要的代码或在给定进程中每秒调用超过10万次的代码,您可能需要注意要更新多少个指标。
Java计数器根据争用需要12-17ns的时间递增。 其他语言将具有类似的性能。 如果那段时间对于您的内部循环很重要,请限制在内部循环中增加的指标数量,并避免使用标签(或缓存标签查找的结果,go中的 With() 函数或者 java中的 label() 函数)。
还请注意涉及时间或持续时间的度量标准更新,因为获取时间可能涉及系统调用。 与所有涉及性能关键代码的问题一样,基准测试是确定任何给定更改的影响的最佳方法。
避免丢失指标
直到发生某些事情之前,不存在的时间序列很难处理,因为通常的简单操作已不足以正确处理它们。 为避免这种情况,请针对您知道可能存在的任何时间序列导出默认值,例如0。
大多数Prometheus客户端库(包括Go,Java和Python)都会自动为您导出一个值为0,没有任何标签的指标。
原文地址
- 点赞
- 收藏
- 关注作者
评论(0)