微服务方兴未艾如火如荼之际,除 Spring cloud 等经典框架之外,新一代的微服务开发技术正在悄然兴起,那就是Service Mesh(服务网格)。2018 年是Service Mesh 元年,其被誉为是下一代微服务架构。

这两年,服务网格已成为云原生应用技术栈里的关键组件。Paypal,Ticketmaster和Credit Karma 等这些拥有大流量的公司,都为其生产环境应用添加了服务网格。2017年1月,Linkerd(某个云原生应用程序服务网格的开源项目)已经成为云原生计算基金会的官方项目。但究竟什么是服务网格呢?为什么它突然显得如此重要?

本文将会讨论服务网格,并通过过去十多年应用程序架构的变化来追溯其演变;此外,将会把服务网格与API网关、边缘代理和企业服务总线等相关、但不同的概念区分开来。在最后,我们试图预测服务网格的发展方向。


服务网格是什么?

服务网格是专门用来处理服务与服务之间通讯的基础设施层,用以可靠地传递服务请求。这是通过采用复杂的服务拓扑完成的,这些服务构成了现代的云原生应用。实际上,服务网格的常见实施是通过一系列轻量级的网络代理形成的阵列来实现的,这些代理和应用代码一起运行,但是应用通常并不知道它们的存在(有时候也有些区别)。

作为单独的一层,服务网格这个概念与云原生应用的兴起息息相关。在云原生模型中,单个应用程序可能包含数百个服务; 每个服务可能有数千个实例; 并且每个实例可能处于不断变化的状态中,因为它们经由诸如Kubernetes这样的编排器动态调度。在这种情况下,服务间的通讯不仅极其复杂,而且是应用runtime里普遍存在和最基本的一部分。管理好这些服务间的通讯对于确保端到端的性能和可靠性至关重要。


服务网格是个网络模型吗?

服务网格的确是种网络模型,它是基于TCP / IP之上的一层抽象层。它的前提条件是下层存在第三第四层网络,并且能够从一点到另一点传送数据。(它还假设三四层网络以及其他的相关环境并不可靠,因此服务网格也必须能够处理网络故障。)

从某种意义上说,服务网格类似于TCP / IP。正如TCP技术栈抽象了在网络端点之间可靠传递数据一样,服务网格抽象了在服务之间可靠地传递请求的机制。与TCP一样,服务网格并不关心实际载荷以及编码方式。应用程序本身有一个更高级别的目标,而服务网格的工作,和TCP类似的,是在出现任何网络故障的实现这一目标。

而与TCP不同的是,服务网格除了 “仅仅确保应用正常”这个目标以外,还有一个非常重要的工作:它提供了一个统一的、在整个应用程序范围内的可见性和可控性。服务间的通讯通常是不可见的,存在于隐形的基础设施层;而服务网格把这些通讯显性化,令其成为应用生态系统的关键部分,并且可以对其进行监控,管理和控制。


服务网格到底做了什么?

在云原生应用程序中,服务请求的传递非常复杂。像Linkerd 这样的服务网格,使用了一系列技术手段来管理这种复杂性:链路中断,延迟感知负载均衡,最终一致(“建议”)服务发现,重发,截止时间等等。这些功能必须全部协同工作,这些功能与其运行的复杂环境之间的相互作用可能非常微妙。

例如,当通过Linkerd向服务发出请求时,一个非常简化的事件时间表如下:

  • Linkerd采用动态路由规则来确定请求者想要的服务。请求是否应该路由到生产环境或测试环境?到本地数据中心或云数据中心?采用正在测试的服务的最新版本,还是在生产中经过审查的较旧版本?所有这些路由规则都是动态可配置的,并且可以全局应用,也可以应用于任意流量切片。
  • 找到正确的目的地后,Linkerd从相关的服务发现端点检索相应的实例池,其中可能有几个。如果这些信息与Linkerd自己发现的信息不同,那么Linkerd会决定信任哪些信息来源。
  • Linkerd根据各种因素选择最有可能返回快速响应的实例,这些因素包括观察到的最近请求的延迟。
  • Linkerd尝试将请求发送到实例,记录结果的延迟和响应类型。
  • 如果实例关闭,无响应或无法处理请求,则Linkerd会在另一个实例上重试该请求。
  • 如果实例始终返回错误,则Linkerd会将其从负载均衡资源池中剔除,并且稍后定期重试(例如,实例可能正在经历瞬态故障)。
  • 如果请求的deadline已过,则Linkerd会主动发送请求失败,而不用进一步重试进而增加负载。
  • Linkerd采用度量和分布式跟踪的形式追踪上述行为的各个方面,并将其发送到集中式度量系统。

