微服务的先决条件


Phil Calcado于2017年5月在布达佩斯的Craft Conf发表了关于Microservices几个先决条件的演讲,被分布式领域的其他作品纷纷引用,在本文中他详细扩展论述了这些微服务的先决条件。下面是他的原文:

2017年5月,我在布达佩斯的Craft Conf发表了关于Microservices经济学的演讲。您可以在这里观看视频录制或在这里阅读幻灯片。在这个演讲中,简要讨论了一系列微服务提出的先决条件,这是我认为你在考虑广泛采用微服务架构风格之前应该采取的措施。自从演讲以来,这些措施列表已被分布式系统空间中的其他作品所引用,所以我想通过这个帖子来扩展这些先决条件。

为什么会烦恼?
当您决定采用微服务器时,您将明确地不仅仅是将一个或几个移动的部件移至更复杂的系统。在这个新的世界中,许多运动部分以团队不可预知的方式行事,因为服务会不断创造,改变和消灭。该系统具有能够快速更改和调整适应能力,可为您的组织带来巨大的收益,但您需要确保有一些保护措施已经到位,否则您的交付可能在不断变化的情况下停滞不前。

这些保护措施就是我们在这里讨论的先决条件。无视其中一部分或所有部分虽然可以也可以成功,但是它们的存在有望提高成功的可能性,并减少迁移过程中的噪音和混乱。

诚然,这里列出的先决条件很长,根据您的组织的文化和基础设施,可能需要大量投资。应该预期这个前期成本。微服务架构不应比其他风格更容易,您需要确定您在作出决定之前评估投资回报率。

真的是很多。我可以尝试让你放心,告诉你这些不是微服务的必需品,但除非你有一个中型到大型的团1队,否则我不认为你应该使用微服务。您应该以最简单的方式开始您的架构,这样可能会起作用。

您不需要对这里提到的先决条件提供复杂甚至成熟的答案。即使在像DigitalOcean和SoundCloud这样的成熟公司,我们也是一开始进行非常基本的实现。包括有很多的探索和通常的复制和粘贴。

你应该确保你有关于这些列表问题有一个可行答案,但不要迷恋它们。你今天的答案不一定是长期的解决方案。随着时间的推移,您将会学到更多的知识,同时技术空间也在逐渐成熟,其中一些技术已经成为现货。

另一个选择是忘记微服务,并将您的架构的下一个迭代集中在更粗糙的服务上。更少的可移动部件肯定会大大降低这些先决条件要求,随着工程组织和平台的成熟,您随时可以减少服务的大小和范围。

先决条件
正如我在前一段关于我们在SoundCloud中采用微服务的演讲中所讨论的,我非常感谢Martin Fowler在微服务先决条件方面的工作:

1.快速配置:你可在几个小时内启动一个新服务器

2.基础监控:使用很多这些松耦合的微服务,很难发现问题根源,因此监控是基础的。

3.快速应用部署:管理这么多服务,你应该能快速部署它们,无论测试环境或产品环境。


在Martin对微服务这些特点定义之前,SoundCloud已经开始向这种架构迁移,但是我们也得出了相同的结论。当我转到我的第二个大型微服务实施时,这次是在DigitalOcean,我们再次证实了上述项目的需要。同时,我已经确定了一些其他缺少的项目,这些项目已被证明对于成功实施微型服务至关重要:

1.存储易于配置
2.轻松访问边缘
4.认证/授权
5.标准化RPC

所以我的完整列表中的微服务先决条件按优先顺序如下:

1.计算资源快速配置

2.基本监控

3.快速部署

4.存储易于配置

5.轻松访问边缘

6.认证/授权

7.标准化RPC


1.快速配置计算资源

Martin马丁说:

您可以在几个小时内启动新的服务器。自然,这适合云计算,但如果没有全面的云服务,这也是可以实现的。为了能够进行这种快速配置,您需要大量的自动化 - 它可能不需要完全自动化才能开始,但是以后要做出大量的微服务时,就需要这样做。

