1 - (2017)什么是服务网格以及为什么我们需要服务网格?

(2017)什么是服务网格以及为什么我们需要服务网格?

前言

What’s a service mesh? And why do I need one?

William Morgan, 发表于2017年4月24日

https://buoyant.io/what-is-a-service-mesh

https://linkerd.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/

中文翻译 by 薛命灯

https://www.infoq.cn/news/2017/11/WHAT-SERVICE-MESH-WHY-NEED/


Service Mesh(服务网格)是一个基础设施层,让服务之间的通信更安全、快速和可靠。如果你在构建云原生应用,那么就需要 Service Mesh。

在过去的一年中,Service Mesh 已经成为云原生技术栈里的一个关键组件。很多拥有高负载业务流量的公司都在他们的生产应用里加入了 Service Mesh,如 PayPal、Lyft、Ticketmaster 和 Credit Karma 等。今年一月份,Service Mesh 组件 Linkerd 成为 CNCF(Cloud Native Computing Foundation)的官方项目。不过话说回来,Service Mesh 到底是什么?为什么它突然间变得如此重要?

在这篇文章里,我将给出 Service Mesh 的定义,并追溯过去十年间 Service Mesh 在应用架构中的演变过程。我会解释 Service Mesh 与 API 网关、边缘代理(Edge Proxy)和企业服务总线之间的区别。最后,我会描述 Service Mesh 将何去何从以及我们可以作何期待。

什么是 Service Mesh?

Service Mesh 是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,Service Mesh 保证请求可以在这些拓扑中可靠地穿梭。在实际应用当中,Service Mesh 通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但应用程序不需要知道它们的存在。

随着云原生应用的崛起,Service Mesh 逐渐成为一个独立的基础设施层。在云原生模型里,一个应用可以由数百个服务组成,每个服务可能有数千个实例,而每个实例可能会持续地发生变化。服务间通信不仅异常复杂,而且也是运行时行为的基础。管理好服务间通信对于保证端到端的性能和可靠性来说是非常重要的。

Service Mesh 是一种网络模型吗?

Service Mesh 实际上就是处于 TCP/IP 之上的一个抽象层,它假设底层的 L3/L4 网络能够点对点地传输字节(当然,它也假设网络环境是不可靠的,所以 Service Mesh 必须具备处理网络故障的能力)。

从某种程度上说,Service Mesh 有点类似 TCP/IP。TCP 对网络端点间传输字节的机制进行了抽象,而 Service Mesh 则是对服务节点间请求的路由机制进行了抽象。Service Mesh 不关心消息体是什么,也不关心它们是如何编码的。应用程序的目标是“将某些东西从 A 传送到 B”,而 Service Mesh 所要做的就是实现这个目标,并处理传送过程中可能出现的任何故障。

与 TCP 不同的是,Service Mesh 有着更高的目标:为应用运行时提供统一的、应用层面的可见性和可控性。Service Mesh 将服务间通信从底层的基础设施中分离出来,让它成为整个生态系统的一等公民——它因此可以被监控、托管和控制。

Service Mesh 可以做什么?

在云原生应用中传输服务请求是一项非常复杂的任务。以 Linkerd 为例,它使用了一系列强大的技术来管理这种复杂性:回路断路器、负载均衡、延迟感知、最终一致性服务发现、重试和超时。这些技术需要组合在一起,并互相协调,它们与环境之间的交互也非常微妙。

举个例子,当一个请求流经 Linkerd 时,会发生如下的一系列事件。

  1. Linkerd 根据动态路由规则确定请求是发给哪个服务的,比如是发给生产环境里的服务还是发给 staging 环境里的服务?是发给本地数据中心的服务还是发给云端的服务?是发给最新版本的服务还是发给旧版本的服务?这些路由规则可以动态配置,可以应用在全局的流量上,也可以应用在部分流量上。
  2. 在确定了请求的目标服务后,Linkerd 从服务发现端点获取相应的服务实例。如果服务实例的信息出现了偏差,Linkerd 需要决定哪个信息来源更值得信任。
  3. Linkerd 基于某些因素(比如最近处理请求的延迟情况)选择更有可能快速返回响应的实例。
  4. Linkerd 向选中的实例发送请求,并把延迟情况和响应类型记录下来。
  5. 如果选中的实例发生宕机、没有响应或无法处理请求,Linkerd 就把请求发给另一个实例(前提是请求必须是幂等的)。
  6. 如果一个实例持续返回错误,Linkerd 就会将其从负载均衡池中移除,并在稍后定时重试(这个实例有可能只是临时发生故障)。
  7. 如果请求超时,Linkerd 会主动放弃请求,不会进行额外的重试。
  8. Linkerd 以度量指标和分布式日志的方式记录上述各种行为,然后将度量指标发送给中心度量指标系统。

除此之外,Linkerd 还能发起和终止 TLS、执行协议升级、动态调整流量、在数据中心之间进行失效备援。

2db8bfa00b21112c2be994f45d65d8b4

Linkerd 的这些特性可以保证局部的弹性和应用层面的弹性。大规模分布式系统有一个共性:局部故障累积到一定程度就会造成系统层面的灾难。Service Mesh 的作用就是在底层系统的负载达到上限之前通过分散流量和快速失效来防止这些故障破坏到整个系统。

为什么我们需要 Service Mesh?

Service Mesh 并非新出现的功能。一直以来,Web 应用程序需要自己管理复杂的服务间通信,从过去十多年间应用程序的演化就可以看到 Service Mesh 的影子。

2000 年左右的中型 Web 应用一般使用了三层模型:应用逻辑层、Web 服务逻辑层和存储逻辑层。层与层之间的交互虽然也不算简单,但复杂性是很有限的,毕竟一个请求最多只需要两个跳转。虽然这里不存在“网格”,但仍然存在跳转通信逻辑。

随着规模的增长,这种架构就显得力不从心了。像 Google、Netflix、Twitter 这样的公司面临着大规模流量的挑战,他们实现了一种高效的解决方案,也就是云原生应用的前身:应用层被拆分为多个服务(也叫作微服务),这个时候层就变成了一种拓扑结构。这样的系统需要一个通用的通信层,以一个“富客户端”包的形式存在,如 Twitter 的 Finagle 、Netflix 的 Hystrix 和 Google 的 Stubby。

一般来说,像 Finagle、Stubby 和 Hystrix 这样的包就是最初的 Service Mesh。云原生模型在原先的微服务模型中加入了两个额外的元素:容器(比如 Docker)和编排层(如 Kubernetes)。容器提供了资源隔离和依赖管理,编排层对底层的硬件进行抽象池化。

这三个组件让应用程序在云环境中具备了伸缩能力和处理局部故障的能力。但随着服务和实例的数量增长,编排层需要无时不刻地调度实例,请求在服务拓扑间穿梭的路线也变得异常复杂,再加上可以使用任意语言来开发不同的服务,所以之前那种“富客户端”包的方式就行不通了。

这种复杂性和迫切性催生了服务间通信层的出现,这个层既不会与应用程序的代码耦合,又能捕捉到底层环境高度动态的特点,它就是 Service Mesh。

Service Mesh 的未来

尽管 Service Mesh 在云原生系统方面的应用已经有了快速的增长,但仍然存在巨大的提升空间。无服务器 (Serverless) 计算(如 Amazon 的 Lambda)正好需要 Service Mesh 的命名和链接模型,这让 Service Mesh 在云原生生态系统中的角色得到了彰显。服务识别和访问策略在云原生环境中仍显初级,而 Service Mesh 毫无疑问将成为这方面不可或缺的基础。就像 TCP/IP 一样,Service Mesh 将在底层基础设施这条道上更进一步。

结论

Service Mesh 是云原生技术栈中一个非常关键的组件。Linkerd 项目在启动一年多之后正式成为 CNCF 的官方项目,并拥有了众多的贡献者和用户。Linkerd 的用户横跨初创公司(如 Monzo)到大规模的互联网公司(如 PayPal、Ticketmaster、Credit Karma),再到拥有数百年历史的老牌公司(如 Houghton Mifflin Harcourt)。

2 - (2017)Service Mesh:下一代微服务

(QCon 2017年演讲)Service Mesh:下一代微服务

我在2017年10月,QCon 上海做的主题演讲

Service Mesh:下一代微服务

https://skyao.io/talk/201710-service-mesh-next-generation-microservice/

3 - (2017)Pattern: Service Mesh

博客文章 Pattern: Service Mesh,作者 Phil Calçado

Phil Calçado 编写的博客 Pattern: Service Mesh,这是早期讲述 servicemesh 和 sidecar 最透彻的文章。

前言

Pattern: Service Mesh 的英文原文发表于 Phil Calçado 的个人博客网站:

https://philcalcado.com/2017/08/03/pattern_service_mesh.html

网上找到的中文翻译:

以下内容为转载自最后一篇的翻译内容加我的少许修改。


自从几十年前第一次引入分布式系统以来,我们了解到分布式系统能够实现我们以前根本想象不到的用例,但它们也带来了各种新的问题。

当这些系统还很少见和简单的时候,工程师们通过尽量减少远程交互来处理增加的复杂性。处理分布式的最安全方式是尽可能地避免使用分布式,即使这意味着在各个系统中存在重复的逻辑和数据。

但行业需求推动我们前进,分布式系统从几台较大的中央计算机演变为成百上千的小型服务。在这个新的世界里,我们必须解决问题,应对新的挑战和开放性问题,首先是以个案的方式完成的临时解决方案,随后是更复杂的东西。首先,具体问题具体分析,针对某个问题给出有针对性的解决办法,然后再提供更先进更复杂的解决方案。随着我们对问题领域越来越熟悉、提出的解决办法越来越好,我们开始将一些最常见的需求总结归纳为模式、库,以及最终的平台。

第一次计算机联网

由于人们首先想到的是让两台或多台电脑相互通讯,因此,他们就设想出了这样的东西:

互相之间可以通讯的两个服务可以满足最终用户的一些需求。但这个示意图显然过于简单了,缺少了包括通过代码操作的字节转换和在线路上收发的电信号转换在内的多个层。虽然,一定程度上的抽象对于我们的讨论是必需的,但还是让我们来添加网络协议栈组件以增加一点细节内容吧:

上述模型的变种自20世纪50年代以来一直使用至今。一开始,计算机很稀少,也很昂贵,所以两个节点之间的每个链接都精心制作和维护。随着计算机变得越来越便宜,越来越流行,连接的数量和通过它们的数据量急剧增加。随着人们越来越依赖网络系统,工程师们需要确保他们构建的软件能够达到用户所要求的服务质量。

而为了达到预期的质量水平,有许多问题急需解决。人们需要找到解决方案让机器互相发现、通过同一条线路同时处理多个连接、允许机器在非直连的情况下互相通信、在网络上路由数据包、加密流量等等。

在这其中,有一种叫做流量控制的东西,下面我们以此为例。流量控制是一种机制,防止服务器发送的数据包超过下游服务器可以承受的上限。这是必要的,因为在一个联网的系统中,你至少有两个不同的、独立的计算机,彼此之间互不了解。计算机A以给定的速率向计算机B发送字节,但不能保证B可以连续地以足够快的速度来处理接收到的字节。例如,B可能正在忙于并行运行其他任务,或者数据包可能不按顺序到达,并且B可能被阻塞以等待本应该先到达的数据包。这意味着A不仅不知道B的预期性能,而且还可能让事情变得更糟,因为这可能会让B过载,B现在必须对所有这些传入的数据包进行排队处理。

一段时间以来,大家寄希望于建立网络服务和应用程序的开发者能够通过编写代码来解决上面提出的挑战。在我们的这个流程控制示例中,应用程序本身必须包含某种逻辑来确保服务不会因为数据包而过载。这种重度联网逻辑与业务逻辑一样重要。在我们的抽象示意图中,它是这样的:

幸运的是,技术的发展日新月异,随着像TCP/IP这样的标准的横空出世,流量控制和许多其他问题的解决方案被融入进了网络协议栈本身。这意味着这些流量控制代码仍然存在,但已经从应用程序转移到了操作系统提供的底层网络层中:

这个模型相当地成功。几乎任何一个组织都能够使用商业操作系统附带的TCP/IP协议栈来驱动他们的业务,即使有高性能和高可靠性的要求。

第一次使用微服务

