无人值守的运维到底靠谱不靠谱? 无人值守时代,运维如何保障发布质量?

阿里巴巴千亿交易背后,如何尽量避免发布故障?在面对实际运维过程中遇到的问题该如何解决?近日,在 GOPS 大会上,阿里巴巴运维技术专家少荃,给我们带来了解决方案和思路。

作者:陆叶平(花名少荃),阿里巴巴研发效能事业部技术专家。目前从事运维中台 (阿里内部叫诺曼底) 建设方面的工作,是集团内最大的应用发布系统 (海狼) 负责人。

前言

近几年,我们在发布效率和稳定性方面做了不少工作,其中效率简单的说就是发布耗时,一个是发布的速度,比如一个应用是 1 个小时发布完成,还是 5 分钟发布完成?另一个是人员介入,开发在发布过程中是否需要介入处理各种发布过程中出现的问题?这两者都做好了,才能说是发布效率提升了。稳定性最基础的是系统的稳定性,保障系统的可用,而最关键的是要保障通过系统来进行发布的应用的稳定性,不会因为发布而导致服务不可用等故障出现。

效率这块我们在集团内比较受好评的产品是 SP2P 的文件分发系统,叫做蜻蜓,我们根据阿里自身的一些特点,实现了一套安全高效的 P2P 分发,同时在 P2P 的协议上引入了超级节点,就是 S,提升了 P2P 网络的启动速度,目前已经开源。稳定性这块我们去年做了一个产品,叫做无人值守发布,对发布进行检测,看看发布是否会引起问题,来提升发布的可靠性,今天就和大家一起交流下这方面的心得。

线上发布之痛

我们为什么要在稳定性方面投入大量精力呢?先让我们来看一个笑话。

变更故障

这个笑话可能没那么好笑,但是它真真切切的说明了一个问题:理想和现实的差异,你以为是有四个单身狗陪你,但是实际却是另外两对情侣。这个和我们做生产环境的发布是一样的,我们以为凭借我们出色的逻辑思维能力,已经把所有场景都想到了,测试也做的很充分了,但是,发布上线后,经常会遇到实际结果和预期不一致,故障发生了。我们针对阿里的故障产生原因做了统计,其中很大一部分都是线上变更引起的,相信在座各位也会遇到或者制造过故障,开发和运维的同学对故障都是很敬畏的。

故障大家都遇到过,但是故障的影响差异会比较大。有些故障可能是故障发现后处理了一会就恢复了,有些故障则可能会导致严重的后果。所以我们需要尽量避免变更带来的故障。

业务挑战:阿里的特殊业务场景

回到阿里,我们都知道,去年双 11 的成交额已经达到了 1682 亿,想象下,这么大的交易额下,如果出现了故障,那会怎么样?

阿里现在的业务多样化发展,新零售、线下支付等一些新的业务场景,要求我们对故障更加敏感,要能够更好地避免故障,更快地发现和处理故障。想一下,如果是线下场景,比如用支付宝坐地铁,如果出现几分钟的服务不可用,那会怎么样?

如何才能有效的避免故障发生呢?

那么,如何才能在发布的时候有效的避免故障发生呢?

靠“蒙”?大家知道肯定不行。可是细想一下,很多时候确实或多或少在“蒙”。我个人是有过类似感受的。我们虽然不会随便到不经过测试就进行线上发布,但是虽然已经经过了多轮测试,肯定还是没有办法覆盖线上各种复杂多样的场景的,而这些没有办法覆盖的场景,就只能靠运气去 "蒙" 了,运气好的,这些场景没有问题,运气不好,刚好就其中一个场景出问题,出现故障了。

