open-falcon编写的整个脑洞历程

转自:  http://mp.weixin.qq.com/s?__biz=MjM5OTcxMzE0MQ==&mid=400225178&idx=1&sn=c98609a9b66f84549e41cd421b4df74d&scene=1&srcid=1028BIXhzmVdUytAJoaq6qZE&uin=MjQ2NTU1&key=04dce534b3b035efcb777ebe7e23bb9357a70bdd4d5153c5e30a9134fa08c6d2ed170f011f6fd0c7b74a7b512bf720a3&devicetype=iMac+MacBookPro11%2C3+OSX+OSX+10.11.1+build%2815B40%29&version=11020201&lang=en&pass_ticket=O%2Bfjisb5SfgX9nASftiKoA8yJbLZ5WQ3DV%2BB2EA4SUs%3D

 

open-falcon编写的整个脑洞历程

2015-10-28 秦晓辉 Go中国

讲师:秦晓辉

个人介绍:

一个来自运维部的研发,长期从事自动化运维平台的构建,参与百度自动化部署系统Archer、小米的监控系统open-falcon,偶尔做做PaaS,国内首个开源PaaS平台(噱头,哈)DINP的作者。代码熟手,工程师,not程序员。

分享的主题:《open-falcon编写的整个脑洞历程》

1. zabbix时代的痛

2. open-falcon的目标制定

3. 架构设计的习惯性

4. 试用之后

5. 一些折中

6. 未来改进

 

大家好,我是秦晓辉,来自小米运维部,毕业这几年一直做运维平台开发,对部署系统、监控系统相对熟悉一些,这次主要是给大家介绍一下open-falcon开发过程中的一些思考,力求以最直白的表述重走一下开发过程,如果你们公司也要写一套监控,或许会有一点帮助,群里大牛众多,献丑了。

本来想提纲挈领的讲一讲,顶多40分钟搞定,写稿件的时候越写越多,限于个人表达能力的问题,而又想尽量讲清楚,总是啰啰嗦嗦的,最后发现时间关系还是没法全部讲到,大家见谅。

我们先来讲讲zabbix时代的痛,小米初期是使用zabbix来做监控的。zabbix大家应该是知道的,大名鼎鼎,成熟稳健,很多公司都在用。小米也不例外,初期做运维开发的人比较少,机器量、业务量也少,zabbix可以很好的满足需求。

我们要开发一个项目、产品,肯定是要解决一些问题,zabbix是很好,但是机器量、业务量上来之后,zabbix就有些力不从心了。那我们先来说一下zabbix存在的问题。

小米在大规模使用zabbix的年代,我当时在研究CloudFoundry,直到众位sre受不了zabbix了,希望开发一个新的监控系统,我们开始组建监控团队,开始open-falcon的设计、编码。所以对zabbix的问题可能理解不深入,抛砖引玉……

1. 性能瓶颈。zabbix是使用MySQL来存放监控历史数据的。一台机器假设有100个监控项,2000台机器,就是20w监控项,监控系统的数据采集没有高峰低谷,是持续性的,周期性的,一般是一分钟采集一次。

机器量越来越大,数据量就越来越大,MySQL的写入逐渐成为瓶颈,业界有一些proxy的方案,我们有试用,也尝试把采集周期调长,比如3分钟采集一次,或者5分钟采集一次,但是都治标不治本。

zabbix有些数据采集是通过pull的方式,也就是server端主动探测的方式,当目标机器量大了之后,这些pull任务也经常出现积压。维护zabbix的同学焦头烂额。

2. 管理成本高昂。为了让zabbix压力小一点,我们整个公司搭建了多套zabbix,比如多看自己用一套、电商自己用一套、米聊自己用一套,如果要做一些公司级别的统计,需要去多个数据源拉取数据。每套zabbix都得有运维人员跟进,人力成本上升。

3. zabbix有些易用性问题。比如zabbix的模板是不支持继承的,机器分组也是扁平化的,监控策略不容易复用。zabbix要采集哪些数据,是需要在server端做手工配置的,我们认为这是一个本该省去的操作。

4. 监控系统没有统一化。机器相关的数据采集、监控我们使用zabbix来做,但是我们还需要业务的监控,比如某个thrift rpc接口,每分钟调用的cpslatency,我们希望做监控,某些url5xx4xx我们也希望做监控,某个开源软件,比如redisopenstackmysql的一些状态统计数据,我们也希望做监控。

