Service Mesh模式起源

分布式系统帮助我们解决了很多过去甚至无法思考的用例,但同时也带来了诸多新的问题。

当系统规模较小、架构较简单时,开发者通过减少远程交互数量来降低额外的复杂性。像处理分发的最安全方法是尽可能避免它,即使这意味着产生跨系统的重复逻辑和数据。

但现实情况是,从开始的几台大型中央计算机,到如今成百上千个小型服务,行业反战的需求要求我们不得不作出突破。我们需要走出困境,解决不断涌现的新挑战和悬而未决的问题,先采取个案处理的临时解决办法,再用更复杂的办法来应对。但我们不断的解决问题、设计出更好的解决方案,解决那些最常见需求的模式、库和平台随之出现。

计算机的联网

起初,人们想要实现两台或多台电脑之间的交互:

为最终用户完成某个目标的服务对话。这显然是一个过于简化的视图,因为在代码操作的字节和通过电线发送和接收的电信号之间转换的许多层都丢失了。但是,抽象概念对于我们的讨论是足够的。让我们通过将网络堆栈显示为一个不同的组件来添加更多的细节:

通过一个服务与另一个服务对话以实现最终用户的某个目标,这里我们把网络堆栈加入进来:

上述模型从20世纪50年代以来一直被反复使用。一开始,计算机很少见而且价格昂贵,因此两个节点之间的每条连接都会被精心设计和维护。然而随着计算机越来越便宜、越来越流行,连接数量和数据量急剧增加,当人们越来月以来网络系统,开发就必须确保所构建软件符合用户的服务质量要求。

想要达到与其水平,就需要解决很多问题,例如让机器找到彼此、在一条线上处理多个并发连接、允许机器在不直接连接的情况下相互通信、在网络间路由包、加密通信等等。

以流控制(flow control)为例。流控制本身是一种机制,阻止一台服务器发送比下游服务器处理上限更多数据包。在网络系统中,我们至少有两台独立的计算机,它们彼此不太“了解”,因此流控制是必要的。计算机A以给定速率向计算机B发送字节,不能保证B将以足够快、一致的的速度处理收到的字节。例如,计算机B可能正忙于并行运行其他任务,或者包可能会无序到达,而计算机B被阻塞等待应该首先到达的数据包。换句话说,计算机A不仅不具备计算机B所期望的性能,而且很可能会让事情变得更糟,可能会使计算机B过载,而计算机B不得不排队等待所有进入的数据包以进行处理。

曾经有一段时间,人们期望构建网络服务和应用的人能够处理他们编写代码中提出的上述挑战。在我们的流控制示例中,意味着应用本身必须包含所需逻辑,以确保我们没有用数据包重载服务。这种大量使用网络的逻辑与业务逻辑并行。抽象图如下所示:

幸运的是,随着技术快速发展,出现了很多标准(如TCP/IP)可以将流控制或其他问题的解决方案集成到网络堆栈中——代码仍然存在,但已经从应用转移到了操作系统提供的底层网络层中。

但考虑到高性能和可靠性,很少有组织能说自己只使用商用操作系统附带的TCP/IP堆栈来推动业务发展。

微服务架构的出现

到如今,计算机已经无处不在,上述网络堆栈也已经证明了自己是可靠连接系统的“事实上的”工具集。由于有了更多的节点和稳定的连接,业界开始使用各种类型的网络系统,从细粒度的分布式代理和对象,到由更大但仍然重度分布的组件组成的面向服务的架构。

这种极端的分布带来了许多有趣的高级用例和好处,但也带来了一些挑战。有些挑战是全新的,而有些挑战只是我们在讨论原始网络时讨论过的高级版本。

在90年代,Peter Deutsch和他在Sun Microsystems的同事们编辑了“分布式计算的8个谬误”,其中列出了人们在使用分布式系统时倾向于做出的一些假设。Peter的观点是,这些在更原始的网络架构或理论模型中可能是正确的,但在现代世界中并不正确:

  1. 网络是可靠的(The Network is Reliable)
  2. 延迟为零(Latency is Zero)
  3. 带宽是无限的(Bandwidth is Infinite)
  4. 网络是安全的(The Network is Secure)
  5. 拓扑不会改变(Topology does not Change)
  6. 有一名管理员(There is one Administrator)
  7. 传输成本为零(Transport Cost is Zero)
  8. 网络是同质的(The Network is Homogenous)

