25 24 | 监控设计,让一切都有迹可循,尽在掌控

发布时间 2023-04-20 14:35:52作者: 程序杰杰

你好,我是乔新亮。

这一讲,我想和你聊聊如何做好监控设计。

你可能会想,为什么要聊监控呢?做监控不是很简单吗?

所有做技术的同学,基本都会根据公司的日志规范,在代码中打印 Log ,以记录告警和报错。许多企业,也会将日志收集分析,以此形成对系统状态的监控。如果条件允许,团队还可以使用各类免费或付费的服务器监控报警服务,多方便啊,这有啥好讲的呢?

其实在我眼里,这些都只是构成了监控的一部分,并非完整的监控体系。要想深刻理解监控的概念,我们首先要学会问自己:为什么要做监控系统?这就像许多工作方法论里强调的一样,做事先问目的 —— “start with why”。

监控的目标是及时发现系统的问题,并尽可能快地做出相应的动作,让系统一直处于健康的状态。监控,可以拆分为“监”和“控”分别理解,其含义恰好对应着两种主要手段,也就是“监视”和“控制”(中文真是博大精深)。

比如说,生产环境出现了个 bug,怎样定位问题?这需要做好“监视”;发现问题根源后如何正确响应?这需要做好“控制”。

比较无奈的情况是,研发人员知道系统出了问题,但无法定位问题;更悲催的情况是,研发人员能够定位问题,但无法控制问题,只能眼睁睁地看着故障发生。但无论哪种情况发生了,从结果来看,其实区别都不大。唯一的区别可能是,前者至少让人心存希望,后者则让人感觉整颗心都“冰凉冰凉”的。

说到这里,我不禁有点感慨:监控、监控,意味着既要监视也要控制,没有控制的监视达不到目标,没有监视的控制无法形成行动计划。

从业至今,我曾处理过许多因认知不足,而导致的系统监控问题。下面,我们就选取其中一件比较典型的案例,尝试着一同复盘下。

技术团队的“怪现象”

这个案例发生于某日下班后 —— 大约是在傍晚 20:00 左右,团队发现了一个出现在生产环境的异常。该异常来自一个可以连接众多终端设备的程序,其具体表现为:所在服务器的 CPU 负载突然飙升。但发现异常后,团队既无法将其恢复,也无法定位问题。

发现问题后,共有十几位工程师先后参与了问题分析。很快,两个小时就过去了,监控系统仍然显示 CPU 占用异常,问题还是没有解决。这下,产品负责人着急了,向我求助,并将我拉进了问题调查组的电话会议。

进入会议后,我立刻问了大家一个问题:

“最近线上有什么版本变动吗?都回退了吗?”

团队回答道:“都回退了。”

我几乎没有任何犹豫地说道,不可能,一定没有全部回退。

要知道,计算机是非常可靠的。如果一套代码在一台机器上可以正常、稳定地运行,那么在外部环境没有发生任何变更的情况下,一周、一个月、一年后,它大概率能继续保持正常、稳定地运行。

你可能会想,老乔又开始信口胡说了,难道就没有意外情况吗?

有,我也确实遇见过,但在近 20 年职业生涯里,我只遇见过一次(ZooKeeper 因虚拟机连接数过多而崩溃事件,在“高可用设计”部分,我们曾简单提到)。所以,这种情况几乎是“可遇不可求”的,在大部分时间里都可以直接忽略。

果然,在我的追问下,负责发布的同学说,系统没有回退到上一个版本,而是按研发同学的指挥选择性回退的。

好呀,真相大白,我命令他迅速按公司研发制度回退。

没成想,回退到上一版本后,系统依然不正常。于是,团队继续查看前端、服务端、数据库等各类监控数据,试图分析问题到底出在哪里。

我再一次找到团队,问道,真的都回退了吗?

团队可怜兮兮地回答,真的都回退了!

我的回答是,不可能,鬼才信你们…… 于是,我开始继续不依不饶地追问。

终于,在我的“威逼利诱”下,有位同学一拍脑门,犹豫地说道:“我在数据库里加了条索引,不过这肯定不会导致负载异常……”

还没说完,我就哭笑不得地打断了他的话,谈这些做什么?赶紧回退去。

当这个改动回退后,一切都恢复了正常。

最终,一个本该三分钟内被搞定的生产问题,用了几个小时才解决。幸亏发布窗口是业务低峰期,否则已酿成重大损失。在许多公司里,相关人员几乎一定会受到处罚。

事后复盘时,我问团队,当我询问版本有无全部回退时,为何不及时声明?

