设计模式:Service Mesh

原文链接: http://philcalcado.com/2017/08/03/pattern_service_mesh.html

自从数十年前,分布式系统的概念诞生以来,工程师们越来越明白,利用分布式系统可以完成许多意想不到的功能。但是,分布式系统同样带来了许多新的问题。

当分布式系统还未普及,相对比较简单的时候,工程师们通过最小化远程依赖来应对其带来的复杂度。解决分布式问题最简单的方式就是尽可能的避免它,即时会带来大量跨系统的重复逻辑和数据也在所不惜。

但是工业化的进程推动着我们不断进步:从前只是几个大型的中央服务器,到现在成百上千个小的服务。在这种新的场景下,我们必须改变之前的“鸵鸟心态”,开始逐步去解决分布式系统带来的问题和挑战。首先是针对性的去提出解决一个一个小问题,逐渐到深入到更复杂的场景。随着越来越了解分布式系统所存在的问题,并且设计出了较好的解决方案,我们开始将通用的需求总结成设计模式和依赖库,并最终形成了平台化的解决方案。

最初的计算机网络

在人们一开始想要在多个计算机之间进行通信的时候,他们预期的效果是这样的

一个服务和另一个服务进行通信,从而实现某一些功能。显然,这是一个极度简化了的模型,网络中各个层次对字节、帧以及电信号的发送和接收都省略了。但是,对于本篇文章来说,这个抽象模型已经足够了。下面,来添加一个表示网络堆栈的部分。

自从上世纪50年代以来,上图所示模型就已经开始广泛使用了。在刚开始的时候,计算机尚未普及并且十分昂贵,所以节点之间的连接经过了精心的设计和维护。随着计算机的普及并变得廉价,连接的数量以及连接中传递的数据量出现了爆炸式的增长。随着人们越来越依赖互联网,工程师们必须保证他们开发的软件能够为用户提供令人满意的服务质量。

要想达到这样的服务质量,许多的问题亟需被解决:服务器之间如何发现对方?同一个数据线上如何并行处理多个连接?没有直接相连的两个服务器如何相互通信?数据包如何在网络中路由?怎么加密数据?

在这些问题中,我们用流量控制来作一个例子。流量控制用来防止一个服务器向另一个服务器发送过多的数据包。流量控制之所以很重要,是因为在网络中,各个独立的服务器并不了解相互的情况。服务器A以一定的速率向服务器B发送数据,但是没办法保证服务器B能够持续的以足够快的速度接收并处理数据。比如,服务器B可能忙于运行其他的任务,或者数据包没有按顺序到来,服务器B需要阻塞并等待靠前的数据包。这就意味着,不仅服务器A无法从服务器B那得到可靠的服务,甚至可能使得服务器B负载过大,阻塞后续所有的数据包。

有一段时间,工程师们普遍认为需要在开发网络服务和应用的时候,在代码中处理这些问题。在流量控制这个例子中,这就意味着应用本身必须包含响应的逻辑,避免其不会使其他服务过载。这部分重度依赖网络的逻辑和业务逻辑放在了一起。在抽象模型中,表现如下:

幸运的是,技术迅速的进步,并出现了通用的标准来解决很多网络堆栈中存在的问题。比如,TCP/IP协议来解决流量控制的问题。这意味着这段代码依然存在,但是它们从你的应用代码中抽取了处理啊,并转移到了操作系统提供的网络堆栈中。

这个模型成功的普及了开来。很少有企业会出现使用TCP/IP协议无法满足在常见操作系统上运行的业务逻辑的情况,即时是业务对性能和可靠性有很高的要求。

最初的微服务

经过多年的发展,计算机变得越来越普及和廉价,而上面所描述的网络堆栈模型也成为可靠网络系统中的稳定存在的部分。随着越来越多的节点和连接被加入到网络及通中,企业开始利用网络系统的各种特性来进行开发,从精确划分分布式代理和对象到面向服务的架构。

这种极度分布的架构带来了许多有意思的使用场景,但它也同样面临着一些挑战。这其中,有一些挑战是全新的,但是仍然有一些挑战和之前原始网络中讨论过的问题有高度的相似性。