多年以来,计算机变得越来越便宜,并且到处可见,而上面提到的网络协议栈已被证明是用于可靠连接系统的事实上的工具集。随着节点和稳定连接的数量越来越多,行业中出现了各种各样的网络系统,从细粒度的分布式代理和对象到由较大但重分布式组件组成的面向服务的架构。

这样的分布式系统给我们带来了很多有趣的更高级别的案例和好处,但也出现了几个难题。其中一些是全新的,但其他的只是我们在讨论原始网络时遇到难题的更高版本而已。

在90年代,Peter Deutsch和他在Sun公司的同事工程师们撰写了“分布式计算的八大错误”一文,其中列出了人们在使用分布式系统时通常会做出的一些假设。Peter认为,这些假设在更原始的网络架构或理论模型中可能是真实的,但在现代世界中是不成立的:

  1. 网络是可靠的
  2. 延迟为零
  3. 带宽是无限的
  4. 网络是安全的
  5. 拓扑是不变的
  6. 有一个管理员
  7. 传输成本为零
  8. 网络是同构的

大家把上面这个列表斥为“谬论”,因此,工程师们不能忽视这些问题,必须明确地处理这些问题。

为了处理更复杂的问题,需要转向更加分散的系统(我们通常所说的微服务架构),这在可操作性方面提出了新的要求。之前我们已经详细讨论了一些内容,但下面则列出了一个必须要处理的东西:

  1. 计算资源的快速提供
  2. 基本的监控
  3. 快速部署
  4. 易于扩展的存储
  5. 可轻松访问边缘
  6. 认证与授权
  7. 标准化的RPC

因此,尽管数十年前开发的TCP/IP协议栈和通用网络模型仍然是计算机之间相互通讯的有力工具,但更复杂的架构引入了另一个层面的要求,这再次需要由在这方面工作的工程师来实现。

例如,对于服务发现和断路器,这两种技术已用于解决上面列出的几个弹性和分布式问题。

历史往往会重演,第一批基于微服务构建的系统遵循了与前几代联网计算机类似的策略。这意味着落实上述需求的责任落在了编写服务的工程师身上。

服务发现是在满足给定查询条件的情况下自动查找服务实例的过程,例如,一个名叫 Teams 的服务需要找到一个名为 Players 的服务实例,其中该实例的 environment 属性设置为 production。你将调用一些服务发现进程,它们会返回一个满足条件的服务列表。对于更集中的架构而言,这是一个非常简单的任务,可以通常使用DNS、负载均衡器和一些端口号的约定(例如,所有服务将HTTP服务器绑定到8080端口)来实现。而在更分散的环境中,任务开始变得越来越复杂,以前可以通过盲目信任 DNS 来查找依赖关系的服务现在必须要处理诸如客户端负载均衡、多种不同环境、地理位置上分散的服务器等问题。如果之前只需要一行代码来解析主机名,那么现在你的服务则需要很多行代码来处理由分布式引入的各种问题。

断路器是由Michael Nygard在其编写的“Release It”一书中引入的模式。我非常喜欢Martin Fowler对该模式的一些总结:

断路器背后的基本思路非常简单。将一个受保护的函数调用包含在用于监视故障的断路器对象中。一旦故障达到一定阈值,则断路器跳闸,并且对断路器的所有后续调用都将返回错误,并完全不接受对受保护函数的调用。通常,如果断路器发生跳闸,你还需要某种监控警报。

这些都是非常简单的设备,它们能为服务之间的交互提供更多的可靠性。然而,跟其他的东西一样,随着分布式水平的提高,它们也会变得越来越复杂。系统发生错误的概率随着分布式水平的提高呈指数级增长,因此即使简单的事情,如“如果断路器跳闸,则监控警报”,也就不那么简单了。一个组件中的一个故障可能会在许多客户端和客户端的客户端上产生连锁反应,从而触发数千个电路同时跳闸。而且,以前可能只需几行代码就能处理某个问题,而现在需要编写大量的代码才能处理这些只存在于这个新世界的问题。

事实上,上面举的两个例子可能很难正确实现,这也是大型复杂库,如 Twitter 的 Finagle 和 Facebook 的 Proxygen,深受欢迎的原因,它们能避免在每个服务中重写相同的逻辑。

大多数采用微服务架构的组织都遵循了上面提到的那个模型,如 Netflix、Twitter 和 SoundCloud。随着系统中服务数量的增加,他们发现了这种方法存在着各种弊端。

即使是使用像 Finagle 这样的库,项目团队仍然需要投入大量的时间来将这个库与系统的其他部分结合起来,这是一个代价非常高的难题。根据我在 SoundCloud 和 DigitalOcean 的经验,我估计在100-250人规模的工程师组织中,需要有1/10的人员来构建模型。有时,这种代价很容易看到,因为工程师被分配到了专门构建工具的团队中,但是更多的时候,这种代价是看不见的,因为它表现为在产品研发上需要花费更多的时间。

第二个问题是,上面的设置限制了可用于微服务的工具、运行时和语言。用于微服务的类库通常是为特定平台编写的,无论是编程语言还是像JVM这样的运行时。如果开发团队使用了类库不支持的平台,那么通常需要将代码移植到新的平台。这浪费了本来就很短的工程时间。工程师没办法再把重点放在核心业务和产品上,而是不得不花时间来构建工具和基础架构。那就是为什么一些像 SoundCloud 和 DigitalOcean 这样的中型企业认为其内部服务只需支持一个平台,分别是 Scala 或者 Go。

这个模型最后一个值得讨论的问题是管理方面的问题。类库模型可能对解决微服务架构需求所需功能的实现进行抽象,但它本身仍然是需要维护的组件。必须要确保数千个服务实例所使用的类库的版本是相同的或至少是兼容的,并且每次更新都意味着要集成、测试和重新部署所有服务,即使服务本身没有任何改变。

下一个逻辑

类似于我们在网络协议栈中看到的那样,大规模分布式服务所需的功能应该放到底层的平台中。

人们使用高级协议(如HTTP)编写非常复杂的应用程序和服务,甚至无需考虑TCP是如何控制网络上的数据包的。这种情况就是微服务所需要的,那些从事服务开发工作的工程师可以专注于业务逻辑的开发,从而避免浪费时间去编写自己的服务基础设施代码或管理整个系统的库和框架。

将这个想法结合到我们的图表中,我们可以得到如下所示的内容:

不幸的是,通过改变网络协议栈来添加这个层并不是一个可行的任务。许多人的解决方案是通过一组代理来实现。这个的想法是,服务不会直接连接到它的下游,而是让所有的流量都将通过一个小小的软件来透明地添加所需功能。

在这个领域第一个有记载的进步使用了边车(sidecars)这个概念。“边车”是一个辅助进程,它与主应用程序一起运行,并为其提供额外的功能。在2013年, Airbnb 写了一篇有关 Synapse 和 Nerve 的文章,这是“边车”的一个开源实现。一年后,Netflix 推出了 Prana,专门用于让非JVM应用程序从他们的Netflix OSS 生态系统中受益。在 SoundCloud,我们构建了可以让遗留的 Ruby 程序使用我们为JVM微服务构建的基础设施的“边三轮”。

虽然有这么几个开源的代理实现,但它们往往被设计为需要与特定的基础架构组件配合使用。例如,在服务发现方面,Airbnb 的 Nerve 和 Synapse 假设了服务是在 Zookeeper 中注册,而对于 Prana,则应该使用 Netflix 自己的 Eureka 服务注册表。

随着微服务架构的日益普及,我们最近看到了一波新的代理浪潮,它们足以灵活地适应不同的基础设施组件和偏好。 这个领域中第一个广为人知的系统是Linkerd,它由 Buoyant 创建出来,源于他们的工程师先前在 Twitter 微服务平台上的工作。很快,Lyft 的工程团队宣布了 Envoy 的发布,它遵循了类似的原则。

Service Mesh

在这种模式中,每个服务都配备了一个代理“边车”。由于这些服务只能通过代理“边车”进行通信,我们最终会得到类似于下图的部署方案:

Buoyant的首席执行官威廉·摩根表示,代理之间的互连形成了服务网格。2017年初,威廉写下了这个平台的定义,并称它为服务网格:

服务网格是用于处理服务到服务通信的专用基础设施层。它负责通过复杂的服务拓扑来可靠地传递请求。实际上,服务网格通常被实现为与应用程序代码一起部署的轻量级网络代理矩阵,并且它不会被应用程序所感知。

这个定义最强大的地方可能就在于它不再把代理看作是孤立的组件,并承认它们本身就是一个有价值的网络。

随着微服务部署被迁移到更为复杂的运行时中去,如Kubernetes和Mesos,人们开始使用一些平台上的工具来实现网格网络这一想法。他们实现的网络正从互相之间隔离的独立代理,转移到一个合适的并且有点集中的控制面上来。

最近公布的Istio项目是这类系统中最著名的例子。

完全理解服务网格在更大规模系统中的影响还为时尚早,但这种架构已经凸显出两大优势。首先,不必编写针对微服务架构的定制化软件,即可让许多小公司拥有以前只有大型企业才能拥有的功能,从而创建出各种有趣的案例。第二,这种架构可以让我们最终实现使用最佳工具或语言进行工作的梦想,并且不必担心每个平台的库和模式的可用性。

4 - (2021)服务网格:每位软件工程师需要了解的世界上最被过度炒作的技术

服务网格:每位软件工程师需要了解的世界上最被过度炒作的技术

前言

The Service Mesh

What every software engineer needs to know about the world’s most over-hyped technology

William Morgan, 发表时间未知,从过度炒作这个梗猜测是2021年。

https://buoyant.io/service-mesh-manifesto/

服务网格:每位软件工程师需要了解的世界上最被过度炒作的技术

没找到中文翻译,以下内容为自行翻译。


介绍

如果你是工作在后端系统的软件工程师,在过去几年中你可以经常听到"服务网格"这个词。由于一系列奇怪的事件,这个词像一个巨大的雪球一样在行业内滚动,粘附在越来越喧哗的营销和炒作上,并且没有任何迹象表明很快就会停止。

服务网格诞生于云原生生态系统阴暗而充满趋势的水域中,不幸的是,这意味着大量的服务网格内容从 “低营养的废话” 到使用一个技术术语–“基本上是废话”。但是,如果你能穿透所有的噪音,服务网格也有一些真实、具体和重要的价值。

在本指南中,我将尝试这样做:为服务网格提供一个诚实、深入、以工程师为中心的指南。我不仅要介绍什么,还要介绍为什么,以及现在为什么。最后,我将尝试描述为什么我认为这项特殊技术吸引了如此疯狂的炒作,这本身就是一个有趣的故事。

我是谁?

大家好,我是威廉-摩根, Linkerd 的创建者之一,Linkerd 是第一个服务网格项目,也是服务网格这个词的诞生地。(对不起!)我也是 Buoyant 的首席执行官,Buoyant是一家创业公司,建立了像 Linkerd 和 Buoyant Cloud 这样很酷的服务网格。

