解决演练环境问题的斗争一直是开发团队面临的持续挑战,安全共享可以解决这个问题。
译自 Why Staging Doesn't Scale for Microservice Testing,作者 Arjun Iyer。
想象一下:你是一家中等规模工程团队的开发人员。你刚刚完成了一个很棒的新功能的编码。你很兴奋地将其部署到演练环境进行测试。但是等等...这是什么?演练环境又坏了。
听起来熟悉吗?如果你沮丧地点头,你并不孤单。与演练环境的永恒斗争,是软件开发史上的一个古老故事。
在我们深入探讨细节之前,让我们先解决一个关键问题:为什么你应该关心演练环境?答案很简单:它们是你代码和生产环境之间的最后一道防线。在当今的微服务架构和持续交付的世界中,稳定演练环境的重要性不可言喻。
请考虑以下情况:根据2021 年 DevOps 状态报告,高绩效 IT 组织部署代码的频率是低绩效组织的 973 倍。这意味着每天都有大量的代码变更进入演练环境。如果这些环境不稳定或不可靠,就像在流沙上建造房屋一样——灾难不可避免。
演练环境故障的影响是深远的:
- 发布延迟: 当演练环境不稳定时,你精心制定的发布计划就会泡汤。突然之间,你承诺下周发布的功能看起来更像是下个月了。
- 开发人员生产力: 没有什么比等待演练环境可用或稳定更能扼杀生产力的了。这就像当你已经迟到了一个重要会议时,却困在了交通堵塞中。
- 质量保证噩梦: 如果演练环境不可靠,你怎么能相信你的测试是有效的?这就像在有人不断更改文字的情况下,试图校对一本书。
- 生产风险增加: 当演练环境无法捕获错误时,这些错误就会进入生产环境。我们都知道,在生产环境中修复错误就像给正在参加马拉松比赛的病人做手术一样。
现在,让我们来解决问题的核心。为什么演练环境难以保持稳定?我们正在处理一个经典的“两难境地”:
- 开发人员需要一个稳定的演练环境来测试他们的代码变更。
- 将代码变更部署到测试环境的行为可能会使环境对其他人来说变得不稳定。
欢迎来到演练环境的世界!
多年来,工程团队想出了各种解决此问题的变通方法。让我们看看其中一些“解决方案”及其优缺点。
一些团队使用 Slack 来“锁定”演练环境进行测试。过程如下:
开发人员 A:“锁定演练环境一个小时。不要碰!” 开发人员 B:“但我需要测试我的关键错误修复!” 开发人员 A:“太可惜了,我比你先到!”
虽然这可能适用于一个由三个开发人员和一只宠物仓鼠组成的团队,但它无法扩展到更大的团队。这就像在一个整个办公大楼只有一个浴室的情况下——混乱是不可避免的。
其他团队转向功能标志,认为他们找到了演练环境管理的圣杯。其理念是在演练环境中禁用新代码,直到它准备好进行测试。
但问题是:你最终仍然需要启用该标志,猜猜怎么了?你又回到了起点,为一个稳定的环境而战,以测试你现已启用的功能。
一些组织决定创建多个“较低”环境。理论上,开发人员可以在这些环境中进行初始测试,然后再移至“正式”演练环境。听起来不错,对吧?
错了。这种方法就像用修建更多高速公路来解决交通拥堵一样。当然,它可能在最初有所帮助,但很快,你就会在更大范围内处理相同的问题。
这种方法的缺点是显而易见的。较低环境通常缺乏与第三方服务的完全集成,而是依赖于无法真正代表生产行为的模拟。跨多个环境协调更新会变成一个后勤噩梦,引入不一致和错误。资源消耗很大,包括基础设施成本和持续维护。
也许最阴险的是,这些较低环境可能会造成一种虚假的安全感。它们可能无法揭示在完整演练环境或生产环境中会出现的错误,从而导致后续出现令人不快的意外。随着你的微服务架构的增长,维护这些多个环境的复杂性也会随之增加。
讽刺的是,在试图解决演练环境问题时,我们却创造了一系列新的问题。这就像典型的治标不治本。我们需要的不是更多环境,而是更智能地利用现有环境。
另一种解决演练环境问题的方法是在代码合并之前使用模拟进行集成测试。这种方法有其优点,但也存在重大挑战。
从积极方面来看,模拟可以提供一种快速轻便的方式来模拟依赖关系,使开发人员能够独立测试他们的代码。这可以加快开发过程,并及早发现基本的集成问题。对于简单的系统或定义明确的接口,模拟可以成为测试库中的有效工具。
然而,随着系统复杂性的增加,模拟的局限性变得更加明显。维护服务依赖关系的准确模拟需要付出巨大的努力,尤其是在快速发展的微服务环境中。随着服务的改变,模拟也需要相应地更新,这可能成为一项耗时的任务。
此外,虽然模拟可以发现某些类型的问题,但它们可能会错过仅在真实集成环境中才会发生的细微交互。这会导致一种错误的安全感,即代码通过所有基于模拟的测试,但在实际集成场景中失败。
维护全面且最新的模拟所需的努力通常会随着系统复杂性的增加而呈指数级增长。在某个时刻,在创建和更新模拟上投入的时间可能会超过它们带来的益处,尤其是在与在更现实的环境中进行测试相比时。
那么,我们注定要生活在一个演练环境永远无法修复的世界吗?正如我们所见,传统的演练环境方法充满了挑战。为了克服这些挑战,我们需要换个思路。
这让我们想到了一个很有前景的新方法:在共享环境中进行金丝雀式测试。这种方法允许开发人员在共享的演练环境中独立测试他们的更改。它的工作原理是创建受开发人员更改影响的服务的“影子”部署,同时保持环境的其余部分不变。这种方法类似于生产环境中的金丝雀部署,但应用于演练环境。
主要优势在于开发人员可以共享环境而不会影响彼此的工作。当开发人员想要测试更改时,系统会创建一个通过环境的唯一路径,其中包含他们修改的服务,同时使用所有其他服务的现有版本。
此外,这种方法能够以每次代码更改或拉取请求的粒度进行测试。这意味着开发人员可以在开发过程的早期阶段发现问题,通常是在代码合并到主分支之前。通过在这个阶段识别问题,团队可以显著降低将错误引入共享代码库的可能性。
这种方法提供了彻底测试所需的隔离,而无需管理多个完整环境的开销。它允许团队在类似生产的环境中进行彻底的测试,及早发现问题,并同时维护一个稳定的共享环境。
正如著名的软件工程师和教育家Kent C. Dodds所写:
你的测试越接近你的软件的使用方式,它们就能给你带来越多的信心。
— Kent C. Dodds 🌌 (@kentcdodds)
[2018 年 3 月 23 日]
这种方法不仅仅是空想,它正在科技界一些最具创新力的公司中成功实施:
- DoorDash 的快速反馈循环:DoorDash 实施了一个系统来增强Kubernetes产品开发在生产环境中的反馈循环。此设置允许在生产环境中快速迭代和稳健地测试新功能,从而加快开发速度,而不会影响服务的质量或性能。在 DoorDash 的工程博客上了解更多信息。
- Uber 的 SLATE:Uber 通过 SLATE(短期应用程序测试环境)解决了演练环境挑战,允许开发人员创建按需的、短暂的测试环境,这些环境集成了依赖项的生产实例。此设置增强了隔离并提高了开发速度,为端到端测试需求提供了稳健的解决方案,同时最大程度地减少了对生产系统的影响。在 Uber 的博客上了解更多关于 SLATE 的信息。
- Lyft 的环境管理:Lyft 开发了一个复杂的控制平面来管理其共享开发环境,从而显着简化了与其微服务架构相关的复杂性。该系统通过改进演练和开发环境中服务依赖项的管理和可见性来提高开发人员的生产力。在 Lyft 的工程博客上了解 Lyft 的方法。
这些行业领导者已经认识到安全共享环境在解决永恒的演练困境方面的强大力量。通过采用这种方法,他们能够提高开发人员的生产力,改进代码质量并加快发布周期。
- 成本效益:消除对多个资源密集型环境的需求。
- 隔离测试:开发人员可以测试更改,而不会影响其他人的工作。
- 早期问题检测:在合并之前,在单个代码更改级别捕获问题。
- 现实测试:使用与生产环境非常相似的共享环境。
- 可扩展:适应大型团队和复杂的微服务架构。
借助涉及 Kubernetes 和 Istio 和 Linkerd 等服务网格的云原生基础设施,实施能够安全共享演练环境的复杂路由比以往更容易。这种方法允许团队使用“演练环境中的金丝雀”策略验证更改,提供了一种强大的方法来尽早发现问题,同时保持稳定的共享环境。
此外,这种方法还支持“功能预览”等强大功能。这些功能允许开发团队创建其服务的临时、隔离版本,以展示特定功能或更改。产品经理、QA 团队和 UX 设计师可以通过移动或 Web 前端访问这些预览,就像最终用户一样。
此功能允许在开发过程中对新功能进行全面测试和早期反馈。利益相关者可以在现实环境中与该功能进行交互,在代码最终确定之前识别潜在问题或改进。
通过促进这种级别的协作和早期测试,功能预览显着简化了软件交付流程。它们降低了后期更改的可能性,提高了功能在进入生产环境之前的质量,并最终导致更快、更自信的发布。
演练环境的斗争一直是开发团队面临的持久挑战。是时候摆脱破损的演练环境、延迟发布和沮丧的开发人员的循环了。
通过采用允许安全共享演练环境的方法,团队可以解决多年来困扰生产前测试的核心问题。这种策略颠覆了传统的 Catch-22:这种方法不是部署破坏演练环境,而是通过每次测试来增强稳定性。
也许最重要的是,这种方法使团队更接近主分支始终准备好在生产环境中部署的理想状态。通过在代码合并之前启用全面测试,大多数问题会在早期被发现并解决,从而显着降低了合并后出现意外的风险。
想知道如何将这些策略实施到您的开发流程中?访问Signadot 的网站,了解如何为您的演练环境带来稳定性、为您的开发工作流程带来效率以及为您的发布带来信心。您的团队的生产力(和理智)将感谢您。