拉取无法扩展——是这样吗?

我们来谈谈一个特别顽固的误解。每当讨论监控系统并提及 Prometheus 基于拉取的指标采集方法时,总有人会插话说基于拉取的方法“根本无法扩展”。给出的理由往往模糊不清,或者仅适用于与 Prometheus 根本不同的系统。事实上,我们在最大规模的环境中实践过基于拉取的监控,这种说法与我们自己的运营经验背道而驰。

我们已经有一篇 FAQ 条目解释了为什么 Prometheus 选择拉取而非推送,但这篇 FAQ 并未专门讨论扩展性方面。让我们仔细看看围绕这个说法常见的误解,并分析它们是否以及如何适用于 Prometheus。

Prometheus 不是 Nagios

当人们想到主动拉取的监控系统时,他们通常会想到 Nagios。Nagios 有一个名声是不容易扩展,部分原因是在 Nagios 主机上为了主动检查会生成子进程,这些子进程可以在 Nagios 主机上运行任意操作来确定某个主机或服务的健康状况。这种检查架构确实不容易扩展,因为中心的 Nagios 主机很快就会不堪重负。结果是,人们通常将检查配置为只每隔几分钟执行一次,否则就会遇到更严重的问题。

然而,Prometheus 采取了一种根本不同的方法。它不执行检查脚本,而只是通过网络从一组已插桩的目标收集时间序列数据。对于每个目标,Prometheus 服务器只是通过 HTTP 以高度并行的方式(使用 goroutines)抓取该目标所有指标的当前状态,并且没有其他与拉取相关的执行开销。这引出了下一点

谁发起连接并不重要

就扩展性而言,谁发起用于传输指标的 TCP 连接并不重要。无论哪种方式,建立连接的开销与指标载荷和所需其他工作相比都很小。

但您可能会说,基于推送的方法可以使用 UDP 并完全避免连接建立!确实如此,但 Prometheus 中的 TCP/HTTP 开销与 Prometheus 服务器为了摄取数据而必须做的其他工作(尤其是在磁盘上持久化时间序列数据)相比仍然可以忽略不计。给出一些数字:一个大型 Prometheus 服务器可以轻松存储数百万个时间序列,每秒有 800,000 个入站样本的记录(根据 SoundCloud 真实生产指标数据测量)。假设抓取间隔为 10 秒,每台主机有 700 个时间序列,这意味着您可以通过一台 Prometheus 服务器监控 10,000 多台机器。这里的扩展瓶颈从未与拉取指标有关,通常是 Prometheus 服务器将数据摄取到内存中,然后在磁盘/SSD 上持续持久化和过期数据的速度。

此外,尽管如今的网络非常可靠,但使用基于 TCP 的拉取方法可以确保指标数据可靠到达,或者至少监控系统在由于网络中断而导致指标传输失败时能立即知晓。

Prometheus 不是基于事件的系统

有些监控系统是基于事件的。也就是说,它们在每个独立事件发生时(一个 HTTP 请求、一个异常,等等)立即将其报告给中央监控系统。这个中央系统随后要么将事件聚合成指标(StatsD 是这方面的典型例子),要么单独存储事件以便后续处理(ELK stack 是一个例子)。在这样的系统中,拉取确实会是问题:已插桩的服务必须在拉取之间缓冲事件,并且拉取必须非常频繁地进行,以模拟基于推送方法的相同“实时性”,并且不让事件缓冲区溢出。

然而,再次强调,Prometheus 不是基于事件的监控系统。您不会将原始事件发送到 Prometheus,它也无法存储它们。Prometheus 的工作是收集聚合的时间序列数据。这意味着它只对定期收集给定指标集的当前状态感兴趣,而不是导致生成这些指标的底层事件。例如,一个已插桩的服务在处理 HTTP 请求时不会向 Prometheus 发送每条 HTTP 请求的消息,而只会简单地在内存中计数这些请求。这每秒可以发生数十万次而不会产生任何监控流量。然后 Prometheus 只需每隔 15 或 30 秒(或您配置的其他间隔)向服务实例询问当前的计数器值,并将该值与抓取时间戳一起存储为一个样本。其他指标类型,如 Gauges、Histograms 和 Summaries,也以类似方式处理。由此产生的监控流量很低,在这种情况下,基于拉取的方法也不会造成问题。

