突破指标上限:Zepto 借助 Mimir 实现的可观测性演进
在Zepto,可靠且可扩展的可观测性对于维持我们快节奏的运营至关重要。我们现有的监控系统基于Prometheus构建,并搭配了Thanos边车(Sidecar),但随着我们的基础设施和服务体系不断扩大,该系统开始面临严峻挑战。为确保稳定性并获得更深入的洞察,我们着手从头构建一个全新的、强大的指标平台。这篇博客详细阐述了我们为何需要做出改变,如何选择Grafana Mimir作为我们的长期解决方案,以及在从先前设置迁移到高度可扩展的多租户架构过程中我们克服的关键技术难题。我们将涵盖所面临的问题、评估过程、迁移过程中意想不到的挑战以及我们设计的解决方案。
- 问题陈述:使用Grafana Mimir,用一个可横向扩展的多租户指标后端替换难以使用的Prometheus+Thanos架构
- 动机: 我们希望解决现有设置中的可靠性问题(内存不足导致的进程终止、查询缓慢、警报故障)以及管理成本问题。
- 目标: 实现高可扩展性,为使用者保持Prometheus API兼容性,在迁移过程中对开发者体验零影响,并实施有效的多租户机制以实现更好的管理。
问题定义
们最初的监控架构由高可用性(HA)Prometheus实例组成,这些实例从基础设施组件(每30秒一次)和终端用户应用程序微服务(每5-20秒一次)抓取指标。平均而言,我们有800 - 900万个活跃序列,再加上高频抓取,导致样本摄入率非常高。由于采用HA架构(重复的数据流),这给整个架构带来了巨大压力。
与此同时,预测显示我们将在一年内增长到6000万个活跃序列。我们的系统已经出现了压力迹象——平均读取延迟一直远高于2秒,查询失败率也攀升至**5%**以上。
我们遇到了几个关键问题:
- 内存峰值与内存不足导致的进程终止: 即使配备180GB的大容量内存且数据保留时间相对较短(6小时),Prometheus实例仍偶尔会因内存不足错误而崩溃。
- 慢查询: 由于每个Prometheus实例只有一个大型索引,查询指标数据的速度很慢。
- 警报系统中断情况: 这种不稳定状况导致我们的警报系统出现了短暂中断,影响了其主要功能。
- 恢复缓慢: 由于预写日志(WAL)重放速度缓慢,恢复失败的Prometheus实例非常耗时,这就要求我们在进行变更部署和实现高可用性时,需设置多个 Prometheus 副本。
对自动化和扩展的影响: 指标系统出现故障还可能导致网关等关键组件无法顺利扩展,并扰乱重要的自动化工作流程。
这些问题引发了严重的可靠性担忧,不仅关乎可观测性平台本身,还涉及依赖及时警报的核心服务。
解决方案的原则
有几个因素影响了我们的方法:
- 规模与负载: 我们需要一种解决方案,既能应对当前较高的摄取率,又能为未来的增长做好准备。
- 高可用数据传输流: 我们的可观测性设置必须具备高可用性,这一点不容商榷。该解决方案必须简化架构并提高其可靠性,避免任何组件冗余。
- 基于命名空间的租户机制:在Zepto,命名空间是围绕团队构建的,以便实现团队特定的控制。应用层必须具备开箱即用的类似隔离和控制功能。
- Prometheus 兼容性: 对于现有用户(仪表盘、警报)的无缝迁移而言,保持与 Prometheus 应用程序编程接口(API)的兼容性至关重要,同时还能确保对开发人员工作流程的干预降至最低。
建议的解决方案
在评估了VictoriaMetrics、Thanos Receiver架构和Grafana Mimir等方案后,我们选择了Mimir作为我们的长期战略指标数据存储,因为它符合我们所有的核心原则,同时在摄入层控制提供精细,并支持对象存储作为长期存储解决方案。
我们进行了广泛的负载测试,以验证Mimir的性能。使用Avalanche,模拟了多达3.5亿个活跃序列和超过2GB/秒的摄取量,并持续了24小时。
由于对其性能有信心,我们计划迁移到Mimir。
迁移策略:
- 现有Prometheus(临时): 我们继续运行高可用性Prometheus实例,但将其保留期缩短至2小时,数据块持续时间缩短至30分钟。它们将作为数据源,将数据写入Mimir。
- Grafana Mimir Cluster 集群: 部署了一个为基于命名空间的多租户配置的 Mimir 集群。
- 远程写入配置: 将Prometheus配置为将指标远程写入Mimir,最初创建了一个双源架构以进行数据验证。
这种架构使我们能够比较新旧系统的数据,并确保过渡期间的一致性。
面临的主要挑战(第一部分):内存不足(OOM)导致进程终止
我们通过在Prometheus中配置大约30条基于命名空间的远程写入规则开始迁移。几乎与此同时,我们遇到了两个关键问题:
- Prometheus内存峰值问题: 多个远程写入配置,可能涉及用于命名空间路由的正则表达式过滤,给Prometheus实例带来了严重的内存压力,导致因内存不足而被终止。
- Mimir 数据量膨胀: 在 Mimir 中观察到的样本摄入速率几乎是我们在容量规划期间预期的两倍,这导致了 Mimir 写入路径中的CPU/内存压力。
这种组合导致了服务中断,因为我们的两个高可用性Prometheus实例同时因内存不足被终止。
我们发现了一个开源工具cortex-tenant,可用于解决因复杂过滤导致的Prometheus内存问题。cortex-tenant无需依赖Prometheus中可能较为繁重的正则表达式过滤,而是能高效地从指标样本中提取标签并添加必要的租户信息,从而显著降低Prometheus的开销。
摄入数据翻倍的根本原因很简单:Mimir 默认情况下不会对来自高可用性(HA)Prometheus 副本的样本进行去重。我们通过启用 Mimir 的 HA Tracker 功能解决了这个问题。
有了HA重复数据删除功能以及cortex-tenant,我们稳定了双写架构。我们对Prometheus和Mimir之间的数据一致性进行了为期七天的监测。结果看起来不错,这让我们有信心继续迁移使用者,从警报开始。
临的主要挑战(第二部分):查询扇出问题
当我们将警报规则复制到Mimir上时,遇到了新一波问题。Mimir的组件,如摄取器、分发器和查询器,开始出现高延迟和内存溢出导致进程被终止的情况。尽管对各种配置进行了调整,问题依然存在。
详细的根本原因分析(RCA)揭示了我们的多租户方法与联合查询相结合所产生的意外后果。警报查询,尤其是那些需要全局视图的查询,正在执行 扇出,跨 Mimir 内的 所有 租户时间序列数据库(TSDB)进行查询。这产生了大量不必要的负载。
最简单的解决方法本可以是恢复到单租户的Mimir设置,或者与开发人员合作在每个查询中添加租户,但这两种方法似乎都不切实际。
我们的解决方案是将此过程自动化,这促使我们构建了自己的自定义租户注入器。该服务定期运行(每12小时一次),分析警报规则,并为每个查询注入所需的特定租户ID。这样,通过仅将查询导向必要的租户时间序列数据库(TSDB),避免了低效的扇出。对于需要来自多个命名空间数据的联合警报,它会智能地将多个租户ID添加到同一查询中。未来,我们计划将自定义租户注入器开源,以造福更广泛的社区。
经验教训
从一个艰难运行的传统系统到可扩展的Mimir平台,这段历程让我们学到了几条宝贵的经验:
- 显式高可用性处理:分布式系统通常需要针对高可用性场景进行显式配置,例如Mimir的用于重复数据删除的高可用性跟踪器。不要认为这是自动实现的。
- 多租户复杂性: 虽然功能强大,但多租户增加了复杂性,尤其是在查询模式方面。简单的 “全查询” 方法可能对性能有害。精心设计和有针对性的查询至关重要。
- 用定制工具弥合差距: 有时,现成的解决方案需要增强。我们定制的租户注入器对于在我们特定的环境中实现多租户警报至关重要。
- 分阶段部署是关键: 双写阶段以及使用者(如警报)的逐步迁移,对于在不造成重大干扰的情况下逐步识别和解决问题至关重要。
- 负载测试的局限性: 尽管负载测试至关重要,但它主要验证的数据摄取和基本查询路径。复杂的操作查询模式,如我们的警报扇出,仅在实际使用中才会暴露出来。
结论
如今,我们基于Mimir的指标平台已全面上线,其处理的负载比初始设置时大幅增加,峰值超过4000万个活跃序列,每秒摄取量高达200万个数据点。
平均写入延迟在5毫秒以内,即时查询和范围查询的平均读取延迟均在400毫秒以内,同时将整体查询失败率降低至0.1%以下。
该项目成功解决了我们最初面临的可靠性和可扩展性挑战,为Zepto在未来数年的可观测性奠定了坚实基础。
原文:https://blog.zeptonow.com/breaking-the-metrics-ceiling-zeptos-observability-evolution-with-mimir-18718c446670