编者按:可靠稳定的产品背后需要有靠谱的监控报警框架做支撑,这篇文章为创业公司搭建监控和报警框架提供了思路。本文来自岂安科技Bigsec(微信公众号:bigsec-com)的投稿,作者吕梦琪,bigsec框架研发负责人。
从大公司投身到创业型的小公司,最深的感受就是“由奢入俭难”这五个字。以前公司里有完善的框架体系,涵盖了分布式log、监控、实时报警、大数据存储等等方面,并且有成熟的团队来运营,使用者大部分时间只要做好集成就行;换到了小公司,以我们bigsec为例,初始的技术团队只有3人,起步阶段一穷二白,而且要做两个体系的产品,每天业务的压力就很大,做起事来只能用些比较粗糙的手段。业务的压力和质量的追求始终是个矛盾。然而,该有的决不能少,所以我们还是尽量抽出一些时间做好部分必须的框架工作。在我们看来,监控和报警框架是优先级最高的:
- 创业型公司刚上来时都比较粗糙,测试什么的没法做到非常充分,出现问题的概率也比较大,需要做好监控。
- 作为技术型公司,我们的技术架构相对来说比较激进和复杂,也不那么“经典”,并且迭代频繁。对一个复杂系统的把握,必然是大量的自动化的监控、度量,时刻要知道系统里每个组件的各种运行指标。实际上,有经验的工程师会体会到,做好监控和运营,在难度和重要性上要远高于你写的功能代码。
- 人少,就要自动化高。只有做好监控和自动化报警,才能抽出更多的精力忙业务,晚上才能放心睡觉。
因此,想要做出可靠稳定的产品,就首先要有靠谱的监控报警框架去做支撑。而对于像bigsec这样的创业公司在做这件事情时,还需要关心以下几点:
- 有没有成熟的开源产品。大公司可以花费一个团队专心做一件事情;而小公司每个人都是非常珍惜的资源,半个人的开销都嫌大,所以会更多的借力于开源产品
- 坑多不多。开源产品的质量和支持无法和商业产品相比,所以我们需要选用可以hold住的,坑少且稳定的产品使用
- 能否支持跨语言。bigsec的产品基本是c、java、python、json的混合产品,尤其是后端主要由java和python组成,需要至少能涵盖到这两种语言。
- 可伸缩性是否足够好。我们的业务和数据在快速发展,所以使用的产品必须能支持后期海量数据的涌入
- 是否有一定的扩展性。使用过程中必然会有一些特殊的需求,如何快速的做些定制化也是需要考量的点
- 能否同时支持单机和分布式的部署。bigsec情况比较特殊,既有传统的私有化部署的软件解决方案,又有公有的saas以及配套的大规模计算集群,因此,我们很多产品都要有高低配两种实现,同时通过配置来无缝切换。监控系统也不例外
极度重要,要求又多,资源还少,所以我们在监控和报警方面还是花了一些心思。下面,我们会详细介绍我们所做的工作。
先扯扯监控
首先要谈监控。监控的要点就是通过定义多种metrics(关于metrics这里不展开了,本文主要讨论我们做的事情),来辅助我们去了解产品从硬件到软件,从LB到后端数据库的实时运行状况,帮助我们发现问题,故障甄别,以及确认恢复。这是最重要的事情,这是最重要的事情,这是最重要的事情。
举个栗子
废话少叙,先来张以前的图看个大概:
此图是我们业务系统metrics的一个例子,显示了我们前置nginx的部分metrics,通过实时的分析nginx log,我们可以得到所有机房nginx在吞吐量、延时、负载分配、流量等等多方面的实时信息,一目了然;还可以根据不同维度进行分析比较,帮我们有效的找到各种异常情况(图里就有一个小缺口)。类似的metrics,bigsec目前已经有几百个,通过不同的面板组织起来,并且还在不断的增加。目前公司的原则是每个项目在开发之前就需要尽可能多的定义出相应的metrics,做好详尽的监控。
技术选型
眼尖的同学会发现我们用了开源组件grafana,事实上我们在metrics存储上采用的就是influxdb/redis+grafana的组合:
- 在我们的saas后台,采用influxdb+grafana 2.0(2.0有单独的后台服务)的组合,存储了海量的metrics,同时满足大量数据的写入,以及监控报警系统的频繁读取,同时保留横向扩展的可能性。
- 在我们的测试环境/私有化部署环境,采用redis+grafana 1.9的组合,这个组合部署简单,开销相对较小,可以满足少量的metrics使用。实现上,我们根据influxdb的存储结构在redis上复刻了一份,并且通过proxy来模拟influxdb的接口
- 实现方式上,我们提供了python/java两个库,并通过配置文件来作redis/influxdb的无缝切换。每个应用根据自己的需求来决定配置,并调用api将metrics信息记录到合适的地方;同时框架自身也做了一些组件专门用来收集系统层面的metrics(比如上面的例子就是通过syslog服务来接受nginx日志,并做实时的metrics统计)
得出这样的架构选型,我们当初也是伤透了脑筋:
- 前公司用的是类opentsdb的系统,在使用便捷性和性能上没的说,但后端强依赖于hbase,对于我们并不合适。
- 当时也看了其他针对这种 Time-series data的开源方案,目前其实没有什么特别好的方案,可以看这里的吐槽:https://news.ycombinator.com/item?id=9805742
- 最终我们还是选了influxdb做为主力,这是一个相对轻量的开源时间序列数据库,很适合于做为metrics使用:它有类似sql的查询语句比较容易上手;自带简易管理界面;可以用grafana作为前端看板;还有各个语言的客户端支持;最后,它最近还是比较火。
- 选redis的原因在于:私有环境下需要一个简单的方案;比较熟悉,当influxdb碰到问题时,redis版可以作为备胎顶上。
- 最初我们也考虑过用elasticsearch这个大杀器来做metrics使用,然而:
- es 是重读轻写。由于是搜索引擎的出身,它强调索引,你写一条记录,还伴随着大量的索引工作,有人做过实验,es和influxdb之间在存储上是10x的关系。所以es注定写性能不是强项(就单机而言),而且索引的建立必然带来延时和复杂性。当然有了索引,在做一些过滤和聚合的时候,搜索引擎的优势就发挥出来了,能出更多的报表,也能支持长时间的查询。
- influxdb是面向时间序列的数据库,这一类数据的特征是数据量大,写入压力高,所以influxdb在索引上没有侧重,保证了大量数据的快速存储;缺陷在于,没有索引,每次查询需要过滤全量数据,但是基本上能保证读到最新数据(没有延迟索引的影响)。所以influxdb是轻读重写得。
- 我们的metrics主要是监控当前状况,偶尔会回溯一下历史,同时这些数据会被实时报警系统使用,要求响应比较快。从使用场景和成本的角度,我们最终选择了influxdb做metrics存储,elasticsearch单做BI工具使用。
metrics监控架构
此图概括描述了我们的监控结构。
- python和java程序通过metrics库将相应的数据打到制定的地方
- 程序里用到的框架组件(如rpc,分布式log等)会由组件自身进行打点,方便框架层面的统一监控排错
- 程序里的业务metrics需要由工程师手动打点,来记录每个业务和程序模块的特殊运行状况
- 为了保证后端metrics数据写入的稳定性,我们在client段做了部分聚合操作,减少打点数据 ‘ * redis和influxdb做成驱动形式,通过配置来指定,开发人员不需要关心具体的实现
其他监控工具
上文描述的metrics系统解决了我们大部分的问题,是我们监控系统的主要成分,同时,我们还使用了一些其他零散的手段:
- uptime。Uptime是一个开源项目,通过获取网页的心跳数据来检测网页的可用性,如图:
- 系统资源(cpu、内存、硬盘)监控。系统监控工具很多,一开始我们使用的是collectd这个传统的工具;后来出于定制化、统一化、练兵的需要,我们改成自己写java程序,通过jmx来获取 相关数据,并打入到metrics系。collectd就停止使用了
- 脚本和外部工具。在遇到特殊需求,通用的系统无法满足的时候,我们也会通过写shell脚本来做一些工作,这种方式开发效率和功能上都比较棒,只是不能很好的和其他数据集成;同时,目前互联网上也有不少监控服务,我们也用了一些,来作为自身监控系统的补足和备胎。
二次开发
因为主要借助于开源系统,所以有时候需要进行一些二次开发来满足公司的定制化需求。这里举一些比较有用的例子:
- grafana默认的分组显示(group by)只支持一个tag,这种使用场景比较有限。为了让其能支持多个版本,我们在两个版本上都修改了它的前端JS代码,如下图所示,修改后的版本可以显示多个tag组合的数据情况(这里是我们的rpc统计中,所有服务的延时范围统计)
- grafana不支持聚合嵌套,所以像distinct count这样的功能无法实现,这个也通过修改前端代码解决。
- grafana可以建多个metrics进行比较查看,但永远显示的都是最新的数据,不方便做同环比比较。我们通过proxy来返回一段时间前的数据,来达到这个目的。
- Uptime检测https的网页会有证书错误的问题,需要手动在代码里禁用相应的环境变量。
接着是报警
光有监控是不够的,因为这么多数据和报表,无法通过人肉的方式跟踪,所以在收集到这么多数据之后,需要有自动化的报警系统来进行进一步的分析和处理。为此,我们基于收集到的海量数据,开发了一个轻量级的报警系统,包括报警系统的完整架构如下图所示:
这套系统主要由DataSource,Drivers,Rules,Actions等几部分组成:
- DataSource和相应的Driver对应了不同的监控数据来源。
- rules表示我们的一些报警规则
- actions是规则命中后的触发动作
DataSource和Driver
data source表示不同的数据来源,每种数据来源都由相应的driver来获取,并抽象成统一的数据格式(我们采用了类时间序列的格式),这样可以把数据抽取系统和规则引擎完全解耦,减少开发复杂度。目前我们的datasource包括:
- tsdb中的metrics数据。
- 这是最主要的数据来源,通过获取存储在redis/influxdb中的metrics数据,我们可以对海量的监控指标进行详尽的分析;
- grafana面板可以生成influxdb dsl,我们的报警系统直接支持利用此DSL进行报警,这样使用者在grafana面板上配置好监控项后,可以很方便的进行相应的报警。
- 通过上文描述的metrics proxy可以获取metrics的历史数据,方便做同环比检测
- uptime的数据。uptime可以对各个url进行监控,通过获取其数据可以进行网站存活性报警。
- 其他数据。还有其他类型的数据,比如collectd等,也可以方便的集成到报警系统中来。
Rules
从各种data source定期的获得统一格式的监控数据后,下一步就是通过报警规则进行数据检查了,来验证数据是否超出了预设的阀值。报警规则向来是个复杂的问题,需要满足各种各样的需求。为此,我们在开发规则引擎时比较重视减少开发的复杂程度。目前我们的规则以下两类:
- 单数据源简单规则。简单规则通过对每次最新的监控数据进行阈值比较,来获得报警,比如:
- 上下限阈值比较。这种是最简单的,定义好上限和下限,就可以发现异常值
- 数据存活性比较。当发现某一监控项的数据存在(或消失)时,即报警,用来检查错误指标(或存活指标)
- 多次报警。当简单规则触发的内部报警在一段时间内超过一定的次数时,才进行真正的报警。
- 报警cooldown。当同一报警不停出现时,此规则会进行相应的抑制。
- 断崖式报警。当监控数据出现断崖式特征时,才进行报警。
- 同环比报警。对同一监控项可以拉取不同时间段的两条数据,就可以进行相应的报警。
- 组合运算报警。比如说nginx 2xx状态比例的监控,可以通过对2xx次数和总访问次数的计算来获取。
这里只是举例描述了一些规则类型,实际系统中会有更多的类型
Actions
在获得报警数据后,需要促发一些行为,来完成整个自动化。
- 最常用的报警动作就是发邮件了,通过对每一类报警制定不同的监控人,可以是相关人员第一时间获悉系统异常。
- 微信报警,邮件的补充。
- 规则引擎产生的数据可以进一步写回metrics系统,作第二轮的监控报警。比如前文描述的2xx比例(类似的还有各种比例等)。在这种情况下,报警系统相当于一个定时的自动化引擎,来做一些定期的数据处理,方便我们做更好的监控和报表。实际上,这个规则引擎会成为我们后期自动化任务引擎的基础。
有了这套系统,目前我们的运营监控基本实现了自动化。系统故障时会有相应的报警邮件来通知,这样开发人员可以集中精力在新功能的研发上。
数字化运营
实际上,整套报警监控系统不但帮助我们去维护网站/系统的稳定性,提高自动化程度,还能提升我们的数字化运营能力,最大限度的提升整个公司的效率
- 简单报表。grafana这种可视化工具可以解决大部分初期的报表需求,免掉了初期BI人员的投入
- 定期报表。我们利用报警系统,做了简单的修改,可以对一些监控项,在每天凌晨进行强制报警(数据采集选取1天,报警显示详细数据),这样每天造成都可以收到过去一天的统计报表。由于复用了现有的系统,省掉了相关报表功能的开发。
小结
本文作为bigsec在过去的大半年中,在监控报警上做的一些工作的总结,事实上,在后面的日子里,还需要进行更多更复杂的工作:
- 接收其他来源的数据,同时大力完善公司内部的监控体系
- 完善分布式log机制,方便排障和更细粒度的监控。
- 将报警监控系统和生产的业务发布系统打通,来实现弹性扩容和自动容灾的可能性