在上世纪90年代,就职于太阳微系统公司(Sun Microsystems)的Peter Deutsch和它的同事们提出了“分布式计算中的8个谬论”(The 8 Fallacies of Distributed Computing)。他列出了人们在搭建分布式系统时候经常会提出的一些假设。Peter的观点是,也许在简单的网络架构或者理论模型中,这些假设是成立的,但是在现代系统中,这些假设不会依然成立:

  • 网络是可靠的(The network is reliable)
  • 延迟为0(Latency is zero)
  • 带宽无限(Bandwidth is infinite)
  • 网络是安全的(The network is secure)
  • 网络拓扑不会变化(Topology doesn’t change)
  • 只有一个管理员(There is one administrator)
  • 传输的开销为0(Transport cost is zero)
  • 网络是同构的(The network is homogeneous)

之所以要将这些假设称之为谬论,就是想告诫工程师们不能忽略这些问题,而是需要明确的解决它们。

在更加分布化的系统中(通常称之为微服务架构),这些问题得到了进一步的细化,许多可操作的需求被提出来了。在上面我们已经详细讨论了部分问题,下面是需要解决的问题列表:

  • 计算资源快速扩容
  • 基础监控
  • 快速调度
  • 存储资源扩容
  • 对外部进行访问
  • 认证/授权
  • 标准化的RPC

尽管TCP/IP协议栈和其他通用的网络模型已经运用了数十年并且仍然是服务器之间通信的可靠工具,但是现代高度复杂化的架构引入了更高层次的需求,并且需要工程师在开发这些架构的时候进行满足。

举个例子,服务发现和断路器,两个用来解决可靠性和一些分布式系统问题的技术。

历史往往具有重复性:第一个基于微服务设计的公司使用了初期网络系统采用的策略。这意味着开发服务的工程师必须同时担起解决上述需求的责任。

服务发现是指自动发现为某个特定请求提供实现方法的服务实例的过程。比如,一个Teams服务需要找到生产环境中存在的Players实例。你会发起一个服务发现的过程,并返回一个存活服务的列表。在相对集中的架构中,这个过程通过简单的通过DNS、负载均衡、约定端口等方式来完成。在相对分布的环境中,这个任务变得比较复杂,这个原本只需要简单的信任DNS发现的过程,变得需要考虑客户端负载均衡、多环境(线上/线下)、机房地理位置等。原来只需要一行代码去解析域名的问题,现在,需要大量的重复代码去解决高度分布所带来的各种边缘问题。

断路器是在Michael Nygard的Release it一书中引入的设计模式。我比较喜欢Martin Fowler对这个模式的概括

断路器的设计理念十分简单。封装出一个叫做断路器的对象,并对错误进行监控。一旦错误的次数达到了某个特定阈值,断路器被触发,后续所有的调用都不在继续原本的逻辑,直接返回错误。通常来说,当断路器被触发的时候,会产生一个报警。
The basic idea behind the circuit breaker is very simple. You wrap a protected function call in a circuit breaker object, which monitors for failures. Once the failures reach a certain threshold, the circuit breaker trips, and all further calls to the circuit breaker return with an error, without the protected call being made at all. Usually you’ll also want some kind of monitor alert if the circuit breaker trips.

断路器是一个简单而有效的设备,可以使服务之间的交互更加可靠。然而,和其他组件一样,随着分布式的程度增加,它会变得更加复杂。系统中某个服务出现错误的概率也随着分布式的程度指数增长,甚至简单的像“断路器触发的时候发出报警”也不再那么简单明了:一个组件的错误会雪崩式的在服务之间传递,同时触发上千次的断路报警。再一次的,原来只需要一行代码去解析域名的问题,现在,需要大量的重复代码去解决新的问题。

事实上,上面举例这两个技术很难被正确的实现。这也导致一些相对成熟的依赖库,比如Twitter的Finagle和Facebook的Proxygen变得非常得受欢迎。

上图所描述的这个模型,被微服务的先驱者们广泛的使用,比如Netflix、Twitter和SoundCloud。随着系统中服务的数量越来越多,他们逐渐被困在了这个方案的缺陷中。

也许最麻烦的挑战在于,即时使用了想Finagle这样的依赖库,企业仍然需要让其工程师们花费许多精力来将业务逻辑和依赖库组合起来。根据我在SoundCloud和DigitalOcean的经历,一个有100-250个工程师的企业,需要花费1/10的员工去搭建基础的组件。有的时候,这个工作会被明确的分配到团队中的某些工程师手上,但更多的时候,这个工作会被忽略,从而无形中增加了产品开发的总体花费。