正如你可能想象的那样,我非常有偏见,对这个话题有一些强烈的看法。也就是说,所以我将尽力把社论化的东西降到最低(除了一个部分,“为什么人们对这个问题谈得这么多?",在那里我将揭开一些观点),我将尽力以尽可能客观的方式来写这个指南。当我需要具体的例子时,我将主要依靠 Linkerd,但当我知道与其他网格实现的差异时,我将会把它们指出来。

好的,现在开始!

什么是服务网格?

对于所有的炒作,服务网格是非常简单的。它只不过是一堆用户空间的代理,卡在你的服务的"旁边”(我们稍后会讨论 “旁边” 的意思),加上一套管理流程。代理被称为服务网格的数据平面,而管理进程被称为控制平面。数据平面拦截服务间的调用,并对这些调用进行"处理";控制平面协调代理的行为,并为运维人员提供API,以操纵和测量整个网格。

diag1

这些代理是什么?它们是7层感知的TCP代理,就像 haproxy 和 NGINX 一样。代理的选择各不相同:Linkerd 使用的是 Rust 的"微代理",简称 Linkerd-proxy,是我们专门为服务网格建立的。其他网格使用不同的代理:Envoy是一个常见的选择。但代理的选择是一个实现细节。(2020年1月编辑:请参阅《为什么Linkerd不使用Envoy》以了解为什么 Linkerd 使用 Linkerd2-proxy 而不是 Envoy。)

这些代理是做什么的?当然,他们代理服务的调用。(严格地说,它们既是"代理",也是"反向代理",处理传入和传出的调用)。它们实现了一个专注于服务间调用的功能集。这种对服务间流量的关注是服务网格代理与API网关或Ingress代理的不同之处,后者关注从外部世界到整个集群的调用。

所以,这就是数据平面。控制平面更简单:它是一组组件,提供数据平面所需的任何元件,以协调的方式行事,包括服务发现、TLS证书发放、指标汇总等等。数据平面调用控制平面以告知其行为;控制平面反过来提供一个API,允许用户修改和检查整个数据平面的行为。

下面是 Linkerd 的控制平面和数据平面的图示。可以看到,控制平面有几个不同的组件,包括一个小型的Prometheus实例,它从代理处汇总指标数据,以及destination(服务发现)、identity(证书授权)和 public-api(web和CLI终端)等组件。相比之下,数据平面只是应用实例旁边的单个 linkerd-proxy。这只是逻辑图;在部署时,你可能最终会有每个控制平面组件的三个副本,但有数百或数千个数据平面代理。

(这张图中的蓝色方框代表Kubernetes pod的边界。你可以看到,linkerd-proxy 容器实际上与应用容器运行在同一个pod中。这种模式被称为sidecar容器。)

control-plane

服务网格有几个重要的影响。首先,由于代理功能集是为服务间调用而设计的,服务网格只有在你的应用被构建为服务时才有意义。你可以把它用在单体上,但要运行一个代理就需要大量的机器,而且功能集也不是很合适。

另一个结果是,服务网格将需要很多很多的代理。事实上,Linkerd 为每个服务的每个实例添加一个 linkerd-proxy。(其他一些网格的实现也会为每个节点/主机/虚拟机添加一个代理。无论哪种方式都是很多的)。) 这种大量使用代理的做法本身就有一些影响:

  • 不管这些数据平面代理是什么,它们一定要快。每一次调用都会增加两跳,一个在客户端,一个在服务器端。

  • 另外,代理需要小而轻。每一个代理都会消耗内存和CPU,而且这种消耗会随着你的应用线性扩展。

  • 需要一个系统来部署和更新大量的代理。你不希望必须用手来做这件事。

但是,至少在10,000英尺的水平上,这就是服务网格的全部内容:部署了大量的用户空间代理来对内部的、服务间流量进行"处理",使用控制平面来改变它们的行为并查询它们产生的数据。

现在让我们继续讨论为什么。

为什么服务网格有意义?

如果你是第一次遇到服务网格的想法,你可以原谅你的第一反应是轻微的惊恐。服务网格的设计意味着它不仅会给你的应用程序增加延迟,还会消耗资源,而且还会引入一大堆机器。前一分钟你还在安装一个服务网格,后一分钟你就突然要负责操作成百上千的代理。为什么会有人想这么做呢?

答案有两部分。首先,由于生态系统正在发生一些其他变化,部署这些代理的运营成本可以大大降低。关于这一点,后面还有更多内容。

更重要的答案是,这种设计实际上是将额外的逻辑引入系统的一个好方法。这不仅是因为有大量的功能你可以在那里添加,而且还因为你可以在不改变生态系统的情况下添加它们。事实上,整个服务网格模型是建立在这个洞察力之上的:在一个多服务系统中,无论单个服务实际做什么,它们之间的流量是一个理想的功能插入点。

例如,Linkerd,像大多数网格一样,有一个第七层的功能集,主要集中在HTTP调用,包括HTTP/2和gRPC:

  1. 可靠性特性:请求重试、超时、金丝雀(流量拆分/转移)等。

  2. 可观察性特性:每个服务或单个路由的成功率、延迟和请求量的汇总;服务拓扑图的绘制;等等。

  3. 安全特性:双向TLS、访问控制等。

这些功能中有许多是在请求层面操作的(因此是"L7代理")。例如,如果服务 Foo 对服务 Bar 进行HTTP调用,Foo 一方的 linkerd-proxy 可以根据观察到的每个实例的延迟,在 Bar 的所有实例中智能地平衡该调用;如果请求失败,它可以重试,如果它是幂等的;它可以记录响应代码和延迟;等等。同样地,Bar那边的 linkerd-proxy 可以拒绝调用,如果它不被允许,或者超过了速率限制;它可以从自己的角度记录延迟;等等。

代理人也可以在连接层面上"做事情"。例如,Foo 的 linkerd-proxy 可以启动TLS连接,Bar 的 linkerd-proxy 可以终止它,而且双方都可以验证对方的TLS证书。这不仅提供了服务间的加密,而且提供了服务身份的加密安全形式–Foo和Bar可以"证明"它们是它们所说的人。

不管是在请求层面还是在连接层面,需要注意的重要问题是,服务网格的功能都是操作性的。Linkerd 中没有任何关于转换请求有效载荷语义的内容,例如,向JSON blob 添加字段或转换 protobuf。这是一个重要的区别,当我们谈论 ESB 和中间件时,会再次触及。

所以,这就是服务网格可以提供的一系列功能。但为什么不直接在应用程序中实现它们呢?为什么还要麻烦代理呢?

为什么服务网格是个好主意?

虽然功能集很有趣,但服务网格的核心价值其实并不在功能上。毕竟,我们可以直接在应用程序本身中实现这些功能。(事实上,我们将在后面看到这是服务网格的起源。) 如果我不得不把它归结为一句话,服务网格的价值可以归结为以下几点:服务网格提供了对运行现代服务器端软件至关重要的功能,这些功能在堆栈中是统一的,并与应用程序代码解耦。

让我们一点一点地看。

**对运行现代服务器端软件至关重要的功能。**如果你正在构建一个连接到公共互联网的事务性服务器端应用程序,接受来自外部世界的请求,并在较短的时间内对其作出响应–想想Web应用、API服务器和大部分现代服务器端软件–如果你正在将这个系统作为一个服务的集合,以同步的方式相互交谈,如果你正在不断地修改这个软件以增加更多的功能,如果你的任务是在修改这个系统的同时保持其运行,那么恭喜你,你正在构建现代服务器端软件。而上面列出的所有这些光荣的功能实际上对你来说都是至关重要的。该应用程序必须是可靠的;必须是安全的;而且必须能够观察到它在做什么。而这正是服务网格能提供帮助的地方。

(好吧,我在这里偷偷地说了一个观点:这种方法是构建服务器端软件的现代方式。今天世界上有一些人正在构建单体或 “反应式微服务” 以及其他不符合上述定义的东西,他们持有不同的观点。)

在堆栈中是统一的。服务网格不仅仅提供关键的功能,它们还能适用于应用程序中的每一个服务,无论服务是用什么语言编写的,使用什么框架,由谁编写,如何部署,或任何其他开发或部署的细节。

与应用程序代码解耦。最后,服务网不只是在你的堆栈中统一提供功能,它是以一种不需要改变应用的方式提供的。服务网格功能的基本所有权–包括配置、更新、操作、维护等方面的操作所有权–纯粹是在平台层面,独立于应用程序。应用程序可以在不涉及服务网格的情况下改变,而服务网格也可以在不涉及应用程序的情况下改变。

简而言之:服务网格不仅提供重要的功能,而且是以一种全局的、统一的、独立于应用程序的方式提供的。因此,尽管服务网格的功能可以在服务代码中实现(甚至作为一个库被链接到每个服务中),但这种方法不会提供解耦和统一性,而这正是服务网格价值的核心所在。

而你所要做的就是增加大量的代理! 我保证,我们很快就会讨论增加所有这些代理的运营成本。但首先,我们需要稍息,从人的角度来研究这个解耦的想法。

服务网格能帮助谁?

尽管可能很不方便,但事实证明,为了使技术真正产生影响,它必须被人类采用。那么,谁采用了服务网格?谁会从中受益?

如果你正在构建我上面所说的现代服务器软件,你可以大致认为你的团队分为服务所有者和平台所有者,前者从事构建业务逻辑的工作,后者则是构建这些服务所运行的内部平台。在小型组织中,这些人可能是同一个人,但随着组织规模的扩大,这些角色通常会变得更加明确,甚至进一步细分。(这里还有很多关于devops的变化性质、微服务的组织影响等的内容。但现在让我们把这些描述作为一个既定的事实)。)

从这个角度看,服务网格的直接受益者是平台所有者。毕竟,平台团队的目标是建立内部平台,让服务所有者可以在上面运行他们的业务逻辑,并且以一种让服务所有者尽可能独立于操作细节的方式来实现。服务网格不仅提供对实现这一目标至关重要的功能,而且其方式不会反过来引起对服务所有者的依赖。

服务所有者也受益,尽管是以更间接的方式。服务所有者的目标是在构建业务逻辑时尽可能地富有成效,而他们需要担心的运维机制越少,就越容易实现。他们不需要为实施重试策略或TLS而操心,而是可以纯粹地关注业务逻辑问题,并相信平台会处理好其他问题。这对他们来说也是一个很大的好处。

平台和服务所有者之间解耦的组织价值怎么强调都不过分。事实上,我认为这可能是服务网格有价值的关键原因。

当我们最早的 Linkerd 采用者之一告诉我们他们采用服务网格的原因时,我们学到了这个教训:因为它允许他们 “不必与人交谈”。这是一家大公司的平台团队,正在向 Kubernetes 迁移。由于他们的应用程序处理敏感信息,他们希望对集群上的所有通信进行加密。有数百个服务和数百个开发团队,他们不希望说服每个开发团队将 TLS 添加到他们的路线图中。通过安装 Linkerd,他们将该功能的所有权从开发人员手中转移到了平台团队手中,因为对他们来说,这是一个强加的任务。Linkerd 并没有为他们解决一个技术问题,而是解决了一个组织问题。

简而言之,服务网格与其说是一个技术问题的解决方案,不如说是一个社会技术问题的解决方案。

服务网格是否能解决我所有的问题?

是的。呃,不是!

如果你看一下上面概述的三类功能–可靠性、安全性和可观察性–应该很清楚,服务网格不是这些领域的完整解决方案。虽然 Linkerd 可以在知道请求是无效的情况下重试,但它不能决定在服务完全中断的情况下向用户返回什么–应用程序必须做出这些决定。虽然 Linkerd 可以报告成功率等,但它不能查看服务内部并报告内部指标–应用程序必须有工具。虽然 Linkerd 可以 “免费” 做双向TLS这样的事情,但安全解决方案的内容远不止这些。

在这些领域中,服务网格提供的功能子集是属于平台功能的。我的意思是这些功能:

  1. 独立于业务逻辑。为 Foo 和 Bar 之间的调用计算流量延迟直方图的方式与 Foo 首先调用 Bar 的原因完全无关。

  2. 难以正确实现。Linkerd的重试是用重试预算等复杂的东西来设置参数的,因为对重试的天真做法是导致 “重试风暴” 和其他分布式系统故障模式的必经之路。

  3. 统一实施时最有效。互相TLS的机制只有在每个人都在做的时候才真正有意义。

因为这些功能是在代理层实现的,而不是在应用层,服务网格在平台而不是应用层面提供这些功能。服务用什么语言编写,或使用什么框架,或由谁编写,或如何得到的,都不重要。代理的功能独立于所有这些,而且这种功能的所有权–包括配置、更新、操作、维护等方面的操作所有权–纯粹是在平台层面。

服务网格的示例特征

可观察性 可靠性 安全性
服务网格 服务成功率 请求重试 所有服务之间都是双向TLS
平台(非服务网格) 日志聚合 数据集的多个副本 静止状态下的数据加密
应用程序 内部功能使用的仪表化 处理整个组件宕机时的故障 确保用户只能访问自己的数据

总结:服务网格并不是可靠性、或可观察性、或安全性的完整解决方案。这些领域更广泛的所有权必然涉及服务所有者、运维和SRE团队以及组织的其他部分。服务网格只能提供每个领域的平台层"切片"。

为什么服务网格现在有意义了?

在这一点上,你可能会对自己说:好吧,如果这个服务网格的东西这么棒,为什么十年前我们没有在我们的堆栈中玩转数百万个代理?

对此有一个浅显的答案,那就是十年前大家都在构建单体,所以没有人需要服务网格。这是事实,但我认为忽略了问题的关键。甚至在十年前,“微服务"的概念作为构建大规模系统的可行方式被广泛讨论,并在 Twitter、Facebook、Google 和 Netflix 等公司公开付诸实践。至少在我所接触到的部分行业中,普遍的看法是,微服务是构建大规模系统的"正确方式”,即使它们做起来真的很痛苦。

