文章概览
作者介绍
钱芳园,十多年DB相关的运维和开发经验,目前关注于数据库平台的自动化开发。
一、引言
线上的告警系统运行多年来一直很稳定,但是也曾出现过一些问题。数据库告警指标明明已经很高了,但是却没有告警出来,有些指标告警出来了,但是却排查不到什么问题,甚至监控图上都没有什么变化。如果修改告警间隔以及告警次数可能会造成误报和漏报,针对这种情况,需要系统性梳理当前告警系统存在的风险,有针对性地解决这些问题。
线上数据库、服务器信息等各种指标的采集也分散在各个地方,没有纳入统一的管理。这些指标的采集需要进行详细的梳理,并统一进行管理,为后续其他的系统的使用打好基础。
本文主要关注的是数据库(MySQL、Redis)方面的监控,针对其他方面的监控也略有涉及。监控的原理是相同的,只是在细节方面有所不同,不同的告警系统在设计上可以有所参考。
二、名词解释
collectd:一个定期收集系统和应用程序性能指标的守护进程,并提供以多种方式存储这些值的机制。
nagios:一个开源监控系统,可以监控整个 IT 基础设施,确保系统、应用程序、服务和业务流程正常运行。
nrpe:nagios插件,nrpe允许nagios远程执行各种命令或插件。
prometheus:一个基于云原生的开源系统监控,以时间序列数据的形式收集和存储指标。
exporter:prometheus的数据采集端,需要运行在被采集的服务器上,将采集到的监控数据转化为prometheus能识别的格式,prometheus会定时从exporter上拉取数据。
三、现状与痛点:旧系统存在的瓶颈
3.1 现状
监控告警对线上数据库是必须的,通过采集线上服务的关键指标,设定关键阈值可以确保数据库出现异常时,通知到相关的人员,尽快解决问题,避免因为数据库异常对业务造成影响。
当前去哪儿大部分数据库并没有上云,依然采用实体机的部署模式,大部分都是一机多实例。去哪儿数据库监控系统是基于开源的collectd采集系统和nagios告警系统定制的。
当前数据库指标采集系统架构:
每个数据库服务器上部署一套collectd服务,collectd服务通过定时采集数据库的状态信息,将信息发送给collectd server。collectd server再将数据分类存储到监控数据库中,通过web页面查询数据库监控指标详情。
当前数据库监控告警系统架构:
每个数据库服务器上部署一套nrpe服务,nagios server会定时向nrpe服务发起检测请求,nrpe执行检查脚本,并将结果返回给nagios server。nagios server通过判断之后将需要告警的信息发送给alert服务,alert服务会做二次判断,并补充一些元数据信息,将告警信息发送给相关的负责人。
当前两个系统在大部分情况下都能满足需求,但是随着后面需求不断变更,有些需求没有办法得到满足,并且有些问题也逐渐暴露出来。
3.2 痛点
目前数据库指标采集系统存在一些问题:
1、collectd的push模式可能会出现数据断点且较难排查问题根源。从collectd端采集数据,经由网络层发送给collectd server,collectd server再将数据发送到存储层。collectd采用的是udp协议,udp协议并不保证数据准确到达。由于数据包经过链路的节点较多,断点多为偶发的情况,较难以排查到中间链路是什么节点由于什么原因导致的。
2、采集的指标较少,目前仅有基础的数据库的指标信息和服务器指标信息,缺少高级别的数据库监控指标。
3、存储层监控数据缺乏必要的元数据信息。collectd server收到推送的数据之后,直接将数据推送到存储层。此时应该需要补充一些元数据信息,这些元数据信息对聚合高层次数据至关重要,这导致目前无法做部分高层次维度的聚合。
而数据库告警系统在当前场景下也存在着比较明显的问题:
1、监控粒度较粗,监控系统仅支持分钟级别的监控,无法满足细粒度的探测要求。
2、采集间隔为瞬时值,可能在部分场景下无法探测到告警信息。nagios server向nrpe发起探测请求时,nrpe会执行指定的脚本,对于数据采集有两种情况:
对于即时状态是这样采集数据:
stat = collect()
if stat > threshold:
alert()
对于非即时状态,需要计算差值会是这样采集数据:
stat1 = collect()
sleep X
stat2 = collect()
if (stat2 - stat1)/X > threshold:
alert()
一般情况下X会远小于采集间隔,这种针对非即时状态的采集在部分场景情况下,可能会造成漏报:
如上图,0-10、60-70、120-130均为采集区间,采集区间内并没有超出阈值,但是在其他区间内超出阈值。采集区间与告警区间可能刚好存在非重叠的状态,这就导致当前指标本来应该告警,但是没有告警,造成漏报。
3、每次下发新的监控需要重启agent(这个问题是系统架构的问题,并非不可解决,但是解决成本偏高)。
四、技术选型:从Push到Pull模式
针对两个系统的问题,我们需要详细的分析下指标采集系统和监控告警系统的模式。首先分析指标采集系统:
指标采集系统的数据模式大致分为两大类,push和pull模式,每种模式都有各自的优势和缺点。
push模式即推送模式,agent定时采集数据,agent将采集好的数据推送到server,server将数据进行聚合之后推送到存储层;
pull模式即拉取模式,server定时向指定的agent发起采集数据需求,agent采集完成返回server,server再将聚合好之后的数据推送到存储层;
push模式的优势:
push模式的劣势:
pull模式的优势:
pull模式的劣势:
通过对比两种模式,我们选择使用pull模式,可以友好的添加标签,减少数据断点的概率。
其次分析一下监控告警系统:
告警的数据来源可以是指标采集系统,也可以是其他系统,需要关注点在于告警采集的频率与指标采集系统采集的频率是否相同。通常情况下告警采集的数据会与阈值进行比较,仅将有超过阈值的告警数据进行存储,其他的将会丢弃,故告警系统一般情况下不会考虑数据存储的大小。但是指标采集系统需要将每次采集的数据都存储下来,采集间隔越小,系统的存储成本将会成倍的增长(暂时不考虑数据压缩带来的节省)。为了确保告警的及时性,也为了不对指标采集系统增加带来更多的负担,告警系统将自行采集数据。
Qunar业务线对故障要求“1-5-10”,即一分钟发现问题,五分钟定位问题,十分钟解决问题,这也对告警系统提出更高的要求。告警采集数据的频率较指标采集系统高,较小的频率可以尽快的反应数据库的状态。在数据库和告警系统可以承受范围内,提高告警采集的频率,确保可以及早判定数据库存在问题,及时解决问题,避免影响业务。
五、新系统的目标
根据公司对告警系统的“1-5-10”故障响应体系的要求,也为了能够满足业务和DBA对数据库的告警要求,制定如下目标:
数据库指标采集系统 :
数据库监控告警系统:
六、全面采集系统:分组和分级采集
指标采集系统最重要的是确认采集的范围和频率,采集的范围需要全面,且不能遗漏,否则可能会对之后的分析不利。
6.1 采集范围
针对一套服务需要采集的指标范围大致可以划分为如下需求:
一个数据库服务通常是以进程形式存在的,并提供端口进行访问。服务内部信息是指一个进程内部的状态指标,通常需要连接到数据库,进入到进程内部获取信息。服务外部指标是指进程占用的各种资源,包括cpu、内存、io、网络、存储等,需要从进程外部获取的信息。所以除了采集服务内部指标,也需要采集服务外部指标。举例如下:
针对MySQL数据库需要采集的内部指标,包括:Thread和Client,Instance、DB和Table的状态,事务,SQL请求和SQL指纹、InnoDBBufferPool和MyIsAM Cache、InnoDB引擎和MyISAM引擎,Redo和Binlog,Galera(PXC架构)和Replication(复制)等指标。
针对MySQL数据库需要采集的外部指标,包括:进程占用的CPU、Memory、IO、Network、Schedule等相关的指标以及MySQL自身占用的存储,包括Instance、DB、Table级别的文件信息。
针对操作系统需要采集的内部指标包括:CPU、Memory、Disk、Network、System等相关的指标。
针对操作系统需要采集的外部指标包括:Hardware、交换机、网卡链路等相关的指标。
所以需要采集的范围包括:数据库(MySQL/Redis)、操作系统。
6.2 采集频率
确认了采集的范围,下面需要确认采集的频率。指标按照变化频率来分可以大致分为三类:
stat类型:即时状态,一般情况下不同的时间点,数据是不同的,当前数值有明确的含义,需要即时存储,对应Prometheus的Gauge类型,例如当前时间点线程的数量、内存使用量、数据库复制延迟多少秒;
util类型:非即时状态,大部分情况下需要做计算才能表示出明确的含义,对应Prometheus的Counter、Histogram、Summary类型,例如CPU利用率是两个时间点之差、网卡流量是两个时间点之差;
info类型:信息类型,一般情况下数据不会变化,只需要存储最新的一份数据就可以,例如数据库的版本、配置信息、服务器对应交换机接口。
需要注意的是具体指标上,有些指标的状态如果使用的不多,通常情况下大部分变化不大,可以不需要实时存储,分别按照小时、天、周级别分别存储即可。
针对不同的数据存储频率的要求,采用合适的存储,可以最大化节省存储空间。根据以上需求设计了如下数据库监控采集系统架构:
高频指标采集业内有成熟的监控采集架构,这里采用的是Prometheus监控存储方案。通过对Prometheus进行改造,定制化exporter,支持个性化的数据采集。在每个服务器上都部署一套exporter服务,针对不同级别的服务,Prometheus可以按照不同的时间间隔定时拉取指标,将指标聚合完成之后添加上标签,最后存储到监控存储上。
低频指标采集采用的是自定义的方案,Server定时向agent发起采集请求,将数据汇总之后添加标签并写入到数据库中。
监控采集系统的目标是在合适的成本内,尽可能多的采集各种需要数据。针对指标关联上标签,为后面做聚合、分析、预测、规划等提供重要的依据支撑。
七、智能告警策略:减少误报和漏报
针对告警系统,更应该关注的不是告警的次数,反而是告警的真实性和有效性、及时性。告警漏报和误报就像两个极端,既要避免漏报,也需要保证减少误报,新的告警系统需要尽可能满足这两点需求。
告警其实就是告警阈值*触发次数的关系。告警系统的阈值是最关键的,如何设定一个合理的阈值是充满考验的。阈值较高,则可能会问题发现不及时从而造成业务损失,导致漏报。如果阈值较低,那么会造成较多的误报,带来过多的干扰,影响正常的工作。
7.1 计算阈值
即时状态计算阈值的逻辑是这样:
stat = collect()
if stat > threshold:
alert()
针对非即时状态的计算,计算的时间间隔一定是采集间隔,即第二次采集的数据减去第一次采集的数据时间之差为采集间隔,这样就不会造成漏报。非即时状态的计算逻辑为:
last_time = now()
last = collect()
...
current_time= now()
current = collect()
if (current - last)/(current_time - last_time) > threshold:
alert()
其中current_time - last_time为采集间隔。
7.2 告警阈值
一般情况下业务的请求会有波峰和波谷,以及定时任务、促销和不定时的操作等造成的干扰,通常情况下告警的阈值是一个固定值,这其实是不合理的。以MySQL进程占用的cpu核数为例:
每天0点之后逐渐走向低峰期,最低点在凌晨4点左右,之后逐步开始走出低峰期,大约在8点有定时任务直到12点,然后下午逐步走入高峰期,晚上高峰持续,23点之后就逐渐走低。其中8:00到12:00左右是因为业务的定时任务造成的压力,且定时任务不可以取消,业务期望尽快执行完成。
根据此图,如果设置阈值的话,一般情况设置为5是比较合适的,但是排除定时任务的导致的CPU增长,这个阈值相比业务正常需求要高太多。
为了精准告警,可以将报警阈值设置为如下格式:
通过在不同的时间段设置不同的阈值,避免统一阈值,这样既避免业务在非特殊时间因为高阈值导致告警漏报,也可以避免业务因为特殊时期的阈值较低导致频繁误报。
7.3 触发策略
通过针对每个时间段设置不同的阈值可以最大化的减少误报,但是也可能存在这种情况,业务请求的频率也并非是恒定的,可能会存在瞬时值较高,导致偶发的超出阈值,但是又不能设置较高的阈值。这种情况目前在针对即时状态下检测的误报频率较高,目前只有通过增加告警阈值的触发次数,减少误报的频率。所以告警的检测时长就会变成检测间隔*触发次数,这无形之中会降低告警的及时性。为了尽可能满足公司“1-5-10”故障响应体系的要求,提高告警的敏感性,需要尽早确认状态是否存在问题。
这里可以考虑使用动态检测频率。比如数据库线程的running状态,表示当前有多少线程在运行。如果告警系统检测到业务瞬间造成请求瞬时值会比较高,会自动缩减下次检测间隔。下次检测时,如果检测结果依旧触发阈值,那就表示确实需要告警,反之则表示只是偶发的请求,可以忽略或者记录下来。
场景如下:设置检测间隔为10s,触发次数为3次,
固定监测频率
那么如果采用固定检测频率,则告警检测时长为10s * 3 = 30s。
动态监测频率
如果采用动态监测频率,在第一次监测到告警之后将检测间隔自动缩短为5s,那么将在第30s时发送告警,检测时长为20s。
采用动态检测频率,告警时长缩短为20s,缩短了三分之一。通过引入动态检测频率,相比固定的检测区间使告警具备很好的及时性。但是也可能会遇到以下这种情况:
这种情况常见于指标在阈值范围波动,可能是阈值不合理,也可能是状态有问题。如果是阈值不合理可以通过定期的巡检,分析,跟踪报告,提供更合理的指标。如果是后面这种情况,那应该如何避免?
可以采用探测结果聚合法,即针对连续M个检测结果,如果其中N个结果超过阈值,且最近一个结果超过阈值那应该告警。举例来说,如果M=3,N=2,以上图为例,在第35s时刻的计算逻辑为:
检测区间10-20超出阈值,20-25正常,25-35超出阈值,那么最近的三次检测中有两次超出阈值,且最近一次超出阈值,那么在第35s时刻就应该告警。
还有一种情况是预期之内的负载增加,比如:服务器上开始跑备份任务造成IO增加、运营操作增加促销导致Redis内存临时增长、业务发布需要临时加载一批数据等,如果是因为这种操作导致负载适当增加,即指标超过阈值但是在可以承受的范围之内,那这种告警不是必须的。这种是需要多系统做联动,告警系统检测到有指定的操作时,需要临时修改告警指标并设定时效,可以尽可能避免误报。
7.4 告警自愈
没有告警才是最好的告警,因为没有人想要业务出现问题。如果告警不可避免,那应该减少告警的次数。如果告警之后,系统可以自动处理,那就没有必要告警。所以告警不是目标,自愈才是目标,业务无问题是终极目标。
告警出现问题之后,需要相关负责人去做相关的检查,排查服务是否出现问题。目前存在两种情况:其一是为业务触发告警阈值的时间范围较短,当负责人去检查指标的时候,发现指标已经恢复,其二是部分告警可能是重复出现,暂时无法彻底恢复,但是又不能直接屏蔽。针对这种情况,系统需要自动采集相关的指标相关信息,单纯的指标告警阈值并不具备任何可分析的性,需要下钻到具体的指标详情。
以下列举部分指标的下钻操作和后续动作:
目前自愈模块收到告警之后,会根据不同的指标执行下钻操作,采集指标相关的详情信息,并根据下钻的结果生成相应的动作。但是目前在执行动作这里,除了可以指定的操作会自动执行,其他操作都需要人工进行确认,才可以执行相应的操作。
根据以上设计,数据库告警系统的架构设计如下:
Monitor会根据不同的级别设定,采集不同服务的监控指标,根据不同时间段采用不同的告警阈值和触发策略。一旦触发告警,Alert会及时通知对应的负责人,Heal模块收到告警的时候会自动下钻指标,并执行既定的操作,在页面上查询告警时会自动关联分析之后的结果。
7.5 其他
7.5.1 告警收敛
为了避免告警风暴,当前告警收敛是基于实例维度进行聚合,即当实例有相同类型的告警时,会自动收敛到同一个实例的告警下,只做简单的提示,不做强提醒。告警页面也可以设置收敛时间,在收敛时间内,只做统计,不再提示。后续计划在分析阶段自动判定告警的收敛范围,当一个告警触发时,自愈阶段会自动分析出后续告警需要收敛的范围,判断出是指标、实例、集群、服务器、交换机还是机房级别。不同的级别会收敛对应级别的指标,可以大幅度缩减告警风暴带来的影响,负责人可以专注处理告警,不再被告警风暴所打扰。
7.5.2 AI监控
目前告警层面并没有过多涉及AI,最多的只是一些算法规则上的优化。其实在告警的流程中,AI不应该有过多的涉及,因为当前的AI在处理速度上并没有提供秒级别的处理能力。但是在自愈分析阶段,如果目前现有的规则无法处理时,可以考虑使用AI提供后续的处理建议,但是依然需要人工进行判断,人工执行。
7.5.3 其他类型
告警的原理都是相同的,新的告警系统具备插件式的功能,通过自定义探测方式,可以轻松扩展到其他类型监控,例如web请求探测、端口存活探测、进程是否存在、主机存活探测,以及其他类型的数据库监控等。
八、新系统成效:部分结果展示
示例一:深入细节定位问题SQL
MySQL执行SQL的QPS监控
MySQL执行SQL的耗时监控
这是MySQL SQL维度的监控。深入到SQL维度的监控可以清楚的看到在某个时间点对数据库造成压力最大SQL是哪个,是因为SQL的请求量上涨导致的还是SQL的索引错误导致的,可以更迅速的定位具体的原因,为SQL优化提供依据。
示例二:定位进程负载原因和请求量预估
MySQL集群QPS监控
MySQL集群TPS监控
MySQL进程占用的CPU
MySQL进程占用的读IO大小
MySQL进程占用的写IO大小
这是MySQL集群和进程维度的监控。通过对集群维度和进程维度的监控,可以看到对服务器压力最大的是哪个MySQL进程(亦或者是其他进程),并根据请求类型判定出是读还是写导致的,也可以根据请求量和进程占用的资源,可以大致评估出当前服务器可以承载的请求量上限。
例如当前集群单节点请求量QPS峰值约为4.5k,cpu负载为2.8核,如果当前服务器有32核,2核作为保留给系统和其他服务使用,cpu使用率最大为80%,那么在理想情况下(只考虑读以及cpu使用率)当前单节点可以承载的峰值QPS为30*80%/2.8*2.5k≈21w QPS。
示例三:资源容量评估,提前规划服务器
服务器数据盘容量评估报告
MySQL集群实例、库、表的数据采集
这是MySQL实例、库、表维度的数据采集。从【服务器数据盘容量评估报告】图中可以看出当前服务器的数据盘容量可以支撑多长时间。从【MySQL实例、库、表的数据采集】图中可以看出最需要注意的是哪个库表,增量主要来源于哪些库表,由此为依据,尽早对资源做规划。
示例四:主机和网络监控,排查抖动原因
主机CPU和网卡流量维度的监控
主机ping丢包率监控
Redis集群QPS和连接数监控
这是主机和网络方面的监控。从主机方面的监控可以看出在21:33时刻cpu和网卡波动不是很明显,但是某个业务的Redis QPS和连接数波动很大。通过对当前服务器从不同的IDC采集的丢包率监控,可以看出四条线路里面有三条发生了丢包,由此可以得出21:33是网络抖动导致的业务请求出现抖动。
示例五:个性化定制标签,高纬度的汇总趋势图
部门Redis请求的QPS汇总
部门Redis使用的cpu核数汇总
部门Redis使用的内存汇总
部门下Redis使用的内存Top
通过个性化标签可以定制高纬度的汇总信息,这是部门维度的Redis监控。通过定制化展示部门下的Redis/MySQL/服务器等各个维度资源的使用量和增长趋势,可以根据使用情况和变化趋势提前规划资源的使用。由图中可以看出当前部门下的Redis请求量和cpu使用率均保持稳定,增幅不明显但是内存使用量在逐步增长。从部门下Redis【使用的内存Top集群】图中可以诊断出是哪个集群的容量在逐步增长导致的,并据此采取治理措施。
九、总结与展望
新的采集系统采集的数据更加完整,并带有高级别的标签,通过标签进行聚合可以看到更高级别的数据,为后续进行巡检、分析、报告提供了支撑。监控系统通过优化探测流程,支持插件化的功能,可以精准发现服务问题,即时通知负责人介入处理,尽可能满足公司“1-5-10”故障响应体系的要求。
作者丨去哪儿DBA团队
来源丨公众号:Qunar技术沙龙(ID:QunarTL)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn