庖丁解牛(一):监控系统

庖丁解牛(一):监控系统

auxtenauxten
好朋友“雪糕”是前Baidu的高工,当年我们一起参与构建了一个庞大的运维自动化系统Noah。转载一些他的关于监控系统的感悟,我也深有同感。

我们在后来也用Python写了个简易版:51reboot/rebootMon-4 · GitHub

最近借着出去分享的机会,画了张简化的监控系统架构图:

写在前面

我从事运维自动化相关的工作,也已经8年了。当初刚开始做的时候,运维开发(devops)这词还不火。很少人知道。国内对运维的理解,也就是机房、服务器、苦逼的7*24小时值班。甚至当时还流传着段子,招运维要人高马大,扛得动服务器。

很幸运的主导了两个一线互联网公司的公司级的运维自动化。这俩公司的服务器,都是几十万台级的,IDC都是几十个。很多事情都是摸索着来。一路走来也形成了一些对运维自动化的理解。第一家公司好容易做的七七八八的时候,到第二家实施发现原有的很多理解都被打烂了重新来。但这种经历反而凝练了一些经验。我个人性格原因,不喜欢到外面去讲。偶尔实在没法推辞过去的,也诚惶诚恐的准备,到最后发现很多干货还是没办法一言以蔽之。最后留在外面的都是一些只言片语,不成系统,更不敢说能指导多少。

终于下定决心要把我个人的一些理解,通过系列文章来写一写。文笔有限,可能写的不尽人意。也欢迎大家加入运维开发讨论交流群来交流,群号 365534424

废话少说,直接上文。

我始终认为监控对于运维来说,犹如眼睛对人来说一样重要。不管说的多么高大上,运维工作里面很大一部分还是响应型的工作。来自于架构调整、服务变更或者来自于监控。说监控是整个运维或者服务生命周期里面最重要的一环都不为过。从事前发现,预警或者故障报警,到事后提供监控现场供回溯追查,监控系统贯穿了运维整个环节。怎么才能有一副好视力,今天我们就来稍微谈谈。

正因为监控系统如此重要和通用,所以业内最成熟、最多的产品也是监控系统。商用的、开源的监控系统比比皆是。也有一些很优秀的开源系统应用很广泛。比如Zabbix、cacti、nagios、ganglia等。对于使用开源系统,还是自己开发(相信看这个文章的,应该都不会有购买监控系统的打算),是使用者自己决定的。业务和团队规模都不足够的时候,直接拿来主义,开源的系统能解决基本问题,性价比高。业务如果后期发展的好,规模快速扩大,复杂度迅速增加的情况下,开源系统就难以为继了。表现在时效性、扩展性、二次开发、支持的服务规模(或者叫系统容量)、良好的权限控制等各方面。

从上面几点来看,一个监控系统需要具备的是数据采集、扩展性、告警管理、高可用、历史数据存储与展示、权限管理等几个方面。

监控本质上就是对被监控对象的状态进行判定。这个监控对象可以是服务器、交换机,也可以是一个几千台服务器的集群,还可以是带宽、CPU利用率,甚至深入到服务内部,监控服务内部的进程、线程、cache命中率等。

监控对象多种多样,既有实体的,也有虚拟的。 被监控对象的状态进行判定,这句话里面有三个要素。被监控对象、状态、判定。所以监控系统要能够适配足够多的监控对象类型、收集并能转换为可衡量的状态值,才能支持下一步的判定动作。例如,一台服务器上的nginx服务的连接数。服务器上的nginx服务,就是被监控的对象;连接数就是被监控对象的指标,那么状态呢?我们可以定义为,超过1万就是不正常,否则是正常。

从这个角度做框,我们来看看监控系统的核心指标都有哪些。首先是能监控的对象范围要越多越好(当然你可以说小而精的也挺美。但维护多套监控系统也是代价)。也就是数据采集,能采集的渠道、支持的方式、采集的指标越多越好。

关于扩展性的定义

可伸缩性(可扩展性)是一种对软件系统计算处理能力的设计指标,高可伸缩性代表一种弹性,在系统扩展成长过程中,软件能够保证旺盛的生命力,通过很少的改动甚至只是硬件设备的添置,就能实现整个系统处理能力的线性增长,实现高吞吐量和低延迟高性能。

   可伸缩性和纯粹性能调优有本质区别, 可伸缩性是高性能、低成本和可维护性等诸多因素的综合考量和平衡,可伸缩性讲究平滑线性的性能提升,更侧重于系统的水平伸缩,通过廉价的服务器实现分布式 计算;而普通性能优化只是单台机器的性能指标优化。他们共同点都是根据应用系统特点在吞吐量和延迟之间进行一个侧重选择,当然水平伸缩分区后会带来CAP定理约束。


可扩展与过度设计的矛盾

具体讨论到监控系统的可扩展性,我们这里特指系统可以随着被监控对象的规模扩大而无需对架构做大的变更修改。一千台服务器的时候,是这个架构,一万台服务器的时候还是这个架构,最好十万台的时候只需要增加服务器就可以,架构还是那个架构。听起来很棒吧。这也是每个系统架构设计者的梦想。但现实照进理想的时候,发现理想很残酷。首先是对于设计者来说,当他在一家只有几百台服务器规模的公司时,很难去想到自己的系统可能有一天会在跑在几万台的服务器规模上。这里面也有一个架构设计里面的原则,就是要尽量避免过度设计。如果一个几百台服务器规模的公司的运维开发,对他的老板说要做一个系统可以支撑几万台服务器,但因此要多花了多少时间去架构和重构,我想老板会认为自己的运维开发一定是疯了。架构原则之一也是要尽量避免过度设计。

但软件设计依然还是推崇良好的架构、良好的可扩展性。否则架构设计的价值就会打很大的折扣,代码复用和系统实现成本会随着规模的扩大而线性增长。良好的架构可以通过迭代得出之后,反过来指导低阶的系统设计。但低阶的无法预测高阶的。这就是架构的用处之一。所以这里稍后我会介绍一下我对于监控系统架构的一些经验和心得。

一个称职的设计者,是可以站在几百台服务器的规模时,考虑到几千台的情况的。但他考虑几万台的话就有点过度了。这里并非说能支撑几万台的系统架构不优秀。只是如果他不知道,那也没必要过度考虑。如果能提前知道,甄嬛就会说,那显然是极好的。

但很显然需要考虑的内容会越来越多。几十台的时候你可能只需要考虑一个机房了,几百台的时候会有2、3个机房,当几千台的时候可能依然在10个IDC以内,但当几万台的时候很可能已经超过15个IDC了。而且地理位置的分布会更广泛,由此而带来的运营商的覆盖、网络的复杂度、业务的复杂度也会完全不同。非逼着一个运维开发去完全臆想着来做是不现实的。他没有经历过实际的这种需求场景,是没有办法考虑到这样那样的各种问题的。所以我完全理解一些大公司对于开源项目的态度。好一点的可能拿来改改用,进一步可能单独拉一个分支开始改,更甚的就改的完全和主干不一样了 ,其实还有就是自己造轮子的。但有时候就是这样,自己不造轮子,开源的轮子用着的确不好使。

监控的可扩展性

具体到监控系统,可扩展性体现在哪些方面呢?我们从头捋一下。监控系统的输入是监控到的各项监控数据。这些数据经过一系列的处理,最终存储下来用于事后分析和离线分析,同时更主要的作用是要实时的报警。整个这个过程我们可以视为是一个流式计算的过程。说到流式计算其实大家想到的是storm这些。这倒是另外一个我曾经想过的思路,就是把所有处理过程放到strom上去。balabalabala.... 说远了。但我们仔细去看,strom也好,流式计算平台也罢,都是分布式的。分布式架构的一个特性就是良好的扩展性。随着服务器规模的扩大,对于中间的数据处理层的可扩展性要求,就是计算能力要能具备扩展性。简单来说就是数据多了,通过加服务器或者升级服务器就能搞定。