通常来讲,为了尽可能不要去“蒙”,我们会对上线流程加入各种验证环节,来保证发布尽可能可靠。例如发布前,我们会通过各种测试来验证功能是否 ok,包括单元测试、集成测试等,发布过程中,我们会通过一些发布策略,例如先预发 (预发布是一种特殊的线上环境,和线上使用同样的资源,比如数据库等,但是不会有用户流量进来)、然后灰度、然后分批滚动发布等方式,逐步将变更更新到线上,发布完成后,又会借助一些故障预警系统,例如像阿里有 GOC 来尽早的发现故障,进行处理,这些环节的这些手段都已经有成熟的系统来进行支持,但是发布的时候,我们常常还是心里没有底。

"人工智能" 的解决方案

那么,还有什么办法能够帮助我们尽可能地保障发布质量呢?大家可能都已经在做了:就是 "人工" 智能的发布保障。

在发布过程中,盯着各种屏幕,去看各种数据,来人肉的判断本次发布有没有问题。在阿里,这些屏幕包括:监控、发布单、机器、GOC 故障预警等。监控能够反映出来当前系统的一些状况,例如机器的负载是否上去了,接口的成功率是否下降了,发布单则能让我们了解当前的发布情况,有多少机器已经更新到新版本了,有多少还在跑旧版本,有多少机器启动又遇到异常了等等,盯着机器则可以看一些日志信息,是否有一些新的异常出现了,异常的量是否很大等等,GOC 让我们在故障发生的第一时间就能知道,结合自己发布的内容判断是否是本次发布引起,需要进行处理。

这种方式相比之前让人放心多了,是因为现在我们看到的是最真实的线上环境的情况,而不是单单的测试数据。但是这种人肉盯屏的方式也存在着很大的问题,首先是成本太高了,发布过程中需要有熟练工盯着各种屏幕去看,片刻不离,其次是人的因素太大了,同样的发布情况,不同的人分析出来的结果可能完全是不一样的,即使是同一个人,因为状态或者其他方面的原因,针对同样的一些数据,可能分析出来的结果也不一样,另外,人也有局限性,各种数据刷新很快,肉眼分析的方式根本都来不及看。

既然这种盯屏的方式被证明是有效的,但是存在一些问题,那么我们就考虑通过系统化来解决这些问题,所以,就有了 "无人值守发布"。

无人值守发布

无人值守发布主要是把上述过程自动化、智能化。通过自动化采集这些实时的线上核心数据,进行智能化分析,迅速对发布状况进行判断,是否有故障发生,有的话则立即终止当前发布。

无人值守发布的两大核心能力,一个是故障检测,一个是异常推荐。故障检测主要是发现现在的问题。异常推荐主要是防范于未然,是指发布出现了问题,但是不一定会引起故障,这些异常给开发的同学透明出来,需要开发注意,比较常见的是出现了一些异常,这些异常从绝对数量或者涨幅来看没有非常明显,但可能是需要处理的。

什么是无人值守发布

首先是发布单详情页面中的无人值守信息展示,发布单详情页面是发布过程中最常会去看的页面,所以我们选择把无人值守检测出来的一些信息展示到这个页面,在一个页面中把可以做的事情都做掉。当然,并不是说开发同学一定要自己去刷这个页面才能够知道当前发布是否有异常,当发布出现异常的情况下,系统会先自动暂停当前的发布,然后通过钉钉等一些通知方式,告知开发的同学,你的某个发布出现了异常,需要你去看下。

这些展示的信息包括了左侧的当前发布是否有异常的概要信息,通过概要信息,可以知道当前发布有没有问题,如果有问题,可以看右侧的问题分类,是基础监控指标出问题了,还是业务指标出问题了,或者是日志出问题了,日志出问题具体是哪个日志有问题了,在这里都可以看到。

如果这里的信息还不够来判断是否发布有问题,那么点击查看详情,可以看到更加详细明确的异常信息,来进行判断。

无人值守发布的时候需要应用接入到无人值守发布系统,当然大部分情况下这是一个自动化的过程,系统会判断应用是否符合接入标准,如果符合,会自动接入,但是也有一些情况会导致应用无法自动接入,这种情况下,也会告知用户当前应用是否接入了,如果未接入,需要做一些配置或者改造来接入。