当然,虽然十年前就有公司在运维微服务,但他们基本上没有到处安装代理来形成服务网格。不过,如果你仔细观察,他们在做一些相关的事情:许多这些组织强制要求使用特定的内部库来进行网络通信(有时称为"胖客户端"库)。Netflix 有 Hysterix,谷歌有 Stubby 库,而 Twitter 有 Finagle。例如,Finagle 是 Twitter 的每项新服务都必须使用的,它同时处理客户端和服务器端的连接,并实现重试、请求路由、负载平衡和仪表等。它为整个 Twitter 堆栈提供了一个一致的可靠性和可观察性层,与服务本身的实际工作无关。当然,它只适用于JVM语言,而且它有一个编程模型,你必须围绕它建立整个应用程序,但它提供的操作功能几乎与服务网格的功能完全相同。

因此,十年前,我们不仅有了微服务,还有了原生的服务网格库,解决了许多今天服务网格所解决的问题。但我们还没有服务网格。首先需要改变一些别的东西。

而这正是更深层次的答案所在,它埋藏在过去十年发生的另一个差异中:部署微服务的成本大幅降低。我在上面列出的那些十年前公开使用微服务的公司–Twitter、Netflix、Facebook、Google–都是规模巨大、资源丰富的公司。他们不仅有需求,而且有人才来构建、部署和运维重要的微服务应用。Twitter从单体迁移到微服务所花费的工程时间和精力之多,让人难以想象。

对比今天,你可能会遇到微服务与开发人员比例为5:1甚至10:1的初创公司,更重要的是,他们有能力处理这个问题。如果运行50个微服务对于一个5人的初创公司来说是一个合理的方法,那么显然有什么东西降低了采用微服务的成本。

monzo-topology

备注:这张图片来自 https://twitter.com/JackKleeman/status/1190407465659183104,在 monzo 有1500个微服务,150个工程师,蓝色线条代表容许网络规则容许的流量。

微服务运维成本的大幅降低的结果是:容器和容器编排器的采用率上升。而这正是什么变化使服务网格得以实现这一问题的更深层次答案所在。使得服务网格在运维上可行的,也使得微服务在运维上可行的东西是:Kubernetes 和 Docker。

为什么?嗯,Docker解决了一件大事:打包问题。通过允许将应用及其(非网络)运行时的依赖打包到一个容器中,应用现在是一个可替换的单元,可以被扔到任何地方并运行。同样,Docker使运行多语言堆栈变得更加容易:因为容器是原子执行单元,对于部署和操作来说,容器内是什么并不重要,不管它是JVM应用还是Node应用,还是Go、Python或Ruby。你只需运行它。

Kubernetes解决了下一步的问题:现在我有一堆"可执行的东西",我也有一堆"可以执行这些可执行的东西"(也就是机器),我需要它们之间的映射关系。从广义上讲,你给 Kubernetes 一堆容器和一堆机器,它就能找出这种映射。(当然,这是一个动态的和不断变化的东西,因为新的容器在系统中滚动,机器进入和退出操作,等等。但是,Kubernetes会把它弄清楚。)

一旦有了 Kubernetes,运行一个服务的部署时间成本与运行十个服务没有太大区别,实际上与100个服务没有太大区别。结合容器作为鼓励多语言实现的打包机制,结果是大量的新应用被实现为用各种语言编写的微服务–正是服务网格最适合的环境。

因此,最后我们来看看为什么服务网格现在是可行的:Kubernetes为服务提供的统一性也直接适用于服务网格的运维挑战。把代理打包到容器中,告诉Kubernetes把它们贴在任何地方,然后就可以了!你得到了一个服务网格,所有的部署时间机制都由Kubernetes为你处理。

总结一下:与10年前相比,现在的服务网格之所以有意义,是因为 Kubernetes 和 Docker 的兴起不仅极大地增加了运行服务网格的需求,使你的应用可以轻松地构建成一个多角化的微服务架构,而且通过提供部署和维护 sidecar 代理机群的机制,极大地降低了运行服务网格的成本。

为什么人们对服务网格谈论得如此之多?

内容警告:在本节中,我采用了推测、猜想、仅是个人观点。

人们只需要搜索"服务网格",就会遇到一个kafka式的热梦景观,充斥着了混乱的项目、低热量的循环内容和普遍的回声室扭曲。所有闪亮的新技术都有一定程度的这种情况,但服务网格似乎特别糟糕。为什么会这样?

嗯,部分原因是我的错。我已经尽了最大的努力,一有机会就谈论 Linkerd 和服务网格,在无数的博客文章、播客和像这样的文章中。但我并没有那么强大。要真正回答这个问题,我必须谈一谈服务网格。如果不谈一个特别的项目,就不可能谈及这个景观。Istio是一个开源的服务网格,是谷歌、IBM和Lyft之间的合作。

Istio 的显著之处在于两点。首先,谷歌在其背后付出了巨大的营销努力。据我估计,今天知道这个服务网格的大多数人都是通过Istio介绍的。第二件了不起的事情是Istio的反响有多差。显然,我在利益相关者,但在我看来,Istio已经形成了相当大的公众反响,这对一个开源项目来说是不常见的(虽然不是闻所未闻的)。

撇开我的个人理论不谈,我相信谷歌的参与才是服务网格被炒得如此火热的真正原因。具体来说,a)Istio被谷歌大力推广;b)其相应的乏善可陈;以及 c)最近Kubernetes的陨落让每个人都记忆犹新,这些因素结合在一起,形成了一种令人陶醉的无氧环境,理性思考的能力被扼杀,只剩下一种奇怪的云原生郁金香狂热。

当然,从 Linkerd 的角度来看,这是……我想我会将其描述为一种混合的祝福。我的意思是,现在服务网格是一个"东西",这很好–2016年Linkerd刚刚起步时,情况并非如此,而且真的很难让人注意到。我们现在没有这个问题了!但糟糕的是,服务网格如此混乱,甚至很难理解哪些项目是服务网格,更不用说哪个项目最适合你的用例。这对每个人都是一种伤害。(当然,在某些情况下,Istio或其他项目会是比Linkerd更正确的选择–它远不是一个万能的解决方案。)

在Linkerd方面,我们的策略是忽略这些噪音,继续专注于为我们的社区解决真正的问题,并基本上等待整个事情的结束。炒作的程度最终会消退,我们都可以继续我们的生活。

不过,在此期间,我们都将不得不一起承受这一切。

那么…我,一个卑微的软件工程师,应该关心服务网格吗?

如果你是一个软件工程师,以下是我对你是否应该关心服务网的基本评判标准。

如果你是一个纯粹的业务逻辑实施的开发者角色。不,你真的不需要关心服务网。我的意思是,我们当然欢迎你去关心,但理想情况下,服务网格不会直接影响你的生活。继续建立甜蜜的商业逻辑,让你周围的人都得到报酬。

如果你在一个正在使用Kubernetes的组织中担任平台角色。是的,你100%应该关心。除非你采用K8s纯粹是为了运行一个单体或做批处理(在这种情况下,我会认真地问为什么要用K8s),否则你最终会遇到这样的情况:你有很多微服务,都是由其他人编写的,都在相互访问,都被捆绑在一起,成为一个运行时依赖的邪恶捆绑,你需要一种方法来处理这些。由于你是在Kubernetes上,你将有几个服务网格的选择,你应该对哪些服务网格,甚至你是否想要任何一个服务网格有一个明智的看法。(从Linkerd开始)

如果你在一个不使用Kubernetes的组织中担任平台角色,但却在"做微服务"。是的,你应该关心,但这将是复杂的。当然,你可以通过在各处部署大量的代理来获得服务网格的价值,但Kubernetes的优点是部署模式,如果你必须自己管理这些代理,你的投资回报率方程将看起来非常不同。

如果你在一个"做单体"的组织中担任平台角色。不,你可能不需要关心。如果你正在运维一个单体,甚至是一个"单体的集合",有明确的和不经常变化的通信模式,那么服务网格不会增加很多,你可以忽略它并希望它消失。

结论

服务网格可能实际上并不拥有 “世界上最被过度炒作的技术” 的称号–这个可疑的称号可能是属于比特币或人工智能。也许它只是排在前五名。但是,如果你能穿过层层噪音,对于在Kubernetes上构建应用程序的人来说,有一些真正的价值可以利用。

最后,我很想让你试试 Linkerd–在Kubernetes集群上安装它应该需要60秒,甚至只是在你的笔记本电脑上安装一个Minikube,你可以自己看看我到底在说什么。

5 - (2021)分布式系统在 Kubernetes 上的进化

分布式系统在 Kubernetes 上的进化

内容出处

The Evolution of Distributed Systems on Kubernetes

https://www.infoq.com/articles/distributed-systems-kubernetes/

分布式系统在 Kubernetes 上的进化

本文翻译自 Bilgin Ibryam 的文章 The Evolution of Distributed Systems on Kubernetes。

https://cloudnative.to/blog/distributed-systems-kubernetes/


在 3 月份的 QCon 上,我做了一个关于 Kubernetes 的分布式系统进化的演讲。首先,我想先问一个问题,微服务之后是什么?我相信大家都有各自的答案,我也有我的答案。你会在最后发现我的想法是什么。为了达到这个目的,我建议大家看看分布式系统的需求是什么?以及这些需求在过去是如何发展的,从单体应用开始到 Kubernetes,再到最近的 Dapr、Istio、Knative 等项目,它们是如何改变我们做分布式系统的方式。我们将尝试对未来做一些预测。

现代分布式应用

为了给这个话题提供更多的背景信息,我认为的分布式系统是由数百个组件组成的系统。这些组件可以是有状态的、无状态的或者无服务器的。此外,这些组件可以用不同的语言创建,运行在混合环境上,并开发开源技术、开放标准和互操作性。我相信你可以使用闭源软件来构建这样的系统,也可以在 AWS 和其他地方构建。具体到这次演讲,我将关注 Kubernetes 生态系统,以及你如何在 Kubernetes 平台上构建这样一个系统。

我们从分布式系统的需求讲起。我认为是我们要创建一个应用或者服务,并写一些业务逻辑。那从运行时的平台到构建分布式系统,我们还需要什么呢?在底层,最开始是我们要一些生命周期的能力。当你用任一语言开发你的应用时,我们希望有能力把这个应用可靠地打包和部署、回滚、健康检查。并且能够把应用部署到不同的节点上,并实现资源隔离、扩展、配置管理,以及所有这些。这些都是你创建分布式应用所需要的第一点。

img

第二点是围绕网络。我们有了应用之后,我们希望它能够可靠地连接到其他服务,无论该服务是在集群内部还是在外部。我们希望其具有服务发现、负载均衡的能力。为了不同的发布策略或是其他的一些原因的我们希望有流量转移的能力。然后我们还希望其具有与其他系统进行弹性通信的能力,无论是通过重试、超时还是断路器。要有适当的安全保障,并且要有足够的监控、追踪、可观察性等等。

img

我们有了网络之后,接下来就是我们希望有能力与不同的 API 和端点交互,即资源绑定–与其他协议和不同的数据格式交互。甚至能够从一种数据格式转换成另一种数据格式。我还会在这里加入诸如过滤功能,也就是说,当我们订阅一个主题时,我们也许只对某些事件感兴趣。

img

你认为最后一类是什么?是状态。当我在说状态和有状态的抽象时,我并不是在谈论实际的状态管理,比如数据库或者文件系统的功能。我要说的更多是有关幕后依赖状态的开发人员抽象。可能,你需要具有工作流管理的能力。也许你想管理运行时间长的进程或者做临时调度或者某些定时任务来定期运行服务。也许你还想进行分布式缓存,具有幂等性或者支持回滚。所有这些都是开发人员级的原语,但在幕后,它们依赖于具有某种状态。你想随意使用这些抽象来创建完善的分布式系统。

img

我们将使用这个分布式系统原语的框架来评估它们在 Kubernetes 和其他项目上的变化情况。

单体架构——传统中间件功能

假设我们从单体架构以及如何获得这些能力开始。在那种情况下,首先是当我说单体的时候,在分布式应用的情况下我想到的是 ESB。ESB 是相当强大的,当我们检查我们的需求列表时,我们会说 ESB 对所有有状态的抽象有很好的支持。

使用 ESB,你可以进行长时间运行的流程的编排、分布式事务、回滚和幂等。此外,ESB 还提供了出色的资源绑定能力,并且有数百个连接器,支持转换、编排,甚至有联网功能。最后,ESB 甚至可以做服务发现和负载均衡。