将上述清单斥为“谬论”,意味着开发者不能忽视这些问题,必须明确地解决它们。

更复杂的是,转向更加分布式的系统——我们通常称之为微服务架构——在可操作性方面引入了新的需求。以下是我们必须处理的一些问题:

  1. 快速提供计算资源(Rapid provisioning of compute resources)
  2. 基本的监视(Basic monitoring)
  3. 快速部署(Rapid deployment)
  4. 容易提供存储(Easy to provision storage)
  5. 容易接近边缘(Easy access to the edge)
  6. 身份验证/授权(Authentication/Authorisation)
  7. 标准化的RPC(Standardised RPC)

因此,尽管几十年前开发的TCP/IP栈和通用网络模型仍然是使计算机相互通信的强大工具,但更复杂的体系结构引入了另一层需求,而在这些架构中工作的开发者必须再次满足这些需求。

例如,考虑服务发现和断路器,这两种技术用于解决上面列出的部分弹性和分布式挑战。

历史往往会重演,第一批基于微服务构建系统的组织遵循的策略与前几代网络计算机的策略非常相似,这意味着处理上述要求的责任放在了编写服务的开发者身上。

服务发现是自动查找哪些服务实例满足给定查询的过程,例如名为Teams的服务需要查找名为Players的服务实例,并将属性环境设置为production。我们将调用一些服务发现过程,该过程将返回合适服务器的列表。对于大多数一体化架构,这是一个简单的任务,通常使用DNS、负载平衡器和一些端口号约定(例如所有服务将其HTTP服务器绑定到端口8080)。在分布式环境中,任务开始变得更加复杂,以前信任DNS查找依赖关系的服务现在必须处理诸如客户端负载平衡、多个不同环境、地理位置分散的服务器等等问题。之前我们需要一行代码解析主机名,而现在,我们的服务需要许多行样板文件来处理更高版本引入的各种情况。

断路器是迈克尔·尼加德提出的一种模式,Martin Fowler把该模式总结为:

断路器背后的逻辑很简单,在断路器对象中包装一个用于监视故障的受保护的函数调用。一旦故障达到某个阈值,断路器就会跳闸,并且所有对断路器的进一步调用都会返回错误,不会进行任何保护调用。通常情况下,我们还会需要一些对断路器的检测警报。

这样简单的设备可以为服务之间的交互增加更多可靠性。然而同样的,随着分布水平提高,它们往往会变得非常复杂,系统出现问题的可能性也随之水涨船高,即使是“断路器跳闸就会发出某种监控警报”这种小事也不简单了。过去只需几行代码的东西现在需要大量的样板来处理。

但坦白说,上面列出的这两个例子很难正确地实现,这也是为什么像Twitter的Finagle和Facebook的Proxygen这样复杂的大型库会变得流行,用来避免在每个服务中重写相同的逻辑。

上面所描述的模型被大多数开创性的微服务架构企业所采纳,如Netflix、Twitter和SoundCloud,而随着系统中的服务数量的增加,他们还偶然发现了这种方法的各种缺陷。

即使使用Finagle这样的库,企业仍然需要从其工程团队中投入时间来构建连接库和其他生态系统的“粘合剂”。按照SoundCloud和DigitalOcean的经验,在一个100-250人的工程师组织中,遵循以上策略,需要将1/10的员工用于构建工具。当开发者被分配给专门负责构建工具的团队时,这种成本是很明显的,但更常见的情况是,一些不可见的隐性成本和时间成本。

第二个问题是,上述方法限制了微服务可以使用的工具、运行时和语言。微服务的库通常是为特定平台编写的,无论是编程语言还是JVM之类的运行时。如果一个企业使用的平台不是库支持的平台,那么它通常需要将代码移植到新的平台本身。开发者必须再次构建工具和基础设施,而不是把精力放在核心业务和产品上。这就是为什么像SoundCloud和DigitalOcean这样的中型组织决定只支持一个用于内部服务的平台——scala和Go。