第二个问题是,上述的依赖库对工具、环境以及开发语言有严格的要求。微服务依赖库通常会给予某一个特点的平台来实现,可能是一个语言也可能是类似JVM的环境。如果一个企业的使用了依赖库不支持的平台,那么通常需要自己将代码转移到新的平台。这会花费昂贵的开发时间。工程师需要再一次的花费时间来搭建工具和基础架构,而不是将精力放在核心业务和产品上面。这也是为什么中型企业会让他们的内部服务统一只使用一种特定的平台。

关于这个模型最后一个值得讨论的问题是治理。依赖库的模型的确将微服务部分问题的解决方案给封装了起来,但它本身仍然是一个需要维护的组件。要确保上千个服务的实例使用了相同或者兼容版本的依赖库并不是一件简单的事情,而每一次的升级意味着整合、测试、重新部署所有的服务,即时服务本身并没有作任何更改。

一次模型升级

和网络堆栈一样,我们会希望将分布式系统通常会需要的特性抽取到底层平台。

人们使用高层的协议(如HTTP)开发复杂的应用和服务的时候,并不需要去考虑TCP协议是如何处理网络中的数据包的。这种场景也是微服务所希望看到的:工程师们专注于他们的业务逻辑,避免自己去些服务基础架构的代码或者是自己去管理整体的依赖库和框架。

把这些需求整合起来,我们就可以得到下图所示的模型:

然而,通过修改网络堆栈来增加这一个层次的功能是不可行的。所以很多实践者会选择通过一系列的代理来实现。这个设计原理是:服务本身不直接连接到其他的服务,而是将所有的流量到送到一个小的代理,由代理来实现需要的特性。

第一个对这个方案进行说明的文档用了sidecar(边车,摩托车旁边的那个座位)这样一个概念。sidecar是一个附加进程,和应用一同启动,并提供而外的特性。在2013年,Airbnb开源了它们的sidecar实现Synapse和Nerve。一年后,Netflix开发了Prana,能够使得非JVM应用也能在NetflixOSS生态中使用到sidecard的功能。在SoundCloud,我们开发一个允许Ruby合法调用JVM微服务的sidecar。

尽管已经有了许多开源的代理实现,但这些代理通常是被设计用来工作在某个特性的基础架构上。举个例子,在进行服务发现的时候,Airbnb的Nerve和Synapse默认服务在Zookeeper进行了注册,而Prana则需要服务在Netflix自己的Eureka服务进行注册。

随着微服务架构越来越流行,能够灵活适配到不同基础设施的代理掀起了一波新的潮流。这其中,第一个被广泛认知的系统是Linkerd,由Buoyant基于其在Twitter微服务平台的经验开发出来的。没过多久,Lyft的工程师团队开源了Envoy,实现了相似的功能。

Service Mesh

在这个模型中,每一个微服务都会伴随一个sidecar代理。所有的服务都通过sidecar代理来进行交互,可以得到下图所示的结构:

Buoyant的CEO William Morgan发现了代理之间的交互形成了网格状的网络。在2017年初,William给出了一个该平台的定义,并命名为Service Mesh

Service Mesh是一个专门用来处理服务和服务之间通信的基础设施。它复杂确保在一个由复杂服务构成的拓扑的现代云应用中,请求能够被稳定的传递。在实践中,Service Mesh通常通过一系列轻量级的代理来进行实现。这些代理和应用并行存在,而应用不需要感知到代理的存在。
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.

也许在这个定义中最犀利的观点在于:不再将代理当成一个独立的组件,而是认识到代理本身形成一个极具价值的网络。

这里写图片描述

随着企业逐步将它们的微服务部署到更加复杂的运行环境(比如Kubernetes和Mesos)中,企业开始使用这些平台中自带的工具来实现网格网络的概念。他们逐渐抛弃了原来相互独立各自工作的代理,而转向中心化的控制台。

让我们从整体来看一下这个结构,可以发现,实际的数据流仍然是在代理和代理之间进行流动,但是控制台知道每一个代理实例的状态。通过控制台,可以实现访问控制和日志收集等需要协同的功能:

最近开源的Istio是目前最突出的一个实现方案。(译者:最近google开源Conduit应该是分庭抗礼的架势)

目前,Service Mesh给大规模系统带来的影响还无法完全的评估。但是有两个好处已经得到了证明。首先,不需要再去自己实现微服务架构所需的基础设施,使得很多小企业能够享受到之前大公司才有的分布式特性。其次,这个架构可以让我们使用最好的工具和语言去实现业务功能,而不需要担心某些依赖库或者模式是不是存在。

猜你喜欢

转载自blog.csdn.net/hwz2311245/article/details/79295896