他在这里使用"服务器servers"这个词语,但是现在可以使用实际的服务器,虚拟机,容器,功能或所有这些功能的组合概念表达。这就是为什么我添加了“计算资源”到这个项目,因为它几乎意味着任何会运行你的代码的一些CPU和内存。

十多年前,我们曾经在应用服务器上部署我们的服务和应用程序。这些是大量软件层,复用了单个计算单元,以便许多应用程序和服务可以同时使用它,并且这种部署架构是多年的规范。当时,每秒服务几百个请求被认为是Internet规模,这种设计允许组织最大限度地利用不同服务的昂贵硬件,有时甚至提供不同公司共享昂贵应用服务器的多租户服务。


随着时间的推移,计算资源的成本大大降低,无论是内部部署还是由云提供。这减少了对应用服务器层的需求。即使应用程序服务器为部署在其上的应用程序(例如自动安全,服务发现,管理面板等)也提供了开箱即用的服务,但是会由此带来操作这些更复杂的服务器变得非常昂贵。最重要的是,随着流量的增加,我们从垂直扩展到水平可伸缩性,这些产品从未得到很好的支持。

所有这些趋势促使我们发展成当前使用的最常见的部署架构,其中服务实例与计算资源之间存在1:1的关系。


这种1:1的关系直接影响微服务架构。如前所述,微服务术语还不是很明确,但有一点你可以绝对肯定,当有人说这句话的是,他们将有很多的小服务。鉴于上述部署架构,这也意味着您将拥有大量的计算单位。这就需要自动,快速,灵活地配置计算单元来满足您的微服务需求。

当我在2015年加入DigitalOcean时,花了很多时间与我的团队一起考虑了我们的内部系统,即云的“控制面”。之后,它主要由三个不同的单体monolith组成,它们通过主数据包定义的一组固定的虚拟机上运行。我们很快就清楚,这是一个复杂且容易出错的工作流程,它不能扩展。在迁移到微服务之前,我们不得不改进这些配置情况。

负责解决这个问题的团队决定使用容器和Kubernetes作为我们的新计算平台,我们在2016年的头六个月都确保所有新服务都部署在这个新系统上,并将旧版单体系统迁移到这个新系统。这一步使我们能够继续推进我们的架构变革,同时仍在努力解决我们不得不发布的许多新产品。事实上,我们的监控和警报功能是在新系统中开发的第一个产品,并且是动态追踪“子弹”的,整合了平台团队的后端很积压的和优先的事项。


2.基本监测

马丁说:

随着许多松散耦合的服务在生产环境中合作,事情肯定会出现错误,这些在测试环境中难以发现。因此,必须建立一个监测制度,以迅速发现严重问题。这里的基准是检测技术问题(计数错误,服务可用性等),但也值得监控业务问题(例如检测订单下降)。如果突然出现问题,则需要确保您可以快速回滚,因此...

如上所述,微服务架构是一个复杂的系统。你可以控制和预测有很多。许多这种混乱是由不断变化的状态驱动的,因为服务每天部署和重新部署多次。

事实证明,这个问题并不是微服务的独有之处。事实上,John Allspaw和其他人在为Flickr和Etsy开发单体架构同时,已经在近十年的时间内围绕这些挑战构建了一个工具箱。在他的工作中,Allspaw记录了处理快节奏变化的关键因素:

换个方法:

MTTR(平均修复时间)比MTBF(平均故障间隔时间)更重要

(对于大多数类型的F)

我绝对不会说,失败应该是可以接受的条件。我认为,由于失败将会发生,它同样重要(或在某些情况下更重要)花费时间和精力来应对失败,而不是试图阻止它。他同意哈蒙德所说:

如果你认为你可以防止失败,那么你就不会发展自己的反应能力。

平均故障间隔时间(MTBF)是操作期间系统故障之间经过的时间。平均修复时间(MTTR)是解决操作中的问题所需的平均时间。简单来说,MTBF告诉您,故障发生的频率如何,而MTTR告诉您一旦检测到问题就能解决问题。在不断变化的系统中,您无法控制MTBF,所以最好投资拥有一个伟大的MTTR。