但现在我的监控需要知道我的服务实例!

采用基于拉取的方法,您的监控系统需要知道哪些服务实例存在以及如何连接到它们。有些人担心这会给监控系统带来额外的配置工作,并将其视为一个运维扩展性问题。

我们认为,对于认真的监控设置来说,您无论如何都无法避免这种配置工作:如果您的监控系统不知道这个世界应该是什么样子,以及应该有哪些被监控的服务实例,那么它又如何能判断一个实例是否从未报告、是否由于中断而宕机,或者是否真的不应该再存在了呢?只有在您完全不关心单个实例的健康状况时,这才是可接受的,例如当您只运行瞬时工作负载,只需要足够多的工作负载报告某些结果即可。大多数环境并非完全如此。

如果监控系统无论如何都需要知道这个世界的期望状态,那么基于推送的方法实际上总共需要更多的配置。您的监控系统不仅需要知道应该存在哪些服务实例,而且您的服务实例现在也需要知道如何到达您的监控系统。拉取方法不仅需要更少的配置,它还使您的监控设置更加灵活。使用拉取,您可以轻松地在您的笔记本电脑上运行一个生产监控的副本进行实验。它还允许您使用其他工具抓取指标或手动检查指标端点。为了实现高可用性,拉取允许您并行运行两个配置相同的 Prometheus 服务器。最后,如果必须移动监控可达到的端点,拉取方法不需要您重新配置所有的指标源。

在实际操作层面,Prometheus 提供了内置的支持,可以轻松配置世界的期望状态,支持各种云提供商和容器调度系统的服务发现机制:Consul、Marathon、Kubernetes、EC2、基于 DNS 的服务发现、Azure、Zookeeper Serversets 等等。如果需要,Prometheus 还允许您插入自定义机制。在微服务世界或任何多层架构中,如果您的监控系统使用与您的服务实例用于发现其后端相同的方法来发现要监控的目标,这根本上是一个优势。这样,您可以确保您监控的是正在服务生产流量的相同目标,并且您只需维护一种发现机制。

不小心对您的监控系统进行 DDoS 攻击

无论您是拉取还是推送,任何时间序列数据库如果您发送的样本数量超过它能处理的,它都会崩溃。然而,根据我们的经验,基于推送的方法更有可能不小心导致您的监控系统宕机。如果对从哪些实例摄取哪些指标的控制不是集中化的(在您的监控系统中),那么您就有实验性或异常作业突然向您的生产监控系统推送大量垃圾数据并导致其宕机的危险。尽管基于拉取的方法仍然有很多方法可能发生这种情况(它只控制从哪里拉取指标,而不控制指标载荷的大小和性质),但风险较低。更重要的是,这些事件可以在一个中心点得到缓解。

实际应用中的证据

除了 Prometheus 已经被用于监控现实世界中非常大的环境(例如DigitalOcean 使用它监控数百万台机器)之外,还有其他在最大规模环境中成功使用基于拉取监控的突出例子。Prometheus 的灵感来自于 Google 的 Borgmon,Borgmon 过去(并且部分地仍然)在 Google 内部使用基于拉取的方法监控所有关键生产服务。我们在 Google 使用 Borgmon 时遇到的任何扩展问题也并非由于其拉取方法。如果基于拉取的方法可以扩展到包含数十个数据中心和数百万台机器的全球环境,您就很难说拉取无法扩展。

但拉取模式还有其他问题!

确实存在一些难以使用基于拉取方法监控的环境。一个突出的例子是,当您有许多散布在全球各地的端点,由于防火墙或复杂的网络设置而无法直接访问,并且在每个网络段中运行 Prometheus 服务器是不可行的。这并非 Prometheus 的设计目标环境,尽管通常可以通过一些变通方法实现(通过 Pushgateway 或重构您的设置)。无论如何,这些关于基于拉取监控的剩余担忧通常与扩展性无关,而是由于开放 TCP 连接相关的网络操作困难。

那么,一切都好吗?

本文探讨了围绕基于拉取监控方法最常见的扩展性担忧。随着 Prometheus 和其他基于拉取系统在非常大的环境中成功使用,并且拉取本身在现实中并未构成瓶颈,结果应该很清楚:“拉取无法扩展”的论点并非真正的担忧。我们希望未来的讨论将更多地关注那些比这个无关紧要的问题更重要的事情。