使用Prometheus监视Python Web应用程序

image.png

本文介绍了如何使用一个简单的Flask应用程序作为示例,通过监控uWSGI + nginx运行的方式来完全使其正常运行。

一段不起眼的历史

Prometheus最初是Google的Borgmon衍生监控工具。

在本地环境中,Borgmon依赖于普遍且简单的服务发现。由于受Borg管理,监控对象服务应该很容易被找到。例如,在特定用户的集群中执行的所有作业,或者更复杂的部署情况下,存在构成作业的所有子任务。

这些可以分别作为 Prometheus 的 metrics,与 Borgmon 一样,成为从 varz 端点获取数据的单个目标。它们通常是用 C ++、Java、Go 或者(不太常见)Python 编写的多线程服务器。

Prometheus继承了Borgman对环境的许多假设。特别是,客户端库假定度量是来自于多个执行线程在共享地址空间中运行的各种库和子系统。而服务器端则假设Prometheus是一个目标有一个(可能是)多线程程序。

这种方法不行。

这些前提在特别是Python世界中,在许多非Google部署中已经被破坏。在这里,通常使用WSGI应用服务器来将请求(例如使用Django或Flask)分发给多个工作进程(每个工作进程均为进程而非线程)。

在uWSGI下运行的Flask应用的Prometheus Python客户端的简单部署中,每个针对/metrics的请求都会命中不同的工作进程,并导出各自的计数器、直方图等。因此,监控数据会变得混乱无序。

实际上,每个特定计数器的每个刮取值只返回一个工作者的值,而不是整个作业的值。这些值会在应用程序中跳来跳去,因此它们并不对整个应用程序有任何实际用途。

解决方案

阿米特·萨哈(Amit Saha)在详细的文章中讨论了相同问题的各种解决方案。根据其中一篇文章的描述,Prometheus的Python客户端包含了一个多进程模式,旨在处理这种情况,并且gunicorn是应用服务器的一个动机示例。

这是通过在应用程序的所有进程之间共享mmap()’d字典目录来实现的。然后,每个进程在被Prometheus抓取时执行计算,以返回整个应用程序的指标共享视图。

这有一些在文件中记录的“标题”中存在的缺点。例如,缺少每个流程的免费Python测量指标,缺乏特定指标类型的完整支持,以及稍微复杂的测量类型等。

实现端到端的架构也是困难的。在这里,我们将展示实现每个部分所需的内容以及在环境中如何实现它们。如果顺利的话,这个完整的例子应该对所有将来进行类似工作的人有所帮助。

    1. 共享目录需要作为进程的环境变量prometheus_multiproc_dir传递。使用uWSGI的env选项进行传递,请参考uwsgi.ini。

 

    1. 客户端的共享目录需要在应用程序重启后进行清理。这可能有点难理解,但可以使用uWSGI的硬编码钩子之一exec-asap,在读取配置文件后立即执行Shell脚本,以便在进行其他处理之前清理共享目录。请参考uwsgi.ini。

 

    1. 在这个脚本中,我们删除并重新创建Prometheus客户端的共享数据目录。

 

    1. 为了确保适当的权限,我们需要在supervisor的root下以root身份运行uwsgi,并删除uwsgi内的privs。

 

    1. 应用程序需要设置Python客户端的多进程模式。主要是根据Saha的发布的文档进行的操作,请参考metrics.py。

 

    1. 还请注意,这还包括了一个合适的中间件,用于导出响应状态和延迟的Prometheus指标。

 

    1. uWSGI需要设置应用程序环境,以使应用程序在fork()后加载。

 

    1. 默认情况下,uWSGI尝试通过在加载应用程序后进行fork()来节省内存。这确实具有写时复制的好处,可以大大节省内存。

 

    1. 但是,它似乎干扰了客户端的多进程模式操作。可能是因为在fork()之前应用了锁定。

 

    使用uWSGI的lazy-apps选项可以在fork后加载应用程序,这样可以获得更清洁的环境。

我认为通过这些方法,可以使在uWSGI下运行的Flask应用程序的/metrics端点正常工作,并完全实现pandoras_flask演示的功能能。

请注意,在演示中,我们将不同端口的指标终端点正确地公开给合适的应用程序。这样一来,用户无需访问就可以轻松允许对监控的访问。

在部署过程中,应该可以使用uwsgi_exporter从uWSGI本身获取更多的统计信息。

特征

在Saha的博客帖子中,他通过使用本地的statsd推送指标作为推荐解决方案,解释了一系列替代方案。这并不是我们实际偏好的方式。

最终,通过在像Kubernetes这样的容器编排环境中执行所有操作,Prometheus提供了闪耀的本地化环境,但这是在现有的Python应用程序堆栈中获取其他优势的重要步骤。

可能的最佳Prometheus中间步骤是将每个子进程单独注册为抓取目标。这是Django Prometheus采用的方法,但建议中的“端口范围”方法略显复杂。

在我们的环境中,可以通过以下方法来实施这个想法(可能仍在实施中)。

    1. 在每个进程的线程内运行Web服务器,并在临时端口上进行监听,并处理/metrics查询。

 

    1. 注册Web服务器,并定期更新其地址(例如:hostname:32769)到短TTL的etcd路径。大多数服务发现需求已经使用etcd了。

 

    使用基于文件的服务发现,在Prometheus中确定这些目标,并对它们进行个性化配置。

我认为这种方法不会比使用Python客户端的多进程模式更复杂,但会带来一些自身的复杂性。

请注意,每个工作人员都有一个目标,这可能是导致时间序列爆炸的原因。例如,在这种情况下,跟踪来自8个Python客户端的响应时间的默认单一直方图度量将在应用其他标签之前生成大约140个单独的时间序列。请注意,这不是Prometheus处理的问题,但请注意,在扩展时可能会增加(或增加)这个问题。

总结

首先,无论采用什么方法,将标准的Python Web应用堆栈的指标导出到Prometheus都有些复杂。我希望这篇文章能对那些想要开始使用现有的nginx + uwsgi + Flask应用程序的人有所帮助。

当我们在容器编排下运行更多的服务时(我们正在努力做的事情),我们希望能够更轻松地将Prometheus监控与这些服务集成在一起。

建议那些知名度较高的Prometheus用户来查看托管的Prometheus服务。如果需要演示,请随时告知。

广告
将在 10 秒后关闭
bannerAds