最后一个值得讨论的问题是治理。库模型可以抽象实现处理微服务架构需求所需的特性,但它本身仍然是一个需要维护的组件。确保数千个服务实例使用相同或至少兼容的库版本并非易事,每一次更新都意味着集成、测试和重新部署所有服务——即使服务本身没有任何变化。

下一步

与我们在网络堆栈中看到的类似,将大规模分布式服务所需的特性提取到底层平台是非常可取的。

人们使用更高级别的协议(如HTTP)编写非常复杂的应用程序和服务,甚至不考虑TCP如何控制网络上的数据包。这种情况正是微服务所需要的,在这种情况下,从事服务的开发者可以专注于他们的业务逻辑,避免在编写自己的服务基础架构代码或管理整个团队的库和框架上浪费时间。

把这个想法和我们的图表结合起来,我们可以得出如下结论:

不幸的是,更改网络堆栈以添加这个层并不是一项可行的任务。许多从业者发现的解决方案是将其作为一组代理实现。这里的想法是,服务不会直接连接到它的下游依赖项,而是所有的流量都将通过一个小软件来透明地添加所需的特性。

这种思想下的第一个有记录的开发利用了“sidecars”概念。sidecar是一种辅助进程,在应用旁执行,并提供额外的特性。2013年,Airbnb写了关于Synapse和Nerve的文章,一种sidecar的开源实现。一年后,Netflix引入了Prana,这是一种致力于允许非jvm应用程序从他们的NetflixOSS生态系统中获益的sidecar。SoundCloud也构建了sidecars,让Ruby legacy能够使用为JVM微服务构建的基础设施。

虽然有几个这样的开源代理实现,但它们往往被设计成与特定的基础设施组件一起工作。例如,当谈到服务发现Airbnb的神经和突触时,我们假设服务是在Zookeeper中注册的,而对于Prana,我们应该使用Netflix自己的Eureka服务注册。

随着微服务架构的日益流行,出现了一种新的代理,它们足够灵活,可以适应不同的基础设施组件和首选项。第一个广为人知的系统是Linkerd,基于他们在Twitter微服务平台上的工作而创建。很快,Lyft的工程团队也宣布了遵循类似原则的项目——Envoy。

Service Mesh - 服务网格

在这种模型中,我们的每个服务都将有一个附属的代理sidecar。考虑到服务之间仅通过sidecar代理进行通信,我们最终的部署类似于下面的图:

Buoyant首席执行官威廉·摩根观察到代理之间的互连形成了一个网状网络,于是在2017年初,为这个平台写了一个定义,并称之为Service Mesh(服务网格):

Service Mesh是处理服务间通信的基础设施层,负责实现请求的可靠传递。在实践中,Service Mesh通常实现为轻量级网络代理,与应用部署在一起,但是对应用透明。

该定义最有意义的地方在于,它不再将代理视为独立的组件,而是承认它们形成的网络本身是有价值的。

随着企业将其微服务部署移动到更复杂的运行时,如Kubernetes和Mesos,人们和企业已经开始使用这些平台提供的工具来正确地实现网格网络的想法。它们正在从一组独立的代理转向一个适当的、某种程度上集中的控制平面。

在鸟瞰图,我们能看到实际的服务流量仍然从代理直接流向代理,但是控制平面知道每个代理实例。控制平面使代理能够实现访问控制和度量收集等功能:

要完全理解大型系统中Service Mesh的影响还为时过早。这种方法的两个好处在我看来已经很明显了。首先,不需要编写定制软件来处理微服务架构最终代码,这将允许许多较小的组织享受以前只有大型企业才能使用的功能,从而创建各种有趣的用例。第二,这种体系结构可能让我们最终实现使用最佳工具/语言完成工作的梦想,而不必担心每个平台的库和模式的可用性。

  • END -

关于Rainbond

Rainbond是一款以应用为中心的开源PaaS,由好雨基于Docker、Kubernetes等容器技术自主研发,可作为公有云或私有云环境下的应用交付平台、DevOps平台、自动化运维平台和行业云平台,或作为企业级的混合云多云管理工具、Kubernetes容器管理工具或Service Mesh微服务架构治理工具。

阅读更多