一个正在线上运营的系统,如果遇到有反馈说,产品出现了bug,作为运维,作为研发,正确的应对策略和步骤,应该是怎样的呢?
那很多人会说,出bug第一步就去检查代码呗。
但代码哪是那么容易检查的。
-
一个程序员水平高低,写代码的时候你很可能看不出来,但出bug的时候,分析代码的时候,有经验有思路的,其处理效率往往比缺乏经验没思路的高一个数量级。
-
一般而言,一个大公司,已经上线的系统,bug应该不是说那种非常表露的,往往可能需要一定条件才会触发。
一般的思路和建议是
(1)第一步,评估影响范围
- 这个问题究竟影响了多大范围的用户,以及什么范围的用户。这是作为线上运营人员需要非常明确的,而且对问题的解决优先级也有重要的意义。同时,基于影响范围的判断,也对问题的可能性做了初步的排查。
- 那么,怎么评估影响范围呢?
- 两个方向,一是通过统计数据,而是通过日志分析
- 用户反映网站或APP打开慢,打开卡,或者打开报错无法登陆。这种属于全局问题,这种问题建议看统计数据来评估,比如当前活跃用户,日活跃用户及活跃用户的时间分布,与比如说上周同期对比。一个简单的实时在线对比, 可以快速定位出故障的严重程度和影响用户规模。 那么,如何进一步排查呢?这要看你平时记录的数据全不全,比如,在当前在线或最新访问记录里,是否有地区分布,用户上网环境分布,用户客户端浏览器分布,用户手机类型和操作系统型号分布,这样可以快速做一个排查。
- 如果不是全局性问题,比如邮件验证出错,这种问题去看同时在线或访问数据就没有意义了,可以通过日志数据做快速分析,比如,最近用户邮件验证的成功率是多少,以前是多少,如果有历史数据对比,就可以知道大概是不是有较多用户出现了问题,然后,继续细分,比如说,不同邮件服务商的验证成功率多少,不同账号注册方式的邮件验证成功率多少
- 这样,这个问题的影响范围,就可以快速确认了。
- 如果以上数据全都正常,那么说明这个问题属于极个别用户在极个别环境下才会触发的问题,当然问题依然需要解决,但至少不用太焦虑。
(2)第二步,试图重现问题
- 评估完影响范围后,根据评估的的范围特征,试图用同样的方式重现问题。
- 通常,问题都会在特定条件下触发,而问题就在于,你无法明确知道,这个特定条件究竟包含了哪些因素。
- 如果根据用户反馈的信息,以及如上基于数据分析的结论,能够明确该问题的触发条件,并实现完整的问题重现,那么对解决问题,就会非常有帮助,研发和运维就可以在测试环境里逐步调试问题。
- 但如果,用户反馈信息有限,以及基于数据分析的结论并不能重现问题,也就是存在不明确的触发条件,这个重现就比较困难。
- 这时候,我常用的方法是,在与问题有关的代码中,增加非常多的记录节点,加入更具体的追踪代码,将更具体的信息获取,并输出到日志。通过定期对这些日志的分析,找到出问题用户的日志,并基于日志深入分析问题。也就是,基于用户的真实行为的数据记录,重现和回放问题。
- 这里一个很重要的方法就是,跟踪定位中间数据。一些程序员修bug很没有效率,往往是缺乏一个概念,你如果得到的结果与预期不一致,你应该把程序流转中每个节点的中间数据,中间变量都拿出来看看,是不是和预期一致,才能更容易定位问题。这是定位很多程序逻辑bug的重要手段。
- 也就是说,应该尽量明确问题的触发条件,实现问题重现,在不能明确问题触发条件的情况下,在代码中增加节点跟踪,通过线上用户实际操作中的数据反馈,获得bug可能的触发条件和代码的逻辑漏洞。
(3)临时方案和终极方案
- 如果问题符合以下两个条件:
- 影响范围较大
- 难以定位,或者可以定位但解决成本很高
- 那么,需要尽快提供临时替代方案,或补丁方案。
- 临时替代方案,通过功能降级或者一定程度的减少易用性或其他产品特性,保证基本业务逻辑的顺畅,保证用户可以完整完成基本的业务诉求。这个方案与原有系统的功能模块是替换关系。 当问题影响范围非常大的时候,适合这样,但对用户的体验伤害也是很大的。
- 补丁方案,不替代现有方案,但针对现有遇到问题和障碍的用户,额外提供第二种选择,以功能降级或一定程度减少易用性或其他产品特性,保证基本业务逻辑的顺畅及用户完整完成业务诉求的能力。 当问题影响范围不能被忽视,但大多数用户并不受影响的时候,建议使用这样的方案,保证大部分用户的体验毫无损害,并尽可能让少部分遇到问题的用户可以平稳过渡。
- 但以上都是过渡方案,但有时候,有些过渡方案可以用好几年,甚至很久很久,甚至在系统已经彻底完成升级和修补后,为避免其他潜在的未知问题,补丁方案仍然静悄悄的躲在代码库的角落里,等待哪个不走运的用户去触发。
- 这里强调一点,我们在做解决方案的时候,要尽可能多留一个心眼,就是,永远要考虑系统异常该怎么办。
- 降级方案,补丁方案,事故跳转方案,类似这样的策略,也是系统设计,系统架构中需要认真思考的,这种策略也许永远不会生效,当然这样最好,但一旦生效,有可能就是救命的策略。否则,产品用户群断崖式下跌可能随时都会发生。
(4)风险评估及持续优化
- 实际上很多公司都会遇到一个尴尬的问题,解决一个旧问题,带来几个新问题,这是技术演进中经常遇到的障碍,我们看这么多互联网巨头,这么多成熟的线上产品,哪个产品说,成熟度很高,不需要程序员继续维护,继续修补了?几乎没有。
- 而且就算是当前状态下没有问题的产品,谁敢说随着使用频次,使用人群,使用场景的增加,后面就没有问题。
- 修补问题的时候,不要头疼医头,脚疼医脚,不要只是为了解决当前问题而思考,也要从综合层面思考。
- 对系统的整体是否有影响,对其他相关的功能和特性是否有影响,修补是否带来额外的负载和性能开销。
- 此外,对业务逻辑的梳理,如果这个业务已经属于打满了各种目的的补丁,很多补丁各种影响,那么就应该从全局思考,是否可以有更优雅,逻辑上更加顺畅的解决方案,而不是一个补丁接着一个补丁的去应付,当然,短期靠一个补丁去应对是必要的,但长期呢?
- 不过这事也不绝对,对于即将放弃,或者属于下坡路,只在维持状态的产品,可能打个补丁就够了,不会再考虑架构优化的事情了,对一些价值明显偏低的项目投入太多技术资源也是不合理的。所以,技术人员看这个事情的时候,也要理解公司业务层面的考量。
关于打补丁
当发现bug后,尤其是自己的bug,很多开发人员马上就想到了bug的修复方案,然后立刻去写代码打补丁。问题是匆忙之间打补丁,如果没有经过充分的测试,可能会引入新的bug,真实更严重的bug。如果要充分测试,那么意味着时间会比较长,而线上故障的时间越长,可能意味着损失也越大。
所以面对线上故障,第一步应该对故障进行评级,看对用户的影响范围,如果是核心业务,大面积影响用户,那么当务之急是恢复生产,然后再考虑如何去修复 Bug
恢复生产并不一定需要修复bug,可以用一个临时性的方案,比如说回滚系统到上一个稳定的版本;重复服务看是否能恢复正常。当然在恢复之前,应该尽可能保留当时的日志、故障场景的截图、内存的dump等信息,用来后继查找故障原因使用。
如何快速定位bug
快速定位bug在哪里,关键在于通过有效的手段,逐步缩小问题范围,直到找到bug在哪里。
比如说:
-
一种常见手段就是先重现 Bug,因为有了重现的步骤,就等于将问题的范围,缩小到重现 Bug 的这几步操作相关的代码上,就很容易发现问题在哪。
-
还有一种手段就是分析错误日志,通过错误日志,可以马上定位到错误在哪里。所以对于平时写程序,无论是客户端还是服务端,注意收集错误日志是非常重要的,可以帮助你在排查问题的时候节约时间。
-
还有一些不能重现的 Bug,则不是那么容易发现,其实也可以按照缩小问题范围的思路来定位。
- 比如说,Bug 是在最近一次部署后发现的,并且回滚部署后就恢复了正常,那么就说明问题很可能是由于这一次部署和上一次部署之间的代码变更导致的,如果代码变更不多,就可以通过分析变更的代码来定位。
- 像内存泄露或者CPU高的问题,一般可以通过分析dump文件,分析当前是哪些线程占用资源多,线程运行的代码是什么;哪些变量占用资源多。从而可以缩短范围,快速发现问题在哪里
- 排除法也是一种缩小范围的方法,尤其是在架构比较复杂的情况,一次用户请求的操作可能经过多个服务,如果配合日志,那么可以对一个请求经过的每一个服务都进行日志分析,对于正常的服务可以逐一排除,直到找到出问题的环节,从而可以缩小问题范围。
问题:
- 谁来主导线上故障处理的过程?
- 故障排查是不是应该有一个标准的分析过程,让运维、开发、安全各方能更好的协作?
- 便利性和安全如何平衡?
- 谁主导线上故障,我觉得有两个指标要考虑:一个是这个人或者角色要懂技术懂业务,这样出现故障,能对故障进行评级;另一个是要能调动开发和运维去协调处理,这样出现故障能找到合适的人去处理,不然也只能干着急。
- 故障排查上:如果是操作系统、数据库、网络等非应用程序故障,应该是运维负责;如果是应用程序故障,应该是开发负责,即使开发最近没有去做发布也应该是开发去查。因为只有开发对于应用程序的结构最清楚,才能找出来问题。排查过程中,运维要给予配合。
- 应该搭建像ELK这样的日志管理系统,将应用程序日志也放上去,这样正常情况下就不需要去登录服务器了,直接就可以通过日志工具查看异常信息。另外,一些特殊情况应该允许开发人员登录服务器排查定位。
总结
对于线上故障,新手用野路子解决问题,高手用模型解决问题:
- 给问题评级。紧迫的调动优势资源先解决,一般的往后放放。
- 尽快恢复生产。生产是企业的首要职责,遇到问题优先恢复生产,减少直接损失,然后再正式地解决问题。
- 找到问题出现的位置。“搜集证据”,通过“粗调”在时空上缩小包围圈,再用“精调”明确问题点,运用排除法最终锁定问题。
- 分析原因,修复问题。
- 提出解决方案。钥匙不一定插在锁眼里,要沿着问题的线索不停“倒带”找到根源。再针对根源,站在系统和流程的高度制定解决方案,避免问题复现。
重点:
- 通过故障报警 + 业务骨干轮值机制,让正确的人第一时间响应问题。
- 通过实战演习,确保应急预案稳定可行。
- 通过使用日志记录和分析工具,积累、整理日常生产信息,出现问题才有得分析,否则重现问题也无济于事。