无人值守发布详情

这个是无人值守发布信息展示的详情页面,在这个上面,可以看到更加明细的一些信息,比如异常数量的发布前后趋势对比,业务监控各个指标的变化情况等。通过这个页面,开发的同学基本上有足够的信息来判断本次拦截是否有效,是否需要进行回滚等操作。

无人值守接入

这个是应用接入无人值守发布的一个页面,主要需要配置业务监控指标、日志路径等。

无人值守的实战案例

这是一个典型的案例,其中一些数据做了隐藏或者处理。发布过程中日志中某个异常出现了大幅度增长,我们可以从左侧看到异常的数量,点击异常信息还可以看到更加明确的异常堆栈信息,右侧可以看到异常数量出现了明显增加,下面可以看到这个检测被用户判断为确实有问题,最终执行了关闭发布单进行回滚的操作。

用户反馈

这些是用户的一些反馈。应用接入无人值守发布,对提升发布的稳定性起了立竿见影的效果。

指标

上面这些案例都代表了一部分用户的感受和反馈,那么整体效果怎么样,还是要拿数据来说话。

业界对于异常检测这块有两个主要的指标:一个是召回率,一个是准确率。

召回率主要用来反映漏报的情况,准确率主要用来反馈误报的情况。漏报和误报的概念比较好理解。漏报就是本来有 10 个故障,系统报了 9 个,那么漏报了 1 个,召回率是 90%,误报就是只有 10 个故障,报了 20 个出来,多出来的 10 个就属于误报,那么准确率就是 50%。

目前准确率方面,我们已经做到了 60% 左右,也就是说差不多每报 2 次,就有一次确实是有问题的,这种体验应该算还不错。

召回率方面,我们已经做到了 90%,这个 90% 是指出现了一次故障我们没有报出来,我们有效拦截了 9 次,这 9 次中可能会引起故障,也可能只是有问题,但是不会造成故障,但是因为及时发现了,都没有造成故障,很难明确说这 9 次里面到底有多少是会造成故障的,所以计算召回率的时候没有单独计算故障的召回率,而是把故障和异常一起计算进去了。

关于先重点抓哪个指标,我们也经历过一些波折。一开始的目标是拦截尽可能多的故障,所以比较注重召回率,导致长期一段时间内,准确率很低,拦是拦了不少,但是误报相当多,报 10 次里面可能只有一次是有效的,如果我们是用户,可能几次误报以后,就对这个产品失去信心了,这个导致我们不敢大面积推广。后来调整策略,优先解决准确率的问题,反正没我们系统之前这些故障也是存在,有了系统,能减少一些就是好的,所以先不追求召回率,把准确率做上去后,可以大面积进行推广了,受益面大了,避免的故障也自然多了。当然,后面还是继续抓了召回率的。

无人值守发布实现

前面说了不少,但是都没有提到系统的具体实现,接下来我们看是怎么去实现无人值守发布的?

首先看下我们的产品分层和业务流程。

产品架构和业务流程

我们的系统大致分了三层,最上面一层是发布系统层,我们的产品叫海狼,主要是发布单的提交、执行以及无人值守信息的展示和反馈,这一层是可以扩展的,除了发布系统外,也可以对接其他的一些变更系统。

中间是无人值守的核心系统,根据收集到的分析任务,采集对应的数据,进行分析检测。

最下面一层是离线分析层,主要用来做一些算法的训练、回放验证等,后面再具体介绍。

大致的业务过程是,用户在发布系统中提交了一个发布计划,这个时候会通过 Normandy(诺曼底) 这个平台进行发布 (海狼是诺曼底平台的一部分,负责发布的执行),海狼开始执行发布单后,无人值守系统就会收到发布单执行的事件,然后开始分析,分析的时候会利用离线算出来的一些特征集,然后和当前的指标进行比较检测,如果有异常,那么会通过海狼的接口进行暂停发布单的操作,用户可以在发布单页面看到对应信息,然后进行一些判断后提交反馈,是有效拦截,还是误报等。

