作者 | 辰啸
点击查看视频
大家好,我是辰啸,来自阿里icbu互动与端技术团队,今天给大家分享的主题是前端故障演练的探索与实践。
前端可用性的困局在于,一个看起来很体面的页面,有吸引力的视觉,友好的交互,但你不知道背后到底是什么情况。让我想起有一次走进某个机房,机柜上码着各种高端机器,一片祥和。但是走到背后,线头交错,你永远也不知道拔了这一根影响到的是哪几台机器,一旦出现问题,某些情况下连恢复都伴随着风险。
而作为发生在客户端上的问题,先天上存在着感知相对弱一些的缺陷。别人家的事情肯定是不如自己家发生的事情容易观察到的。
有这样明显的问题,但与此相悖的是,前端安全生产水位远远满足不了当前的诉求,发展上颇显迟钝。这背后反应的问题主要有3个:
这也是我们尝试探索前端故障演练的核心思路来源。
整体而言这还是一个比较新的领域,今天更多地是分享一些我们在探索过程中的思考和感悟,会介绍我们的整体方法论混沌工程,过程中遇到的核心技术挑战,并结合一个演练实战让大家更有体感。其中有一部分思考路径上的产出,也可以在别的领域得到广泛使用,其意义超出了前端故障演练本身,希望能给到大家一些启发。
混沌工程
什么是混沌工程?Netflix在2012年发布了Chaos Monkey,向业界介绍了这个思想。引述一位业界先驱的话,混沌工程是一种深思熟虑的,经过计划来揭露系统弱点的试验。简单来说,就是将异常扰动注入已经呈现稳态的系统中,观测系统因此而产生的变化并作出对策,使今后系统面对同类异常扰动的变化delta在空间和时间维度上尽可能的小。
混沌工程不是一次性的试验,通过验证、改进和再试验,形成一种往复性的提升机制。它在设计上强调了5大基本要素,在后面的介绍中会为大家再展开这一部分的讨论。
故障演练就是混沌工程的实现方式之一。
以往阿里内部的实践中,已经有比较成熟的故障演练体系了。但当时的故障演练关注的主要是诸如线程池满,数据库连接慢,断网断电,磁盘满坏慢等问题。概括来看,就是主要验证企业的在自己的物理设施范围之内,是否可以应对突发情况,对外提供持续可用的互联网服务能力。我们把它概括为在岸持续可用。那么前端故障演练有哪些不同?
如果我们把BFF、Serverless等领域归结为这一类的后端演练,把话题聚焦到纯前端,我们会发现,实际前端的故障演练关注的代码、资源等数据,都是在离开企业的物理设施,来到客户端上以后才开始运作。
因此前端故障演练的本质就转变为与此相对的概念即离岸持续可用。我们需要关注的是代码的生产过程是否能防御缺陷的混入,也关注故障发生时系统的发现能力和组织的响应能力。
举几个例子,我们通过演练可以去验证CodeReview是否严肃,自动化用例的漏检和误检率处于什么水平,兼容性、国际化、性能问题的预防和发现能力表现如何,或者去评估前端的监控覆盖率、告警触达率以及人员的应急响应效率等等。
在这样的思考之下,我们开始着手准备打造一个前端故障演练体系。这边也给大家分享一下我们当时遇到的一些核心挑战
核心挑战 演练的切面视角
我们可以对刚才大家看到的前端需要关注的生产和消费过程再做细分,我这里简单划为4部分,也就是研发、工程、发现和响应4个需要验证的切面。因为前端的生产和消费链路整体是线性的,在前置切面引入的缺陷,会流经后续的切面。举例来说,研发环节不慎写的故障代码,会走过构建发布,在客户端上执行,会验证到系统的发现能力和组织的响应能力。也就是故障的注入具有贯通性。
在设计故障注入能力时需要考虑到这种贯通性,刚才的举的例子就可以抽象为源码变异注入这种能力。当然,如果我们对某种注入能力做特殊的设计,也能做到让它的影响面限制在某个固定切面。比如CR变异注入,我们通过使变异的diff只在运行时零时产生而不实际产生落库的代码,做到了避免污染后续的切面。在这样的整体设计之下,我们会具备一个由一批贯通性的注入能力以及一批基础注入能力组成的立体注入体系。
一个注入能力影响的切面越多,验证的链路越完整,但实施成本越高。业务应该根据自己需要验证的环节选择实施成本最低的注入方式。因为篇幅所限,无法一一介绍每种注入能力的实现,我这里就举一个例子。
一天,小陈的老板说,又要大促了,需求很多,上得又仓促,有点问题在所难免,但还是希望有比较好的处理表现。小陈觉得,当下团队业务的主要问题是:
这种情况下,小陈考虑针对发现和响应两个切面做演练能力设计,这对应的就是上图贯通性的静态资源劫持能力。
稳态假设
对照混沌工程的5大要素,首先应该具备一个稳态假设。
这里的稳态也就是各类能代表当前业务能力的关键指标的集合,并且他们在一个固定的业务周期里处于相对稳定的状态。现在大家或多或少也有自己的一些监控体系,系统监控关注物理设施的运行状况,业务监控关注业务指标的变化,而端侧监控通常在端上以主动上报的方式记录客户端上的关注点,也是前端可用性领域核心关注的一种监控类型。找到了稳态就可以制定一个假设,以小陈团队的某个xx管家业务为例,如果把主js搞挂,那么同比来看,xx管家首页js error数/率会飙升,并且首页主功能模块曝光率会骤跌。
雏形示意
于是一个静态资源劫持的注入方式雏形就诞生了。蓝军小陈把有问题的主js发到cdn上,页面引用了这个js,引起故障,错误日志上报至监控平台触发告警,由红军来跟进处理。
但这个雏形存在着几个致命的问题:
如何使影响范围可控?
如果做个演练动不动就要搞个故障出来,很可能得不偿失。这个话题在混沌工程中可以归纳为最小化爆炸半径。我的个人看法是,前端目前安全生产水位暂时无法支持任何形式的生产环境有损演练,其收益风险比过低。
有一部分同学表示,那如果仿照灰度策略,控制比例,让极小部分的用户受影响,是不是解决这问题了?但是这样会遇到一个新的问题。
如何达到触发监控告警的量级?
控制比例到极低,固然可以使影响范围也控制到极低,但多数以绝对值为触发方式的监控项将同时失去作用,也就起不到演练的作用了。
那如果换个思路。独立出一套环境来演练,我们自己去模仿用户访问网站来触发故障,再放大一下倍数呢?
问题是,多数业务对用户的角色、权限有明确划分,有些故障点的触发具有一定业务逻辑,比如需要通过一系列操作才能执行到相应的功能。亦即:
如何仿真用户的线上访问?
如何触发具有一定业务逻辑的故障点?
解法
让我们看一下这些问题如何解决。
前端安全环境
首先,为了保障业务的安全可控,我们确实需要一套与线上隔离的环境。资源故障不注入到线上的cdn,参与演练的客户端的资源请求都被劫持到drill cdn;为了使数据交互不会产生脏数据,数据请求都被劫持到drill server数据服务,当然这里数据服务是否有更轻量的实现方式后文会提到,这里我们先记录一下。这样一来,演练整体与线上能形成物理隔离,达到对业务的无损演练。整个参与演练的客户端,drill cdn以及数据服务三部分我们可以称之为三位一体的前端安全环境。
在实现上,以PC场景为例,我们深圳团队落地了名为f2etest的webdriver云调度体系,依赖云端的虚拟机,及每机并发的复数个浏览器实例,实现了客户端数量上的弹性物理放大。假设有20台机器,每台起10个浏览器,我们可以并发的客户端数就达到了200。由这些浏览器发起对待演练页面的访问,相应的资源请求则通过ip代理方式,劫持到演练用cdn源站上。这个源站上实现了流量转发、资源版本控制、网络状态模拟、错误资源存储等能力,足以模仿绝大部分资源请求响应的可能遇到的情况。这套f2etest也可以承载功能验证,UI自动化等任务,已经上云,欢迎大家试用。
右下角Hub Plan是为了解决我们上文中提及的用户和业务逻辑仿真以及数据服务的轻量实现而存在的,在下下页PPT中为大家介绍。
运行时,因缺陷注入导致的页面故障,在有监控部署过的情况下,会产生日志上报。通过Whistle修改上报请求中与监控平台达成的协议部分,如上报次数、采样率、通道标志位等,使日志得以被监控平台采信并达到触发告警的量级。
在下面这张PPT中也可以得到展示。
蓝军的缺陷不再直接注入线上,而是注入前端安全环境,并通过内置的弹性物理放大,触发故障上报日志。为了保证大流量业务的使用,我们设计了双重的放大机制,在安全环境外通过与平台的上报协议锚定,包括反降噪协议和逻辑放大协议,保证量级满足。逻辑放大倍数N应取安全环境中参与上报的客户端数与页面正常线上PV比值的倒数。
通过前端安全环境我们做好了最小化爆炸半径,以及触发监控量级的准备。接下去要解决的就是用户仿真、业务逻辑和数据服务的问题。
流量构造
这里思考这样一件事,过去我们在尝试ui自动化领域的实践中,认为保持业务的功能性逻辑f不变时,若用户数据x和用户行为y也不变,则应得到不变的页面反馈R,包括显示、交互等等。我们反面来看这个思考,如果保持用户数据x和用户行为y不变,R发生了变化,则都可以归因为f发生了变化。所以我们可以把用户数据、行为和反馈都存下来,通过回放的方式去触发相应的逻辑,一旦与原先的反馈产生不同或者说故障,都可以说是因为现在的页面逻辑有缺陷注入导致。这套机制能解决了我们上述提及的3个问题。
我如果去考虑传统UI自动化方案,也就是书写用例的方式来做。会有几个老大难问题。
最后我们寻找到的钥匙是流量即用例,也就是上文提及的Hub Plan
在我们实现的这套机制中,将经授权许可的用户行为、资源以及原生api等数据通过serviceworker统一上报。行为经过数据聚合和清洗后得到经过精简的、但能覆盖页面绝大部分业务逻辑的核心用例,并存储起来。资源等其他数据也类似处理。当演练调度执行器要求回放时,将用户行为用例交给f2etest调度webdriver进行执行,针对执行过程中发起的请求,由service worker和whistle拦截后以之前存储的资源作为返回。整体形成了一个沙盒。
这是用户行为用例和回放沙盒的截图示意。这些都由统一的演练调度设施管理。实际演练运行过程中演练发起和参与者都不需要关注这些,这套设施会无感执行,此处只做演示。
之前我们提及到一个轻量的数据服务实现方式,通过Hub这一点也得到了解决。我们不需要去重新建设一个隔离的后端服务环境,那样做需要梳理的上下游依赖关系极其恐怖。取而代之的,我们将用户请求到的html、其他响应和原生API返回结果等数据也作为资源存储下来。当回放开始,浏览器对html的请求通过whistle拦截,返回的页面上包含了当时采集到的其他数据,这些数据被写到全局变量中;这样做的好处是显著提高了回放性能,避免了频繁的数据交互。此外页面上还被注入了一个请求匹配SDK,当前域的service worker,和其他下发的自定义拦截策略。当页面需要请求时service worker会根据拦截策略进行拦截,通过SDK寻找全局变量中相匹配的结果并返回结果。
最后整体上,一个静态资源劫持的注入能力流程就如图所示。
蓝军发起演练后,通过演练平台下发策略,在安全环境中调度起大量客户端实例,由这些客户端发起对待演练页面的访问。指定的静态资源请求会被劫持到高度定制的drill cdn模拟源站上,这个模拟源站可以返回任意指定的响应状态,包括但不限于资源加载失败、超时,甚至返回内含指定错误代码的JS。这些响应状态返回到客户端后,造成相应的故障。若监控部署准确,则会有告警通知到受演练的红军,进一步验证红军响应动作。若监控部署不当甚至未部署监控,则本次演练结果相当于红军直接失败。
我们通过监控体系,安全环境,流量回放,支持到了稳态假设,最小化爆炸半径,生产仿真和多样化事件这4个混沌工程的核心要点,并且策略之间有重叠配合。另一个自动化持续我们也在逐步探索中,当下呢先抛出一些我们的思考。
弱点诊断
我们尝试通过总结过往故障、常见故障、其他业务故障甚至过往不达标的演练,推导出一个具有高质量的剧本池,其中的剧本各自运用了不同类型的注入方式,来验证各自切面的能力。通过循环往复的演练,我们能得到一块业务的总体得分,通常预防、发现、响应这几部分形成一张雷达图,便于我们针对薄弱环节挑选剧本反复演练。那么怎么做到自动化呢?我们预计会尝试在money test的领域探索,通过对稳态系统的破坏性注入尝试,发掘引起稳态变化剧烈的几次尝试,对应系统的薄弱环节,自动生成具体的演练方案,形成剧本,扩展剧本池,很好地解决了剧本需要人为补充的问题。
演练实战
了解了演练部分设计之后,让我们用一个实际的例子来描述下如何进行一次成功的演练。
一次演练必须有定义明确的演练计划,小陈挑选了团队中一个名为某管家首页的业务,意图验证监控覆盖率、告警有效性及人员响应效率 。注入方式就是刚才介绍的静态资源劫持,且对故障注入后的现象做了两个核心的稳态假设:
首页JS Error数/率显著上升
发表评论