我们称后面这些数据为performance数据,我同事来炜曾经专门写了一个系统叫perfcounter,用来存放这些performance数据。perfcounter使用rrdtool来绘图,rrd是一个环形数据库,很多监控系统的数据都是使用rrd来存储的,这个就不展开讲了,大家可以google一下rrd相关知识。

perfcounter的绘图做得不错,但是报警功能比较薄弱。zabbix本身有多套,再加上perfcounter,入口比较多,要看监控数据,可能要去不同的地方分别查看,比较麻烦。迫切需要一个大一统的解决方案。

不可否认,zabbix是个很优秀的方案,初期zabbix帮我们解决了很大的问题,不过如上所述,我们在使用过程中也遇到了一些问题,刚开始的时候提到过,zabbix时代我还在做PaaS,所以上面的说法主要是在做open-falcon设计的时候从各位sre了解到的。

下面我们来说说open-falcon的目标制定。我们已经确定要做一个新的监控解决方案了。于是开始讨论,这货应该做成什么样子。每个人可能都会有一些思考,但是每个point基本都比较散乱,我初入监控这个业务,主要是推动大家的讨论,然后整理方案,再讨论,再整理,最终出一个产品需求文档和概要设计。

有人说,我们应该既可以处理机器监控,也可以处理业务监控,还可以处理各种开源软件的监控。

没错,就是要这种大一统,而且不同的系统有不同的采集方式,有不同的监控指标,DBAMySQL熟悉,知道应该采集哪些指标,云存储团队对HBase熟悉,知道应该采集哪些指标,但是我作为监控系统的开发人员,我对MySQL不熟,对HBase不熟,所以我们无法提供其对应的采集脚本。

下面重点!

所以我们要制定规范,制定推送接口,大家自己去采集自己的系统,完事按照我们的规范,把数据组织成监控系统需要的数据格式,发送给监控系统的接口。这样大家就可以共建监控数据,把各种需要监控的软件、平台都纳入进来。

当然了,对于操作系统的一些指标,比如cpu、内存、IO、网卡、磁盘、负载等等,这个还是需要我们监控系统开发人员去提供采集机制的。

于是,我们仿照zabbix_agentd,编写了falcon-agent

这一部分主要是讲监控系统的数据采集机制

这里有哪些思考的点呢?首先,我们不希望用户在server端配置哪个机器应该采集哪个指标,这样做麻烦且没必要。我们尽量把agent做得可以自发现,比如某机器有12块盘,agent应该可以自动探测到,然后采集各块盘的指标pushserver;比如某个机器有2块网卡,agent也应该自动探测到,把各块网卡的流量、丢包率等信息采集pushserver

就是说,我们希望在装机的时候就把agent安装好,agent就可以自动去采集相关数据了,省去了服务端的配置。

顺着这个思路继续延伸一下哈,天不遂人愿,我们希望服务端不做任何配置,agent就可以自动去采集,这是我们的设计哲学,但是有的时候还是要打破一下,比如端口存活监控、进程数监控。

我们拿端口存活监控来举例,按照我们自动去采集数据的哲学,agent应该如何处理端口存活性呢?大家稍微思考一下。

刚开始我们的想法是这样的:我们可以把当前机器上监听的所有tcp端口收集到,汇报给server,比如某机器监听了2280两个端口,ss -tln,获取之。这样的确是可以收集到数据。但是我现在要做80端口的存活性监控,server端应该怎么做呢?

想想cpu.idle,我们通常会在server端做配置,说某个机器的cpu.idle,连续3<5%,就报警,最多报3次。这个策略要想正常工作,cpu.idle的数据就应该源源不断的上来。每个周期都不能少。

但是端口监控,ss -tln,可以获取当前有哪些tcp端口在监听,假设现在nginx进程挂了,80端口不再监听,ss -tln就只能获取到22端口,获取不到80端口了,于是汇报给server端的数据就少了80端口的数据。

类似cpu.idle的监控方式是需要数据源源不断上来的,于是,端口监控在这种模式下无法完成。怎么办?

我们可以写一个nodata的组件,来判断数据多长时间没上来就报警。但是nodata组件实现起来还有点小麻烦,而且还存在另外两个问题,一个是策略配置方式与cpu.idle等数据的策略配置方式不一样,比如cpu.idleall(#3)<5报警,端口存活需要配置成类似:nodata(300),方式不同用户的使用成本会上升;再一点是有些机器可能监听了N多个端口,但是真正需要监控的端口,只有少量几个,自发现端口造成资源浪费。

all(#3) 表示连续3次都

nodata(300) 表示300s没数据上来

换个方式……

端口不再做成自发现的。用户要想对端口做监控,最后肯定是要配置策略的。我们从用户配置的策略中找出所有需要监控的端口,通过hbs模块下发给agent,这样agent就可以只对特定端口做监控了。比如刚才的例子,某主机需要监控2280两个端口,用户必然在服务端配置策略,我们从策略中分析出2280,然后下发给对应的agentagent就可以只监控这俩端口了,端口活着,汇报个1给服务端,端口死了,汇报个0给服务端,比如:net.port.listen/port=22 value=1net.port.li1net.port.li1net.port.listen/port=80 value=0

下发这个动作是通过hbs这个模块来完成的,即heartbeat serveragent每分钟去调用hbs,询问自己应该监听哪些端口、哪些进程、要执行哪些插件。通过这种机制来下发一些状态信息。

OK,数据采集这部分,就是这么多内容:

1. server端制定接口规范,以此接入各种监控数据

2. agent自发现采集各种Linux性能指标,无需server端做配置

3. 进程、端口监控等无法做到自发现的,又不想引入nodata组件,想让策略配置统一化,就需要hbs来下发信息。

继续下个点之前,这里再补充两点不太重要的。

1. agent要采用什么语言来开发?

开发open-falcon这个系统之前,其实我最熟悉的语言是java。但是agentrun在所有目标机器上的,用自己最熟悉的java来开发么?要run agent,每个机器上都要先启动一个java虚拟机,这有点扯……

我们希望找一个资源占用尽量少的语言,CC++或许是个不错的选择,但是CC++我并不熟悉,据说写不好还容易内存泄露,呵呵。C/C++相对更底层,对于不同的操作系统可能要写很多分支,简单看过zabbix_agentd的源码,各种操作系统的分支处理,很麻烦。当然了,zabbix希望兼容各种操作系统,所以写的分支比较多,我们公司的操作系统清一色centos,不会有这么多分支判断。

我们希望找一个更工程化的语言,不容易出错的语言,毕竟要在所有的机器上部署,今天这个core了明天那个core了也让人受不了。

go语言看着还不错哦。首先,go是静态编译的,编译完了一个二进制,扔到相同平台的机器上可以直接跑,不需要安装乱七八糟的lib库,这点特别吸引我,特别是我在上家公司做过相当长一段时间的自动化部署,这种部署友好的静态编译发布,我喜欢。

go的进程不需要java那种虚拟机,资源占用比较少;go的并发支持是语言层面的,server端需要并发的组件用着合适;go的资源回收是defer关键字,也不是传统的try...catch...finally,干净且不容易出错;gofunction可以有多个返回值,错误处理通常是一个额外的error类型的返回值,无需抛异常,因为是返回值,不刻意忽略的话就肯定会记得处理,否则编译都过不了;gogithub结合,生态建立的比较快;go的模板看起来怪怪的,还好我不需要用……

2. agent怎么扩展?

我们可以在agent中预置一些采集项,但是我们不是神,无法覆盖所有的需求。有些用户需要扩展agent的功能,采集更多的指标。我们需要提供这种机制。这就是插件机制的设计初衷。

zabbix是有一个目录,大家只要把采集脚本放到这个目录,zabbix就去执行。这样做自然是可以解决问题,但是有些问题其实是扔给了使用者,比如脚本的分发管理问题。

那我们来思考,要采集数据发送给server端,应该要做哪些事情呢?

a) 写一个采集数据的脚本

b) 把脚本分发到需要采集数据的机器上

c) 写一个cron每隔一段时间去跑这个脚本

d) 收集脚本的输出,调用server的接口去push数据

e) 根据需求相应的对脚本做上线、下线、升级

插件机制是如何解决这几个过程的呢?首先,脚本肯定还是要用户自己写,但是我们提供一个脚本的管理,采集脚本是代码,代码就需要版本管理,我们内部有个gitlab,要求用户把插件提交到我们指定的git repo。然后agent每隔一段时间去git pull这个git repo,采集脚本就完成了分发。