两个阶段

上述是一个大致的过程,具体实现方面,我们经过了两个大的版本迭代,下面针对两个版本分别介绍下。

1.0 实现

通过前面的介绍,应该大致了解,无人值守发布就是分析发布过程中各种指标数据,来判断发布是否有异常,那么具体有哪些指标数据可以用来分析呢?大致总结了下,有以下几类:

首先是业务指标,这个最直接反应当前发布有没有问题,如果影响到了业务,那么基本上就是有问题的。如果业务指标能够覆盖所有的故障场景,那么理论上只要分析业务指标就行了,但是现实往往是很多业务指标的完善都跟不上业务发展的,业务上去了,指标还没上,这是很现实的事情。

其次是一些基础指标,例如机器的内存使用情况,cpu 使用率,load 情况,磁盘 io 等,这些指标一般在发布过程中不太会发生明显的变化,但是一旦发生了明显变化,就可能有问题了。

还有些中间件的指标,阿里内部广泛使用的 hsf、tair、metaq 等,都有相应的 qps、rt、成功率等指标,如果发布后成功率突然跌的比较明显或者 qps 跌 0 等,那么也很有可能是有问题了。

还有一个比较关键的是日志,阿里比较多的应用是 java 的,我们会在日志中把一些异常的堆栈信息都打印出来,这些异常信息反映了代码运行过程中的一个不正常状态,所以是一个很宝贵的指标数据。通过分析这些异常的出现情况、涨幅情况、或者是否出现了一些常见的容易引起故障的异常,例如 ClassNotFound 等,我们可以做出足够有用的判断。

指标和算法选取

指标这么多,我们一开始应该从哪入手呢?

第一个版本的时候,我们选择了基础监控和日志这两方面入手。原因比较简单,基础监控的覆盖率够高,有足够多的数据可以让我们分析,而日志根据经验则非常重要。至于业务监控和中间件指标,由于数据方面等一些问题,第一个版本我们没有去考虑。

那怎么对基础监控和日志的指标进行分析呢?我们采用的是使用一些简单的规则加上复杂的算法共用的方式,针对一些情况,例如出现了前面提到的危险异常等,采用规则的方式,直接进行拦截,针对异常的涨幅变化等,则采用算法来评判这个涨幅是否在合理范围内。

如何实现

确定好了指标和分析思路,我们再看看需要做哪些事情。首先要做的是数据采集,我们面临的问题是需要采集哪些数据,怎么尽快地采集这些数据。其次是对数据进行处理,原始的数据中会有一些干扰的数据,干扰的来源可能是多方面的,可能是数据采集系统本身的问题,也可能是与业务自身的特点有关,需要把这些干扰的数据能够剔除掉。然后就是针对采集和处理后的这些数据,制定什么样的规则,使用什么样的算法,来对它们进行分析,尽可能准确的判断出发布后的数据是否有问题。

数据如何采集

首先我们来看看数据怎么采集?

采集之前,先明确检测的大致思路:发布前和发布后的指标进行对比,已发布和未发布的机器进行对比。所以,我们要采集的是时间序列的数据,也就是每个时间点某个指标是什么样的一个数据,例如某个时间点,系统的 load 是多少,某个时间点,某类异常出现了多少次等。

具体要采集哪些指标,上面已经明确了,只要把这些指标再做一个分析,把最重要最能反映故障情况的一些指标挑选出来,采集过来就行。

而从哪些机器上采集指标呢?前面提到,我们检测思路中有一条是已发布和未发布的机器进行对比,所以我们为每个应用设置了两组机器,一个是发布组,一个是参照组,只采集这两组机器的数据,而不是所有机器的数据都采集。至于采集时间,也不用采集所有数据,只要采集发布前后一段时间内的数据就可以。