它具有围绕网络连接的弹性的所有功能,因此它可以进行重试。可能 ESB 本质上不是很分布式,所以它不需要非常高级的网络和发布能力。ESB 欠缺的主要是生命周期管理。因为它是单一运行时,所以第一件事就是你只能使用一种语言。通常是创建实际运行时的语言,Java、.NET 或者其他的语言。然后,因为是单一运行时,我们不能轻松地进行声明式的部署或者自动调配。部署是相当大且非常重的,所以它通常涉及到人机交互。这种单体架构的另一个难点是扩展:“我们无法扩展单个组件。”

最后却并非最不重要的一点是,围绕隔离,无论是资源隔离还是故障隔离。使用单体架构无法完成所有这些工作。从我们的需求框架来看,ESB 的单体架构不符合条件。

img

云原生架构——微服务和 Kubernetes

接下来,我建议我们研究一下云原生架构以及这些需求是如何变化的。如果我们从一个非常高的层面来看,这些架构是如何发生变化的,云原生可能始于微服务运动。微服务使我们可以按业务领域进行拆分单体应用。事实证明,容器和 Kubernetes 实际上是管理这些微服务的优秀平台。让我们来看一下 Kubernetes 对于微服务特别有吸引力的一些具体特性和功能。

img

从一开始,进行健康状况探测的能力就是 Kubernetes 受欢迎的原因。在实践中,这意味着当你将容器部署到 Pod 中时,Kubernetes 会检查进程的运行状况。通常情况下,该过程模型还不够好。你可能仍然有一个已启动并正在运行的进程,但是它并不健康。这就是为什么还可以使用就绪度和存活度检查的原因。Kubernetes 会做一个就绪度检查,以确定你的应用在启动期间何时准备接受流量。它将进行活跃度检查,以检查服务的运行状况。在 Kubernetes 之前,这并不是很流行,但今天几乎所有语言、所有框架、所有运行时都有健康检查功能,你可以在其中快速启动端点。

img

Kubernetes 引入的下一个特性是围绕应用程序的托管生命周期——我的意思是,你不再控制何时启动、何时关闭服务。你相信平台可以做到这一点。Kubernetes 可以启动你的应用;它可以将其关闭,然后在不同的节点上移动它。为此,你必须正确执行平台在应用启动和关闭期间告诉你的事件。

Kubernetes 流行的另一件特性是围绕着声明式部署。这意味着你不再需要启动服务;检查日志是否已经启动。你不必手动升级实例——支持声明式部署的 Kubernetes 可以为你做到这一点。根据你选择的策略,它可以停止旧实例并启动新实例。此外,如果出现问题,可以进行回滚。

另外就是声明你的资源需求。创建服务时,将其容器化。最好告诉平台该服务将需要多少 CPU 和内存。Kubernetes 利用这些信息为你的工作负载找到最佳节点。在使用 Kubernetes 之前,我们必须根据我们的标准将实例手动放置到一个节点上。现在,我们可以根据自己的偏好来指导 Kubernetes,它将为我们做出最佳的决策。

如今,在 Kubernetes 上,你可以进行多语言配置管理。无需在应用程序运行时进行配置查找就可以进行任何操作。Kubernetes 会确保配置最终在工作负载所在的同一节点上。这些配置被映射为卷或环境变量,以供你的应用程序使用。

事实证明,我刚才谈到的那些特定功能也是相关的。比如说,如果要进行自动放置,则必须告诉 Kubernetes 服务的资源需求。然后,你必须告诉它要使用的部署策略。为了让策略正确运行,你的应用程序必须执行来自环境的事件。它必须执行健康检查。一旦采用了所有这些最佳实践并使用所有这些功能,你的应用就会成为出色的云原生公民,并且可以在 Kubernetes 上实现自动化了(这是在 Kubernetes 上运行工作负载的基本模式)。最后,还有围绕着构建 Pod 中的容器、配置管理和行为,还有其他模式。

我要简要介绍的下一个主题是工作负载。从生命周期的角度来看,我们希望能够运行不同的工作负载。我们也可以在 Kubernetes 上做到这一点。运行十二要素应用程序和无状态微服务非常简单。Kubernetes 可以做到这一点。这不是你将要承担的唯一工作量。可能你还有有状态的工作负载,你可以使用有状态集在 Kubernetes 上完成此工作。

你可能还有的另一个工作负载是单例。也许你希望某个应用程序的实例是整个集群中应用程序的唯一一个实例–你希望它成为可靠的单例。如果失败,则重新启动。因此,你可以根据需求以及是否希望单例至少具有一种或最多一种语义来在有状态集和副本集之间进行选择。你可能还有的另一个工作负载是围绕作业和定时作业–有了 Kubernetes,你也可以实现这些。

如果我们将所有这些 Kubernetes 功能映射到我们的需求,则 Kubernetes 可以满足生命周期需求。我通常创建的需求列表主要是由 Kubernetes 今天提供给我们的。这些是任何平台上的预期功能,而 Kubernetes 可以为你的部署做的是配置管理、资源隔离和故障隔离。此外,除了无服务器本身之外,它还支持其他工作负载。

img

然后,如果这就是 Kubernetes 给开发者提供的全部功能,那么我们该如何扩展 Kubernetes 呢?以及如何使它具有更多功能?因此,我想描述当今使用的两种常用方法。

进程外扩展机制

首先是 Pod 的概念,Pod 是用于在节点上部署容器的抽象。此外,Pod 给我们提供了两组保证:

  • 第一组是部署保证 – Pod 中的所有容器始终位于同一个节点上。这意味着它们可以通过 localhost 相互通信,也可以使用文件系统或通过其他 IPC 机制进行异步通信。
  • Pod 给我们的另一组保证是围绕生命周期的。Pod 中的所有容器并非都相等。

img

根据使用的是 init 容器还是应用程序容器,你会获得不同的保证。例如,init 容器在开始时运行;当 Pod 启动时,它按顺序一个接一个地运行。他们仅在之前的容器已成功完成时运行。它们有助于实现由容器驱动的类似工作流的逻辑。

另一方面,应用程序容器是并行运行的。它们在整个 Pod 的生命周期中运行,这也是 sidecar 模式的基础。sidecar 可以运行多个容器,这些容器可以协作并共同为用户提供价值。这也是当今我们看到的扩展 Kubernetes 附加功能的主要机制之一。

img

为了解释以下功能,我必须简要地告诉你 Kubernetes 内部的工作方式。它是基于调谐循环的。调谐循环的思想是将期望状态驱动到实际状态。在 Kubernetes 中,很多功能都是靠这个来实现的。例如,当你说我要两个 Pod 实例,这系统的期望状态。有一个控制循环不断地运行,并检查你的 Pod 是否有两个实例。如果不存在两个实例,它将计算差值。它将确保存在两个实例。

这方面的例子有很多。一些是副本集或有状态集。资源定义映射到控制器是什么,并且每个资源定义都有一个控制器。该控制器确保现实世界与所需控制器相匹配,你甚至可以编写自己的自定义控制器。

当在 Pod 中运行应用程序时,你将无法在运行时加载任何配置文件更改。然而,你可以编写一个自定义控制器,检测 config map 的变化,重新启动 Pod 和应用程序–从而获取配置更改。

事实证明,即使 Kubernetes 拥有丰富的资源集合,但它们并不能满足你的所有不同需求。Kubernetes 引入了自定义资源定义的概念。这意味着你可以对需求进行建模并定义适用于 Kubernetes 的 API。它与其他 Kubernetes 原生资源共存。你可以用能理解模型的任何语言编写自己的控制器。你可以设计一个用 Java 实现的 ConfigWatcher,描述我们前面所解释的内容。这就是 operator 模式,即与自定义资源定义一起使用的控制器。如今,我们看到很多 operator 加入,这就是第二种扩展 Kubernetes 附加功能的方式。

接下来,我想简单介绍一下基于 Kubernetes 构建的一些平台,这些平台大量使用 sidecar 和 operator 来给开发者提供额外的功能。

什么是服务网格?

让我们从服务网格开始,什么是服务网格?

我们有两个服务,服务 A 要调用服务 B,并且可以用任何语言。把这个当做是我们的应用工作负载。服务网格使用 sidecar 控制器,并在我们的服务旁边注入一个代理。你最终会在 Pod 中得到两个容器。代理是一个透明的代理,你的应用对这个代理完全无感知–它拦截所有传入和传出的流量。此外,代理还充当数据防火墙。

这些服务代理的集合代表了你的数据平面,并且很小且无状态。为了获得所有状态和配置,它们依赖于控制平面。控制平面是保持所有配置,收集指标,做出决定并与数据平面进行交互的有状态部分。此外,它们是不同控制平面和数据平面的正确选择。事实证明,我们还需要一个组件-一个 API 网关,以将数据获取到我们的集群中。一些服务网格具有自己的 API 网关,而某些使用第三方。如果你研究下所有这些组件,它们将提供我们所需的功能。

API 网关主要专注于抽象我们服务的实现。它隐藏细节并提供边界功能。服务网格则相反。在某种程度上,它增强了服务内的可见性和可靠性。可以说,API 网关和服务网格共同提供了所有网络需求。要在 Kubernetes 上获得网络功能,仅使用服务是不够的:“你需要一些服务网格。”

img

什么是 Knative?

我要讨论的下一个主题是 Knative,这是 Google 几年前启动的一个项目。它是 Kubernetes 之上的一层,可为您提供无服务器功能,并具有两个主要模块:

  • Knative 服务 - 围绕着请求-应答交互,以及
  • Knative Eventing - 更多的是用于事件驱动的交互。

只是让你感受一下,Knative Serving 是什么?通过 Knative Serving,你可以定义服务,但这不同于 Kubernetes 服务。这是 Knative 服务。使用 Knative 服务定义工作负载后,你就会得到具有无服务器的特征的部署。你不需要有启动并运行实例。它可以在请求到达时从零开始。你得到的是无服务器的能力;它可以迅速扩容,也可以缩容到零。

Knative Eventing 为我们提供了一个完全声明式的事件管理系统。假设我们有一些要与之集成的外部系统,以及一些外部的事件生产者。在底部,我们将应用程序放在具有 HTTP 端点的容器中。借助 Knative Eventing,我们可以启动代理,该代理可以触发 Kafka 映射的代理,也可以在内存或者某些云服务中。此外,我们可以启动连接到外部系统的导入器,并将事件导入到我们的代理中。这些导入器可以基于,例如,具有数百个连接器的 Apache Camel。

一旦我们将事件发送给代理,然后用 YAML 文件声明,我们可以让容器订阅这些事件。在我们的容器中,我们不需要任何消息客户端–比如 Kafka 客户端。我们的容器将使用云事件通过 HTTP POST 获取事件。这是一个完全平台管理的消息传递基础设施。作为开发人员,你必须在容器中编写业务代码,并且不处理任何消息传递逻辑。

img

从我们的需求的角度来看,Knative 可以满足其中的一些要求。从生命周期的角度来看,它为我们的工作负载提供了无服务器的功能,因此能够将其扩展到零,并从零开始激活。从网络的角度来看,如果服务网格之间存在某些重叠,则 Knative 也可以进行流量转移。从绑定的角度来看,它对使用 Knative 导入程序进行绑定提供了很好的支持。它可以使我们进行发布/订阅,或点对点交互,甚至可以进行一些排序。它可以满足几类需求。

什么是 Dapr?

另一个使用 sidecar 和 operator 的项目是 Dapr,它是微软几个月前才开始并且正在迅速流行起来。此外,1.0 版本 被认为是生产可用的。它是一个作为 sidecar 的分布式系统工具包–Dapr 中的所有内容都是作为 sidecar 提供的,并且有一套他们所谓的构件或功能集的集合。

这些功能是什么呢?第一组功能是围绕网络。Dapr 可以进行服务发现和服务之间的点对点集成。同样,它也可以进行服务网格的追踪、可靠通信、重试和恢复。第二套功能是围绕资源绑定:

  • 它有很多云 API、不同系统的连接器,以及
  • 也可以做消息发布/订阅和其他逻辑。

有趣的是,Dapr 还引入了状态管理的概念。除了 Knative 和服务网格提供的功能外,Dapr 在状态存储之上进行了抽象。此外,你通过存储机制支持与 Dapr 进行基于键值的交互。

