如果您正在检测自己的代码,则应遵循关于如何使用 Prometheus 客户端库检测代码的通用规则。 当从另一个监控或检测系统获取指标时,情况往往并非如此黑白分明。
本文档包含您在编写 Exporter 或自定义收集器时应考虑的事项。 所涵盖的理论对于那些进行直接检测的人员也将很有意义。
如果您正在编写 Exporter,并且对这里的任何内容不清楚,请通过 IRC (#prometheus on libera) 或邮件列表与我们联系。
编写 Exporter 时您需要做出的主要决定是您愿意投入多少工作来从中获得完美的指标。
如果所讨论的系统只有少量很少更改的指标,那么获得一切完美是一个简单的选择,HAProxy Exporter就是一个很好的例子。
另一方面,如果您尝试在系统具有数百个指标(这些指标会随着新版本频繁更改)的情况下获得完美,那么您就为自己签署了大量持续的工作。MySQL Exporter就处于频谱的这一端。
Node Exporter是这些的混合体,复杂性因模块而异。 例如,mdadm
收集器手动解析文件并公开专门为该收集器创建的指标,因此我们不妨使指标正确。 对于 meminfo
收集器,结果因内核版本而异,因此我们最终只进行了足够的转换来创建有效的指标。
当使用应用程序时,您的目标应该是 Exporter 不需要用户进行自定义配置,只需告诉它应用程序在哪里即可。 您可能还需要提供过滤掉某些指标的能力,如果它们在大型设置上可能过于精细和昂贵,例如 HAProxy Exporter 允许过滤每个服务器的统计信息。 同样,可能存在默认情况下禁用的昂贵指标。
当使用其他监控系统、框架和协议时,您通常需要提供额外的配置或自定义来生成适合 Prometheus 的指标。 在最佳情况下,监控系统具有与 Prometheus 类似的数据模型,您可以自动确定如何转换指标。 Cloudwatch、SNMP 和 collectd 就是这种情况。 最多,我们需要让用户选择他们想要提取哪些指标的能力。
在其他情况下,来自系统的指标是完全非标准的,具体取决于系统的使用情况和底层应用程序。 在这种情况下,用户必须告诉我们如何转换指标。 JMX Exporter 是这里最糟糕的情况,Graphite 和 StatsD Exporter 也需要配置才能提取标签。
建议确保 Exporter 在没有配置的情况下开箱即用,并在需要转换时提供一些示例配置。
YAML 是标准的 Prometheus 配置格式,所有配置默认情况下都应使用 YAML。
遵循关于指标命名的最佳实践。
通常,指标名称应允许熟悉 Prometheus 但不熟悉特定系统的人员很好地猜测指标的含义。 名为 http_requests_total
的指标不是非常有用 - 这些指标是在传入时、在某些过滤器中还是在到达用户代码时测量的? 而 requests_total
就更糟了,请求的类型是什么?
对于直接检测,给定的指标应仅存在于一个文件中。 相应地,在 Exporter 和收集器中,指标应仅适用于一个子系统并相应地命名。
指标名称永远不应以程序方式生成,除非在编写自定义收集器或 Exporter 时。
应用程序的指标名称通常应以 Exporter 名称为前缀,例如 haproxy_up
。
指标必须使用基本单位(例如秒、字节),并将它们转换为更易读的内容留给图形工具。 无论您最终使用什么单位,指标名称中的单位都必须与正在使用的单位匹配。 同样,暴露比率,而不是百分比。 更好的是,为比率的两个组成部分中的每一个指定一个计数器。
指标名称不应包含它们导出的标签,例如 by_type
,因为如果标签被聚合掉,这将没有意义。
唯一的例外是当您通过多个指标导出具有不同标签的相同数据时,在这种情况下,这通常是区分它们的最明智方法。 对于直接检测,只有当使用所有标签导出单个指标会导致基数过高时,才会出现这种情况。
Prometheus 指标和标签名称以 snake_case
编写。 将 camelCase
转换为 snake_case
是可取的,尽管对于 myTCPExample
或 isNaN
之类的内容,自动执行此操作并不总是产生好的结果,因此有时最好保持原样。
暴露的指标不应包含冒号,这些冒号保留供用户定义的记录规则在聚合时使用。
只有 [a-zA-Z0-9:_]
在指标名称中有效。
_sum
、_count
、_bucket
和 _total
后缀由摘要、直方图和计数器使用。 除非您正在生成其中之一,否则请避免使用这些后缀。
_total
是计数器的约定,如果您正在使用 COUNTER 类型,则应使用它。
process_
和 scrape_
前缀是保留的。 如果它们遵循匹配的语义,则可以向这些前缀添加您自己的前缀。 例如,Prometheus 具有 scrape_duration_seconds
来表示抓取花费了多长时间,最佳实践是也拥有一个以 Exporter 为中心的指标,例如 jmx_scrape_duration_seconds
,说明特定的 Exporter 花费了多长时间来完成其工作。 对于您可以访问 PID 的进程统计信息,Go 和 Python 都提供了可以为您处理此问题的收集器。 HAProxy Exporter 就是一个很好的例子。
当您有成功的请求计数和失败的请求计数时,暴露此信息的最佳方式是使用一个指标表示总请求数,另一个指标表示失败的请求数。 这使得计算失败率变得容易。 不要使用一个带有失败或成功标签的指标。 同样,对于缓存的命中或未命中,最好使用一个指标表示总数,另一个指标表示命中数。
考虑一下使用监控的人员可能会对指标名称进行代码或 Web 搜索的可能性。 如果名称非常成熟,并且不太可能在习惯这些名称的人员领域之外使用,例如 SNMP 和网络工程师,那么将它们保持原样可能是一个好主意。 此逻辑不适用于所有 Exporter,例如 MySQL Exporter 指标可能被各种人员使用,而不仅仅是 DBA。 带有原始名称的 HELP
字符串可以提供与使用原始名称相同的大部分好处。
阅读关于标签的一般建议。
避免使用 type
作为标签名称,它太通用且通常毫无意义。 您还应尽可能尝试避免可能与目标标签冲突的名称,例如 region
、zone
、cluster
、availability_zone
、az
、datacenter
、dc
、owner
、customer
、stage
、service
、environment
和 env
。 但是,如果这就是应用程序调用某些资源的方式,最好不要通过重命名来造成混淆。
避免仅仅因为它们共享前缀就将事物放入一个指标的诱惑。 除非您确定某些东西作为一个指标有意义,否则多个指标更安全。
标签 le
对于直方图具有特殊含义,quantile
对于摘要具有特殊含义。 通常应避免使用这些标签。
读/写和发送/接收最好作为单独的指标,而不是作为标签。 这通常是因为您一次只关心其中一个,并且以这种方式使用它们更容易。
经验法则是,一个指标在求和或平均时应该有意义。 Exporter 还会出现另一种情况,即数据从根本上是表格形式的,否则将需要用户对指标名称进行正则表达式才能使用。 考虑一下您主板上的电压传感器,虽然对它们进行数学运算毫无意义,但将它们放在一个指标中而不是每个传感器一个指标是有意义的。 指标内的所有值都应(几乎)始终具有相同的单位,例如,考虑一下风扇速度是否与电压混合在一起,并且您无法自动分离它们。
不要这样做
my_metric{label="a"} 1 my_metric{label="b"} 6 my_metric{label="total"} 7
或这样做
my_metric{label="a"} 1 my_metric{label="b"} 6 my_metric{} 7
前者会破坏对您的指标执行 sum()
的人员,后者会破坏总和并且很难使用。 某些客户端库(例如 Go)会积极尝试阻止您在自定义收集器中执行后者,并且所有客户端库都应阻止您在直接检测中执行后者。 永远不要执行任何一种操作,而是依赖 Prometheus 聚合。
如果您的监控暴露了这样的总数,请删除总数。 如果您必须出于某种原因保留它,例如总数包括未单独计数的事物,请使用不同的指标名称。
检测标签应尽可能少,每个额外的标签都是用户在编写 PromQL 时需要考虑的一个标签。 因此,避免使用可以删除而不影响时间序列唯一性的检测标签。 可以通过信息指标添加有关指标的其他信息,有关示例,请参见下文如何处理版本号。
但是,在某些情况下,几乎所有指标用户都希望获得其他信息。 如果是这样,添加非唯一标签而不是信息指标是正确的解决方案。 例如,mysqld_exporter 的 mysqld_perf_schema_events_statements_total
的 digest
标签是完整查询模式的哈希值,足以保证唯一性。 但是,如果没有人类可读的 digest_text
标签,它就几乎没有用处,对于长查询,digest_text
标签将仅包含查询模式的开头,因此不是唯一的。 因此,我们最终同时拥有了供人类使用的 digest_text
标签和用于唯一性的 digest
标签。
如果您发现自己想要将相同的标签应用于所有指标,请停止。
通常有两种情况会出现这种情况。
第一种情况是对于某些标签,在指标上拥有该标签会很有用,例如软件的版本号。 相反,请使用 http://www.robustperception.io/how-to-have-labels-for-machine-roles/ 中描述的方法。
第二种情况是当标签实际上是目标标签时。 这些是诸如区域、集群名称等之类的东西,它们来自您的基础设施设置,而不是应用程序本身。 应用程序无需说明它在您的标签分类法中的位置,这应由运行 Prometheus 服务器的人员配置,并且监控同一应用程序的不同人员可以为其提供不同的名称。
因此,这些标签属于 Prometheus 的抓取配置中,通过您正在使用的任何服务发现。 在此处应用机器角色的概念也可以,因为它至少对于抓取它的某些人来说可能是有用的信息。
您应该尝试将指标的类型与 Prometheus 类型匹配。 这通常意味着计数器和仪表。 摘要的 _count
和 _sum
也相对常见,并且偶尔您会看到分位数。 直方图很少见,如果您遇到直方图,请记住暴露格式会暴露累积值。
通常,指标的类型并不明显,尤其是在您自动处理一组指标时。 总的来说,UNTYPED
是一个安全的默认值。
计数器不能向下计数,因此如果您的计数器类型来自另一个可以递减的检测系统(例如 Dropwizard 指标),那么它不是计数器,而是一个仪表。 UNTYPED
可能是那里使用的最佳类型,因为如果将其用作计数器,则 GAUGE
会产生误导。
当您转换指标时,用户能够追溯到原始指标是什么,以及导致该转换的规则是什么,这很有用。 将收集器或 Exporter 的名称、应用的任何规则的 ID 以及原始指标的名称和详细信息放入帮助字符串将极大地帮助用户。
Prometheus 不喜欢一个指标具有不同的帮助字符串。 如果您从许多其他指标制作一个指标,请选择其中一个指标放入帮助字符串。
有关此示例,SNMP Exporter 使用 OID,JMX Exporter 放入示例 mBean 名称。HAProxy Exporter 具有手写字符串。Node Exporter 也有各种各样的例子。
某些检测系统除了最小值、最大值和标准偏差之外,还暴露 1 分钟、5 分钟、15 分钟速率、自应用程序启动以来的平均速率(这些在 Dropwizard 指标中称为 mean
)。
这些都应该删除,因为它们不是很有用并且会增加混乱。 Prometheus 可以自行计算速率,并且通常更准确,因为暴露的平均值通常是指数衰减的。 您不知道计算最小值或最大值的时间,并且标准偏差在统计上是无用的,如果您需要计算它,您可以随时暴露平方和 _sum
和 _count
。
分位数有相关问题,您可以选择删除它们或将它们放入摘要中。
许多监控系统没有标签,而是执行类似 my.class.path.mymetric.labelvalue1.labelvalue2.labelvalue3
的操作。
Graphite 和 StatsD Exporter 共享一种使用小型配置语言转换这些内容的方法。 其他 Exporter 应实现相同的操作。 该转换当前仅在 Go 中实现,并且将受益于被分解为单独的库。
在为您的 Exporter 实现收集器时,您永远不应使用通常的直接检测方法,然后在每次抓取时更新指标。
而是每次都创建新指标。 在 Go 中,这可以通过在您的 Collect()
方法中使用 MustNewConstMetric 来完成。 对于 Python,请参阅 https://github.com/prometheus/client_python#custom-collectors,对于 Java,请在您的收集方法中生成 List<MetricFamilySamples>
,有关示例,请参阅 StandardExports.java。
这样做的原因有两个。 首先,两次抓取可能同时发生,并且直接检测使用实际上是文件级全局变量的东西,因此您会遇到竞争条件。 其次,如果标签值消失,它仍然会被导出。
通过直接检测来检测您自己的 Exporter 是可以的,例如,Exporter 在所有抓取中传输的总字节数或执行的调用次数。 对于诸如 Blackbox Exporter 和 SNMP Exporter 之类的 Exporter,它们不绑定到单个目标,这些应仅在普通的 /metrics
调用中公开,而不是在特定目标的抓取中公开。
有时您想导出关于抓取的指标,例如抓取花费了多长时间或您处理了多少条记录。
这些应作为仪表公开,因为它们是关于事件(抓取)的,并且指标名称以 Exporter 名称为前缀,例如 jmx_scrape_duration_seconds
。 通常排除 _exporter
,并且如果 Exporter 也适合仅用作收集器,则绝对排除它。
应避免使用其他抓取“元”指标。 例如,抓取次数的计数器或抓取持续时间的直方图。 让 Exporter 跟踪这些指标会复制 Prometheus 本身的 自动生成的指标。 这增加了每个 Exporter 实例的存储成本。
许多系统(例如 Elasticsearch)都公开机器指标,例如 CPU、内存和文件系统信息。 由于 Node Exporter 在 Prometheus 生态系统中提供了这些指标,因此应删除此类指标。
在 Java 世界中,许多检测框架都暴露了进程级和 JVM 级统计信息,例如 CPU 和 GC。 Java 客户端和 JMX Exporter 已经通过 DefaultExports.java 以首选形式包含了这些内容,因此也应删除这些内容。
与其他语言和框架类似。
每个 Exporter 应准确监控一个应用程序实例,最好与它位于同一台机器上。 这意味着对于您运行的每个 HAProxy,您都运行一个 haproxy_exporter
进程。 对于每台具有 Mesos worker 的机器,您都在其上运行 Mesos Exporter,如果一台机器同时具有两者,则再运行一个用于 master 的 Exporter。
这背后的理论是,对于直接检测,这就是您要做的,我们正在尝试在其他布局中尽可能接近这一点。 这意味着所有服务发现都在 Prometheus 中完成,而不是在 Exporter 中完成。 这也具有 Prometheus 具有目标信息的好处,它允许用户使用 Blackbox Exporter 探测您的服务。
有两个例外
第一个是运行在您正在监控的应用程序旁边是完全没有意义的。 SNMP、Blackbox 和 IPMI Exporter 是这方面的主要示例。 IPMI 和 SNMP Exporter,因为设备通常是黑盒,无法在其上运行代码(尽管如果您可以在它们上面运行 Node Exporter,那会更好),而 Blackbox Exporter,您正在监控类似 DNS 名称的东西,也没有任何东西可以运行。 在这种情况下,Prometheus 仍应执行服务发现,并将要抓取的目标传递给 Exporter。 有关示例,请参见 Blackbox 和 SNMP Exporter。
请注意,目前只能使用 Go、Python 和 Java 客户端库编写此类 Exporter。
第二个例外是您从系统的随机实例中提取一些统计信息,并且不关心您正在与哪个实例对话。 考虑一组您想要对数据运行一些业务查询以进行导出的 MySQL 副本。 使用您的常用负载均衡方法与一个副本对话的 Exporter 是最明智的方法。
当您监控具有 master 选举的系统时,这不适用,在这种情况下,您应该单独监控每个实例,并在 Prometheus 中处理“master 性”。 这是因为并非始终只有一个 master,并且在 Prometheus 的监视下更改目标会引起奇怪的现象。
指标应仅在 Prometheus 抓取它们时从应用程序中拉取,Exporter 不应基于自己的计时器执行抓取。 也就是说,所有抓取都应是同步的。
因此,您不应在您暴露的指标上设置时间戳,让 Prometheus 负责处理。 如果您认为需要时间戳,那么您可能需要 Pushgateway。
如果指标的检索成本特别高,即超过一分钟,则可以接受缓存它。 这应在 HELP
字符串中注明。
Prometheus 的默认抓取超时时间为 10 秒。 如果您的 Exporter 预计会超过此时间,则应在用户文档中明确指出。
某些应用程序和监控系统仅推送指标,例如 StatsD、Graphite 和 collectd。
这里有两个注意事项。
首先,您何时使指标过期? Collectd 和与 Graphite 通信的内容都定期导出,当它们停止时,我们希望停止暴露指标。 Collectd 包括过期时间,因此我们使用它,Graphite 不包括,因此它是 Exporter 上的一个标志。
StatsD 有点不同,因为它处理的是事件而不是指标。 最佳模型是在每个应用程序旁边运行一个 Exporter,并在应用程序重新启动时重新启动它们,以便清除状态。
其次,这些类型的系统倾向于允许您的用户发送增量或原始计数器。 您应尽可能依赖原始计数器,因为这是通用的 Prometheus 模型。
对于服务级指标,例如服务级批处理作业,您应该让您的 Exporter 推送到 Pushgateway,并在事件发生后退出,而不是自行处理状态。 对于实例级批处理指标,目前尚无明确的模式。 选项是滥用 Node Exporter 的 textfile 收集器,依赖内存状态(如果您不需要在重新启动后持久化,这可能是最佳选择)或实现类似于 textfile 收集器的功能。
对于您正在与之通信的应用程序不响应或存在其他问题的抓取失败,目前有两种模式。
第一种是返回 5xx 错误。
第二种是具有一个 myexporter_up
,例如 haproxy_up
变量,其值取决于抓取是否工作,值为 0 或 1。
后者更好,因为即使抓取失败,您仍然可以获得一些有用的指标,例如 HAProxy Exporter 提供的进程统计信息。 前者对于用户来说更容易处理,因为 up
以通常的方式工作,尽管您无法区分 Exporter 宕机和应用程序宕机。
如果访问 http://yourexporter/
具有一个简单的 HTML 页面,其中包含 Exporter 的名称以及指向 /metrics
页面的链接,则对用户来说会更好。
用户可能在同一台机器上有许多 Exporter 和 Prometheus 组件,因此为了使操作更轻松,每个组件都有一个唯一的端口号。
https://github.com/prometheus/prometheus/wiki/Default-port-allocations 是我们跟踪它们的位置,这是公开可编辑的。
在开发 Exporter 时,请随时获取下一个空闲端口号,最好在公开宣布之前。 如果您尚未准备好发布,则放置您的用户名和 WIP 也可以。
这是一个注册表,旨在使我们的用户的生活更轻松一些,而不是承诺开发特定的 Exporter。 对于内部应用程序的 Exporter,我们建议使用默认端口分配范围之外的端口。
一旦您准备好向全世界公告您的 Exporter,请发送电子邮件到邮件列表并发送 PR 以将其添加到可用 Exporter 列表。
本文档是开源的。 请通过提交问题或拉取请求来帮助改进它。