当您开始投资减少MTTR时,您开始意识到这样太频繁。事件恢复并不是事件管理中唯一的一步,我一再看到事件管理中最痛苦的部分是平均检测时间(MTTD)。此度量反映了事件发生与操作员检测到的时间,从而触发恢复过程。

这使您意识到您需要投资遥测来快速检测问题。虽然这种需求存在于任何架构风格中,但微服务在这里增加了一些不同的挑战。在一个单体架构中,你总是知道问题出在哪里:显然是在单体之中!剩下的是找出问题所在的类或功能。在这个世界上,诸如NewRelic之类的复杂工具可以帮助您进入代码级别来检测问题:

虽然这些工具也广泛应用于微服务体系结构中,但是在检测到哪些服务或服务以意想不到的方式运行后,它们才有用。由于许多服务相互协调调用才能完成每个请求,您还需要确保您可以将服务彼此进行比较,从而允许您精确定位异常值,而不会因环境问题而分心。

所以,您应该更喜欢整个微型服务的基本遥测,并通过多项核心服务的详细遥测。

在SoundCloud,我们在微服务监控方面的经验使我们专注于标准化仪表板和警报。我们确保每一个服务都以相同的粒度导出一组通用的指标。然后,我们使用它们来构建仪表板,首先在Graphite上,最终在Prometheus上,允许我们在不同的服务之间比较这些指标。


仪表板为我们提供了减少MTTD所需的洞察力,但是我们很快意识到这还不够。随着数十个小型团队部署了数十个服务,您需要能够将潜在问题与变更相关联,新代码部署和基础设施更改。在我们的例子中,我们构建了一个小型Feed服务能够反映工程师和自动化工具所做的所有更改。我们更改了我们的部署工具,以确保每一次更改,即使您只是通过添加一个或两个其他实例来扩展服务,都能被报告给该Feed。

检查任何最近的变化成为事件检测工作流程的第一步。

3.快速部署

马丁说:

管理许多服务,您需要能够快速部署它们,无论是测试环境还是生产环境。通常这将涉及一个可以在不到几个小时内执行的DeploymentPipeline。在早期阶段,一些手动干预是好的,但是您将会尽快完成自动化。

马丁提出这项是对前一项的直接跟进。他指出,事件的迅速恢复可能需要部署一些新的代码或配置,因为部署应尽可能快速和确定。

我完全同意这一点,但对我来说,另外一个基本的驱动因素是与事件响应没有直接关系的前提条件。使用单体可以容忍有一个麻烦和非常手动的部署过程。容忍出现错误的风险,每次部署成本都高,因此通常每次部署通常都会包含许多变化,影响由不同的人和团队开发出来的各种功能。

使用微服务,它变成另一个方面:对单个功能的单一更改可能需要部署许多服务。您将不得不执行不同服务的许多部署,重要的是这些部署中的每一个都是廉价的,风险很低。正如马丁所说,建立管道往往适合这个方式。

这是我们在SoundCloud中最挣扎的先决条件。我们的单体使用Capistrano和shell脚本部署在一个漫长而互动的过程中。包含有关如何执行部署的说明文件都非常复杂,并且有许多角色通常被称为tax_code.md。

正如我在SoundCloud的工程博客中首次描述的那样,一开始我们就决定我们的服务可以用任何语言和运行时。这个策略有几个优点,但其中的缺点是我们无法对应用程序的部署做出假设。我们有.jar文件,Ruby脚本,Go二进制文件...一切。作为所有代码库的最小公分母,我们将其标准化:

1. 每个服务都有一个Makefile位于服务代码目录的根目录下。这个脚本有一个build目标,即使它所做的只是调用另一个构建系统,如SBT或Rake。

2.make命令完成后,部署工具将创建一个包含该目录中所有内容的SquashFS伪像,包括代码,资产和生成的二进制文件。

3.代码还应该包含描述如何运行每个进程的Heroku样式的procfile。在部署了SquashFS映像之后,操作员必须按照与Heroku相同的方式放大/缩小版本的进程。