还有几个边界的地方需要把扩展性支持好。第一个就是入口。或者叫做数据的接收口。外面的数据源源不断的进入,如果要想做到扩展性良好,第一个需要考虑的就是接收环节。数据可以走TCP、UDP、SNMP、HTTP等多种协议进入到监控系统。考虑到数万服务器的规模,这个地方比较考验技术底子。如果走SNMP、HTTP当然可以,但这两个协议都走在应用层,必然会带来额外的开销。拿HTTP举例子,我们拿Nginx或者apache做server,其实天然带有可扩展性。数据收到以后,存到一个存储即可(不管这个存储是缓存还是永久存储)。这个过程,不带有状态,所以天然具有可扩展性。一个Nginx实例扛不住了,再来一个,再来一个,再来十个。这样就解决了接口的可扩展问题。

另外一个可扩展是存储环节。这个存储主要是监控数据的持久化存储。前面我们说,数据接收、计算环节都可以通过一些方式支持可扩展。那存储必然会成为一个瓶颈。这个在很多系统里面都是这样,前端可以通过Web Server实现可扩展,但最终大家都跑到一个数据库上读写。哪怕是读写分离的,还是一个主库。主库压力山大。

这个地方我推荐用一些分布式存储来解决这个问题。但不是很推荐mango这种比较奇葩的。因为写入的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。

综上,对于可扩展性,我们的思路是:分布式、无状态。

我借用一个高可用性的定义: 高可用性H.A.(High Availability)指的是通过尽量缩短因日常维护操作(计划)和突发的系统崩溃(非计划)所导致的停机时间,以提高系统和应用的可用性。它与被认为是不间断操作的容错技术有所不同。HA系统是目前企业防止核心计算机系统因故障停机的最有效手段。

那么高可用,就是高可用性良好的系统。多少算高呢,我以前呆过的BAT某公司喜欢用小数点以后几个9来衡量。当然小数点前面默认就是99。大家普遍认为4个9是还不错的,5个9是核心业务应该具备的。怎么计算呢,这个我后面也可以说一说算法。

对于运维工程师来说,要是能运维一个可用性非常高的系统,我想是一件幸事。系统能高可用,运维处理报警的优先级就不用那么高了。大冬天晚上收个报警,也不用立刻从被窝里面爬起来连VPN处理了。可用性越高,运维工程师睡觉越安心。


影响高可用的因素与计算方式

影响可用性的因素都有哪些?让我们来捋捋。一个服务分为软件、硬件。拿一个网站来解解。

假设有个网站,域名是Reboot教育 - 高效你的学习。部署了一个nginx,部署在一台服务器上,这台服务器在一个叫做zw的电信机房。

用户从浏览器输入Reboot教育 - 高效你的学习开始,直到他在浏览器上能打开这个网页内容为止,有哪些步骤?

第一步,域名解析

第二步,向着服务器发起HTTP请求

第三步,请求走网络,到达服务器所在机房的交换机

第四步,数据走几层交换机之后,到达服务器网卡

第五步,网卡数据经过OS,到达Nginx

第六步,Nginx收到HTTP请求

第七步,Nginx调用ThinkPHP框架(这里假定是这个框架)

第八步,PHP连接Mysql数据库获取数据

第九步,PHP处理数据

第十步,Nginx返回数据到用户端

第十一步,用户端浏览器完整接收数据之后,渲染完毕

粗略的分,11个步骤。这里涉及:DNS、服务器、交换机、OS、Nginx、ThinkPHP、PHP、Mysql

服务器又可以分为:磁盘,以及其它部件如CPU、内存。之所以这样分,是因为磁盘作为存储部件是最容易坏的一个部件。

好了,我们接下来算一算,51reboot这个网站的可用性是多少。一次可用,相当于上面的十一个步骤都得正常才叫可用。那么就要考虑了,DNS的可用性是多少,服务器不宕机(可用性)是多少、Nginx之类的软件的可用性是多少、Mysql、磁盘的可用性是多少、网络的可用性是多少等等。这些可用性的乘积,就是该网站的可用性。

