让我们来谈谈一个特别顽固的迷思。每当讨论监控系统和 Prometheus 基于拉取的指标收集方法时,总会有人插话说基于拉取的方法“从根本上来说是不可扩展的”。给出的理由通常含糊不清,或者只适用于与 Prometheus 根本不同的系统。事实上,在最大规模的拉取式监控工作经验中,这个说法与我们自己的运营经验相悖。
我们已经有一个关于 为什么 Prometheus 选择拉取而不是推送 的常见问题解答条目,但它没有专门关注扩展方面。让我们仔细看看围绕这一说法的常见误解,并分析它们是否以及如何适用于 Prometheus。
Prometheus 不是 Nagios
当人们想到主动拉取的监控系统时,通常会想到 Nagios。Nagios 的名声是扩展性不好,部分原因是它会生成子进程进行主动检查,这些子进程可以在 Nagios 主机上运行任意操作,以确定特定主机或服务的运行状况。这种检查架构确实无法很好地扩展,因为中央 Nagios 主机很快就会不堪重负。因此,人们通常将检查配置为每隔几分钟执行一次,或者他们会遇到更严重的问题。
然而,Prometheus 采取了一种完全不同的方法。它不是执行检查脚本,而只是从一组已检测的目标通过网络收集时间序列数据。对于每个目标,Prometheus 服务器只是通过 HTTP(以高度并行的方式,使用 goroutines)获取该目标的所有指标的当前状态,并且没有其他与拉取相关的执行开销。这引出了下一点
谁发起连接并不重要
出于扩展目的,谁发起传输指标的 TCP 连接并不重要。无论你采用哪种方式,建立连接的工作量与指标有效负载和其他所需的工作相比都很小。
但是,您可能会说,基于推送的方法可以使用 UDP 并完全避免建立连接!没错,但是与 Prometheus 服务器为接收数据所必须做的其他工作(特别是将时间序列数据持久化到磁盘)相比,TCP/HTTP 开销仍然可以忽略不计。为了用数字来说明这一点:一台大型 Prometheus 服务器可以轻松存储数百万个时间序列,每秒记录 800,000 个传入样本(这是在 SoundCloud 使用真实生产指标数据测量的)。考虑到 10 秒的抓取间隔和每个主机 700 个时间序列,这使你能够从一台 Prometheus 服务器监控 10,000 多台机器。这里的扩展瓶颈从来都与拉取指标无关,而是通常与 Prometheus 服务器将数据摄取到内存中,然后持续地将数据持久化和过期到磁盘/SSD 上的速度有关。
此外,尽管现在的网络非常可靠,但使用基于 TCP 的拉取方法可以确保指标数据可靠到达,或者至少在因网络中断导致指标传输失败时,监控系统可以立即知道。
Prometheus 不是一个基于事件的系统
一些监控系统是基于事件的。也就是说,它们会立即向中央监控系统报告每个单独的事件(HTTP 请求、异常,等等)。然后,这个中央系统要么将事件聚合为指标(StatsD 是主要示例),要么单独存储事件以供以后处理(ELK 堆栈就是一个例子)。在这样的系统中,拉取确实会存在问题:被检测的服务必须在拉取之间缓冲事件,并且拉取必须非常频繁地发生,才能模拟基于推送方法的相同的“实时性”并且不会使事件缓冲区不堪重负。
然而,同样,Prometheus 不是一个基于事件的监控系统。你不会向 Prometheus 发送原始事件,它也不能存储它们。Prometheus 致力于收集聚合的时间序列数据。这意味着它只对定期收集给定指标集的当前状态感兴趣,而不是导致生成这些指标的底层事件。例如,一个被检测的服务不会在处理每个 HTTP 请求时向 Prometheus 发送消息,而是会简单地在内存中增加这些请求的计数。这每秒可以发生数十万次,而不会导致任何监控流量。然后,Prometheus 只是每 15 或 30 秒(或你配置的任何时间)向服务实例询问当前的计数器值,并将该值与抓取时间戳一起存储为样本。其他指标类型,如仪表、直方图和摘要,也以类似的方式处理。由此产生的监控流量很低,在这种情况下,基于拉取的方法也不会产生问题。
但是现在我的监控需要知道我的服务实例!
使用基于拉取的方法,你的监控系统需要知道哪些服务实例存在以及如何连接到它们。有些人担心这会在监控系统方面带来额外的配置,并认为这是一个运营可扩展性问题。
我们认为,无论如何,对于严肃的监控设置,你都无法避免这种配置工作:如果你的监控系统不知道世界应该是什么样子,以及应该存在哪些受监控的服务实例,那么当一个实例永远不报告、因中断而停机或者真的不再存在时,它又如何能够知道呢?只有当你根本不在乎各个实例的运行状况时,这才可以接受,就像当你只运行短暂的工作程序时,只要有足够多的工作程序报告一些结果就足够了。大多数环境并非完全如此。
如果监控系统无论如何都需要知道世界的期望状态,那么基于推送的方法实际上需要更多的配置。你的监控系统不仅需要知道应该存在哪些服务实例,而且你的服务实例现在还需要知道如何访问你的监控系统。拉取方法不仅需要更少的配置,而且还使你的监控设置更加灵活。使用拉取,你可以在你的笔记本电脑上运行生产监控的副本以进行实验。它还允许你使用其他工具获取指标或手动检查指标端点。为了实现高可用性,拉取允许你并行运行两个配置相同的 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 和其他基于拉取的系统在非常大型的环境中成功使用,并且拉取方面实际上没有构成瓶颈,结果应该很清楚:“拉取不可扩展”的论点不是一个真正的担忧。我们希望未来的辩论将关注比这个烟雾弹更重要的问题。