这个过程允许我们扩展到十几个服务,但是所需的手动步骤的数量太高,这导致了风险。更糟糕的是,这些较低级别的原语并不直接支持更有趣的部署技术,如蓝色/绿色部署,金丝雀canary 服务器,甚至是A / B测试。由于这些问题,大多数团队最终在提供的工具之上建立了自己的胶水代码。由于这些脚本被视为辅助项目,它们的代码质量差异很大。我们有一些由于缺陷脚本导致的大量生产事故事件。

随着我们从十几个增长到接近一百个服务,我们为部署投入了更好的工具。最大的区别是我们从工程师的笔记本电脑中部署到建立管道(我们开始使用Jenkins,但最终转移到ThoughtWorks的GoCD)。繁重的自动化导致了我们更多的确定性和更快速的构建,特别是当每天的部署次数从一个到数百个时,这就正是人们所需要的了。

易于配置存储

大多数从单体到Microservices的公司将拥有一个单一,大型,维护良好的数据库服务器。经过多年的数据存储,这个数据库设置通常是很好的调整,有许多复制品,并且与其他系统(如搜索引擎和数据分析工具)完美集成。

尽管如此,使用这个单体数据库还有很多挑战,其中大部分与架构更新有关。更改或删除表和列需要手动确保没有代码通过编程或元编程依赖于旧的结构。几年之后,每个经典的数据库重构已被应用到单体,而内部的工具已经被编写为最常见的工具。

然而,采用微服务的团队重新使用共享模式仍然很常见。“只有一个额外的表/列/视图应该不会是一个大问题”,温水煮青蛙。除了上述变化管理开销之外,您的速度下降,您虽然远离数据耦合却在不同微服务之间JOIN耦合了(JOIN是数据库SQL常用耦合语句),这些微服务本来不应该知道对方的内部细节的。

迁移到微服务主要方向是公司倾向于投入大量的配置和部署,但是忘记提供一个合理的方式让存储系统团队可以依靠。即使玩转MySQL服务器只需要几秒钟,当将这些隔离的系统置于生产就绪时,也要注意到这几秒钟涉及到许多需要注意的项目。复制,备份,安全性,调优,遥测和其他几个方面很重要,而且您的工程师经常在设置和拥有数据库系统这两个方面拥有零经验。

如果您正在开展云原生架构,许多数据库即服务产品之一允许您将这些操作任务外包给供应商。在像DigitalOcean这样的云提供商,我们没有这个选择 - 我们是拥有电脑的臭名昭着的其他人。我们有一个中期计划,可以快速,轻松地提供MySQL数据库供内部使用,但是第一步要解锁我们向微服务的转移,这一点要远远不够雄心勃勃。我们投入了大量时间来清理和记录标准化厨师食谱(比喻繁琐细节)和相关脚本,这样可以使任何一个团队都可以将生产级的MySQL服务器升级到无需太多麻烦。


5.轻松访问边缘

公司中第一个微服务器通常是孤立地写成的,由单个人或小团队开发作为解决他们遇到的一些挑战比如业务功能需求等。因为这个服务的范围通常很小,所以作者在开发和测试环境中得到一个能正常工作版本是很容易的。一旦接近生产阶段,工程师面临着一个问题:如何将这个新事物公开给我的外部用户,我的本地网络以外的用户?

与数据库面临的挑战类似,这里的主要问题是,在此之前,公司中没有人必须考虑这个问题。单体架构是完全暴露给用户的,可能是暴露给整个公共互联网多年。它具有保护您的内部VPN免受恶意或错误用户的所有功能。速率限制,日志记录,功能标志,安全性,遥测,警报,请求路由...都在那里。

由于某些原因,这个第一个微服务器最常见的策略似乎是将其直接暴露给互联网,无论是在不同的主机名或特殊路径下。

该技术依赖于客户端(通常是移动或单页应用)将请求的多个端点的结果合并。这对于一个服务可以工作得很好,但随着更多的服务被添加,该模型倾向于破坏。

