vlambda博客
学习文章列表

Mtail导致Nginx报警延迟


问题描述

我们当前在用的 Nginx 监控系统主要存在晚高峰数据延迟和偶发性数据断层两个问题,具体表现就是晚高峰时监控数据总是延迟3分钟以上,极端情况下还会出现监控数据完全停止的情况,导致了Nginx短信电话报警中80%都延迟了3分钟以上,严重影响了报警时效性和数据准确性。

原因分析

Nginx监控的简化架构大致如下图,可以发现日志数据从 Nginx机器 >>> 大数据计算平台 >>> 监控中心 的传输链路有点长,而大流量的传输往往很容易受内网网络波动影响;预聚合服务则是一个运行在大数据平台上的Flink任务,主要功能是对全量日志数据进行清洗和预聚合, 稳定性易受晚高峰流量突增和宿主机器资源的影响,极端情况下flink任务还会出现宕掉的情况,需重启丢弃数据才能恢复。
Mtail导致Nginx报警延迟


优化重构

业界关于Nginx监控的主流的监控方案主要有以下三类:

  • 远端处理日志:基于filebeat、logstash之类的日志采集器将nginx日志传输到kafka里,然后使用流式计算任务(flink、storm、spark等)实时消费进行统计。

    • 优点:kafka、filebeat、flink都是比较成熟的组件,系统搭建比较简单。

    • 缺点:数据传输链路过长、外部依赖太多,稳定性相对较差。

  • 本地处理日志:Agent在nginx机器上进行日志解析、计算,然后通过主动上报或开放一个采集端口由采集端拉取两种方式上报到监控中心。

    • 优点:没有日志传输,也不依赖外部系统,而且由于Nginx机器本身负载也不高,因此可以分出一部分算力给Agent使用,相比方案一可以节省相当一部分后端计算资源。

    • 缺点:没有资源限制功能,如果资源消耗较高可能会影响到Nginx。

  • 基于插件、lua脚本之类的监控增强:常见的有 nginx-module-vts、falcon-ngx_metric之类,可以将Nginx内部状态进行导出,可以配合Open-falcon或Prometheus之类的开源监控。

    • 优点:由于可以直接使用Nginx内部变量,因此不需要解析日志,性能最好。 

    • 缺点:需要重新编译Nginx后才可启用,对nginx的侵入性较大,一旦依赖出现问题对Nginx将是破坏性的影响;另外部署和更新的成本也较高。

 
我们过去用的Nginx监控就是采用的方案一,缺点也和上面列举的一样,继续在上面做优化只能暂时性缓解而无法从根本上解决稳定性问题。方案三也同样存在稳定性方面的问题,除非性能真的是瓶颈一般不考虑此方案。我们在对比业内主流的几个nginx监控方案如Mtail、夜莺-Agent、Promethus Nginx-Vts-exporter,Mtail的在性能消耗、稳定性、功能特性等方面表现都是最优的,采用以Mtail为核心的新版的简化架构如下,对比老架构减少了传输链路,在减小数据延迟的前提下还节省了不小的系统资源。

Mtail导致Nginx报警延迟


出现问题

随着新Nginx监控架构的推进,  Mtail的一些问题也逐渐显露了出来。事情就发生在一个普通的周四晚上,白天的时候配合sa灰度了公司一个较大的域名所属的Nginx集群20台左右机器,Mtail进程的cpu大概维持在60%~70%之间、内存在60m左右,性能也还符合预期。结果晚上下班回家洗完手刚准备拿起玩具逗娃,刷刷刷刷4条Mtail性能报警毫无防备的打乱了我逗娃的节奏,赶紧打开报警机器对应的域名数据一看,监控图线断的简直是惨不忍睹:

Mtail导致Nginx报警延迟

登上Nginx机器发现进程的cpu使用率保持在130%左右,偶尔还会达到150%,对比白天的时候翻了一倍。同批部署的其他机器的上报数据虽然没有出现断线,但是cpu也接近110%。查看Mtail监控日志,cpu上涨的时间和业务晚高峰时间一致,看起来像是晚高峰日志量增加导致Mtail出现了性能问题。可是根据之前的压测记录,Mtail一分钟可以处理100万日志,该批机器晚高峰日志量也就50~60万/分钟左右,按道理说不应该出现这种情况啊。

Mtail导致Nginx报警延迟

继续对比白天和晚上的监控结果,发现晚高峰的时候Mtail的监控对象个数翻了一倍,因为之前压测的时候着重关注了日志量这个指标,对于监控对象个数只是模拟了一个很简单的场景,难道是Mtail的处理性能不只是取决于日志量,还跟监控对象个数有很大关系? 赶紧在隔离环境的机器上使用脚本模拟了60万条线上机器的日志,重新测试后发现原本跑完60万压测日志都花不到30秒的Mtail,换成线上的日志后需要跑近3分钟! 

Mtail导致Nginx报警延迟

妥妥的性能问题,没别的说的赶紧解决吧。首先比较容易想到的就是从程序启动参数上进行优化,可是go不像java那样有丰富的启动参数(有不对的地方请评论区指正),在网上搜到一些参数设置了也是没啥效果。尝试用master分支的代码 build 了下,结果还是将近3分钟,在测试误差范围以内。在Mtail的github的仓库里翻了翻issue列表,最近一次提的性能相关的issue还是17年的。。。

Mtail导致Nginx报警延迟

没别的好办法只能自己上了,尝试着使用Go自带的性能分析工具 pprof  生成一下最近60秒运行的报告,执行top10 命令,发现其中一个Mtail的方法的耗时比较可疑,根据方法名字看应该是和查找监控对象相关的,累计调用耗时占到了50%多,很有可能是晚高峰监控对象增多而该方法执行效率不太高最终出现性能问题了。

Mtail导致Nginx报警延迟

为了印证想法从git上把Mtail的源码拉下来简单扫了几眼,不得不说Google开源的项目结构还是很工整的,代码的模块化做的很好,比较容易的就理清了程序的运行逻辑,很典型的生产者消费者模型。在定位到相关代码后我更加坚信了自己的想法:果然是写出了个 O(M*N)复杂度的查询!!!按照它的实现逻辑,查找性能是与监控对象个数和监控对象维度成反比的,如果晚高峰监控对象比白天的时候多一倍,那么查询性能就弱一倍!

Mtail导致Nginx报警延迟

大晚上提issure等官方修复肯定是来不及了,还好go的语法比较简单,虽然没有开发go的经验也能凭着搜索引擎写写。核心优化的原理就是为每个监控对象计算一个唯一key,使用 map 存储key和监控对象的映射,这样查找的时间复杂度就和监控对象个数没有太大关系了。在通过单元测试后,赶紧把代码打包放到测试环境试了一下,跑同一份线上的日志数据,只花了不到30秒, 性能提升了足足5倍反复核对了几遍数据准确性也没有问题。


后续
后面就是找sa打了个包给出问题的几台机器替换了下Mtail。观察了1周左右,白天的cpu由灰度前的60%~70%降到了20%~30%, 晚高峰也从130%处理不完变成了 60%~70%之间,再也没出现过数据断点的情况。

小插曲

当然也给Google官方提了pr ,经过了几次修改代码风格、补充测试用例后,最终在本周合并进了主分支(https://github.com/google/mtail/pull/478) 

反思

方案选型的时候压测场景务必要做好做全,前期准备的越充分后期遇到的问题就越少。






🔍

作者介绍

Mtail导致Nginx报警延迟

🕵️‍♀️

ABOUT US

有 趣 灵 魂 集 结 地

思 路 总 结 大 本 营

SCAN AND FOLLOW US NOW

@BUG侦探