怎样才能进步研制功率?是依靠于各自独立的本地开发测验环境,还是依靠完整的端到端测验?Lyft的这一系列文章介绍了其开发环境的前史和发展,协助咱们考虑怎么打造一套适合大规模微服务的高效研制环境。本系列共4篇文章,这是第4篇。原文:Scaling productivity on microservices at Lyft (Part 4): Gating Deploys with Automated Acceptance Tests[1]
本文是本系列文章的第四篇,也是最终一篇,主要讲述咱们在Lyft面对越来越多的开发人员和服务时,怎么扩展开发实践。
- 第一部分:开发和测验环境的前史
- 第二部分:优化快速本地开发
- 第三部分:运用掩盖机制在预发环境中扩展服务网格
- 第四部分:根据主动检验测验的布置门禁(本文)
在之前的文章中,咱们描绘了怎么运用上下文传播然后允许多个工程师在同享的预发环境中进行端到端测验。现在咱们看看另一部分——主动端到端测验,咱们将介绍怎么构建一个可伸缩解决方案,让工程师在布置到出产环境之前更有决心。
重新考虑端到端测验
本系列的第1部分介绍了在CI中运转集成测验时遇到的许多应战。服务和工程师数量的爆破式增加导致运转测验的长途开发环境(Onebox)难以扩展,运转测验需求耗费很多时刻。每个服务的集成测验也变得非常笨拙,差不多需求花费一个多小时才能完结,并且信噪比极低。工程师们不信任失利的测验,经常将其忽略,否则就会浪费更多的调试时刻,而这会让事情更糟糕。
在掩盖900多个服务的数千个集成测验中,有一小组真实有价值的端到端场景,咱们以为保护这些场景至关重要。比方用户能够登录,恳求搭车,付出车费。这些场景中的问题在内部称为SEV0(严重程度最高的事情)。这些问题将使乘客无法到达需求去的当地,或者使司机无法获得收入,因而有必要不惜一切代价解决问题。
检验测验
当咱们着眼于想要保护好的具有最高价值的端到端测验时,即使粗略看一下,也会发现它们看起来很像检验测验。这些测验在不需求了解内部完结细节的情况下,描绘了用户怎么与Lyft渠道进行的交互。
考虑到这一点,咱们决定从分布式模型(每个服务界说自己的集成测验集)转移到小型集中式检验测验集。这样做有两个好处。在技术上,将场景放在一同能够协助咱们消除重复,并在相关服务之间同享测验代码。在组织上,由一个单一的一切者担任协调这些测验的整体健康状况(这些测验仍然由不同的人编写和修改),并规划更好的阻隔,以避免失控。
另一个要害决议计划点是何时运转这些测验。咱们期望改动将端到端测验作为“内部”开发循环的一部分运转(在第2部分中描绘了)的习惯,之前开发人员习惯于在开发进程中多次履行端到端测验,以替代单元测验或调用特定服务端点等战略。相反,咱们想让CI快速运转,并鼓舞人们更习惯于将端到端测验推延到进程的后期。由于这些原因,咱们选择在布置到预发环境后运转检验测验,作为出产环境布置的门禁战略之一。
构建结构
引擎
首先,需求一个引擎来供给简略的界面,以便像真实的用户相同运用Lyft的API。幸运的是,咱们现已在预发和出产环境中构建了类似的东西用于生成流量(拜见第1部分中的预发环境)。这个库由几个要害概念组成:
- Actions(动作): 与Lyft API交互,例如,RequestRide Action会调用Lyft API,供给所需的出发地和目的地,开端寻找司机。
- Behaviors(行为): 大脑会根据必定的概率决定下一步该做什么,例如,假设刚刚恳求搭车,下一步应该撤销还是继续等候司机?
- Clients(客户端): 代表与渠道交互的设备,通常是运转Lyft运用程序的手机,用于存储状况和协调actions/behaviors。
这三个简略理念的结合是咱们过去5年在预发和出产环境进行主动化测验的战略的基础,为咱们供给了很好的服务。可是,要在检验测验中重用这些战略仍然需求考虑一个重要的不同——行为的概率性质(probabilistic nature)。在构建带有负载测验的行为时,考虑到随机性非常有助于消除意外bug,因而咱们将其规划为类似于含糊测验(fuzzer)[2]的东西,而这并不适合于具有确认性的对特定流的检验测验。
因而咱们更新了库,允许客户端依照一系列步骤操作,作为行为的替代方案,然后弥补了这一距离。步骤能够是以下任何一个:
- Actions(动作): 如上所述,仅仅履行一个API调用。
- Conditions(条件): 堵塞下一步的履行,直到某个表达式为真,并有可选的超时时刻,例如,司机或许会等到到达起点时,经过PickedUp动作通知Lyft现已接到了乘客。
- Assertions(断言): 保证客户端状况看起来与预期相符,例如,咱们期望保证在恳求搭车之前完结报价。
界说测验
在actions、conditions和assertions构建块就位之后,接下来需求决定在新的集中式主体系中界说测验的格式。从前的集成测验是在代码中进行的,可是咱们决定切换到运用自界说配置语法界说检验测验。尽管有利有弊,但咱们发现以这种办法界说测验能够供给一种强制功能,以坚持测验的简略性和一致性,然后让更多人能够读/写测验,以及更好的保护测验。配置中露出有限的接口,将大多数逻辑完结到前面说到的库中,然后能够更好的与其他检验测验或负载测验运转程序同享。
把一切这些放在一同,看看下面的测验场景示例:
# test_scenarios/standard_ride.yaml
description: A standard Lyft ride between 1 driver and 1 passenger
clients:
- role: passenger
steps:
- type: Action
action: Login
- type: Action
action: SetDestination
- type: Assertion
assertions:
- ["price_quote", "between", 10, 20]
- type: Action
action: RequestRide
- type: Condition
conditions:
- ["ride_status", "equals", "completed"]
- type: Action
action: TipDriver
- role: driver
steps:
- type: Action
action: Login
- type: Action
action: EnterDriverMode
- type: Condition
conditions:
- ["ride_request", "exists", true]
- type: Action
action: AcceptRideRequest
- type: Action
action: PickUpPassenger
- type: Condition
conditions:
- ["location", "equals", "destination"]
- type: Action
action: DropOffPassenger
值得注意的是,假如从零开端的话,那么根据现有的测验结构(如Cucumber/Gherkin[3])或许会更好。而在咱们的比如中,扩展现有的流量生成东西比测验运用这些技术要简单得多。
布置门禁
咱们将端到端测验从PR兼并之前调整到兼并后布置之前,很大程度上进步了开发人员的出产力。尽管一个典型的PR或许平均会包括4个提交(每个提交都会运转测验套件),但通常一次只会布置一到两个PR,因而开发人员由于测验的不安稳而被堵塞的频率简直减少了10倍。此外咱们预期,假如PR没有了端到端测验供给的虚伪的安全感,开发人员就会把更多资源投入到单元测验中,并且会建立feature flag等更安全的发布战略(咱们在第3部分中评论过现在能够根据每个恳求进行配置掩盖)。
为了完结这一点,咱们扩展了内部布置体系的门禁(deploy gate)概念。布置阶段能够被一个或多个门禁所堵塞,门禁表示允许布置进入下一个阶段之前有必要满足的条件。一个典型的门禁比如就是咱们在每个预发布置中都会包括的bake time,这个门禁保证布置的体系运转了特定长度的时刻,以便有任何问题的时候,能够给继续的模仿流量一个触发告警的时机。
每个检验测验都会将门禁添加为被测服务的依靠项,一旦预发环境布置完结,相应的门禁就会发动测验运转,并报告成功或失利。为了不至于减缓开发人员的速度,检验测验的方针是在比默认bake time(10分钟)更短的时刻内完结。
实践
测验什么?
能够说,转换到检验测验的最困难的部分之一是确认检验测验的构成规矩,并在很多集成测验中运用这些规矩。在挑选了数百个集成测验并与服务一切者评论之后,咱们确认了以下规范:
- 检验测验应该只代表要害事务流,应该从用户视点描绘与Lyft渠道的端到端交互。
- 咱们显然不想测验一切场景,因而检验测验对事务有必要是要害的。作为套件中最昂贵的测验,咱们无法担负测验那些短时刻中断不会对事务形成严重损害(即SEV0)的边缘情况或事务流。
尽管站在测验金字塔[4]的视点来看这些规范似乎很明显,但仍然比预期更难运用。开发人员关于删去集成测验的成果感到不安,不管该测验是否被很好的了解或者是否从前捕获过bug。为了简化转换,咱们与团队协作,根据上述规范剖析每一个测验。大约95%的测验要么是多余的,要么能够重写为带有mock的单元测验。剩余的几个测验在去除冗余后被组合成大约40个总的检验测验场景,这些场景将替代一切的集成测验。
成果
从咱们用预发环境检验测验替代CI中的集成测验以来,现已过去了大约6个月。场景数量坚持相对安稳,咱们现已将掩盖范围扩大到运送和自行车&踏板车产品,每周进行几千次测验。咱们看到的主要好处是:
- 大多数PR都能在10分钟内经过单元测验并准备好兼并(之前包括端到端测验时需求30分钟)。
- 从服务中删去了数千个集成测验,无需花费很多时刻来保护和调试这些测验。
- 检验测验迭代起来更快、更可靠,只需求不到一分钟的时刻就能够准备一个以预发为方针的本地环境(运用前面说到的本地开发作业流,而Onebox的初始设置时刻为1小时)。
- 自从将端到端测验从PR中移除后,走漏到出产阶段的bug数量并没有明显增加。
- 检验测验每周在问题走漏到出产环境之前将其捕获。
- 咱们还没有看到期望的那样,在单元测验方面有额定的出资。这需求进一步的调查来了解为什么,以及咱们是否能够/应该做的更多来改动这一点。
将来的作业
到目前为止,咱们对从这些改变中看到的出产力进步感到兴奋,但仍然展望未来的许多改善。
预发阻隔
目前,在将更改布置到预发环境后立即运转测验,或许会在呈现问题时搅扰到其他预发环境用户。咱们期望在本系列第三篇文章中评论的预发掩盖作业的基础上,在将新版本的服务公开给其他用户之前对其运转检验测验。这将为布置增加额定的延迟,因而需求评估收益是否大于成本。
测验掩盖率
考虑到测验背后的主要方针是进步可靠性,咱们期望做更多更直接的改善,而不仅仅是保护这些测验。咱们知道,今天的测验存在距离,这些距离是由之前的集成测验构建的,并与服务一切者评论了哪些事务流重要,需求被测验掩盖。为了缩小距离并进步可靠性,需求保证真实的iOS和Android客户端所做的一切最常用的API调用都能在这些测验中得到表现。一个想法是对流经咱们体系的真实流量和模仿流量之间的增量进行更多的剖析,或许能够经过对分布式跟踪东西的进一步出资完结。
测验场景健康度
最初,渠道团队手艺策划了每个检验测验,并密切重视其安稳性。随着咱们继续扩展更多的事务线,期望每个操作(API调用)具有更细粒度的可调查性,这样就能够主动将毛病发送给适宜的团队处理。这并不意味着分散一切权(咱们以为为更广泛的测验和渠道保留中央一切者非常重要),仅仅更快的提醒产品团队他们的服务呈现了毛病,并尽量减少手艺作业。
总结
在本系列文章中,咱们仔细剖析了Lyft多年来是怎么发展开发和测验办法,并寻求不断进步开发人员的出产力。咱们介绍了Lyft开发环境的前史(第1部分),转向本地优先的第一次研制环境转型(第2部分),在预发环境阻隔测验服务与envoy掩盖(第3部分),用布置期间用一组检验测验替代PR触发的较重的集成测验(本文)。
尽管这种办法或许没办法适用于一切环境,但在缩短开发人员的反馈循环方面取得了很大的成功,并极大简化了支持测验环境的基础设施,然后协助开发人员继续输出代码。
References:
[1] Scaling productivity on microservices at Lyft (Part 4): Gating Deploys with Automated Acceptance Tests: eng.lyft.com/scaling-pro…
[2] Fuzzing: en.wikipedia.org/wiki/Fuzzin…
[3] Gherkin: cucumber.io/docs/gherki…
[4] The Pratical Test Pyramid: martinfowler.com/articles/pr…
你好,我是俞凡,在Motorola做过研制,现在在Mavenir做技术作业,对通讯、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终坚持着浓厚的爱好,平常喜爱阅读、考虑,相信继续学习、终身生长,欢迎一同交流学习。
微信大众号:DeepNoMind