这只是简化版本 - Linkerd还可以发起和终止TLS,执行协议升级,动态转换流量以及数据中心之间的业务转移。

Linkerd服务网格管理服务与服务间的通讯,并将其与应用程序代码分离

值得注意的是,这些功能不仅提供逐点弹性,也提供了应用程序范畴的弹性。对于大规模分布式系统,无论它们是如何构建的,都有一个明确的特征:它们的本地小型故障,有很大的机会升级成全系统性的灾难型故障。因此,当底层系统接近其负荷时,服务网格的设计必须能够通过减少负载和快速失败,来防止这些演变。

为什么服务网格是必要的?

归根到底,与其说服务网格是一个新功能概念,不如说是功能所在位置的转变。Web应用程序必须始终管理服务通讯的复杂性,因此服务网格模型的起源,可以追溯到过去的十五年间这些应用程序的演变过程。

现在我们先回顾一下2000年代中型Web应用程序的典型架构:三层应用程序。在这个模型架构中,应用程序逻辑、Web服务逻辑,乃至存储逻辑都是单独的层。层与层之间的通讯虽然复杂,但范围有限 - 毕竟只有两个跃点(路由)。在这儿没有“网格”的概念,但是在处理每个层代码的跃点之间,是存在着通讯逻辑的。

当这种架构方法被应用在非常大的规模时,它开始分裂。像谷歌、Netflix和Twitter这样的公司,面临着巨大的流量需求,就采用了本质上是云原生方法的前身:应用层被分成许多服务(有时我们将之称为“微服务”),层成为拓扑。这些系统中,普遍的通讯层突然变得相关,而在当时大家通常采用的是“胖客户端”库形式—推特的Finagle、Netflix的 Hystrix,以及谷歌的Stubby就是例证。

从许多方面来说,像Finagle,Stubby和Hystrix这样的库就是第一个服务网格。虽然它们具体涉及周围环境的细节,并且需要使用特定的语言和框架,但它们是作为基础设施的形式来管理服务间通信,并且发现在其原始公司之外使用(就开放源码的Finagle和Hystrix库而言)。

把镜头快进到现代的云原生应用程序。云原生模型将微服务(许多小型服务)方法与两个额外因素相结合:容器(例如 Docker),提供资源隔离和依赖管理;以及编排层(例如 Kubernetes),它将底层硬件抽象为同质池。

这三个组件允许应用程序适应负载下的自然机制,并用于处理云环境中时常存在的局部故障。但是,如果有数百个服务或数千个实例,且业务流程层要对实例从头到尾进行重新安排,单个请求在服务拓扑中遵循的路径可能会变得极其复杂;并且由于容器的存在,每个服务都能很容易地用不同的语言来编写。因此在这种情况下,库方法就不再可行了。

这种组合的复杂性和关键性,更激发了对服务与服务间通讯专用层的需求,该专用层将应用程序代码分离,并且能够捕获底层环境的高度动态性。你可能很容易猜到,这个专用层就是服务网格。


服务网格的未来

尽管在云原生生态系统中,服务网格的应用量正在迅速增长,但目前还有一个广阔而令人兴奋的未来仍有待我们去探索。无服务器计算的要求(例如亚马逊的 Lambda)直接就适用于服务网格模型的命名和链接,并形成其在云原生系统中角色的自然延伸。服务身份和访问策略在云原生环境中仍然是一个相当新的概念,而服务网格很有可能在这里扮演重要角色。另外,服务网格(如之前的TCP / IP)将继续被推送到底层基础架构中。正如Linkerd从像Finagle这样的系统发展而来,当前版本的服务网格作为一个单独的、用户空间代理,必须显性地添加到云原生技术栈中,才能够得以持续发展。


结论

服务网格是云主机堆栈的关键组件。Linkerd成立仅一年多,它就是Cloud Native Computing Foundation的一部分,拥有一个蓬勃发展的贡献者和用户社区。使用者包括像颠覆英国银行业的Monzo这样的创业公司,以及Paypal,Ticketmaster和Credit Karma这种大规模互联网公司,还有像Houghton Mifflin Harcourt这样经营了数百年的公司。