这里还没有考虑更多实际情况。比如,mysql如果和web端不在同一台机器上、甚至不在同一个机房,或者部署的nginx实例不止1个,等等。

上面我们讲了可用性怎么计算。下面我们来看看监控系统的高可用。


先定性,再定量

这是我的原则,事情都是先定性,看是否有必要,再定量,看需要定到多少,具体量化。监控系统本身是监控别的服务和系统是否正常运行的。如果监控系统自身可用性不足,会严重影响监控效果。甚至可以说就是没有什么用处,有大隐患。

这里还涉及了另外一个问题,就是监控系统自身的监控。以后会开篇幅来讲。

前面说了,监控系统本身必须高可用。那下面我们就看这个高可用需要怎么量化。读者觉得应该达到小数点之后几个9?我个人觉得至少2个9.也就是99.99%。否则业务部门的兄弟们急了,因为他们的系统如果要求是99.999%,没人能证明啊。监控系统本身才是99.99%。

如何达到高可用99.99%

这是今天这篇要讨论的另外一个核心问题了。要达到高可用性,还要看系统的架构。按照有没有单点来区分,系统有两类,一类是有单点的架构,一类是没有单点的架构。讲到这里我们有必要说一下什么叫做单点。单点简单来说就是系统里面的某个部位,它在系统里面部署的时候,是一个唯一存在。这个唯一存在不能扩展性的部署一个兄弟实例出来。这种唯一存在将来就最有可能是系统里面的老大难,因为它出了问题,没有兄弟能顶上。

但,我又要说但是了。没有单点,不代表说完全百分之百的没有问题。例如,我们采用Hash的办法,负载均衡的方式,部署了2个Nginx实例。当其中一个实例挂掉的时候,仅影响了50%的请求。不能算整个系统挂掉了。但这个系统的稳定性依然堪忧,我们不能说它的可用性有多高。 如果Hash计算能够结合了实例的健康状态,不健康的自动从hash计算的池子里面摘掉,那可用性就大大提升了。

综上,以及综合我们上一篇,监控系统要想达到高可用,必须要采用去中心化的架构来做。就是让整个系统里面没有任何一个环节是单点。因为单点就意味着瓶颈,意味着可用性提升很难很复杂,不容易做高。

具体说说,怎么才能去单点。我们从监控系统本身的数据流来分析。

数据采集,这个要分类来说。一个是带内的,跑在OS上的代理Agent。这个的高可用,是另外一个领域的事情,就是怎么写一个高可用的、鲁棒性非常好的客户端。我们以后分开篇幅说。

另外是带外的。比如,HTTP或者端口监控,或者存活监控。我们拿存活监控来举例吧。比如说,我们通过ping的方式,来监控服务器是否存活。那么我们需要一个批量发ping包的探测器。这个也是一个数据采集端。只是没有在OS上来采集。当然它也存在鲁棒性的问题,但这是另外一个领域的事情,我们这一篇不谈。这个客户端,如果它挂了,可想而知,被它监控的服务器都失去监控了。所以我们要提高Ping监控模块或者叫环节的可用性。一个最简单办法,我们用两个监控点来监控同一批服务器。但新的问题又来了,俩监控点监控同一台服务器,什么情况下可以断定这个服务器挂了呢?这个是另外一个监控数据合并的问题,我们也放到以后的篇章里面去讨论。 另外一个提高可用性的办法,就是ping监控点部署两个,但两个之间不要同时生效,但两个节点之间有心跳,一个挂了,另外一个接管,也是一个办法,但切换略复杂。

数据采集回来了,要做处理。这个处理或者叫计算环节的高可用,有不少现成的办法。第一,部署两个计算实例,但两个实例需要能互备或者同时发挥作用。第二,纯分布式办法。

存储和Web的分布式方案就更多了。