在较高的层次上,架构是你的应用程序位于顶部,可以使用任何语言。你可以使用 Dapr 提供的客户端库,但你不必这样做。你可以使用语言功能来执行称为 sidecar 的 HTTP 和 gRPC。与 服务网格的区别在于,这里的 Dapr sidecar 不是一个透明的代理。它是一个显式代理,你必须从你的应用中调用它,并通过 HTTP 或 gRPC 与之交互。根据你需要的功能,Dapr 可以与其他如云服务的系统对话。

img

在 Kubernetes 上,Dapr 是作为 sidecar 部署的,并且可以在 Kubernetes 之外工作(不仅仅是 Kubernetes)。此外,它还有一个 operator – 而 sidecar 和 Operator 是主要的扩展机制。其他一些组件管理证书、处理基于 actor 的建模并注入 sidecar。你的工作负载与 sidecar 交互,并尽其所能与其他服务对话,让你与不同的云提供商进行互操作。它还为你提供了额外的分布式系统功能。

综上所述,这些项目所提供的功能,我们可以说 ESB 是分布式系统的早期化身,其中我们有集中式的控制平面和数据平面–但是扩展性不好。在云原生中,集中式控制平面仍然存在,但是数据平面是分散的–并且具有隔音功能和高度的可扩展性。

我们始终需要 Kubernetes 来做良好的生命周期管理,除此之外,你可能还需要一个或多个附加组件。你可能需要 Istio 来进行高级联网。你可能会使用 Knative 来进行无服务器工作负载,或者使用 Dapr 来做集成。这些框架可与 Istio 和 Envoy 很好的配合使用。从 Dapr 和 Knative 的角度来看,你可能必须选择一个。它们共同以云原生的方式提供了我们过去在 ESB 上拥有的东西。

未来云原生趋势–生命周期趋势

在接下来的部分,我列出了一些我认为在这些领域正在发生令人振奋的发展的项目。

img

我想从生命周期开始。通过 Kubernetes,我们可以为应用程序提供一个有用的生命周期,这可能不足以进行更复杂的生命周期管理。比如,如果你有一个更复杂的有状态应用,则可能会有这样的场景,其中 Kubernetes 中的部署原语不足以为应用提供支持。

在这些场景下,你可以使用 operator 模式。你可以使用一个 operator 来进行部署和升级,还可以将 S3 作为服务备份的存储介质。此外,你可能还会发现 Kubernetes 的实际健康检查机制不够好。假设存活检查和就绪检查不够好。在这种情况下,你可以使用 operator 对你的应用进行更智能的存活和就绪检查,然后在此基础上进行恢复。

第三个领域就是自动伸缩和调整。你可以让 operator 更好的了解你的应用,并在平台上进行自动调整。目前,编写 operator 的框架主要有两个,一个是 Kubernetes 特别兴趣小组的 Kubebuilder,另一个是红帽创建的 operator 框架的一部分–operator SDK。它有以下几个方面的内容:

Operator SDK 让你可以编写 operator – operator 生命周期管理器来管理 operator 的生命周期,以及可以发布你的 operator 到 OperatorHub。如今在 OperatorHub,你会看到 100 多个 operator 用于管理数据库、消息队列和监控工具。从生命周期空间来看,operator 可能是 Kubernetes 生态系统中发展最活跃的领域。

网络趋势 - Envoy

我选的另一个项目是 Envoy。服务网格接口规范的引入将使你更轻松地切换不同的服务网格实现。在部署上 Istio 对架构进行了一些整合。你不再需要为控制平面部署 7 个 Pod;现在,你只需要部署一次就可以了。更有趣的是在 Envoy 项目的数据平面上所正在发生的:越来越多的第 7 层协议被添加到 Envoy 中。

img

服务网格增加了对更多协议的支持,比如 MongoDB、ZooKeeper、MySQL、Redis,而最新的协议是 Kafka。我看到 Kafka 社区现在正在进一步改进他们的协议,使其对服务网格更加友好。我们可以预料将会有更紧密的集成、更多的功能。最有可能的是,会有一些桥接的能力。你可以从服务中在你的应用本地做一个 HTTP 调用,而代理将在后台使用 Kafka。你可以在应用外部,在 sidecar 中针对 Kafka 协议进行转换和加密。

另一个令人兴奋的发展是引入了 HTTP 缓存。现在 Envoy 可以进行 HTTP 缓存。你不必在你的应用中使用缓存客户端。所有这些都是在 sidecar 中透明地完成的。有了 tap 过滤器,你可以 tap 流量并获得流量的副本。最近,WebAssembly 的引入,意味着如果你要为 Envoy 编写一些自定义的过滤器,你不必用 C++ 编写,也不必编译整个 Envoy 运行时。你可以用 WebAssembly 写你的过滤器,然后在运行时进行部署。这些大多数还在进行中。它们不存在,说明数据平面和服务网格无意停止,仅支持 HTTP 和 gRPC。他们有兴趣支持更多的应用层协议,为你提供更多的功能,以实现更多的用例。最主要的是,随着 WebAssembly 的引入,你现在可以在 sidecar 中编写自定义逻辑。只要你没有在其中添加一些业务逻辑就可以了。

绑定趋势 - Apache Camel

Apache Camel 是一个用于集成的项目,它具有很多使用企业集成模式连接到不同系统的连接器。 比如 Camel version 3 就深度集成到了 Kubernetes 中,并且使用了我们到目前为止所讲的那些原语,比如 operator。

img

你可以在 Camel 中用 Java、JavaScript 或 YAML 等语言编写你的集成逻辑。最新的版本引入了一个 Camel operator,它在 Kubernetes 中运行并理解你的集成。当你写好 Camel 应用,将其部署到自定义资源中,operator 就知道如何构建容器或查找依赖项。根据平台的能力,不管是只用 Kubernetes,还是带有 Knative 的 Kubernetes,它都可以决定要使用的服务以及如何实现集成。在运行时之外有相当多的智能 – 包括 operator – 所有这些都非常快地发生。为什么我会说这是一个绑定的趋势?主要是因为 Apache Camel 提供的连接器的功能。这里有趣的一点是它如何与 Kubernetes 深度集成。

状态趋势 - Cloudstate

另一个我想讨论的项目是 Cloudstate 和与状态相关的趋势。Cloudstate 是 Lightbend 的一个项目,主要致力于无服务器和功能驱动的开发。最新发布的版本,正在使用 sidecar 和 operator 与 Kubernetes 进行深度集成。

img

这个创意是,当你编写你的功能时,你在功能中要做的就是使用 gRPC 来获取状态并与之进行交互。整个状态管理在与其他 sidecar 群集的 sidear 中进行。它使你能够进行事件溯源、CQRS、键值查询、消息传递。

从应用程序角度来看,你并不了解所有这些复杂性。你所做的只是调用一个本地的 sidecar,而 sidecar 会处理这些复杂的事情。它可以在后台使用两个不同的数据源。而且它拥有开发人员所需的所有有状态抽象。

到目前为止,我们已经看到了云原生生态系统中的最新技术以及一些仍在进行中的开发。我们如何理解这一切?

多运行时微服务已经到来

如果你看微服务在 Kubernetes 上的样子,则将需要使用某些平台功能。此外,你将需要首先使用 Kubernetes 的功能进行生命周期管理。然后,很有可能透明地,你的服务会使用某些服务网格(例如 Envoy)来获得增强的网络功能,无论是流量路由、弹性、增强的安全性,甚至出于监控的目的。除此之外,根据你的场景和使用的工作负载可能需要 Dapr 或者 Knative。所有这些都代表了进程外附加的功能。剩下的就是编写业务逻辑,不是放在最上面而是作为一个单独的运行时来编写。未来的微服务很有可能将是由多个容器组成的这种多运行时。有些是透明的,有些则是非常明确的。

img

智能的 sidecar 和愚蠢的管道

如果更深入地看,那可能是什么样的,你可以使用一些高级语言编写业务逻辑。是什么并不重要,不必仅是 Java,因为你可以使用任何其他语言并在内部开发自定义逻辑。

你的业务逻辑与外部世界的所有交互都是通过 sidecar 发生的,并与平台集成进行生命周期管理。它为外部系统执行网络抽象,为你提供高级的绑定功能和状态抽象。sidecar 是你不需要开发的东西。你可以从货架上拿到它。你用一点 YAML 或 JSON 配置它,然后就可以使用它。这意味着你可以轻松地更新 sidecar,因为它不再被嵌入到你的运行时。这使得打补丁、更新变得更加更容易。它为我们的业务逻辑启用了多语言运行时。

微服务之后是什么?

这让我想到了最初的问题,微服务之后是什么?

img

如果我们看下架构的发展历程,应用架构在很高的层面上是从单体应用开始的。然而微服务给我们提供了如何把一个单体应用拆分成独立的业务域的指导原则。之后又出现了无服务器和功能即服务(FaaS),我们说过可以按操作将其进一步拆分,从而实现极高的可扩展性-因为我们可以分别扩展每个操作。

我想说的是 FaaS 并不是最好的模式 – 因为功能并不是实现合理的复杂服务的最佳模式,在这种情况下,当多个操作必须与同一个数据集进行交互时,你希望它们驻留在一起。可能是多运行时(我把它称为 Mecha 架构),在该架构中你将业务逻辑放在一个容器中,而所有与基础设施相关的关注点作为一个单独的容器存在。它们共同代表多运行时微服务。也许这是一个更合适的模型,因为它有更好的属性。

你可以获得微服务的所有好处。仍然将所有域和所有限界上下文放在一处。你将所有的基础设施和分布式应用需求放在一个单独的容器中,并在运行时将它们组合在一起。大概,现在最接近这种模型的是 Dapr。他们正在遵循这种模型。如果你仅对网络方面感兴趣,那么可能使用 Envoy 也会接近这种模型。

关于作者

Bilgin Ibryam 是红帽公司的产品经理和前架构师、提交人,并且是 Apache 软件基金会的成员。他是开源布道者,经常写博客、发表演讲,是 Kubernetes Patterns 和 Camel Design Patterns 书籍的作者。Bilgin 目前的工作主要集中在分布式系统、事件驱动架构以及可重复的云原生应用开发模式和实践上。请关注他 @bibryam 了解未来类似主题的更新。

6 - (2021)服务网格终极指南第二版——下一代微服务开发

服务网格终极指南第二版——下一代微服务开发

前言

Service Mesh Ultimate Guide - Second Edition: Next Generation Microservices Development

https://www.infoq.com/articles/service-mesh-ultimate-guide-2e/

服务网格终极指南第二版——下一代微服务开发

https://cloudnative.to/blog/service-mesh-ultimate-guide-e2/


主要收获

  • 了解采用服务网格技术的新兴架构趋势,特别是多云、多集群和多租户模式,如何在异构基础设施(裸机、虚拟机和 Kubernetes)中部署服务网格解决方案,以及从边缘计算层到网格的应用 / 服务连接。
  • 了解服务网格生态系统中的一些新模式,如多集群服务网格、媒体服务网格(Media Service Mesh)和混沌网格,以及经典的微服务反模式,如 “死星(Death Star) “架构。
  • 获取最新的关于在部署领域使用服务网格的创新总结,在 Pod(K8s 集群)和 VM(非 K8s 集群)之间进行快速实验、混乱工程和金丝雀部署。
  • 探索服务网格扩展领域的创新,包括:增强身份管理,以确保微服务连接的安全性,包括自定义证书授权插件,自适应路由功能,以提高服务的可用性和可扩展性,以及增强 sidecar 代理。
  • 了解操作方面即将出现的情况,如配置多集群功能和将 Kubernetes 工作负载连接到托管在虚拟机基础设施上的服务器,以及管理多集群服务网格中所有功能和 API 的开发者门户。

在过去的几年里,服务网格技术有了长足的发展。服务网格在各组织采用云原生技术方面发挥着重要作用。通过提供四种主要能力 —— 连接性、可靠性、可观察性和安全性,服务网格已经成为 IT 组织的技术和基础设施现代化工作的核心组成部分。服务网格使开发和运维团队能够在基础设施层面实现这些能力,因此,当涉及到跨领域的非功能需求时,应用团队不需要重新发明轮子。

自本文第一版于 2020 年 2 月发表以来,服务网格技术经历了重大创新,在不断发展的服务网格领域出现了一些新的架构趋势、技术能力和服务网格项目。

