拉取是否可扩展?

2016年7月23日作者 Julius Volz

让我们来谈谈一个特别顽固的迷思。每当有人讨论监控系统,以及 Prometheus 的拉取式指标收集方法时,总会有人跳出来说,拉取式方法“根本无法扩展”。提出的理由通常很模糊,或者只适用于与 Prometheus 本质上不同的系统。事实上,在实际运行中,我们已经在最大的规模上使用过拉取式监控,这一说法与我们的运营经验背道而驰。

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

Prometheus 不是 Nagios

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

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

谁发起连接并不重要

就可扩展性而言,谁发起传输指标的 TCP 连接并不重要。无论你选择哪种方式,建立连接的开销与指标负载和其他所需工作相比都微不足道。

但有人会说,推送式方法可以使用 UDP 并完全避免连接建立!没错,但与 Prometheus 需要处理的其他数据摄取工作(尤其是将时间序列数据持久化到磁盘)相比,TCP/HTTP 的开销仍然可以忽略不计。举例来说:一个大型 Prometheus 服务器可以轻松存储数百万个时间序列,每秒可处理 800,000 个样本(这是 SoundCloud 使用真实生产指标数据测得的)。如果 scrape 间隔为 10 秒,每台主机 700 个时间序列,这允许您从一个 Prometheus 服务器监控超过 10,000 台机器。这里的扩展瓶颈从未与拉取指标有关,而通常与 Prometheus 服务器将数据摄取到内存,然后在磁盘/SSD 上可持续地持久化和过期数据的速度有关。

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

Prometheus 不是一个事件驱动的系统

一些监控系统是事件驱动的。也就是说,它们在事件发生时立即将每个单独的事件(HTTP 请求、异常,任何你想到的)报告给中央监控系统。这个中央系统然后要么将事件聚合为指标(StatsD 是一个典型例子),要么单独存储事件以供以后处理(ELK 堆栈是另一个例子)。在这种系统中,拉取确实会成为问题:被 instrumented 的服务需要在拉取之间缓冲事件,并且拉取必须非常频繁才能模拟推送式方法的相同“实时性”,并且不会压垮事件缓冲区。

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

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

通过拉取式方法,您的监控系统需要知道哪些服务实例存在以及如何连接到它们。有些人担心这需要监控系统进行额外的配置,并将其视为操作可扩展性问题。

我们认为,无论如何,对于严肃的监控设置,您都无法摆脱这种配置工作:如果您的监控系统不知道世界应该是什么样子,也不知道哪些监控中的服务实例应该存在,那么它如何能够知道一个实例从未报告、因故障而宕机、或者确实不再应该存在?只有当您完全不关心单个实例的健康状况时,这才是可以接受的,例如,当您只运行一次性工作者,并且足够多的工作者报告一些结果就足够时。大多数环境并非完全如此。

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

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

意外地 DDoS 您的监控

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

现实世界的证明

除了 Prometheus 已经在实际世界中用于监控非常大的设置(例如,它被用于在 DigitalOcean 监控数百万台机器 )的事实外,还有其他拉取式监控在最大规模的环境中成功使用的突出例子。Prometheus 受到 Google 的 Borgmon 的启发,Borgmon(过去和现在部分)在 Google 内部用于使用拉取式方法监控其所有关键生产服务。我们在 Google 使用 Borgmon 时遇到的任何可扩展性问题都不是因为其拉取式方法。如果拉取式方法能够扩展到拥有数千万个数据中心和数百万台机器的全球环境,您几乎不能说拉取式方法无法扩展。

但是拉取还有其他问题!

确实存在一些难以用拉取式方法监控的设置。一个突出的例子是,当您拥有许多位于世界各地但由于防火墙或复杂的网络设置而无法直接访问的端点,并且在每个网络段运行 Prometheus 服务器不可行时。这并非 Prometheus 最初设计的环境,尽管通常可以找到解决方法(通过 Pushgateway 或重构您的设置)。无论如何,这些对拉取式监控的剩余担忧通常与可扩展性无关,而是由于网络操作在打开 TCP 连接方面的困难。

一切都好吗?

本文解决了关于拉取式监控方法最常见的可扩展性担忧。随着 Prometheus 和其他拉取式系统在非常大的环境中成功使用,并且拉取方面在现实中并未构成瓶颈,结果应该是显而易见的:“拉取无法扩展”的论点并不是一个真正的担忧。我们希望未来的辩论能够聚焦于比这个“红鲱鱼”更重要的问题。