不要拖延解决问题,直到它们变得太大而难以管理。
译自 How To Fail at Microservices,作者 Ashley Davis。
没有人希望微服务失败。没有人一开始就打算这样做。但鉴于微服务的难度和我们可能失败的惊人方式,这很容易做到。
我通常是微服务的倡导者,至少在它们有必要且实施良好的情况下。通常,我会教授更便宜、更直接、更有效地使用微服务的方法,但在本文中,我想采取不同的方式。我将描述我们可能在微服务方面失败的多种方式。最后,我们将看看我们可以做些什么来摆脱我们创造的(或者可能是我们继承的)地狱。
在开发中,有很多种失败的方式。以下列表是根据该领域的经验编制的。这些都是来自真实生产应用程序的真实示例。
我将把所有这些问题都与微服务联系起来。但我承认,许多只是普通的开发问题,被微服务激发到了 11 级。让我们开始吧。
在开发过程中,尤其是在试图快速移动时,你可能会忍不住将非必要问题的解决推迟到以后。
你等待解决基础设施、自动化部署、自动化测试、代码重用等问题的时间越长,这些问题就越根深蒂固,也越难解决。随着你扩展微服务的数量,你遇到的任何问题也会随之扩展。
不断出现的问题最终可能会超过你的团队解决问题的能力。此时,你可能会抱怨团队资源不足。如果你能早点解决最关键的问题,而不是让它们失控,就好了。
如果只有一个关于使用微服务的固定规则,请避免使用共享数据库。
但也许你错过了备忘录,现在你所有的微服务都使用同一个数据库。
现在,你没有数据封装,服务之间高度耦合,单点故障,以及可扩展性瓶颈。
恭喜你,你绕过了微服务的许多优势。
这是微服务,所以我们应该尽可能地缩小它们,对吧?
你可能喜欢根据技术问题(而不是业务问题)来建模你的服务。例如,你可以为每个数据库查询创建一个微服务(我实际上见过)。
你可能认为每个数据库查询一个微服务是分离你的数据问题的好方法,但随后你就会想起这是不可能的,因为你所有的服务都共享一个数据库。
将微服务建立在技术问题上会导致不必要的微型服务,维护成本不断增加。随着服务数量的减少,你需要更多服务才能使其正常工作——这使得整个应用程序比实际需要的更复杂,因为你的服务比实际需要的更小。越来越多的服务会导致通信路径呈指数级增长,网络运营成本也大幅增加。
微服务应该根据业务需求建模,而不是技术问题。每个服务都应该具有业务所需的规模,而不是更小。是的,这意味着你的服务将具有各种规模,但那又怎样——没有人关心你的服务有多大或多小。
当有人开始使用你已建立的微服务应用程序,你发现自己向他们解释手动部署每个微服务的说明时,你应该知道事情进展不顺利。
但也许你没有意识到错误易发的手动部署对开发过程的负面影响。你的开发人员将浪费时间在可以避免的问题上。
自动化部署是我们必须在只有少数微服务时就做好的事情之一。等到你拥有数百个微服务时,就会变得更加困难。
你需要绝对控制你的微服务应用程序的部署吗?你是否希望部署像单体应用程序一样工作?创建一个部署管道(如果可能的话,手动管道),该管道可以同步地批量部署所有微服务。 很棒的工作。现在,您拥有了一个分布式整体。这有点像两者的最坏情况,它破坏了微服务最重要的优势之一。当微服务可以独立部署,而不是一次性全部部署时,它会使开发人员和团队分离,允许他们以自己的速度发布更新,而不会受到部署过程或其他团队的影响。
独立的微服务部署速度很快,也可以快速回滚。另一方面,锁步部署速度很慢。每次测试和部署都需要更长的时间,而且在出现问题时回滚更加困难。
以锁步方式组织、测试和部署所需的时间会降低您执行持续部署的能力。尽管如此,我猜想快速响应客户需求的能力对您来说并不重要。我希望您有一个很棒的 QA 部门。(但似乎没有人再有 QA 部门了)。
使您的开发团队难以测试他们的更改,以真正阻碍他们的效率。当然,在复杂的云环境中工作时,很容易陷入这个陷阱,尤其是在为了完成工作而节省测试时间的情况下。
在本地复制复杂的配置可能非常困难,因此很容易失去在本地测试的能力,并养成必须先部署才能测试代码更改的坏习惯。
本地测试代码更改是快速开发速度所必需的。当您的开发人员被迫通过将其(手动!)部署到开发、QA、测试或登台环境来测试他们的代码时,他们的速度会非常慢。
通过本地测试,开发人员可以进行代码更改并立即看到结果,从而获得快速反馈,并允许快速实验以测试新想法并快速找到问题的解决方案。开发人员在云环境中测试他们的代码必须经历整个部署过程,即使是微小的实验性代码更改也是如此。在云中(在运行在别人的计算机上而不是他们的本地开发计算机上),调试代码中的任何问题也会变得更加困难,更不用说由您的手动部署过程引起的问题了。
本地测试对于快速开发速度至关重要,但还不够。开发人员还需要访问一个现实的类似生产的测试环境。理想情况下,他们应该能够在与客户相同的环境中重现来自客户错误报告的问题。假设出于安全或隐私原因,这是不可能的。在这种情况下,他们需要系统和基础设施来尽可能接近地(减去任何私人或个人客户信息)并尽快地复制生产环境。
如果您的开发人员无法轻松地重现客户问题,那么客户遇到的问题与开发人员追查的问题之间将存在巨大的脱节。如果开发人员无法准确可靠地看到客户遇到的实际问题,他们将花费时间追查错误的问题。
一些团队似乎只知道单元测试,这很奇怪。他们不知道还有各种各样的测试技术吗?
如果锤子是你的唯一工具,那么每个问题看起来都像钉子。如果您只知道单元测试,那么如果您只使用测试范围内的各种测试技术,从单元测试到端到端测试和手动测试,您将调试许多原本可以避免的问题。
此外,尽管运行速度很快,但单元测试是最耗时的测试编写和维护。我认为单元测试应该保留给业务逻辑。但这意味着将您的业务逻辑从所有技术和演示问题中提取出来——这可能是您没有做的事情。如果您想对任何东西进行单元测试,您可能正在尝试对所有东西进行单元测试,即使是不值得进行单元测试的代码也是如此。
当您强迫您的开发人员将所有时间都花在单元测试上时,他们根本没有时间去探索更省时的测试形式。有效地使用集成测试、端到端测试、契约测试和快照/输出测试可以以更少的努力获得更多覆盖范围。
手动测试仍然很重要,我想说,在没有为您的产品制定手动测试计划的情况下,您不应该尝试使用自动化测试。投资一个每个人都可以使用的手动测试计划非常便宜和容易;它比自动化测试便宜得多。就我个人而言,即使您在整个自动化测试范围内取得成功,我仍然认为它仍然是必要的。
任何微服务应用程序都需要大量的工具。我们需要用于构建、部署、管理服务、基础设施、测试、调试和可观察性的工具。假设您在这些领域缺乏足够的工具。在这种情况下,它会导致对您的开发团队造成巨大的但难以察觉的损耗,他们将在黑暗中摸索,只是为了弄清楚发生了什么,更不用说如何解决问题了。
有时,当工具不存在时,我们也需要构建工具。幸运的是,大型公司已经为我们完成了大部分工作,并且有很多好的工具存在(Postman 和 Backstage 都是例子)。但是,当大型公司没有为我们提供服务时,或者如果我们是一家大型公司,或者如果我们只是试图做一些不同或独特的事情,而一些定制工具会有所帮助……我们需要技能和动力来自己构建这些工具(如果我们友好,可以将其作为 开源供其他人受益)。
除了足够的工具之外,我们还需要在使用工具、实践开发和设计分布式应用程序方面具备足够的技能。
微服务成功需要一种追求技术卓越并践行持续改进的文化。如果您没有这种文化,您可能会难以将开发人员提升到分布式应用程序所需的熟练程度。
当您使一切尽可能复杂(架构、代码、开发流程)时,您会给开发人员带来认知负担,导致他们在琐事和繁忙的工作上浪费不必要的时间。
尽可能多地拥有小型服务,以便任何给定的编码任务必须分散到多个服务中。确保每个服务中的代码分散,即使进行最小的更改也需要遵循跨文件的交互网络。使用损坏且有漏洞的抽象,以便这些抽象带来的任何优势都被它们带来的复杂性所掩盖。
当您的流程不必要地复杂时,您的开发团队将参与一种“流程戏剧”,其中成功地遵循流程的复杂规则胜过为客户和企业提供价值。如果您的开发人员总是很忙,但几乎没有产生真正的价值,也许是时候重新评估您的流程是如何运作的了。
希望,我更喜欢简单的事情。任何现代应用程序都倾向于复杂性,但这并不意味着我们不能尽可能地追求简单性。我们应该积极管理复杂性,将其分解为更简单的部分,并在使事情更简单时使用良好的抽象。
信不信由你,微服务旨在管理复杂性。使用得当,微服务不会造成额外的复杂性;它们有助于处理本来就存在的复杂性。但是,如果使用不当,您的问题将加剧。
根据 敏捷宣言,工作代码通常比文档更受欢迎。这难道不是一个不写文档的好借口吗?
当应用程序变得复杂时,当没有人知道它如何运作时,当难以理解如何测试它时,或者当您希望新员工玩得开心时,文档可能非常宝贵。但前提是它必须一致。
没有文档,新接触应用程序或特定微服务(或几个月没有接触过这些部分)的开发人员必须反向工程代码才能了解如何更新和测试它。让您的开发人员一次又一次地这样做并不有效。任何定期创建或更新文档的开发人员都会帮助未来的开发人员(以及他们未来的自己)每次返回子系统或服务时都能更快地启动和运行。
只有当文档成为您文化中不可或缺且有价值的一部分时,您才能在文档方面取得成功。
最终,确保您的应用程序演变成一个分布式泥球的最佳方法是,不要制定可行的未来计划。
不要制定开发策略。不要对架构有愿景。不要制定管理技术债务的计划。事实上,不要将计划传达给团队。不要从团队那里征求关于架构、代码和开发流程存在问题的反馈。
当然,没有计划的极端反面也可能同样糟糕。一个喜欢微观管理的控制狂架构师可能会以其他方式造成灾难。
我们需要的是介于以下两者之间的东西:一个不断适应现实并从世界中获取新输入的稳固架构愿景,以及一个为开发人员提供做出决策和解决日常问题所需的范围,同时取得持续进展的愿景。
我已经说服你放弃微服务了吗?看看微服务失败的各种方式(提醒一下:我最近在现实中看到了所有这些失败),你可能会想知道为什么有人会考虑使用微服务。
这是一个有效的观点。微服务将花费很多。在采用它们之前,我们必须能够为它们提供合理的理由。如果我们不能证明其益处超过成本,那么我们就没有理由使用微服务。我们还需要一支拥有微服务所需的技能、工具和架构知识的团队。
也就是说,微服务提供了许多好处和有效的用例。公司已经在使用微服务,并且正在以很大的方式失败。使用单体架构可能不合适,从微服务转换回单体架构可能不可行。我们还能做什么?
如果我们正在遭受上述任何或所有失败,那么还有希望。有一些具体的方法可以解决这些问题。
首先要理解的是,这不是非此即彼。这不仅仅是微服务与单体架构的问题。 在这两个极端之间存在一个选择范围,将自己定位在中间某个位置可以让我们获得微服务和单体架构的最佳优势。
第二点需要注意的是,除非组织中适当的级别提供支持,否则任何事情都不会改变。有时,这意味着你需要说服你的同事开发人员改变的必要性,但更有可能的是,你需要说服架构师和经理存在问题(或者很多问题,具体情况视情况而定)。如果高层领导不认识到团队面临的问题,或者不理解改变的必要性,那么你推动改变的努力很可能会让你陷入困境,而不是帮助团队。
也就是说,以下是一些解决上述失败的方法:
- 主动努力改善团队的开发人员体验。尽可能简化和自动化开发流程。尽可能地赋予你的开发人员自主权。倾听他们的反馈,并允许他们解决他们有能力解决的问题。快乐、高效、有效且投入的开发人员是实现我们许多改进的关键。
- 尽早解决问题,如果可以的话。不要等到你已经构建了数百个微服务才开始行动。但如果不是,你必须每天留出时间来改进以下领域:可部署性、可测试性、可调试性和可观察性。
- 基于业务需求和客户问题对你的软件进行建模;不要基于技术问题进行建模。无论它现在处于什么状态……在进行更改之前,以正确的方式对任何更改或添加进行建模。
- 不要让你的服务太小。使用建模来确定适当的大小,而不是试图使它们尽可能小。考虑合并感觉太小或单独无法发挥足够作用的相关服务。
- 自动化部署必须完全自动化且极其可靠。如果不是,这是你应该首先修复的事情,这样团队就不会在可避免的部署问题上浪费时间。
- 自动化测试对于扩展多个服务的开发非常重要,我不仅仅是在谈论单元测试。学习你能学习的每种测试技术,并确保团队正在使用最有效的测试套件。为独特的用例构建自己的测试工具,但只有在交付的价值超过构建它们的成本时才这样做。
- 重建你的本地测试能力。如果你的开发人员只能在云中复制软件进行测试,那么你将陷入严重困境。投入必要的时间,让你的开发人员能够在提交代码之前,在他们的本地计算机上可靠地测试他们的代码更改和添加。如果系统本身太大而无法在本地进行测试,那么你必须找到在模拟或模拟其余部分的同时生成系统部分的方法。
- 无论你是否擅长测试和调试问题以及找出如何解决问题,这都会占用大量的开发人员时间。如果你没有积极地提高应用程序的可调试性和可观察性,它很可能会朝着错误的方向发展。购买或构建你需要的工具。让你的开发人员访问生产或类似生产的环境。找到让你的开发人员轻松地复制客户问题的方法,以便为开发人员提供最佳的机会来寻找正确的问题。
- 不要害怕文档。以与代码相同的关注度和欣赏度来审查和奖励文档。培养分享知识的文化。认识到文档不仅仅是在维基中记录内容;它可以采用多种格式:
- 每个仓库都有良好的描述和自述文件。
- 可读的代码和代码示例。
- 测试用例和测试计划。
- 架构决策记录。
- 详细的带注释的图表。
- 内部博客文章。
- 为团队录制视频供以后观看。
- 努力打造技术卓越和持续改进的文化。成功使用微服务需要使用许多(很棒的)工具,我们需要一个在分布式应用程序开发各个方面都拥有高技能水平的团队。
- 通过努力消除不值得的复杂性来积极简化您的应用程序。不断努力减少或消除不必要的复杂代码、架构或流程。这减少了开发人员日常工作中的认知负担:您希望他们解决客户问题,而不是来自不必要复杂性的问题。重构(由可靠的测试支持)应该是每个开发人员的日常仪式。如果他们没有这样做,您就不是朝着更直接、更易于管理的系统前进。相反,您正在走向一个越来越复杂和难以管理的系统。
最终,我们必须问自己:微服务是否让我们的开发人员更容易安全可靠地将有帮助且有价值的功能和修复程序投入生产?
微服务架构的目的是降低部署风险。它通过将我们的部署划分为小块来实现。由于每个块都很小,因此更容易理解、更容易测试,也更容易独立部署。当出现问题时,也更容易回滚。
相反,如果微服务使您的部署变得更加困难,则意味着您可能以错误的方式使用它们,并且您可能犯了一些或所有我概述的错误。如果您不愿意投资以使微服务正常运行,那么使用单体可能是您的正确选择。如果这不可行,请考虑使用混合模型,其中包含各种服务大小,从单体到微服务,具体取决于您的需求。
如果您没有对应用程序有强烈的愿景或计划,则可能意味着您正在朝着错误的方向前进。或者更糟的是,每个开发人员都有一个单独的方向。
您拥有的任何计划都必须不断更新以适应不断变化的现实。让团队参与创建和更新计划;这是让每个人都参与其中的最佳方式。将计划传达给利益相关者,并确保管理层了解其重要性。
每个人都必须参与进来。必须有人为架构的愿景代言,并让每个人都同意它。仅仅记录现有架构是不够的;需要有一个未来计划,并且需要由一个不怕深入系统核心并了解其工作原理的人来带头。更宏观的知识是从更低级的知识中构建出来的。