在过去的一年里,服务网格产品的发展远远超过了原有的 Kubernetes 解决方案,没有托管在 Kubernetes 平台上的应用无法利用服务网格。并非所有的组织都将其所有的业务和 IT 应用程序过渡到 Kubernetes 云平台。因此,自服务网格诞生以来,一直需要这项技术在不同的 IT 基础设施环境中工作。

随着微服务架构的不断采用,应用系统在云供应商、基础设施(Kubernetes、虚拟机、裸机服务器)、地域,甚至在服务网格集成环境中要管理的工作负载类型方面,都已实现解耦和分布式。

让我们从服务网格的历史开始说起,了解服务网格是如何产生的。

2016 年前后,“服务网格 " 这个词出现在微服务、云计算和 DevOps 的领域。Buoyant 团队在 2016 年用这个词来解释他们的产品 Linkerd。和云计算领域的许多概念一样,相关的模式和技术其实有很长的历史。

服务网格的到来主要是由于 IT 领域内的一场风暴。开发人员开始使用多语言(polyglot)方法构建分布式系统,并需要动态服务发现。运维部门开始使用短暂的基础设施,并希望优雅地处理不可避免的通信故障和执行网络策略。平台团队开始接受像 Kubernetes 这样的容器编排系统,并希望使用现代 API 驱动的网络代理(如 Envoy)在系统中和周围动态地路由流量。

本文旨在回答软件架构师和技术负责人的相关问题,如:什么是服务网格?我是否需要服务网格?如何评估不同的服务网格产品?

服务网格模式

服务网格模式专注于管理分布式软件系统中所有服务之间的通信。

背景介绍

该模式的背景有两个方面。首先,工程师们已经采用了微服务架构模式,并通过将多个(理想情况下是单一用途且可独立部署的)服务组合在一起构建他们的应用。第二,组织已经接受了云原生平台技术,如容器(如 Docker)、编排器(如 Kubernetes)和网关。

意图

服务网格模式试图解决的问题包括:

  • 消除了将特定语言的通信库编译到单个服务中的需求,以处理服务发现、路由和应用层(第 7 层)非功能通信要求。
  • 外部化服务通信配置,包括外部服务的网络位置、安全凭证和服务质量目标。
  • 提供对其他服务的被动和主动监测。
  • 在整个分布式系统中分布式地执行策略。
  • 提供可观察性的默认值,并使相关数据的收集标准化。
    • 启用请求记录
    • 配置分布式追踪
    • 收集指标

结构

服务网格模式主要侧重于处理传统上被称为 “东西向 “的基于远程过程调用(RPC)的流量:请求 / 响应类型的通信,源自数据中心内部,在服务之间传播。这与 API 网关或边缘代理相反,后者被设计为处理 “南北 “流量。来自外部的通信,进入数据中心内的一个终端或服务。

服务网格的特点