采集到数据以后,接下来就需要对数据进行一些处理,除了前面提到的一些干扰数据剔除外,我们还需要进行一些维度的聚合,因为拿到的是一些单机数据,所以需要针对已发布未发布等一些维度进行数据聚合合并,最终生成了可以分析的数据。

数据分析方法

数据分析的方法,我们采用的是改进型的 funnel 检测模型,它有这些优点:可以满足针对不同的指标,采用不同的算法的需求,不同的指标有各自的特点,使用同一个算法显然不大合适;其次它的计算需要的资源少,同时检测的速度又够快,还支持很多指标一起分析。

通过上述这些工作,我们大致就把一个检测系统建立 run 起来了,这第一个版本在准确率方面表现不是很好,离线跑的时候能够有 30%、40%,但是线上实际跑的时候只有 10% 上下的准确率,所以我们需要去提升准确率,那怎么提升呢?

答案是不断的分析误报和漏报数据,然后对算法做一些微调。不停的微调算法又带来了一个新的问题,针对这些误报的数据,可能新的算法不会报出来了,但是之前的那些没报的数据呢,用新的算法会不会又报出来了?之前那些报出来的有效拦截,会不会新的算法中就不报出来了?

于是我们又搭建了之前产品架构中提到的离线回放系统,用来对算法进行回放验证,从之前的误报、有效拦截、未拦截等数据中抽取部分数据,每次算法调整后,通过回放系统对这些数据重新进行检测分析,看看准确率和召回率是怎么变化的,误报的是否还在误报,有效拦截的是否漏报了等等。

无人值守回放系统

整个无人值守回放系统大致过程如下:录制模块会将线上检测过的发布单的相关数据录制到回放 db,然后需要回放的时候,通过回放触发接口,触发无人值守进行检测,检测时候会调用回放系统提供的指标 mock 接口,从回放 db 获取数据,而不是从实际的数据源获取数据,将回放检测的结果进行保存,产出回放结果报表。

算法的困境

通过无人值守回放系统,我们建立了可靠的算法验证机制,通过不断的微调算法来提升召回率和准确率。但是,还是遇到了一些问题。

首先是需要不断的去分析检测数据,然后调整算法,这个过程是相当耗费精力的,并且不一定能够有相应的回报。还有很重要的一点是,在实践过程中,我们发现一些明显的误报信息在重复的误报。

所以我们需要去探索一个能够解决这些问题的方案。于是,第二个版本,我们就采用了基于机器学习的方式在原来的基础上做了一些改进。

机器学习的大概过程

首先会有一个离线学习的过程,通过一些历史的发布单的指标数据和拦截数据,以及用户反馈的一些数据,计算出来应用发布时候的一个特征库,发布的时候,会首先采用一些算法来检测出可疑指标,然后对可疑指标和特征库进行比较,如果发现这个可疑指标落在正常的特征库里,那么忽略掉,否则,就认为发布出现了异常进行拦截,拦截完成后,会根据发布单最终的结果和用户的反馈行为将这次拦截是否有效等数据保存起来,作为下次离线计算的一个输入数据。

三大要素

机器学习也面临几个问题需要去解决,首先是去学习什么样的数据,其次是要通过什么样的方法去学习产出什么样的结果,还有一个就是怎么样把这个学习的结果用到后面的发布检测中去。

样本

我们首先看下样本问题,就是学什么数据。我们有的数据大致有这些:发布单数据、发布过程中的指标数据、拦截是否有效的数据、用户反馈的一些数据。

这些数据看起来很多,每天的发布单有好几万,每个发布单又有大量的指标数据,但是实际上,每个应用的特征都是不一样的,所以学习的时候一定是基于应用的维度去学习的,而每个应用的发布数据就很少了,如何从这不多的数据去计算应用的发布特征呢?

