服务网格是用于处理服务到服务通信的专用基础设施层。它负责通过包含现代云本机应用程序的复杂服务拓扑来可靠地传递请求。实际上,服务网格通常实现为轻量级网络代理的数组,这些代理与应用程序代码一起部署,而不需要知道应用程序。
自从几十年前首次引入以来,我们了解到分布式系统能够实现我们之前甚至无法思考的用例,但它们也会引入各种新问题。
当这些系统很少且简单时,工程师通过最小化远程交互的数量来处理增加的复杂性。处理分发的最安全的方法是尽可能地避免它,即使这意味着跨各种系统的重复逻辑和数据。
但是,作为一个行业,我们的需求进一步推动了我们,从几台大型中央计算机到成千上万的小型服务。在这个新世界中,我们必须开始摆脱困境,应对新的挑战和开放性问题,首先是以个案方式完成临时解决方案,然后再采用更复杂的方法。随着我们更多地了解问题领域并设计出更好的解决方案,我们开始将一些最常见的需求纳入模式,库和最终平台。
我们刚开始联网计算机时发生了什么
由于人们首先考虑让两台或更多台计算机相互通话,他们设想了这样的事情:
image
服务与另一个服务对话以实现最终用户的某个目标。这显然是一个过于简化的视图,因为在代码操作的字节和通过线路发送和接收的电信号之间转换的许多层都丢失了。但是,抽象对于我们的讨论是足够的。让我们通过将网络堆栈显示为一个独特的组件来添加更多细节:
image
自20世纪50年代以来,上述模型的变化一直在使用。最初,计算机很少见且价格昂贵,因此两个节点之间的每个链接都经过精心设计和维护。随着计算机变得越来越便宜和越来越流行,连接数量和通过它们的数据量急剧增加。随着人们越来越依赖网络系统,工程师需要确保他们构建的软件符合用户所需的服务质量。
为了达到理想的质量水平,还有许多问题需要回答。人们需要找到机器相互找到的方法,通过同一条线路处理多个同时连接,允许机器在没有直接连接时相互通信,在网络中路由数据包,加密流量等。
其中有一种称为流量控制的东西,我们将以此为例。流控制是一种机制,可以防止一台服务器发送的数据包比下游服务器可以处理的数据包多。这是必要的,因为在网络系统中,您至少有两台不同的独立计算机,彼此之间并不太了解。计算机A以给定的速率向计算机B发送字节,但不能保证B将以一致且足够快的速度处理接收的字节。例如,B可能忙于并行运行其他任务,或者数据包可能无序到达,而B被阻止等待应该首先到达的数据包。这意味着不仅A不会具有B的预期性能,而且还可能使事情变得更糟,因为它可能会使B超载,现在必须排队所有这些传入的数据包以进行处理。
有一段时间,人们期望构建网络服务和应用程序的人员将处理他们编写的代码中的上述挑战。在我们的流控制示例中,它意味着应用程序本身必须包含逻辑,以确保我们不会使用数据包重载服务。这种网络繁重的逻辑与您的业务逻辑并排。在我们的抽象图中,它将是这样的:
image
幸运的是,技术迅速发展,很快就有足够的标准,如TCP / IP,将流控制和许多其他问题纳入网络堆栈本身。这意味着该段代码仍然存在,但它已从您的应用程序提取到您的操作系统提供的底层网络层:
image
这种模式非常成功。即使需要高性能和可靠性,也很少有组织能够使用商用操作系统附带的TCP / IP堆栈来推动业务发展。
我们刚开始使用微服务时发生了什么
多年来,计算机变得更加便宜,无处不在,上面描述的网络堆栈已经证明自己是可靠连接系统的事实上的工具集。随着更多节点和稳定的连接,业界已经使用各种类型的网络系统,从细粒度的分布式代理和对象到由更大但仍然分布很多的组件组成的面向服务的体系结构。
这种极端分布带来了许多有趣的高级用例和好处,但它也出现了一些挑战。其中一些挑战是全新的,但其他挑战只是我们在谈论原始网络时所讨论的更高级别的版本。
在90年代,Peter Deutsch和他在Sun Microsystems的工程师编写了“ 分布式计算的8个谬误 ”,其中列出了人们在使用分布式系统时所做的一些假设。彼得的观点是,这些可能在更原始的网络架构或理论模型中都是如此,但它们在现代世界中并不成立:
- 网络可靠
- 延迟为零
- 带宽是无限的
- 网络是安全的
- 拓扑不会改变
- 有一个管理员
- 运输成本为零
- 网络是同质的
将上面的列表作为“谬误”谴责意味着工程师不能忽视这些问题,他们必须明确地处理它们。
更复杂的是,转向更多的分布式系统 - 我们通常称之为微服务架构 - 在可操作性方面引入了新的需求。我们之前详细讨论了其中一些,但这里有一个快速清单,列出了一个必须处理的内容:
- 快速配置计算资源
- 基本监控
- 快速部署
- 易于配置存储
- 轻松进入边缘
- 认证/授权
- 标准化的RPC
因此,尽管几十年前开发的TCP / IP堆栈和通用网络模型仍然是使计算机相互通信的强大工具,但更复杂的架构引入了另一层要求,再一次,工作的工程师必须满足这些要求。这样的架构。
例如,考虑服务发现和断路器,这两种技术用于解决上面列出的几个弹性和分布挑战。
由于历史往往会重演,第一批基于微服务构建系统的组织遵循的策略与前几代网络计算机的策略非常相似。这意味着处理上述要求的责任由编写服务的工程师负责。
image
服务发现是自动查找哪些服务实例满足给定查询的过程,例如,Teams
需要查找服务实例的服务,该服务Players
的属性environment
设置为production
。您将调用一些服务发现过程,该过程将返回合适服务器的列表。对于更多的单片体系结构,这是一个简单的任务,通常使用DNS,负载平衡器和一些端口号约定(例如,所有服务将其HTTP服务器绑定到端口8080)。在更分散的环境中,任务开始变得更加复杂,以前可能盲目信任其DNS查找以查找依赖关系的服务现在必须处理诸如客户端负载平衡,多个不同环境(例如,分段与生产)之类的事情),地理位置分散的服务器等。如果您需要的只需一行代码来解析主机名,那么现在您的服务需要多行样板来处理更高分布引入的各种极端情况。
断路器是Michael Nygard在他的书“ 发布它”中编目的模式。我喜欢Martin Fowler对模式的总结:
断路器背后的基本思想非常简单。将受保护的函数调用包装在断路器对象中,该对象监视故障。一旦故障达到某个阈值,断路器就会跳闸,并且所有对断路器的进一步调用都会返回错误,而根本不会进行受保护的呼叫。通常,如果断路器跳闸,您还需要某种监控器警报。
这些非常简单的设备可以为您的服务之间的交互增加更多可靠性。然而,就像其他一切一样,随着分配水平的提高,它们往往变得更加复杂。系统中出现问题的可能性随着分布呈指数级增长,因此即使是“断路器跳闸时某种监视器警报”等简单的事情也不一定是直截了当的。一个组件中的一个故障可以在许多客户端和客户端的客户端之间创建一系列效果,同时触发数千个电路跳闸。过去只需几行代码的东西现在需要大量的样板来处理仅存在于这个新世界中的情况。
事实上,上面列出的两个例子可能很难正确实现,像Twitter的Finagle和Facebook的Proxygen这样的大型复杂库变得非常流行,作为避免在每个服务中重写相同逻辑的手段。
image
上面描述的模型是大多数开创微服务架构的组织,如Netflix,Twitter和SoundCloud。随着他们系统中服务的数量的增长,他们也偶然发现了这种方法的各种缺点。
即使在使用像Finagle这样的库时,最昂贵的挑战可能是,组织仍然需要花时间从其工程团队那里建立粘合剂,将图书馆与其他生态系统联系起来。根据我在SoundCloud和DigitalOcean的经验,我估计在100-250工程师组织中遵循这一策略,需要将1/10的员工专门用于构建工具。有时这种成本是明确的,因为工程师被分配到专门用于构建工具的团队,但更常见的是价格标签是不可见的,因为它表现为从而不再使用产品。
第二个问题是上面的设置限制了可用于微服务的工具,运行时和语言。微服务的库通常是针对特定平台编写的,无论是编程语言还是JVM等运行时。如果组织使用除库支持的平台以外的平台,则通常需要将代码移植到新平台本身。这窃取了稀缺的工程时间。工程师不必再从事核心业务和产品,而是必须再次构建工具和基础架构。这就是为什么像SoundCloud和DigitalOcean这样的中型组织决定只为其内部服务 - Scala和Go分别支持一个平台。
该模型值得讨论的最后一个问题是治理。库模型可能会抽象出解决微服务架构需求所需功能的实现,但它本身仍然是需要维护的组件。确保成千上万的服务实例使用相同或至少兼容的库版本并非易事,每次更新都意味着集成,测试和重新部署所有服务 - 即使服务本身没有受到任何影响更改。
下一个逻辑步骤
与我们在网络堆栈中看到的类似,非常希望将大规模分布式服务所需的功能提取到底层平台中。
人们使用更高级别的协议(如HTTP)编写非常复杂的应用程序和服务,甚至不考虑TCP如何控制其网络上的数据包。这种情况是我们对微服务所需要的,其中从事服务工作的工程师可以专注于他们的业务逻辑,避免浪费时间编写自己的服务基础架构代码或管理整个机队的库和框架。
将这个想法融入我们的图表中,我们最终会得到如下内容:
image
遗憾的是,更改网络堆栈以添加此层不是一项可行的任务。许多从业者发现的解决方案是将其作为一组代理实现。这里的想法是服务不会直接连接到它的下游依赖项,而是所有流量都将通过一小块软件透明地添加所需的功能。
在这个空间里首次有记载的发展所用的概念,侧柜。边车是一个辅助过程,它可以在您的应用程序旁边运行,并为其提供额外的功能。2013年,Airbnb撰写了关于Synapse和Nerve的文章,这是他们开源实施的边车。一年后,Netflix推出了Prana,这是一款专用于允许非JVM应用程序从NetflixOSS生态系统中受益的边车。在SoundCloud,我们构建了侧车,使我们的Ruby遗产能够使用我们为JVM微服务构建的基础架构。
image
虽然有几种这样的开源代理实现,但它们往往设计用于特定的基础架构组件。例如,当涉及到服务发现时,Airbnb的Nerve&Synapse假设服务在Zookeeper中注册,而对于Prana,应该使用Netflix自己的Eureka服务注册表。
随着微服务架构的日益普及,我们最近看到了新的代理浪潮,其灵活性足以适应不同的基础架构组件和偏好。这个领域的第一个广为人知的系统是Linkerd,由Buoyant根据他们的工程师之前在Twitter的微服务平台上的工作创建。很快,Lyft的工程团队宣布Envoy遵循类似的原则。
服务网
在这种模型中,您的每个服务都将具有配套代理边车。鉴于服务仅通过sidecar代理相互通信,我们最终得到类似于下图的部署:
image
Buoyant的首席执行官William Morgan发现,代理之间的互连形成了一个网状网络。在2017年初,William为这个平台编写了一个定义,并将其称为Service Mesh:
服务网格是用于处理服务到服务通信的专用基础设施层。它负责通过包含现代云本机应用程序的复杂服务拓扑来可靠地传递请求。实际上,服务网格通常实现为轻量级网络代理的数组,这些代理与应用程序代码一起部署,而不需要知道应用程序。
可能他定义中最强大的方面是它不再将代理视为孤立的组件,并承认它们形成的网络本身就是有价值的东西。
image
随着组织将其微服务部署转移到更复杂的运行时(如Kubernetes和Mesos),人们和组织已开始使用这些平台提供的工具来正确实现网状网络的这种想法。他们正在从一组独立的代理工作中脱离出来,转向一个适当的,有点集中的控制平面。
image
查看我们的鸟瞰图,我们看到实际的服务流量仍然直接从代理流向代理,但控制平面知道每个代理实例。控制平面使代理能够实现访问控制和度量收集等操作,这需要合作:
image
最近宣布的Istio项目是此类系统最突出的例子。
现在要完全了解服务网格在大规模系统中的影响还为时尚早。这种方法的两个好处对我来说已经很明显了。首先,不必编写定制软件来处理微服务架构的最终商品代码,这将使许多小型组织能够享受以前只有大型企业可用的功能,从而创建各种有趣的用例。第二个是这个架构可能让我们最终实现使用最佳工具/语言的梦想,而不必担心每个平台的库和模式的可用性。
致谢
Monica Farrell,Rodrigo Kumpera,Etel Sverdlov,Dave Worth,Mauricio Linhares,Daniel Bryant,Fabio Kung和Carlos Villela对本文的草稿进行了反馈。
修订记录
- 2017年3月3日 - 首次发布
- 2017年8月5日 - 收到反馈意见