我在Shopee维护了一个ServiceMesh系统,大部分RPC调用都经过这个系统,这个系统每分钟处理数千万个请求。为了描述方便,本文称其为Oitsi系统。它所做的事情实际上类似于Istio。Oitsi会为RPC调用设置很多错误码,类似于HTTP协议中的404、502等。Application上报的错误码在一个范围内,而Oitsi内部产生的错误在另一个范围内,比如0-1000,类似于SystemInternalError。监控这些错误代码可以让我们了解系统的运行状态。这个系统从接手以后就有一个问题,就是一直报很多内部错误,比如内部超时,找不到路由信息等等,每分钟都有几万个错误。但是,系统的运行是完全正常的。正常情况下Oitsi系统的误差,从脱敏后的监测就可以看出来。经常有一些差错,一下子就要上万。除了图中几K的错误外,1K以下的错误更密集,但它们都被大量其他错误拉平了,在这张图中并不明显。这给我们带来了很多疑问:Oitsi是真的出了问题,还是“正常错误”?很难判断,每次出现这种情况都需要时间和精力。大多数情况下,经过排查,发现问题是用户“滥用”造成的,无需关心。它掩盖了许多实际问题。例如,新版本发布后,偶尔会出现一些内部错误,这些错误本不应该发生,但却被真实的问题所掩盖。基于这样的监控数据,我们不能设置警报,因为噪音太多了,即使有警报,也和没有警报一样。这让我想起了我之前在ant中遇到过类似问题的工作。我从事一个名为“故障定位”的项目已有一年多了。在Ant中,我们也有很多告警(99%)是无效的,给OnCall同事造成了很大的噪音和干扰。蚂蚁的想法是:开发一个“智能系统”(AIOps),当报警发生时,能自动判断报警是否是噪音,是否是真正的问题,问题出在哪里。以Oitsi为例,当错误数量突然增加时,智能故障定位系统会检查Oitsi的一些指标是否正常,具体是什么服务引起了告警,之前是否监控过类似的曲线模式,如果有,说明一直在发生,很正常,我们可以忽略。这样做了一年了,但仍然行不通。但是,我发现很多报警规则本身就有问题。比如一个每分钟请求量只有两位数的业务,领导的要求是“1分钟内发现故障,5分钟内定位故障”。别说自动定位了,就算是人来判断也不靠谱。为了达到这个目的,监控组设置了很多非常灵敏的告警,交给定位组说:“我们负责发现问题,你们负责定位问题,如果出了问题,“1分钟内触发警报,那么我们的工作就完成了。已经达到标准。不过没问题,我们也触发了很多噪音警报,这是你的工作。”他们的KPI确实是完成了,只要有故障就一定要报警。但事实是,很多时候,警报一发出,大家就打开监控,盯着监控:“等一下,等一下,有请求进来,服务没问题!”所以在这一年的工作中,我有了一个想法,就是从源头上解决问题,比起使用高级魔法系统来解决问题,要简单、彻底得多。我们真的需要这么多人来开发一个“神奇的系统”来帮助我们诊断这种问题吗?比如监控配置错了,那就优化监控。为什么监控配置错误?监控系统太难用,UI变幻莫测,告警无法调试,监控只能保存7天的数据,无法根据历史监控数据配置告警。不少人为了“规矩”,先报警后离开。至于后来触发的警报,他们没有回应。回到Oitsi问题,我搜索了几个服务,发现这些Oitsi内部错误不能完全描述为“正常错误”。毕竟是错误,没有错误也是正常的。我们只是说它没有引起在线问题。它们可以修复。所以一个月前,我决定从源头上解决这些问题。杀死所有不应报告的错误。乍一看,这么多错误,这么多??团队在用,好像很难管理,性价比很低。但是毕竟没有人催我快点完成,我可以一点一点的完成。做一点点就不会错(只要我解决问题的速度比出现新问题的速度快)。于是开始按照以下流程进行处理:在Jira(我们内部的工单系统)中创建一个名为oitsi-abuse的特殊标签,后续的工单可以关联到这个标签上,方便参考之前的Case加工时;创建监控器,专门针对错误做一个面板,点击面板右侧的Legend直接跳转到服务的监控面板,在服务的监控面板上显示下游,并关联PIC(负责人)的CMDB;这样,我从错误数最多的服务开始,查看监控、下游服务、机器日志,看看相关错误码是什么时候开始的,是什么原因引起的。如果确定是服务的问题,就给这个服务的负责人创建一个工单,然后联系他说明这个有什么问题,对我们的监控告警会有什么影响,以及它需要维修;等他确认问题,然后要一个ETA(预计维修时间),把ETA写到workorder里面,检查确认什么时候到;如果是Oitsi自己的问题,去找Oitsi的开发同事排查问题;当所有问题都解决后,设置错误报警,有错误联系开发。通常,是他们所做的配置更改或发布导致了问题。这样其实对业务更健康,我们发现问题的能力也更强。这样一来,其实就这样坐下来之后,发现问题就那么几类,而且排错速度越来越快。中间还有一个库,它会对Oitsi服务做心跳检查。如果此检查设置不正确,则会出现一些错误。许多引用该库的应用程序都存在报告错误的问题。但是我们的系统本身其实已经做了检测来保证心跳等问题。通信之后,这个库的心跳检测行为就可以下线了。于是图书馆出了新版本,我让所有的参考资料都升级了版本,很多错误一下子消失了,很有成就感。这个工作的进度比我想象的要快,一个多月,联系了20多个团队。虽然我也遇到了一些雷人的事情,但是很明显是服务A的问题,所以直接到下游,让我们调查了半天,最后说回来找服务A的负责人,拉了一个群,把log放出来,这才承认是自己的问题,开始排查。但是,大多数团队都非常合作。说明问题后,他们立即去排查,发现下个版本会修复这个问题。如此默契的配合,让我又惊又喜!现在,系统错误维持在200以下,并且已经找到了存在错误的根源,还有3个服务需要修复。最迟将在2周后发布修复程序。可以预见,在不久的将来,这套系统将成为零差错系统!今天系统报错,还是有一些服务一直报错,但是已经大大减少了。虽然这项工作不涉及任何KPI之类的,也没有任何技术含量,但仍然是一些“沟通”的工作,但给我带来了很大的成就感。我相信这也会在未来为我节省很多时间。例如,我们评估系统的SLI和SLO。因为误报太多,确定停机时间需要做大量工作。现在可以通过监测直接确定。这部作品给我的一些感触:从源头上解决问题最彻底;不要害怕沟通;错误的发生是有原因的,经查,零是零,一是一(从这个案例来看,确实所有的错误都是可以解决的);每个公司都有脏活累活(第一个毕业维护爬虫的公司也有很多脏活累活),这些都需要有人来做。需要补充的是,我并不是完全否定故障定位的想法。毕竟,在Ant之前,有四五个团队在做同样的事情。我们(和其他做同样事情的小组)尝试了很多想法,很多人因此被提升(你说你联系了无数的团队,检查了很多问题,这有什么影响?你说你构建了“智能定位”系统,推广稳中有进。)更令人印象深刻的是,有一个项目已经开发了数千个(他们称之为)决策树,这很简单:如果发生这种情况,请检查这个。相当有效,很多配置不当的告警都被这条规则过滤掉了(虽然我觉得还是直接改告警比较好)。我真的很佩服他们的毅力。说了这么多湿货,我们来说说干货。我们实际上还有一个问题需要解决。如果读者有想法,欢迎评论。在ServiceMesh中,所有的服务都是通过Agent来调用的。比如App1要调用App2,它会向本地Agent发送请求,Agent会调用App2所在机器的Agent。在这里,超时的问题很难处理。比如我们设置了1s的超时时间。如果server端的Application已经超时,那么server端的Agent可以报applicationtimeout错误,在我们Oitsi系统中不认为是错误。但是客户的代理呢?它无从得知是Server的应用超时还是Server的Agent超时。因此,当Server超时时,客户端的Agent总会报内部超时错误。我们目前无法区分此错误是否由应用程序引起。
