作者:高铭谦,腾讯云高级工程师
背景
当我们在笔记本电脑、平板电脑和智能手机上运行应用程序时,很容易看到设备是否已开机,Wi-Fi 网络连接是否在线。当磁盘空间不足发出警告时,我们也可以从屏幕中获得通知。当我们能直接与设备交互的时候,我们能很清楚知道设备的运行情况。
假如当你作为一个技术客服,解决来自客户投诉的终端问题的时候,你无法看到具体错误,也无法与设备直接交互,这样会极大增加检测和诊断问题的难度。因此,当运行基于云的服务时,我们会面临一个类似的难题:如何监控这些远程服务,如何了解我们的客户是否满意?
以前我们观察和检查单主机服务的时候,可以登录该主机,运行各种不同的运行时监控工具,并检查日志以确定主机上所发生情况的根本原因。然而,单主机解决方案仅适用于最简单的非关键性服务。另一个极端是多层分布式微服务,它们运行在数百个或数千个服务器、容器或无服务器环境中,当问题发生时,你是否会有足够的时间处理单主机服务的问题,逐一排查呢?
我们需要一个监控 & 运维体系,清楚知道我们云上的服务,在不同地区,不同可用区上运行的所有基于云的服务的实际行为表现。自动化监控、自动化修复工作流程(例如,流量转移)和自动化部署系统对于检测和解决这种规模的绝大多数问题至关重要。然而,在实际情况中,我们仍然需要能够随时了解这些服务、工作流程和自动化部署在做什么。
监控体系的发展史
记得在 17 年前,Win XP 上,运行的第一个 pascal 代码是计算几何面积,在没有复杂的业务逻辑中,点击工具的编译,就能把我们需要的所有信息都打印在命令行界面中。我称之为第一代的监控体系,一个简单的编译器和一个简单的命令行界面。3 年后,开始用 Delphin 编写软件的时候,相对复杂的各个组件和逻辑代码,简单的编译已经不能告诉你程序到底出了什么问题,后来我们有了打断点调试,逐行代码检查,甚至会 print () 一下输出结果。我称之为第二代的监控体系。
事实上,前两代的监控体系并不能称之为监控,因为信息输出到屏幕后,刷新之后就会马上丢失,所以我们并不知道程序在运行时行为和信息,因为那时候也并不需要,连接断开了,就让用户重新连接;程序报错了,就让用户重新打开。到了第三代监控体系,中国互联网时代已经开始进入分布式系统时代,工程师们开始依赖日志信息去发掘服务报错的蛛丝马迹,在第三代监控体系初期,大部分工程师都习惯登录服务器去看系统日志和业务日志,分析系统报错的根本原因。到了后期,随着部署的服务器节点数量变多,逐一登录排查成为运维的瓶颈,后来就有了统一日志管理。将不同节点的日志收集到日志管理服务器上,方便工程师们排查问题。再后来,业界上基于这个时代的监控体系下,衍生出了不同的监控产品,比如当时非常有名的 sumo logic,将日志统一管理后提供了各种数据清洗和 BI 的能力,这个概念也奠定了下一代监控体系的基础。
从前面可以看到,监控体系的改变其实是随着需求以及数据量层级的增长而发生变化的。上一个时代是大数据时代,所有就有了以日志管理 + 基础 BI 能力为基础的监控体系。当下的时代是后大数据时代,也是大数据爆炸时代,我们需要处理的数据远高于上一个时代。AWS 的监控系统每分钟需要处理 PB 级别的指标数据,在这么庞大的数据量里,监控系统如何能将有用的目标信息快速给到工程师,是下一代监控体系的核心目标。
围绕核心目标,我们需要思考以下几个问题:
01
从指标里我们可以得到什么?
指标通常指基础指标 + 自定义指标。基础指标指的是服务器的系统指标,包括 CPU, RAM, Disk I/O, 硬盘空间以及网络相关指标。自定义指标通常是业务系统根据实际情况上报的,通常包括端与端之间的耗时(Latency),请求数,请求错误数,缓存命中率,消息队列 Ingress & Egress 的数量,消费速率,积压请求数量等可以指示服务间状态变化的指标。这些指标是可以直观的将服务之间当前的一个运作情况,直观地反馈给用户(监控者),就像手机信号不好你能看到信号指标变弱;手机电量快不足的时候,你能看到电量指示还剩多少,并不需要等手机提示你 “死亡 30 秒” 的时候才去充电。
试想一下,手机没有信号指示和电量指示,信号差的时候你要打几次电话验证过才知道信号可能变差了,或者等待手机提示 “死亡 30 秒” 的时候,才知道需要去充电。
在云计算服务中也是同样的道理,指标的作用就是让我们的服务,不需要线上 “试一下”,或者等到突然出现大量超时,才去看到系统指标某一项是否快满了,再去决定扩容。云计算里的指标,就像手机里的系统指示,你能清楚知道你整套系统目前处于什么样的一个状态,而不需要牺牲用户去验证。
02
自定义指标如何呈现?
自定义指标,指的是上面所说的耗时,请求数,缓存命中率等可以指示服务间状态变化的指标。对于云服务而言,如何将自定义指标有效的呈现出来,并且成为整套监控运维体系中最关键、最基础的一部分呢?
首先我们的指标一定是建立在有意义,样本数充足并且具有时效性的基础上提取的,比如耗时这个指标,从服务上游的维度切入去提取这个耗时是非常有价值的,比方说下图:
这个是某业务服务的平均耗时,这个图表能告诉我们,这个服务的平均耗时大约在 200~250ms 中浮动。
又比如说,请求数指标从网关层切入,可以清楚看出请求量的 Top N 的服务:
相比于日志信息,自定义指标正确的呈现方式,可以让云服务使用者 / 维护者 / 管理者更直观、更高效的得出结论,而非每次需要的时候,依赖某一个时段的日志信息,聚合后得出一个精确度不高的数据,然后再根据数据推算前后时间的大概值。当然对于统计学基本功好的同学,也能通过分布模型大致推算出某时间段的一个指标数据,比如请求量或者耗时,但也需要大量的数学运算,消耗大量的时间。当我们需要快速获取相对精确且有效的目标信息的时候,将自定义指标可视化可以使得整个工作效率得到质的提升。
再比如当我们需要做一个简单的可用性统计的时候,通常我们可以粗略的用当前时间段的 $ 成功请求数 \over 总请求数 $ 得出可用性。例如:
从上面的例子我们可以总结出一下,一个自定义指标正确的呈现方式有以下几点建议:
03
当指标数据很大的时候,如何排除干扰信息?
针对这个问题,我会从两个方面去解析:
1)从指标自身维度;
2)从服务以及业务自身维度。
首先我们先讨论第一个方面 - 从指标自身维度。理想情况下每秒一个请求(样本)的时候,时序视图上的曲线能相对精确地反映当前的指标情况;但实际上非常小概率服务的 QPS 稳定在 1 这个水平,指标的处理就涉及到聚合。通常而言,在上一代监控体系中就一直沿用的现在的统计方式 - 平均值,可以满足大部分的业务需求。例如 QPS 是 100 的服务,我需要对耗时指标进行每秒的统计,那么在指标处理的时候,就会将这一秒内产生的约 100 个数据点进行聚合并且平均运算,甚至会进行加权平均(对于一些 outlier 数据点,将权值下调)。最后得出的数值就是这一秒展示的数据样本的数值。当然,每秒进行一次统计,这样对整个数据清洗管道的压力是非常大的,所以通常会根据整个数据清洗的服务容量去制定一个相对安全的聚合周期 (Period)。
目前业界上大部分的聚合周期,最细大约在 5 ~ 10 秒 (AWS 在很早之前就已经可将这个粒度优化到 1 秒了)。
但对于上述情况,对于耗时、缓存命中率这类可以直观反映出服务质量的指标,当样本数很大或者很小的时候,简单的样本平均值或者简单的样本值是不能满足直观反映服务质量的诉求的。样本平均值,只能片面的反映出业务的平均水平,拿耗时这个指标作为例子看,平均耗时只能反映出通常情况下,服务的一个耗时情况,却无法反映出不同 SLA 下的服务质量;样本值可以将每一个数值反映出来,但会造成混沌数据,比如说在时序数据里,会产生大量的干扰,对运维和评估没有任何价值。
所以对于时序数据而言,不同的数学统计方式可以更宏观的反映出当前服务的具体情况,比方说百分位(percentile)是一个非常有价值而且常用的统计方式,其中 P99.9, P99, P90, 和 P50(中位数),分别反映出在不同 SLA 的一个耗时(单个指标对比),反映出大约百分之多少的请求落在高耗时的区间(多个指标对比)。
通常百分位可以更准确的反映数据样本的分布规律,而平均数,最大最小值,只能反映出数据在数值上的聚合统计,并未正确且直观的反映出数据样本的规律。为了方便理解百分位与平均数的之间的关系,引用网上的一个数据统计软件的统计结果图片,直观的反映出百分位与普通数值运算的区别。
关于对指标采集如何对百分位进行聚合以及百分位数据点二次聚合的详细算法和实现,可移步 《HyperLogLog in Presto: A significantly faster way to handle cardinality estimation》
https://engineering.fb.com/2018/12/13/data-infrastructure/hyperloglog/
为了更好理解上述例子,下面用了同一个服务,在不同的数据可视化的方式下进行对比。
普通数值统计(最大值,最小值,平均值或者样本值的呈现方式)
从上图,我们可以猜到上述可能每一个请求的耗时数值或者每个跨度的样本平均值的时序图,我们可以由上图得出结论是,在 2020-11-08 和 2020-11-09 两天,平均耗时大约在 1 秒左右,在不同时段有小幅度波动。
结论是,过去两天该服务的耗时在 1 秒左右,出现小幅度波动,可能跟请求大小有关。
百分位统计(P99.9, P99, P90, P50)
从上图,我们可以解读到,有一半的请求,在约 1.49 秒内就成功返回,有 90% 的请求,在约 2.08 秒内返回。另外可以看出在这个时间段,有 99% 的请求是在 6.76 秒内返回的,跟 P90 相比,约 9% 的请求收到影响,耗时上升至最高 6.76 秒。初步可判断服务质量出现异常。同时看到 P99.9 在 9.22 秒内完成一个来回,由于我们设置的是 9 秒超时,所以我们可以判断,有约 0.9% 的请求直接返回超时(相比于 P99)。
上面数据可以看出,P50 和 P90 未出现明显突刺;但 P99 和 P99.9 出现了明显的耗时增加,甚至有 0.9% 的请求出现超时,所以可以初步判断部分服务出现异常导致耗时增加,可以考虑从 1)服务器过载;2)坏节点等原因入手排查。
从以上前后对比,可以非常直观的感受同样的数据样本,在不同的可视化模式下带来的信息采集效率的差异。前者在出现服务质量下滑的时候,仅表现出了小波动,并且反映出服务质量异常的信息;后者仅 4 条线,就能马上定位服务异常的大致情况。在线上事故的时候,时间就是金钱,排除干扰信息,迅速定位问题,才是监控系统的核心功能。
综上,关于回答如何排除指标的干扰信息这个问题,可以参考以下几个建议:
04
当监控的维度很多的时候,如何快速定位目标信息?
当监控维度很多的时候,如何快速定位目标信息,取决于监控面板所展示的信息是否有足够价值,以及运维的时候你所需要的信息。我们建立监控面板的时候应该分为以下两大类型:
宏观监控面板
微观监控面板
所以快速定位目标信息,不仅需要从以上控制面板的分类去建立整个服务系统的控制面板,还依赖于服务上报的指标以及监控的通用性,制作时序图或者控制面板的时候,尽量从全局的维度去考虑,并且留有可过滤或者处理的余地。比如下面是基于云 API 建立的一套从网关维度的监控面板。
图中的 y 轴线,在耗时的视图中,可以看到有一个明显的突刺,从全链路和后端服务链路都出现了一个突刺,但可用性并未显示下降,所以当 Ninja 看到这个突刺的时候,可以马上推断出这里大概率是因为传入的是一个大图。通过这条有指向性的信息,Ninja 可以通过后端服务的监控面板,查看接入层同一个时间段里,请求包体大小是否也存在一个突刺,假如存在突刺,则证实了刚才的推断;假如并没存在突刺,则猜测是后端服务这这个时段某一个依赖服务出现问题,可能是机器负载升高,可能是网络波动,可能是消息积压,甚至更极端的死锁超时杯释放。这些监控所需要的面板,都依赖于业务上报的自定义指标。
假如上述例子还不足够精确地回答 “如何快速定位信息”,那么换个简单的例子去理解,还是上面的监控面板图,产品突然有诉求说看下最近某客户的调用量以及他们的服务质量如何,那么这里只需要通过 uin 筛选客户,监控面板就会实际反映出该客户的调用量以及服务的耗时百分位情况。
虽然上述例子比较抽象,但实际项目中是可以参考一下几个建议去建立属于你们的监控面板的:
05
如何做到自动化监控?
其实这个问题大家很好理解,而且也清楚利用告警系统提示,所以这里我不做冗余的解释,直接进入主题 “如何高效的监控”。监控的本质是让我们实时地知道服务的健康情况,但我们不可能 24 * 7 不间断地看着监控面板,直到发现异常。所以最基本的方式是通过告警,告诉我们服务出现异常了。但对于监控的灵敏度需要如何去拿捏,甚至整个链路应该如何去收敛,是下一代监控体系中需要体现出来的。
假设目前我们有一个人脸融合的业务,大致逻辑是,计费校验 → 从 DB 获取素材信息 → 限频校验 → 下载图片 → 人脸识别 → 人脸融合 (+ 鉴证服务) → 更新限频缓存 → 更新 DB 信息 → 计费上报。假设我们在每一个步骤都埋点上报指标并且建立监控和告警,当某些非关键路径告警,比如鉴证服务,但短时间内并不会造成链路的失败,这时候问题是,是否要通知工程师或者是否要设立这个告警呢?当只出现单个告警的时候,其实问题的答案大家都很容易回答。但假如限制同时出现下载图片告警和鉴证服务告警 (该链路是并行的)以及他们依赖的 cos 也进行了请求相关的指标埋点,那么对于最上游的网关层而言,只会报警可用性下降,所以这里链路就存在两个可能性,下载代理服务或者鉴证服务有问题。到这里,很多工程师可能就会马上跑去看日志,查找问题了。但假设告警拥有链路染色的功能会是怎么样?对于瞬时的下载图片告警,或者内部服务的一些能够自愈的异常告警,并没有直接影响可用性,我可以直接做一个简单的邮件提醒,只对影响可用性的告警进行电话通知。当我发现问题的时候,根据告警的链路,找到产生问题的根本原因,比方说上面的例子,通过告警链路最后找到 COS 的请求异常,直接通过告警设置,通知 COS 相关团队。
以上我们就可以对告警进行收敛,降低无效的告警提示。比如触发告警后,系统通过收敛判断是 COS 异常是因为 COS 扩容的节点没有对我们服务下发脚本添加白名单,此时系统通过告警判断后自动触发脚本,添加白名单,全程并不需要人工接入。这类告警可以降级为更低严重级的提醒。关于如何做自动化监控以及运维这个话题,就不在这里继续展开了,有兴趣的同学可以去关注 AIOps 相关领域的最新论文或者 Google 的《Site Reliability Engineering》。
06
日志其实是用来做什么的?
日志是我们发现服务健康情况的重要信息来源,从上一个时代,工程师们都是依赖日志去发现服务异常、解决服务 Bug 和优化服务质量的。在现在这个时代,仍有大量的工程师在使用日志的方式去运维服务,但已经有部分工程师已经开始慢慢从日志发现过度到指标监控来做线上运维了。因为指标监控从采集效率上以及数据精度上都比日志的方式要高效以及精确,指标对于日志而言,在采集的过程中就已经把非结构数据转换成结构数据了,在整个数据源采集上报、清洗聚合、存储和可视化的过程中,都比日志的方式高效精确的多。所以线上信息的实时性也得以大幅度的提高。
但为什么我们仍需要日志呢?有以下几个方面的原因:
总的来说,日志跟指标是相辅相成的,指标是在日志需要提取关键信息的基础上衍生出的一种新的打点方式,但指标关注的是我们可以预见的情况是否出现异常,日志关注的是我们无法预见的异常情况并且记录下来。正确使用日志和指标监控,可以为线上运维带来极大的便利。
07
结合这套监控体系,如何开展运维工作,将被动运维变成主动运维?
上述的监控体系,是整套运维体系的核心基础。那我们建立了这套监控体系后如何去利用好它,打造一个高效、智能化的运维体系,将被动运维变成主动运维呢?
假如把监控体系比作 “道”,那么运维体系就是 “术”。所以这部分讲的是结合 aws 监控团队的运维标准与腾讯目前资源综合的一个运维方案。
首先我们要知道如何高效利用监控体系。假设现在半夜出现线上问题,可用性从 5 个 9 下降到 3 个 9 了,现在首要目标是快速恢复服务止损。
传统方式:
到这里这一系列步骤是不是大家都经历过?当我们熟悉业务的时候,也许我们加快上述流程,快速定位问题。但假设当这个 API 后端的服务足够庞大和复杂的时候或者将这个运维的工作交给其他不熟悉业务的工程师做,这样时间成本上就会极大的提高。线上事故的首要目标是快速止损而不是解决问题,当 Oncall 且没有其他熟悉业务的工程师在身边的时候,花上大量时间去定位问题,甚至最后可能服务已经恢复了,问题还没找到。这些场景是否很熟悉?
下一代运维体系的模式:
从宏观上去对比,传统方式是从微观到宏观去解决问题。而新的运维体系方式,是从宏观到微观去解决问题。
要从被动运维转变成主动运维,不是单靠线上发现问题或者多写几行业务代码就能实现的。在这套运维体系里,需要主动发现问题,优化服务并且尽可能保证服务具备自愈能力。在这里建议从几大方面着手:
总结与展望
下一代监控运维体系不仅仅是切换一个监控系统,更是一个知识的改革,一个书本上仍未记载的知识体系,需要靠大家共同去建立和维护。提高服务质量,降低运维成本和优化机器资源都在新的监控运维体系中得以展示。对工程师而言,全栈工程师不仅仅是前端加后台,在云计算领域里面,全栈还代表了运维能力,资源调度能力以及架构能力。在新的监控运维体系下,服务的质量和健康情况得以量化,从而将服务质量优化真实的反映在数据和面板上。将重复机械的运维工作自动化,智能化,才能高效的打造出明星级的产品。
----------------------------------------------------
References:
John O'Shea, Principal Engineer at Amazon Web Services. "Building dashboards for operational visibility" .
欢迎联系云监控小助手微信号,加群讨论:)