然后我们在server端提供一个配置,哪些机器应该执行哪些插件,通过hbs把这个信息分发给agentagent就可以去执行了,脚本按照一个什么周期去跑呢?这个信息写在脚本的名称里,比如60_ntpoffset.sh60表示60s执行一次,agent通过对脚本文件名的分析,可以得知执行周期。

执行周期也可以在server端配置,都可以,我们是放到文件名里了

脚本执行完了,把输出打印到stdoutagent截获之后pushserver。插件的升级、回滚,就是通过git reposerver端的配置来完成。

OK,数据采集就说这么多,真的是又臭又长,哈。我们回到系统设计阶段,看还有哪些点值得分享一下。

1. tag的设计。这个要好好跟大伙说说。这个设计灵感来自opentsdb,我们说监控数据有很多,如果对每条监控项都配置报警,是一个很繁重的工作量。那我们能否通过某个手段来对采集项做个聚合,一条配置可以搞定多个监控项?

举个例子,比如某台机器有多个分区:

/

/home

/home/work/data1

/home/work/data2

/home/work/data…

/home/work/data12

我们想配置任何一个分区的磁盘剩余量小于5%就报警,怎么办?上例中,假设我们有12块盘,加上/home分区和/分区,就有14个采集项,写14条策略规则?OMG……

这个时候tag机制就派上用场了,采集到的数据在汇报的时候,每条数据组织成这个样子:

{

"endpoint": "qd-sadev-falcon-graph01.hd",

"metric": "df.bytes.free.percent",

"tags": "fstype=ext4,mount=/home",

"value": 10.2,

"timestamp": 1427204756,

"step": 60,

"counterType": "GAUGE"

}

上面表示qd-sadev-falcon-graph01.hd这个机器的/home分区的磁盘剩余百分比(df.bytes.free.percent

再举个例子

{

"endpoint": "qd-sadev-falcon-graph01.hd",

"metric": "df.bytes.free.percent",

"tags": "fstype=ext4,mount=/home/work/data1",

"value": 10.2,

"timestamp": 1427204756,

"step": 60,

"counterType": "GAUGE"

}

metric(监控项名称)是相同的,只是tag不同,不同的tag表示不同的挂载点。这样一来,我们就可以这么配置策略:说,对于某一批机器而言,df.bytes.free.percent这个采集项,只要value<5就报警。我们没有配置tag,那么这14个挂载点的数据都与这个策略关联,一个策略配置,搞定了14条数据。还有比如网卡也可以用类似的处理方式。

上面的例子还无法完全体现出tag的威力。我们再举个例子,说一个业务监控的例子。小米的好多服务都是java写的,有个团队依据open-falcon提供的接口规范,写了一个通用jar包,所有thrift中间层服务,只要引入这个jar包,就可以自动采集所有rpc接口的调用延迟,于是,产生了N多这样的数据:

{

"endpoint": "qd-sadev-falcon-judge01.hd",

"metric": "latency",

"tags": "department=sadev,project=falcon,module=judge,method=com.mi.falcon.judge.rpc.send",

"value": 10.2,

"timestamp": 1427204756,

"step": 60,

"counterType": "GAUGE"

}

插一句:业务代码中嵌入监控采集逻辑,目前我们的实践来看,真的很好用,嘿

如果我们这么配置:latency/department=sadev all(#2) > 20就报警。就意味着对sadev这个部门的所有rpc接口的latency都做了策略配置。覆盖了N条监控数据。怎么样?有那么点意思吧?

说白了,tag就是一种聚合手段,可以用更少的配置覆盖更多的监控项。

OKtag的设计就讲这么多。下面我们说说模板继承。zabbixhost有一个扁平的group来管理,模板是无法继承的,这个让我们的管理非常不方便。举个例子。

sadev这个部门的所有机器可以放到一个group,配置load.1min大于20报警,sadev这个部门下有很多项目,比如falcon这个项目,falcon这个项目整体使用机器资源比较狠,于是配置的load.1min大于30报警,falcon这个项目下有很多模块,比如graph模块,graph的机器负载更重,正常的load.1min都是35,我们希望load.1min大于40报警。于是问题来了

qd-sadev-falcon-graph01.hd这个机器是graph的一台机器,它应该处于graph这个组,自然也应该处于falcon这个组和sadev这个组,于是,这个机器与三个模板都有绑定关系,load.1min>20的时候报一次警,>30 >40的时候都会报警,这显然不是我们需要的。

模板继承可以解决这个问题,graph.template继承自falcon.templatefalcon.template又继承自sadev.templatesadev.template中配置各种常用监控项,比如cpu.idle < 5报警,df.bytes.free.percent < 5报警,load.1min < 20报警,falcon.templategraph.template中只是load.1min比较特殊,那就只需要对load.1min配置特定的阈值即可。如此一来,graph的机器只有在load.1min>40的时候报警,>20的时候>30的时候都不会报警,大家理解一下。

模板的思考就讲解到这里。下面说说这个机器分组的问题,open-falconzabbix一样,都是使用扁平的分组结构。这种情况有个问题,就是机器的维护不方便,比如上面的例子,我们graph机器要扩容,需要把新扩容的机器加入graph这个组,同时也要加入falcon这个组,加入sadev这个组,比较麻烦。

所以小米内部的实践方式是与内部的机器管理系统相结合,那是一个树状的结构,机器加入graph中,也就自动加入了falcon中,自动加入了sadev中。大家如果要在公司内部引入open-falcon,最好也要做个二次开发,与自己公司的CMDB、服务树、机器管理之类的系统结合一下。

那这里有一个点,好多公司把机器管理、服务树、CMDB这种管理机器的系统做成树状结构,原因何在?部署系统有这个需求么?部署系统的最小部署单位通常是模块,一个模块所在的机器完全可以放到一个扁平的group中。树状结构不是部署系统的需求,而是监控系统的需求,监控系统通常在一个大节点上绑定一些常用的策略配置,然后在小节点上绑定一些特殊的策略配置,相互之间有个继承覆盖关系。好多人不理解这个树状结构的设计初衷,我的理解就是这个样子的,不一定对哈,个人观点而已。

我们继续往下讲:架构设计的习惯性

这个阶段有哪些脑洞历程呢?首先,监控系统的数据量估计是不小,你看一个zabbix都扛不住对吧,哈哈……而且没有高峰低谷,周期性的,每时每刻都有大量数据上来。那第一反应是什么?

没错,上集群!一台机器无论如何都是搞不定的,那就必须要搞多个机器协同工作。

监控系统的本质,在我看来就是采集数据并做处理的一个过程。处理方式最重要的有两种,一个是报警,一个是存储历史数据。这两个处理逻辑差异比较大,报警要求速度很快,尽量在内存里完成;存储历史数据主要是写磁盘,读少写多。

那最直观的思路就是把报警做成一个组件,把数据存储做成另一个组件。先说存储,之前说过,我们内部有个perfcounter系统来存放performance数据,这个系统是用rrd来存放的数据。架构设计的习惯性,让我们再次选择使用rrd来做存储,毕竟rrd是很多监控系统的存储介质,随大流,问题不大。

rrd是本地文件,一台机器的磁盘容量或许可以搞定监控系统的所有数据,但是IO肯定不够,perfcounter当时用了5台机器,全部都是ssdraid10IO还是比较高。所以,要搞多台机器来存放rrd文件,每台机器只处理一部分,压力就小了。

于是,我们习惯性的使用perfcounter采 用的一致性哈希算法,来对数据做分片。数据分片通常有两种方式,一个是使用算法来计算,一个是在中心端记录一个对应关系。监控系统的量会比较大,如果要使 用对应关系,还得维护一个存储,当时我个人其实是主张使用一个存储来存放对应关系的,但是团队其他成员都觉得略麻烦,就放弃了,直接使用一致性哈希来计算 对应关系,架构上显得简单不少。

大家应该知道,使用算法来计算对应规则,会有一个比较麻烦的问题,就是扩容,一旦扩容,一些本来打到老机器的数据就会打到新机器上,虽然一致性哈希的算法会使迁移的量比较少,但是仍然不可避免会有数据迁移。这个问题怎么办呢?

分享一个观点:遇到一个比较棘手的技术问题,如果能在业务层面做规避,将是最省事的。于是我们就想业务上能否接受数据迁移时候的丢失呢?说实话,不太容易接受,能不丢,尽量还是不要丢。

于是我们退一步,扩容的时候,我们不是立马迁移到新机器列表,而是先做一段时间的migrating,比如做一个月。这样一来,扩容之后,对于每个监控项还是会有至少一个月的数据,这样用户是可以接受的。

说完了存储,我们再来说说报警。报警数据要求的历史点数比较少,比如我们通常会配置说某个机器的cpu.idle连续三次达到阈值就报警,这里的“连续三次”就意味着,只要有最近三个点就可以搞定用户这个策略。数据量少,但是访问频繁,因为每个数据上来,都有可能找到其关联的策略,都需要拉取其历史点做判断,这样的场景,没啥说的,用内存吧。

我们把用来处理报警的这个模块称为judgejudge会有很多实例,当时想,某个实例挂了或者重启都不应该影响正常的报警,为了能够水平扩展,尽量做得无状态,那历史数据就不能存在judge的内存里了。redis吧,有名的内存nosql。于是我们就一次性上线了一堆judge实例,前面架设lvs做负载均衡,数据使用redis存储,走起。

稍总结一下,对于server的数据处理,这个属于产品实现细节了,我们因为团队人少,刚开始只有俩人,后来三个,所以力求简单,粗暴的解决问题。数据通过一致性哈希对rrd做一个分片,确实可以大幅缩减开发时间,不过也存在一些问题,大家发现问题在哪里了么?

下面我们先说试用之后立马发现的问题。

当时第一版上线,用了7台机器56redis实例,给judge存放少量历史数据,发现每个redisqps都是4K5K的样子,第一版只是部署了少量agent(具体的量记不清了),结果qps就这么大,当时agent比较少,redis的压力让我们不能接受。

怎么办?大家有思路么?

外部内存的速度如果跟不上,那最直观的想法就是使用进程自身的内存,这里也就是judge组件的内存。那如果用了judge的内存,judge就有状态了,就无法水平扩展了呢。

仔细想想其实问题不大,judge使用内存的话,仍然需要使用算法做分片,既然绘图组件用了一致性哈希,那可以复用一下。同一个监控指标的数据,当前这分钟上来的数据打到某个judge,下一分钟上来的数据,同样应该打到这个judge,一致性哈希可以做到,没问题。

每个judge实例只处理一部分数据,比如50w,放到内存里量也不大,只是GoGC不知道好不好用,如果GoGC不好用,我们就在单机多搞一些judge实例,做进程级隔离,先想好退路,呵呵。

judge是数据驱动型的。就是说,数据来到我这个实例,我就去处理,数据没来,我就不处理,judge前面有个组件叫transfer来做转发,做一致性哈希计算,可以保证数据分发规则,看起来都没啥问题。最惹人烦的是什么呢?是judge重启,大家可以想想judge重启会带来哪些问题

judge一旦重启,就丢掉了历史数据,比如用户配置说某个机器的cpu.idle连续3次达到阈值就报警。前两次都达到阈值了,然后,然后judge重启,这俩点就丢了。judge重启完成,又上来一个达到阈值的新点,已经满足连续3次达到阈值了,但是,却无法报警。如果后面继续有两个点达到阈值那还好,还是会报警,如果后面的点都正常了,那这次本该有的报警就永远都不会报了。

这个问题业务上可以接受么?好吧,我们又来问业务了。首先,judge重启的几率比较小,只有升级的时候才会重启,代码写得注意一些,理论上是不会挂的;再一点,机器一旦出了问题,如果sre不介入处理,通常采集到的点都是持续性达到阈值,所以总是会报出来的,否则基本就可以不用关心。

这么看来,这个改造是可以进行的。于是,代码开始重构,上线之后效果大大好转,处理能力是原来的8倍左右。

本来想单开一节来说说一些折中考虑,发现折中都穿插在前面讲完了,那我们就不再单独讲系统的折中处理了。

时间已经比较久了,其他一些小的点我们就先不讲了,最后我们说说系统的问题以及未来的改造。

最大的问题是什么?相信大家已经想到了,就是每个graph(绘图组件)实例都是单点。如果某一台绘图机器磁盘坏,那这部分数据就丢了。怎么解决呢?

最简单的,硬件解决,我们每个机器都是ssdraid10,稳定性比较高。然后,我们做了一个双写机制,但是现在的绘图机器IO已经比较高了,再搞个双写,势必要加机器,老板最近又在搞什么控制成本云云,就算了吧。监控数据虽然不是丢了会死人那么严重,但是如果不解决仍然让我们如鲠在喉。

于是,我们开始求助于一些分布式存储,我们做数据分片,做双写备份,很大程度上其实就是在解决分布式存储领域的问题,那我们为什么不直接使用分布式存储呢?

opentsdb,嗯,看着还不错,就是用来存放时间序列的数据。我们组一个同事聂安对opentsdb做了一些调研,觉得不能满足需求,两点:1. opentsdb不提供归档,要查看一年的历史数据,opentsdb就真的去把一年的所有点load出来,速度慢不说,浏览器非卡死不可; 2. opentsdbtag数目限制,tag数不能多于8个,但我们在用的过程发现,好多业务数据的tag都是910个,没法减少。

大家看rrd的时候顺便看一下它提供的归档机制,很强大

通过改代码,或许可以调整tag数不能多于8个的限制,但性能可能会急剧下降。另外在很早的时候,同事来炜曾简单尝试过opentsdb,当时只用了10来台opentsdb前端,一打就挂一打就挂,可能是我们机器用的太少,也可能是某些参数没有做调优,时间关系,当时没有深入去研究,但是先入为主的觉得opentsdb不够强。再加上聂安这次调研,我们最终就放弃了opentsdb

小米有好几个hbasecommitter,团队强大,我们现在准备直接使用hbase做存储,云存储团队给我们技术支持,自己做归档,用hbase做后端,实现一个分布式rrd。目前基本开发完成,测试中……如果OK的话,以后就不用担心graph单点数据丢失的问题了。

第二个问题:agent挂了无从知晓

这个问题的本质其实是一个nodata的需求,也就是数据多久没上来应该有个报警机制。开源版本的open-falcon目前没有提供解决方案。应该如何设计这个nodata组件呢?

nodata组件的开源工作应该正在进行中

我们还是不希望改变用户的使用习惯,比如现在每个agent每个周期都会push一个agent.alive=1的数据,用户只要在portal上配置说all(#3) agent.alive != 1就报警。如果没有nodata组件,这个策略是没法工作的,因为agent挂了,不会有agent.alive=0的数据上来。那我们只要写一个组件,在发现没有agent.alive数据的时候就自动push一个agent.alive=0的数据不就可以了么。

O了,这就是nodat组件的工作逻辑,我们可以配置一些关键的指标的default值(当server端发现没有对应数据上来的时候,就自动push一个default值),注意,这里只是配置了一些关键指标,不是所有指标都配置,如果所有指标都配置,工作量不可接受,而且也没必要,比如agent会采集cpu、内存、磁盘、io等很多数据,我们没必要为这些数据都配置default值,只要配置agent.alivedefault值是0,就可以了,就可以监测到agent是否挂掉。

最后一个问题:如何处理集群监控

比如我们有一个集群,挂一两台机器没啥问题,但是挂的机器超过10%就有问题了。另一个例子:我们有个集群,失败请求率小于5%没有问题,大于5%了就要尽快接入处理了。

这种从集群视角去判断阈值的情形,应该如何支持呢?

这个问题如果从简单着手,可以直接做成:对一批机器的某个指标做一个相加。比如某集群有30台机器,要对集群的整体错误率做一个统计,只要每台机器都计算一个本机的错误率:query_fail_rate=query_fail/query_total,然后把所有机器的这个query_fail_rate相加求平均即可。

但是,这样做显然是不太准确,更准确的做法应该是把所有的query_fail相加,把所有的query_total相加,然后做除法。

所以最后可以抽象为一批机器+一个除法表达式。一批机器就是指某个集群的所有机器,一个除法表达式,在上例中,其分子是query_fail,分母是query_total。集群指标聚合的这个组件姑且称之为aggregator,这个aggregator组件得到机器列表和分子、分母之后,就可以去计算了。把每个机器的query_fail查询到并全部相加,把每个机器的所有query_total查询到并全部相加,最后做个除法即可

这种做法是否可以解决集群机器挂的数量达到某个值就报警的需求?没问题的,分子是agent.alive,分母是机器总量,比如30(这个可以动态计算),agent.alive正常来说都是1,如果挂了就是0(我们的nodata组件的成果),比如有3台挂了,分子之和计算出来应该是27,分母是3027/30<90%? 符合阈值就报警。

这种抽象看起来问题不大,最后一点要说明的是,aggregator模块去计算的集群聚合结果应该是每个周期都要计算,计算结果要重新pushfalcon,这样这个集群的聚合结果就可以像一个普通的监控项一样,可以用来绘图,可以用来报警。

OK,已经挺长时间了,整个open-falcon的脑洞历程就讲解这么多,希望对大家有帮助,谢谢大家。

Q1 elk或者stasdgraphite比是否使用方便

A1. 这个方便程度很难讲了,falcon侧重的是做一个监控框架,纳入各种数据,然后做好报警,还有历史数据归档,和elk之类的可能目标不是完全一致,不是一类产品

Q2. 最后说的 集群监控 query_fail_rate 的部分应该也会有 单点问题的吧、怎么规避的呢

A2. aggregator模块的确是会有单点问题的,目前还没有去解决,可以做个选主之类的逻辑,多个实例一起上,但是每次只有一个主在工作

Q3.我想请教一下,open-falcon将大多数监控处理放到了agent,部署升级得关注版本一致性,如果使用snmp协议监控资源,服务端每次采集资源时通过gogoroutine并发采集入库,资源报警和其他程序通过trap反馈给服务端,感觉是否会更方便一些,问的比较弱,见笑了。

A3. snmp没有agent强大,agent作为一个常驻进程运行在机器上,可以干的事情更多一些,而且snmppullserver端的压力会大一些,处理起来略麻烦

Q4.监控在告警时存在误报么、怎么保证每条发送出去的告警都是有用的?

A4. hbs只是用来分发agent要执行的插件、要监控的进程、端口,不做报警相关的处理,这样逻辑结构上更清晰一些:)

Q5. 目前业务层也接入了监控系统,会出现监控系统单点故障导致全局的应用全部不可用吗?

A5. 业务系统中嵌入监控的逻辑,就要很小心了,这个监控逻辑不能阻塞主要业务处理,只是一个旁路,一个单独的线程,监控系统整个挂了,不会影响到各个业务:)

Q6.监控在告警时存在误报么、怎么保证每条发送出去的告警都是有用的?

A6. 误报这个是不存在的,都是按照用户的策略去报警,当然,有的时候用户不知道自己的阈值设置多少合适,随便设置了一个,这种情况报的警可能确实没啥用处

Q7agent端数据采集点有多少,性能压力是怎样的?

A7. agent在所有目标机器上部署,比如有1w台机器,就要部署1wagent,每个agent大约采集200各监控项,现在小米的falcon应用情况是每个周期5000w左右的数据上来,有几十台监控server端协同工作

Q8jduge中的transfer做转发,这个是单点处理么?如果挂了怎么办?

A8. transfer只是一个转发组件,是无状态的,transfer挂个一两台没有问题

Q9、看到agent运行脚本的时候,采集的是stdout的数据,stderr的数据采集么?如果不小心采集了二进制数据,是怎么处理?不小心调试的时候采集数据量比较大,阻塞了网络的情况有么?

A9. 只采集stdout是一种约定,需要汇报的数据写入stdout,脚本运行出错了写入stderr,如果采集的脚本不能decode成要求的数据格式,直接扔掉。目前没有出现调试的时候数据量大阻塞网络的情况:)