不仅客户端代码变得越来越复杂,而且在公共互联网上暴露服务不是一件简单的工作。如果您认真对待客户和用户,您需要确保面向互联网的系统可以处理各种事件,从恶意用户到意外的高峰流量。让每一个单一的服务处理这一点大大增加了每个服务的成本。

我们需要限制一些服务暴露在互联网上。我们考虑建立一个绑定所有服务的网关,但是通过小型工程团队和大量产品递交努力以后,我们发现需要一个中间解决方案。

在我们的例子中,我们开始使用单体作为网关:

客户端 ---->单体服务 ---->多个微服务


所以每个请求都将首先发到单体服务,然后在后台调用其他服务。这个策略在接下来的几个服务中运行良好,但有几个问题。我们发现的第一个问题是,它在新服务和单体服务之间创造了一些奇怪的耦合,其中改变服务通常需要改变并重新部署单体。除此之外,我们的单体运行着一个非常老版本的Rails。没有良好的并发性,我们不得不依靠对所有这些新服务的序列化请求。随着我们添加的每项新服务,我们的请求时间都在增加。

随着时间的推移,我们投资了一个正确的网关,这是在我们迁移到BFF模式的同时引入的。

客户端 ----> Edge Gateway边缘网关 ---->多个微服务 单体服务


我们在DigitalOcean采取了类似的道路,但是由于我们有三个单体而不是一个,迁移到边缘网关不那么容易。

6.认证/授权

这是另一个重要的组成部分,我们通常只想到第一个微服务器接近生产时,微服务器是如何知道谁在提出这个请求,以及他们拥有什么样的权限?对这个问题的天真的方法是使每个微服务器需要用户标识符作为对其的所有请求的一部分,然后根据您的用户授权/认证系统或数据库进行检查。

当您有单体服务时,这可能足够好,但是您会在授权系统中添加更多冗余和昂贵的呼叫,随着服务越多越是这样。

在SoundCloud中,当我们使用单体作为边缘网关时,我们已经在内存中了解有哪些用户以及他们可以做什么。我们更改了HTTP客户端代码,始终将此信息作为从单体到下游服务的所有HTTP请求的头部中传递。

一旦我们迁移到边缘网关,我们决定这个组件将向身份验证服务发出一个请求,并且不仅向用户URN转发地理位置信息和OAuth范围,而且这些请求可以在每次向下游服务发出的呼叫中提供 -在音乐行业,您被允许访问的内容取决于您遇到的国家与您的身份相同。


一旦我们将大多数内部服务迁移到基于Finagle的微服务的内部SDK中,这种更复杂的设置是可能的。


7.标准化RPC

最后一点好像并不重要,但真的很重要。您的架构中包含所有这么多组件,它们将以不可预测的方式进行协作。您需要确保它们可以相互通信,这意味着能够理解字节传送的方式,还有什么约定和标准。

在SoundCloud中,我们初始服务的明显选择是HTTP和JSON。不幸的是,说“只使用HTTP和JSON”实际上并不是行得通。这些格式不会告诉你如何发送授权信息,如何进行分页,如何进行跟踪,RPC使用什么样的架构风格,如何处理故障等。我们也开始痛苦的文字协议的性能问题,还有一些数据密集型团队继续使用Thrift。

对于今天迁移到大量分布式架构的任何公司,我建议您所有的内部RPC采取gRPC。最重要的是,每次你需要连接一个消息时,比方说把它发布在像Kafka这样的总线上,你应该使用 protocol buffers,以便在push和pull用例中都有相同的序列化协议。

gRPC和 protocol buffers本身不会为您提供所需的一切。在SoundCloud和DigitalOcean,我不得不提供一个团队专注于围绕RPC建立微型服务工具,大多数公司都负担不起的。这些天,我们在服务网格的概念中有一个有趣的解决方案,它是“使服务到服务通信安全,快速,可靠”的专用基础设施层。作为一个长期的Finagle用户,我最喜欢的这个游戏中的播放器是linkerd,但在这个空间中有几个选项。

原文:Calçado's Microservices Prerequisites