还有一个最关键的地方,如何实现无状态。只有无状态了,才能达到简单的部署切换,就可以支撑高可用。这个问题我们留待之后来讲。

架构这个词太大了,这里我们缩小一下,只来谈谈宏观的监控系统整体架构。在这个范围里面,web由于负责统一的系统管理和操作功能,缩减为一个模块。

最简单的架构如下图


这是监控系统第一层的架构。比照百度地图的话,我们可以认为这个是全国地图。最粗粒度的几个模块就是这三个。web、数据采集、数据处理。


PUSH PULL

我们先来关注数据采集模块到数据处理和报警模块的这个环节

推和拉,技术选型里面常常遇到的一个选择题。 在Client/server结构中,信息获取方式是按“拉”(Pull)的模型进行的:服务器根据用户终端发送的服务请求进行处理并返回用户所需 的结果。在Push模型中,服务器把信息“推”给Client。虽然两者数据传输的方向都是从服务器流向Client,但操作的发起者是不同的。从“信源”与“用户”的关系来看,信息的流动可分为两种模式,即信息推送与信息拉取模式。

两种模型的对比见表格


其中PUSH的好处是及时性好。但缺点是服务端要有比较复杂的状态管理。同时在到达率等方面都会有一些纠结的地方。而PULL的好处则是服务端简单,状态管理简单,但缺点是时效性上不可控。体现在监控系统上,如果所有要监控的监控项,都是需要Server端PUSH给Client,假设Client所在服务器关机了,那PUSH的时候就是不可达的。Server端就得想办法记录下来,并且再做重试等失败处理。而如果是Client端主动来PULL就好办了,服务器开机启动之后,Client立刻来拉取。到达率肯定要好,对Server的管理也简化了。但缺点就是想生效一个监控项,只能等着Client来PULl,而无法立即生效。

这里还有一个比较经典的例子,也是我面试别人的时候总喜欢问的一个问题。当然我问面试者的时候主要是想去看看TA的逻辑思维能力。

题目:微博大家都用过。里面你可以关注一个人,也可以被人关注。当你发一条微博时,关注你的人都会收到一条提示。当你关注的人发一条微博时,你会收到一条提示。 请问这个提示,是PUSH 还是 PULL到你的微博客户端(浏览器或者手机微博)上的?

面试者:肯定会有人说,PUSH呗。

面试官:OK,然后我就会问了,姚晨在新浪微博上的粉丝数是5000多万,她发一条微博,是不是得PUSH 5000多万个消息到各个账号去?

面试者:额,那就是定时PULL

面试官:确定吗?几千万个客户端都PULL?

面试者:额。。。 面试者开始额头黑线了。

面试官:请问该怎么办?

PUSH的话,姚晨的一条微博,在系统里面就要产生5000万条消息要处理。如果她一天发个100条,估计新浪微博疯了。这还没有考虑很多客户端不登陆,消息就得缓存着。还有很多客户端一下子通知不到,还得处理失败。

PULL的话,如果大量用户在使用的生产系统,对存储和缓存是一个很大的挑战。

具体的,大家可以再去google一下,这个事情其实有很多方案。

经验比较丰富的研发一定会同意我的一个说法:两个争论不休的技术方案,最终能达成一个融合了二者的第三个方案。就好像两个特别对立的谈判方,到最后谈判结果是一个融合或者叫妥协的方案。PUSH和PULL也可以二者融合,将做到取长补短,使二者优势互补。根据推、拉结合顺序及结合方式的差异,又分以下四种不同推拉模式:

  • 先推后拉——先由服务端PUSH,再由Client端有针对性地拉;

  • 先拉后推——根据Client端PULL的信息,服务端进一步主动PUSH与之相关的信息;

  • 推中有拉——在数据推送过程中,允许Client随时中断并PULL更有针对性的信息;

  • 拉中有推——根据Client端PULL的过程,Server主动推送相关的最新信息


几个开源监控系统的PUSH PULL选择