那个同学委屈地说,我以为那条索引不可能惹出这么大的问题……

请注意,在这个故事里,当事人员并不是毫无经验的新手;公司并非没有监控系统,相反还做到了可视化、图形化。

既然如此,为何团队还会被一条小小的索引命令阻滞这么久呢?这是一个非常奇怪的现象。而且,在参加 GTLC 全球技术领导力峰会时,我在和一些同学的沟通中也发现:这种现象并非孤例,很多团队都曾出现过类似的情况。

于是,我不得不去思考,IT 团队的系统监控体系,到底出了什么问题?

IT 团队的系统监控体系,到底出了什么问题?

当陷入思考的泥潭时,我们往往需要将思维抽离,尝试站在更高维度探寻问题的本质。此时,我们不妨将这个案例暂且放一放,重新思考一下监控的本质含义。

在文章开头,我们讲过了,监控是为了让系统一直处于健康状态,具体的手段包括“监视”和“控制”两种。简单来说,就是当生产环境出现问题时,我们要能知道哪里出了问题,并具备相应的控制手段。

生产环境应急恢复的最大挑战在于根因分析,即找到问题的根本原因,这往往是耗时最久的工作。

但如果没有控制手段,那么即使找到了根因,团队也是无能为力,只能干着急,或者静静地期盼奇迹出现,好像系统在下一分钟就能自动恢复健康。

所以,当生产环境发生异常时,大部分团队会这样组织故障恢复工作:

  1. 发现问题后,立即联系各相关系统负责人,以便共同排查问题;
  2. 要求大家在一分钟之内回复:自己治下的系统或服务是否健康(这里要将“健康”的定义想清楚,如,响应时间是否增加超过 30% 等);
  3. 进行根因分析,确认导致问题的系统、服务;
  4. 完成系统恢复工作。

对于步骤 1、2 ,其实挑战都不大,真正的挑战在于步骤 3、4。步骤 3 依赖于相关分析人员的专业程度。一般情况下,随着企业业务的复杂度逐渐增加、系统和服务的数量逐渐增加,人员规模也会越来越大,在步骤 3 上花费的时间就会脱离控制。

此外,对于步骤 3 中涉及的不健康组件,我们需要分析:这种不健康状态是原因,还是结果。关于分析方法,其实也比较简单:

  1. 首先,我们要确认异常是外因导致,还是内因导致。比如,服务响应慢,既可能是因为外部调用量变大,也可能是因为内部进程繁忙,导致 I/O 、内存、网络资源发生争抢。这步判断相对来说比较耗时间,只有当调查足够充分时,结果才可能浮出水面;
  2. 无论是内因还是外因,都要“顺藤摸瓜”,继续进行排查,最后进行恢复。

说到这里,我们不难得出结论:生产应急保障体系的建设,并不像大家想象的那么简单。从系统复杂度到人员专业性,任何不足都会导致问题定位、根因分析花费更多的时间。因此,以上故障恢复流程也存在很大的隐患。

那么,难道生产系统出了问题,我们就只能坐以待毙吗?有没有一个对团队专业度依赖较低的方法呢?答案是,有的。我把它总结为:流控和版本回退,简单、粗暴、实用

流控,就是做好程序的并发流量控制;版本回退,就是在生产环境的发布出现问题时,及时回退到上一个版本。

生产环境出现问题,原因通常只有两个字:变化。常见的“变化”大致有三类:

  1. 外部用户请求量增大;
  2. 产品发布,一般包括代码发布、配置发布、SQL 脚本发布等;
  3. 依赖资源变化,一般是计算、存储、网络基础设施情况变差,比如磁盘存在坏道等。

这样看来,当故障发生时,我们不一定要组织团队进行复杂的根因分析 —— 那是故障恢复后的事儿。相反,只要我们控制住了服务的近期变化,也就等于控制住了故障。思路一变,好像就豁然开朗了。所以, 我们不妨将生产应急恢复方法修改为以下 4 条:

  1. 发现问题后,立即联系各相关系统负责人,以便共同排查问题;
  2. 要求大家在一分钟之内回复:自己治下的系统或服务是否健康(这里要将“健康”的定义想清楚,如,响应时间是否增加超过 30% 等);
  3. 此处组织两批研发力量,并行工作。第一批解决专业问题,继续跟进问题的定位和调试;第二批负责消灭变化,对有变化的模块进行回退,对于外部请求数量升高的模块启动流控;
  4. 恢复系统。

你看,这样就降低了故障恢复对于团队专业性的要求。只要我们保证,对于任何组件,都有以下两种手段同时存在:

  1. 流控手段;
  2. 发布回退手段。