Q10agent是打包到你们的镜像文件中,系统安装完后自动启动,然后上报么?如果是这样,那么什么时候填写的服务器地址?

A10. 我们的系统由系统组统一装机,然后统一初始化,初始化的时候安装一些基础组件,agent就是在这个时机安装,之后各个业务线再做针对自己业务的初始化工作:)

Q11. 有没有相应的监控报表之类的?是否可以对接kibana

A11. 现在基本没有报表性质的东西,只有趋势图,针对各个监控项的历史趋势图。哦,还有一个未恢复的报警列表,各个产品线工程师可以在下班的时候看一眼这个未恢复的报警列表,看是否有忘记处理的。其他就没了,不懂kibana:)

Q12 如此多的监控项和图表如何展示,如何快速找到需要的东西

A12. 一般我们要看什么数据是有很强目的性的,比如某个服务挂了,你可能要看这个服务所在机器的情况,于是就可以拿着机器名之类的去搜索,也可以根据metric(监控项)去搜索,等等。也可以提前做screen,把经常要看的图表放在一个页面

Q13. 是否有监控到异常,然后自动对服务器进行后续操作的?

A13 有的,异常了之后通常的处理是报警,也可以回调一个业务的接口,把相关的参数都传递个这个接口,用户就可以在这个接口中写自己的业务逻辑,比如去重启某个机器

 

posted @ 2015-11-04 17:38  leoncfor  阅读(12215)  评论(0编辑  收藏  举报