zabbix : 带agent方式。agent主动推送数据到服务端。 从client的角度看,是PUSH数据到Server

Cacti : SNMP协议,无Client,或者说Client是SNMP Client. 从Client角度看,是PULL

ganglia: 从Client角度看,是PUSH

在我过去生产环境所构造的监控系统里面,我们采用了PUSH 和PULL结合的方式来达到及时性、到达率的同时解决。我们站在Client的角度来描述这个解决方案。对于监控项的生效,web端变更之后立即使用PUSH的方式来通知Client。但这里一定有达到率的问题。比如Client所在服务器死机了、重启了、当时网络有问题不可达了等等。所以我们在Client端,支持定时PULL。定时去主动联系Server端,获取自己应该生效的监控内容。


HASH

怎么突然又说到HASH了呢。 HASH先来个概念普及吧。看完概念还是不了解的同学,自行面壁去,你计算机数据结构一定没好好学。

我说HASH是因为要为后面介绍高可用性架构有关系的。

HASH你别直接拿去搜,用百度的结果就是哈士奇。

关键词可以是哈希。

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

Hash在算法里面是很基础但使用非常广泛的。特别是在大数据量的情况下。

我这里强调Hash,是想说它的一个作用之一就是散列。把输入散列到几个地方去。提到Hash不得不提一个词叫做一致性Hash,这个算法对于解决缓存命中率有很大好处。在内存缓存、CDN等存储系统中经常使用。

Hash的精髓之一就是按照某种计算规则,把输入散列到不同的输出通道上去。


无状态和有状态

我们拿无状态协议来体验一下无状态是个什么概念。

协议的状态是指下一次传输可以“记住”这次传输信息的能力。典型的如HTTP协议是不会为了下一次连接而维护这次连接所传输的信息,

由于Web服务器要面对很多浏览器的并发访问,为了提高Web服务器对并发访问的处理能力,在设计HTTP协议时规定Web服务器发送HTTP应答报文和文档时,不保存发出请求的Web浏览器进程的任何状态信息。这有可能出现一个浏览器在短短几秒之内两次访问同一对象 时,服务器进程不会因为已经给它发过应答报文而不接受第二期服务请求。由于Web服务器不保存发送请求的Web浏览器进程的任何信息,因此HTTP协议属 于无状态协议(Stateless Protocol)。


监控系统里面的HASH和状态

监控系统对数据的处理,主要是过滤异常数据出来并报警。比如某个服务器的CPU利用率超过了95%,需要报警。但这个时候突然数据处理模块所在服务器宕机了。那么这个异常数据很有可能就丢掉了。

监控系统常见的报警条件是: CPU利用率超过95%,算一次异常。如果5分钟内有3次异常,报警给运维。

这里就有几个数字需要处理,5分钟,3次。前面提到的宕机,会导致一次异常数据丢掉了。假设5分钟内出现了3次,丢掉了一次,那自然不会报警出来。这就是一个有状态的场景。

有状态的情况下,做自动切换或者负载均衡,需要把状态也带过去才行。

比较典型的还有session的问题。如果web是多台主机负载均衡的时候,session存本地是会出问题的。因为用户有可能通过负载均衡的调度,多次请求落在不同的主机上。 本来HTTP协议是无状态的,支持负载均衡的调度。但因为session这个有状态的产物,必须要把session放在公共存储上才行。

结合前面提到的那个架构图。数据进入到了数据计算和报警模块。我们如何保证这个数据计算和报警模块是个高可用的架构。

答案是,把输入的监控数据Hash到不同的数据计算和报警模块实例上去,并且最好是无状态或者弱状态的计算过程。


本文原载于: Reboot运维开发 。作者书面授权发表,拒绝任何形式的转载。

欢迎大家加入运维开发讨论交流群来交流,群号 365534424,本文仅授权51reboot、51cto、知乎auxten专栏 上发布。

「真诚赞赏,手留余香」
还没有人赞赏,快来当第一个赞赏的人吧!
文章被以下专栏收录
14 条评论