是不是很简单?在 IT 行业,对于专业团队而言,一般没有太过复杂的工作;如果工作过于复杂,往往是因为负责人不够专业。还是那句话:大道至简。

问题的关键在于,生产环境是不允许查找 bug 的。在生产环境,研发人员应该寻找并消灭“变化”。从寻找 bug 到寻找变化,是一个非常大的认知转变。

此时,再回顾文章开头的案例,答案就比较清晰了。团队在执行生产环境故障恢复工作时,接连犯下了三个错误:

错误一:负责发布的同学,没有按规定回退至稳定版本,而是询问开发同学的意见,并以其意见为准;

错误二:相关负责人,因为假设“一条索引不会导致故障”而知情不报,导致系统无法完全回退;

错误三:十几名团队成员没有将精力聚焦在线上业务恢复方面,而是试图在生产环境查找 bug。

三个错误如出一辙,全部来自于技术同学的思维惯性。在大家的认知里,制度就是拿来参考的,对应模块的负责人才是专业的,出了事先找人再问制度规范,或者压根不问制度;在大家的认知里,bug 要用排除法解决,“确定”没问题的模块就要排除在外;在大家的认知里,能一眼发现错误的程序员,才是技术高手,如果动不动就回退,和“网管只会重启”有什么区别?

但是我想说的是,当问题发生时,你的潜意识是要找到 bug ,还是找到变化,对应的结果可能是完全不同的。我们的目标是,即使找不到 bug,依然可以做好故障恢复

这样的思维惯性有多么普遍呢?我们举个例子:你是否曾在下班回家的路上,被老板电话叫回公司,查找线上 bug ?

也许你会觉得这样做很合理,但听完这节课以后,希望你能够意识到这种做法的局限性:线上有 bug ,为什么需要研发立刻回公司?先回退就好了嘛。否则,研发现场写程序,压力也是挺大的。

更别提,一些团队还经常出现喜剧性的一幕:生产环境出现了问题,研发火急火燎地改了个版本,结果系统做不到秒级发布,单是发布就用了半个小时。好不容易上线了,还没来得及高兴,就发现了个新问题……

我们必须不断强调并加深印象:生产环境永远不允许调试问题,出现问题立刻回退,查问题要去测试环境

你可能会想,老乔啊,你这就有点纸上谈兵了,很多大型发布涉及多个系统,怎么可能随便回退呢?

其实这个问题,亚马逊在十几年前就给出解决方案了:大版本立项,小版本上线 —— 梳理好各模块的依赖关系,将各个系统、各个服务独立发布。当然,这也需要依赖服务版本化和 CI 能力的支持。

以上我们讲的是对企业系统的监控。除此之外,我们也要做好对企业业务的监控。

也就是说,对于企业任何一个未处于理想状态的业务环节,都要进行监控:做好问题的可视化展现、明确管理执行动作,通过数据化管理,不断完善企业的运营效率和运营质量。道理其实还是相通的。

如果能做到监视一切,分析一切,控制一切,“眼”能看见所有,“脑”能洞察一切,“手”能一手遮天,一切业务数字化,一切数据可视化,一切控制可触发,那么,这个企业的数字化水平一定已经很高了。

结语

今天,我们从一个实际案例出发,主要聊了聊关于企业生产环境系统监控的认知和方法:监控的目的是让系统一直处于健康状态,具体手段则可分为“监视”和“控制”两种;要做好控制,一个重要的方法是做好流控和版本回退。因为在大部分情况下,消除变化就等于消除异常。

实际上,不单是技术、业务系统需要做好监控,研发管理、团队管理都要做好监控。

关于研发管理,我们在“高可用设计”部分曾提到:风险是经由开发环境、SIT 环境、压测环境、PRE 环境,进入生产环境的。所以我们要做的是严格检查各个环境下的异常。所谓研发管理规范,应该为代码版本进入下一个环境设置准入标准。对于任何异常,都有负责人进行修正。

对于团队管理,我们常常说,组织是结果导向的,但管理工作是过程导向的。关注过程自然就会得到好的结果,只盯着结果往往什么也得不到。对于一个项目、一个产品,乃至于团队的健康度,管理者有没有在关键节点设置监控?有没有针对异常做好控制?其差别是巨大的。

如果你认真学习了前面的内容,可能也会发现,无论是管理还是专业技术,很多关键认知都是相通的。很多时候,是“一理通百理明”。在学习的同时,前后交叉思考,也有助于你在更高的维度上掌握这些知识。

今天,我们就聊到这里,下一讲再见。