计算的思路也有两个,一个是算异常的,比较自然的想法,找出异常的特征,下次如果匹配了异常特征,那么就可以判断发布有问题,一个是算正常的,而应用维度异常的发布往往远少于正常发布,甚至可能都从来没有过异常发布,所以基于异常的维度去计算,也不大靠谱,相对比较靠谱点的,只能是通过正常的发布单数据去计算出应用发布的正常发布特征。

样本中的一个挑战是如何来判断一个发布真正是有问题的,我们采取的是发布单行为和用户反馈相结合的方式,如果发布单被回滚了,那么就认为是异常的,如果用户反馈说有异常,那么也认为是异常的。

关键和不靠谱是用来描述用户反馈数据的两个特点的,关键是指用户反馈数据非常重要,是最能够帮助我们去了解应用的各个指标对异常检测是否有帮助的,但是用户反馈数据又具有主观性,发布过程中出现了某个异常,A 开发同学可能会反馈认为没有问题,而 B 同学比较谨慎可能就会反馈认为确实是有问题,如何去平衡这两个特点也是比较棘手的。

这个就是刚才提到的用户反馈数据,通过这个反馈数据,我们可以明确的知道某个指标虽然异常了,但是对这个应用来说,可能是完全没有用的,根本不需要作为检测的依据,那么下次检测的时候就可以忽略掉该指标。

这个反馈数据的采集看似很容易,但是据我所知,在不少公司里,采集这个数据阻力都是比较大的,开发同学不愿意去填写反馈这些信息,比较幸运的是,我们通过一系列方式优化,尽可能地减少这个反馈对开发的干扰,把这个反馈给强制开启来了,采集到的数据对我们的帮助确实相当大。

算法

样本数据有了,接下来就要根据样本数据计算出应用的发布特征了,我们采用的是简单的分类的方法,最初的想法是分成正常、异常、未分类三大类,正常比较好理解,异常是指每次出现都会导致故障的,未分类则是一些新增的或者之前出现过没有变化的一些指标,后面考虑到上面说的异常样本非常小的问题,就把这三类统一成一类了,就是只计算应用发布时候各个指标的一个正常阈值,如果下次发布的时候,指标的值超过了这个阈值,那么可能就是有问题。

具体学习的过程比较简单,总结起来一句话就是:找到正常发布单中指标的最大值,作为应用的正常指标阈值。具体过程是:首先是发布过程中如果出现了异常指标,那么会去看这次发布最终是否是有问题的发布 (通过发布单的行为是否回滚以及用户的反馈等),如果是正常发布,那么和之前的正常阈值进行比较,如果比之前的正常阈值要小,那么忽略,如果比之前的阈值大,那么就更新正常阈值,而如果这次发布是异常发布,那么理论上应该去判断这次的指标是否比正常阈值小,如果小,那么要更新正常阈值,但是实际上,这次发布的问题可能并不一定是这个指标引起的,而且如果确实是这个指标引起的话,那么之前指标比这个值更大的发布应该也是异常的,考虑到这两点,我们现阶段采取的是忽略异常发布单的方式,只针对正常的发布单进行阈值计算。

指标使用

正常阈值的使用也比较简单。发布过程中,如果发现了异常指标,那么会找到该指标对应的正常阈值做比较,如果小于正常阈值,那么忽略掉,如果超过了正常阈值,那么作为可疑指标,在一个窗口期内进行多轮检测,窗口期会根据检测的结果做一些动态调整,如果在窗口期内多次被判定为可疑指标,并且达到了一定比例,那么最终会被判定为异常指标,对发布进行拦截。

整个机器学习的改进过程大致就是这样,通过这个改进,我们一方面解决了之前遇到的一些问题,提升了召回率和准确率,尤其是准确率方面有了显著提升。另外一方面,也释放了大量精力出来,可以更好的优化这个学习的算法。