服务网格的实施通常会提供以下一个或多个功能:

  • 规范化命名并增加逻辑路由,(例如,将代码级名称 “用户服务 " 映射到平台特定位置 “AWS-us-east-1a/prod/users/v4”。
  • 提供流量整形和流量转移
  • 保持负载均衡,通常采用可配置的算法
  • 提供服务发布控制(例如,金丝雀释放和流量分割)
  • 提供按请求的路由(例如,影子流量、故障注入和调试重新路由)。
  • 增加基线可靠性,如健康检查、超时 / 截止日期、断路和重试(预算)。
  • 通过透明的双向传输级安全(TLS)和访问控制列表(ACL)等策略,提高安全性
  • 提供额外的可观察性和监测,如顶线指标(请求量、成功率和延迟),支持分布式追踪,以及 “挖掘” 和检查实时服务间通信的能力。
  • 使得平台团队能够配置 " 理智的默认值”,以保护系统免受不良通信的影响。

服务网格的能力可分为以下四个方面:

  • 连接性
  • 可靠性
  • 安全性
  • 可观察性

让我们看看服务网格技术在这些领域都能提供哪些功能。

连接性

  • 流量控制(路由,分流)
  • 网关(入口、出口)
  • 服务发现
  • A/B 测试、金丝雀
  • 服务超时、重试

可靠性

  • 断路器
  • 故障注入 / 混沌测试

安全性

  • 服务间认证(mTLS)
  • 证书管理
  • 用户认证(JWT)
  • 用户授权(RBAC)
  • 加密

可观察性

  • 监测
  • 遥测、仪表、计量
  • 分布式追踪
  • 服务图表

服务网格架构:内部原理

服务网格由两部分组成:数据平面和控制平面。Matt Klein,Envoy Proxy 的作者,写了一篇关于 “ 服务网格数据平面与控制平面 “的深入探讨。

广义上讲,数据平面 “执行工作”,负责 “有条件地翻译、转发和观察流向和来自 [网络终端] 的每个网络数据包”。在现代系统中,数据平面通常以代理的形式实现,(如 Envoy、HAProxyMOSN),它作为 “sidecar” 与每个服务一起在进程外运行。Linkerd 使用了一种 微型代理方法,该方法针对服务网格的使用情况进行了优化。

控制平面 “监督工作”,并将数据平面的所有单个实例 —— 一组孤立的无状态 sidecar 代理变成一个分布式系统。控制平面不接触系统中的任何数据包 / 请求,相反,它允许人类运维人员为网格中所有正在运行的数据平面提供策略和配置。控制平面还能够收集和集中数据平面的遥测数据,供运维人员使用。

控制平面和数据平面的结合提供了两方面的优势,即策略可以集中定义和管理,同时,同样的政策可以以分散的方式,在 Kubernetes 集群的每个 pod 中本地执行。这些策略可以与安全、路由、断路器或监控有关。

下图取自 Istio 架构文档,虽然标注的技术是 Istio 特有的,但这些组件对所有服务网格的实现都是通用的。

arch

Istio 架构,展示了控制平面和代理数据平面的交互方式(由 Istio 文档提供)。

使用案例

服务网格可以实现或支持多种用例。

动态服务发现和路由

服务网格提供动态服务发现和流量管理,包括用于测试的流量影子(复制),以及用于金丝雀发布和 A/B 实验的流量分割。

服务网格中使用的代理通常是 “应用层 " 感知的(在 OSI 网络堆栈的第 7 层运行)。这意味着流量路由决策和指标的标记可以利用 HTTP 头或其他应用层协议元数据。

服务间通信可靠性

服务网格支持跨领域的可靠性要求的实施和执行,如请求重试、超时、速率限制和断路。服务网格经常被用来补偿(或封装)处理分布式计算的八个谬误。应该注意的是,服务网格只能提供 wire-level 的可靠性支持(如重试 HTTP 请求),最终服务应该对相关的业务影响负责,如避免多个(非幂等的)HTTP POST 请求。

流量的可观察性

由于服务网格处于系统内处理的每个请求的关键路径上,它还可以提供额外的 “可观察性”,例如请求的分布式追踪、HTTP 错误代码的频率以及全局和服务间的延迟。虽然在企业领域是一个被过度使用的短语,但服务网格经常被提议作为一种方法来捕获所有必要的数据,以实现整个系统内流量的统一界面视图。

通信安全

服务网格还支持跨领域安全要求的实施和执行,如提供服务身份(通过 x509 证书),实现应用级服务 / 网络分割(例如,“服务 A” 可以与 “服务 B “通信,但不能与 “服务 C “通信),确保所有通信都经过加密(通过 TLS),并确保存在有效的用户级身份令牌或 “护照 “。

反模式

当反模式的使用出现时,这往往是一个技术成熟的标志。服务网格也不例外。

太多的流量管理层次

当开发人员不与平台或运维团队协商,并在现在通过服务网格实现的代码中重复现有的通信处理逻辑时,就会出现这种反模式。例如,除了服务网格提供的 wire-level 重试策略外,应用程序还在代码中还实现了重试策略。这种反模式会导致重复的事务等问题。

服务网格银弹

在 IT 领域没有 “银弹 “这样的东西,但供应商有时会被诱惑给新技术贴上这个标签。服务网格不会解决微服务、Kubernetes 等容器编排器或云网络的所有通信问题。服务网格的目的只是促进服务件(东西向)的通信,而且部署和运行服务网格有明显的运营成本。

企业服务总线(ESB)2.0

在前微服务面向服务架构(SOA)时代,企业服务总线(ESB)实现了软件组件之间的通信系统。有些人担心 ESB 时代的许多错误会随着服务网格的使用而重演。

通过 ESB 提供的集中的通信控制显然有价值。然而,这些技术的发展是由供应商推动的,这导致了多种问题,例如:ESB 之间缺乏互操作性,行业标准的定制扩展(例如,将供应商的特定配置添加到 WS-* 兼容模式中),以及高成本。ESB 供应商也没有做任何事情来阻止业务逻辑与通信总线的集成和紧耦合。

大爆炸部署

在整个 IT 界有一种诱惑,认为大爆炸式的部署方法是最容易管理的方法,但正如 AccelerateDevOps 报告的研究,事实并非如此。由于服务网格的全面推广意味着这项技术处于处理所有终端用户请求的关键路径上,大爆炸式的部署是非常危险的。

死星建筑

当企业采用微服务架构,开发团队开始创建新的微服务或在应用中利用现有的服务时,服务间的通信成为架构的一个关键部分。如果没有一个良好的治理模式,这可能会导致不同服务之间的紧密耦合。当整个系统在生产中出现问题时,也将很难确定哪个服务出现了问题。

如果缺乏服务沟通战略和治理模式,该架构就会变成所谓的 “死星架构”。

关于这种架构反模式的更多信息,请查看关于云原生架构采用的第一部分第二部分第三部分的文章。

特定领域的服务网格

服务网格的本地实现和过度优化有时会导致服务网格部署范围过窄。开发人员可能更喜欢针对自己的业务领域的服务网格,但这种方法弊大于利。我们不希望实现过于细化的服务网格范围,比如为组织中的每个业务或功能域(如财务、人力资源、会计等)提供专用的服务网格。这就违背了拥有像服务网格这样的通用服务协调解决方案的目的,即企业级服务发现或跨域服务路由等功能。

服务网格的实现和产品

以下是一份非详尽的当前服务网格实施清单。

另外,像 DataDog 这样的其他产品也开始提供与 Linkerd、Istio、Consul Connect 和 AWS App Mesh 等服务网格技术的集成。

服务网格对比

服务网格领域的发展极为迅速,因此任何试图创建比较的努力都可能很快变得过时。然而,确实存在一些比较。应该注意了解来源的偏见(如果有的话)和进行比较的日期。

InfoQ 一直建议服务网格的采用者对每个产品进行自己的尽职调查和试验。

服务网格教程

对于希望试验多服务网格的工程师或建筑师来说,可以使用以下教程、游戏场和工具。

服务网格的历史

自 2013 年底 Airbnb 发布 SmartStack,为新兴的 “ 微服务 “风格架构提供进程外服务发现机制(使用 HAProxy)以来,InfoQ 一直在跟踪这个我们现在称之为 服务网格的话题。许多之前被贴上 “独角兽 “标签的组织在此之前就在研究类似的技术。从 21 世纪初开始,谷歌就在开发其 Stubby RPC 框架,该框架演变成了 gRPC,以及 谷歌前端(GFE)和全局软件负载均衡器(GSLB),在 Istio 中可以看到它们的特质。在 2010 年代早期,Twitter 开始了 Scala 驱动的 Finagle 的工作,Linkerd 服务网格由此产生。

2014 年底,Netflix 发布了一整套基于 JVM 的实用程序,包括 Prana,一个 “sidecar “程序,允许用任何语言编写的应用服务通过 HTTP 与库的独立实例进行通信。2016 年,NGINX 团队开始谈论 “Fabric 模型 “,这与服务网格非常相似,但需要使用他们的商业 NGINX Plus 产品来实现。另外,Linkerd v0.2 在 2016 年 2 月发布,尽管该团队直到后来才开始称它为服务网格。

服务网格历史上的其他亮点包括 2017 年 5 月的 Istio、2018 年 7 月的 Linkerd 2.0、2018 年 11 月的 Consul ConnectGloo Mesh、2019 年 5 月的 服务网格接口(SMI),以及 2019 年 9 月的 Maesh(现在叫 Traefik Mesh)和 Kuma。

即使是在独角兽企业之外出现的服务网格,如 HashiCorp 的 Consul,也从上述技术中获得了灵感,通常旨在实现 CoreOS 提出的 “GIFEE “概念;所有人可用的 Google 基础设施(Google infrastructure for everyone else)。

为了深入了解现代服务网格概念的演变历史,Phil Calçado 写了一篇全面的文章 “ 模式:服务网格 “。

服务网格标准

尽管在过去的几年里,服务网格技术年复一年地发生着重大转变,但服务网格的标准还没有跟上创新的步伐。

使用服务网格解决方案的主要标准是服务网格接口(SMI)。服务网格接口是在 Kubernetes 上运行的服务网格的一个规范。它本身并没有实现服务网格,而是定义了一个通用的标准,可以由各种服务网格供应商来实现。

SMI API 的目标是提供一套通用的、可移植的服务网格 API,Kubernetes 用户可以以一种与提供者无关的方式使用。通过这种方式,人们可以定义使用服务网格技术的应用程序,而不需要与任何特定的实现紧密结合。

SMI 基本上是一个 Kubernetes 自定义资源定义(CRD)和扩展 API 服务器的集合。这些 API 可以安装到任何 Kubernetes 集群,并使用标准工具进行操作。为了激活这些 API,需要在 Kubernetes 集群中运行一个 SMI 提供者。

SMI 规范既允许终端用户的标准化,也允许服务网格技术提供商的创新。SMI 实现了灵活性和互操作性,并涵盖了最常见的服务网格功能。目前的规范组件集中在服务网格能力的连接方面。API 规范包括以下内容。

  • 流量访问控制
  • 流量指标
  • 流量规格
  • 流量分割

目前的 SMI 生态系统包括广泛的服务网格,包括 Istio、Linkerd、Consul Connect、Gloo Mesh 等。

SMI 规范是在 Apache License 2.0 版本下许可的

如果你想了解更多关于 SMI 规范及其 API 细节,请查看以下链接。

服务网格基准测试

服务网格性能是一个捕捉基础设施容量、服务网配置和工作负载元数据细节的标准。SMP 规范用于捕捉以下细节。

  • 环境和基础设施细节
  • 节点的数量和规模,编排器
  • 服务网格和它的配置
  • 工作量 / 应用细节
  • 进行统计分析以确定性能特征

来自 Linkerd 团队的 William Morgan 写了关于 Linkerd 和 Istio 的性能基准测试。还有一篇来自 2019 年的文章,介绍了 Istio 关于服务网格性能基准测试的最佳实践。

重要的是要记住,就像其他性能基准测试一样,你不应该对任何这些外部出版物投入过多的注意力,特别是产品供应商发表的文章。该在你的服务器环境中设计和执行你自己的性能测试,以验证哪个具体产品适合你的应用程序的业务和非功能要求。

探索服务网格的未来

Kasun Indrasiri 探讨了 “ 为事件驱动的消息传递使用服务网格的潜力 “,他在其中讨论了在服务网格中实现消息传递支持的两种主要的新兴架构模式:协议代理 sidecar 和 HTTP 桥接 sidecar。这是服务网格社区中一个活跃的发展领域,在 Envoy 中支持 Apache Kafka 的工作引起了相当多的关注。

Christian Posta 之前在 “Towards a Unified, Standard API for Consolidating Service Meshes 中写过关于服务网格使用标准化的尝试。这篇文章还讨论了 2019 年微软和合作伙伴在 KubeCon EU 上宣布的服务网格接口(SMI)。SMI 定义了一套通用和可移植的 API,旨在为开发人员提供不同服务网格技术的互操作性,包括 Istio、Linkerd 和 Consul Connect。

将服务网格与平台结构整合的主题可以进一步分为两个子主题。

首先,正在进行的工作是减少由服务网格数据平面引入的网络开销。这包括数据平面开发工具包(DPDK),它是一个用户空间应用程序,“绕过了 Linux 内核网络堆栈,直接与网络硬件对话”。还有 Cilium 团队的基于 Linux 的 BPF 解决方案,它利用 Linux 内核中的扩展伯克利包过滤器(eBPF)功能来实现 “非常有效的网络、策略执行和负载均衡功能”。另一个团队正在用网络服务网格(Network Service Mesh)将服务网格的概念映射到 L2/L3 有效载荷,试图 “以云原生的方式重新想象网络功能虚拟化(NFV)"。

其次,有多项举措将服务网格与公共云平台更紧密地结合在一起,从 AWS App MeshGCP Traffic DirectorAzure Service Fabric Mesh 的发布可见端倪。

Buoyant 团队致力于为服务网格技术开发有效的以人为本的控制平面。他们最近发布了 Buoyant Cloud,一个基于 SaaS 的 “团队控制平面”,用于平台团队操作 Kubernetes。这个产品将在下面的章节中详细讨论。

自去年以来,在服务网格领域也有一些创新。

多云、多集群、多租户服务网格

近年来,不同组织对云的采用已经从单一的云解决方案(私有云或公共云)转变为由多个不同供应商(AWS、谷歌、微软 Azure 等)支持的基于多云(私有、公共和混合)的新基础设施。同时,需要支持不同的工作负载(交易、批处理和流媒体),这对实现统一的云架构至关重要。

这些业务和非功能需求反过来又导致需要在异构基础设施(裸机、虚拟机和 Kubernetes)中部署服务网格解决方案。服务网格需要相应转变,以支持这些不同的工作负载和基础设施。

KumaTetrate Service Bridge 这样的技术支持多网格控制平面,以使业务应用在多集群和多云服务网格环境中工作。这些解决方案抽象出跨多个区域的服务网格策略的同步以及跨这些区域的服务连接(和服务发现)。

多集群服务网格技术的另一个新趋势是需要从边缘计算层(物联网设备)到网格层的应用 / 服务连接。

媒体服务网格

思科系统公司开发的媒体流网格(Media Streaming Mesh)或媒体服务网格,用于协调实时应用程序,如多人游戏、多方视频会议或在 Kubernetes 云平台上使用服务网格技术的 CCTV 流。这些应用正越来越多地从单体应用转向微服务架构。服务网格可以通过提供负载均衡、加密和可观察性等功能来帮助应用程序。

混沌网格

Chaos MeshCNCF 托管的项目,是一个开源的、云原生的混沌工程平台,用于托管在 Kubernetes 上的应用程序。虽然不是直接的服务网格实现,但 Chaos Mesh 通过协调应用程序中的故障注入行为来实现混沌工程实验。故障注入是服务网格技术的一个关键能力。

Chaos Mesh 隐藏了底层的实现细节,因此应用开发者可以专注于实际的混沌实验。Chaos Mesh 可以和服务网格一起使用。请看这个用例,该团队如何使用 Linkerd 和 Chaos Mesh 来为他们的项目进行混沌实验。

服务网格作为一种服务

一些服务网格供应商,如 Buoyant,正在提供管理服务网格或 “服务网格作为一种服务 “的解决方案。今年早些时候,Buoyant 宣布公开测试发布一个名为 Buoyant Cloud 的 SaaS 应用程序,允许客户组织利用 Linkerd 服务网格的按需支持功能来管理服务网格。

Buoyant Cloud 解决方案提供的一些功能包括如下:

  • 自动跟踪 Linkerd 数据平面和控制平面的健康状况
  • 在 Kubernetes 平台上管理跨 pod、代理和集群的服务网格生命周期和版本
  • 以 SRE 为重点的工具,包括服务水平目标(SLO)、工作负荷黄金指标跟踪和变更跟踪

网络服务网格(NSM)

网络服务网格(NSM)是云原生计算基金会的另一个沙盒项目,提供了一个混合的、多云的 IP 服务网格。NSM 实现了网络服务连接、安全和可观察性等功能,这些都是服务网格的核心特征。NSM 与现有的容器网络接口(CNI)实现协同工作。

服务网格扩展

服务网格扩展是另一个已经看到很多创新的领域。服务网格扩展的一些发展包括:

  • 增强的身份管理,以确保微服务连接的安全,包括自定义证书授权插件
  • 自适应路由功能,以提高服务的可用性和可扩展性
  • 加强 sidecar 代理权

服务网格业务

采用服务网格的另一个重要领域是服务网格生命周期的运维方面。操作方面 —— 如配置多集群功能和将 Kubernetes 工作负载连接到虚拟机基础设施上托管的服务器,以及管理多集群服务网格中所有功能和 API 的开发者门户 —— 将在生产中服务网格解决方案的整体部署和支持方面发挥重要作用。

常见问题

什么是服务网格?

服务网格是一种在分布式(可能是基于微服务的)软件系统内管理所有服务对服务(东西向)流量的技术。它既提供以业务为重点的功能操作,如路由,也提供非功能支持,如执行安全策略、服务质量和速率限制。它通常(尽管不是唯一的)使用 sidecar 代理来实现,所有服务都通过 sidecar 代理进行通信。

服务网格与 API 网关有什么不同?

关于服务网格的定义,见上文。

另一方面,API 网关管理进入集群的所有入口(南北)流量,并为跨功能的通信要求提供额外支持。它作为进入系统的单一入口点,使多个 API 或服务凝聚在一起,为用户提供统一的体验。

如果我正在部署微服务,我是否需要服务网格?

不一定。服务网格增加了技术栈的操作复杂性,因此通常只有在组织在扩展服务与服务之间的通信方面遇到困难,或者有特定的用例需要解决时才会部署。

我是否需要服务网格来实现微服务的服务发现?

不,服务网格提供了实现服务发现的一种方式。其他解决方案包括特定语言的库(如 Ribbon 和 EurekaFinagle)。

服务网格是否会给我的服务之间的通信增加开销 / 延迟?

是的,当一个服务与另一个服务进行通信时,服务网格至少会增加两个额外的网络跳数(第一个是来自处理源的出站连接的代理,第二个是来自处理目的地的入站连接的代理)。然而,这个额外的网络跳转通常发生在 localhost 或 loopback 网络接口上,并且只增加了少量的延迟(在毫秒级)。实验和了解这对目标用例是否是一个问题,应该是服务网格分析和评估的一部分。

服务网格不应该是 Kubernetes 或应用程序被部署到的 “云原生平台” 的一部分吗?

潜在的。有一种说法是在云原生平台组件内保持关注点的分离(例如,Kubernetes 负责提供容器编排,而服务网格负责服务间的通信)。然而,正在进行的工作是将类似服务网格的功能推向现代平台即服务(PaaS)产品。

我如何实施、部署或推广服务网格?

最好的方法是分析各种服务网格产品(见上文),并遵循所选网格特有的实施准则。一般来说,最好是与所有利益相关者合作,逐步将任何新技术部署到生产中。

我可以建立自己的服务网格吗?

是的,但更相关的问题是,你应该吗?建立一个服务网格是你组织的核心竞争力吗?你能否以更有效的方式为你的客户提供价值?你是否也致力于维护你自己的网络,为安全问题打补丁,并不断更新它以利用新技术?由于现在有一系列的开源和商业服务网格产品,使用现有的解决方案很可能更有效。

在一个软件交付组织内,哪个团队拥有服务网格?

通常,平台或运维团队拥有服务网格,以及 Kubernetes 和持续交付管道基础设施。然而,开发人员将配置服务网格的属性,因此这两个团队应该紧密合作。许多企业正在追随云计算先锋的脚步,如 Netflix、Spotify 和谷歌,并正在创建内部平台团队,为以产品为重点的全周期开发团队提供工具和服务。

Envoy 是一个服务网格吗?

Envoy 是一个云原生代理,最初是由 Lyft 团队设计和构建的。Envoy 经常被用作服务网格的数据平面。然而,为了被认为是一个服务网格,Envoy 必须与控制平面一起使用,这样才能使这些技术集合成为一个服务网格。控制平面可以是简单的集中式配置文件库和指标收集器,也可以是全面 / 复杂的 Istio。

Istio 和 “服务网格 " 这两个词可以互换使用吗?

不,Istio 是服务网格的一种。由于 Istio 在服务网格类别出现时很受欢迎,一些人将 Istio 和服务网格混为一谈。这个混淆的问题并不是服务网格所独有的,同样的挑战发生在 Docker 和容器技术上。

我应该使用哪个服务网格?

这个问题没有唯一的答案。工程师必须了解他们当前的需求,以及他们的实施团队的技能、资源和时间。上面的服务网格比较链接将提供一个良好的探索起点,但我们强烈建议企业至少尝试两个网格,以了解哪些产品、技术和工作流程最适合他们。

我可以在 Kubernetes 之外使用服务网吗?

是的。许多服务网格允许在各种基础设施上安装和管理数据平面代理和相关控制平面。HashiCorp 的 Consul 是最知名的例子,Istio 也被实验性地用于 Cloud Foundry。

其他资源