开始使用服务网格之前,需要了解三件事:什么是服务网格,它是如何工作的,以及为什么(以及何时不要)使用它。

虽然没有公认的服务网格定义,但通常定义如下:

服务网格是一个基础设施层,使您能够从一个单一的控制平面控制工作负载的网络通信。

我们可以将这个定义分解成几个部分来更好地理解它:

所谓基础设施层,是指服务网格不是您服务的一部分;它是独立部署和操作的。由于它不需要了解特定服务的业务逻辑,而且会影响每个服务,因此被视为基础设施或中间件。

图 1-1 显示了一个典型的软件堆栈。服务和应用程序运行在基础设施之上。服务网格位于第一个基础设施层,与存储、度量和其他高级基础设施需求一起。其下是虚拟机、Kubernetes 或任何虚拟云厂商或者编排器,即所有东西的运行平台。最底层是实际的硬件(裸机)。

所谓控制工作负载的网络通信,是指服务网格控制进入和离开微服务、数据库或其他任何进行网络通信的流量。例如,服务网格可能根据规则(如缺少必需的标头)拒绝入网流量,或可能加密外发流量。服务网格完全控制所有进入和离开服务的流量。

最后,所谓从单一控制平面,是指服务网格 operators 可以从一个位置与服务网格交互。假设 operators 想要更改多个服务的配置,那么他们不需要重新配置十几个子系统或修改服务本身;相反,他们只需配置一次服务网格,服务网格就会处理所有变更的传播。

服务网格的工作原理

服务网格由边车代理和控制平面组成。

边车代理

代理是一种应用程序,流量在到达目的地的途中会经过它。您可能听说过的流行代理有 NGINX、HAProxy 和 Envoy。在大多数服务网格中,所有服务流量(流入和流出)都通过专用于每个服务实例的本地代理路由。

图 1-2 显示了具有两个服务实例(前端和后端)的服务网格的样子。当前端调用后端时,前端的本地代理会捕获出站请求。然后,前端的代理将请求转发到后端服务。当请求到达后端服务时,它再次被后端的本地代理捕获并检查。如果请求被允许,后端的代理会将其转发到实际的后端服务。

每个服务实例都必须部署自己的本地代理。这种在主服务旁边部署辅助应用程序(在这种情况下是代理)的模式被称为边车模式,因此本地代理也被称为边车代理。

边车代理是服务网格的重要组成部分,因为它们可以在不修改或重新部署底层服务的情况下控制服务流量。由于边车代理作为与服务分离的进程运行,它们可以重新配置而不影响服务本身。例如,图 1-2 中的后端服务的边车代理可以重新配置以拒绝来自前端服务的流量,而无需更改代码或重新部署后端服务。

那么,谁负责重新配置代理呢?答案是控制平面。

控制平面

控制平面的任务是管理和配置边车代理。如图 1-3 所示,控制平面是一个必须独立部署的单独服务;它不是作为边车部署的。控制平面是服务网格中大部分复杂逻辑所在的地方:它必须监视服务的启动和停止、签发和分发证书、重新配置代理等。边车代理本身相对简单:它们从控制平面接收配置,详细说明要对流量执行哪些操作,然后执行这些操作。

第 1 章:服务网格基础知识 - 图1

如果我们回到我对服务网格的定义——“一个使您能够从单一控制平面控制工作负载网络通信的基础设施层”——您现在可以看到代理和控制平面如何融合在一起。

控制平面是服务网格运营者交互的单一位置。它配置控制网络通信的代理。控制平面和代理共同构成了基础设施层。

具体示例

让我们通过一个具体示例来展示服务网格在实际中的工作原理。图 1-4 显示了这个示例的架构。

第 1 章:服务网格基础知识 - 图2

当前端调用后端时,前端的边车代理会捕获请求。在图 1-5 中,服务网格已经配置了前端的代理,将流量直接传递给后端服务而不做修改。运行在后端旁边的边车代理捕获传入流量并将请求转发到实际的后端服务实例。后端服务实例处理请求并沿相同路径发送响应。

第 1 章:服务网格基础知识 - 图3

现在假设有一个新需求,要获取前端每秒向后端发送的请求数量。您可以修改前端和后端的代码来生成这些指标,但有了服务网格,您可以采取更简单的方法,如图 1-6 所示。首先,您将控制平面配置指标数据库的 URL(步骤 1)。然后,控制平面立即重新配置两个边车代理,并指示它们生成指标(步骤 2)。现在,当前端调用后端时(步骤 3),每个代理会将指标发送到指标数据库(步骤 4),您可以在指标仪表板上查看每秒的请求数量。

第 1 章:服务网格基础知识 - 图4

注意在这里无需更改任何服务的代码,也无需重新部署任何内容。只需一次配置更改,您就立即获得了前端和后端服务的指标。

这个具体示例应该有助于您理解服务网格在实际中的工作方式,但这只是一个简化的图景。在典型的服务网格部署中,控制平面管理着数百个服务和工作负载,因此架构看起来更像图 1-7。这就是它为什么叫做“服务网格”的原因!

第 1 章:服务网格基础知识 - 图5

在更大的网格中,您可以看到,从单一位置控制所有这些服务的网络,而无需重新部署任何服务或更改代码,这一能力是多么强大。这就引出了为什么会使用服务网格的问题。

为什么使用服务网格

服务网格在四个方面提供功能:安全性、可观测性、可靠性和流量控制。服务网格的根本价值主张是能够在不修改服务代码的情况下,跨每个服务和工作负载提供这些功能。

在接下来的部分中,我将详细说明这些领域,但值得注意的是,服务网格提供的功能也可以在服务代码中实现!要问的问题是,如果这些功能可以在服务代码中实现,为什么还要部署服务网格?答案是,当规模达到一定程度时,重新编写每个服务的成本比运行服务网格要高。这将在“何时使用服务网格”一节中详细讨论。

安全性

公司部署服务网格的主要原因之一是为了保护网络。通常,这意味着加密所有工作负载之间的流量,并实施身份验证和授权。

在没有服务网格的微服务架构中解决这个问题可能非常困难。要求每个请求都加密意味着需要以安全的方式向每个服务提供传输层安全性(TLS)证书,并管理自己的证书签发基础设施。身份验证和授权每个请求则意味着需要在每个服务中更新和维护身份验证代码。

服务网格使这项工作变得更加简单,因为它可以签发证书并配置边车代理来加密流量和执行授权——所有这些都无需对底层服务进行任何更改(见图 1-8)。

第 1 章:服务网格基础知识 - 图6

安全案例研究

Annika 是她所在平台团队的首席工程师。首席信息安全官提出了一个要求:到年底前,所有微服务之间的流量必须使用 TLS 加密。

Annika 知道,修改每个服务以支持使用 TLS 进行请求和接收是一个巨大的任务。当前有 30 个开发团队负责数百个服务,其中一些服务已经多年没有更新。考虑到今年计划的所有功能工作,很难在每个团队的工作计划中为更新每个服务争取时间。即使他们能够说服团队修改和重新部署所有服务,他们仍然需要构建一个工具来安全地分发和轮换 TLS 证书。

幸运的是,Annika 听说过服务网格。使用服务网格,Annika 不需要让团队更新任何服务代码。相反,边车代理可以自动执行加密和解密,而底层服务甚至无法感知到。服务网格还处理 TLS 证书的安全分发和轮换。平台团队只需将代理部署到每个服务旁边;Annika 确信她的团队可以在几个月内完成这一工作。

可观测性

可观测性是理解服务在运行时发生了什么的能力。可观测数据对于理解微服务架构和诊断故障至关重要,但配置所有服务以统一的方式生成指标和其他数据可能具有挑战性。

捕获可观测数据是服务网格的完美工作,因为所有请求都流经其代理。服务网格可以配置其代理以一致的格式生成所有服务的指标,而无需修改或重新部署底层服务。

可观测性案例研究

Geordi 遇到了一个困境。他刚加入一家成立三年的创业公司,该公司在迅猛推出功能。现在,创业公司蓬勃发展,每天吸引成千上万的新用户。不幸的是,这些新用户对系统造成了巨大的负载,运维团队越来越频繁地经历故障。

最糟糕的是,由于开发团队快速构建功能,他们从未在微服务中添加指标。这意味着每当网站出现故障时,运维团队就像在黑暗中摸索。他们必须翻阅成千上万的日志来找出问题所在和哪个服务出现了故障。

Geordi 希望所有开发团队更新他们的服务,添加有关请求响应时间和错误的适当指标,但考虑到开发团队当前的工作量,这可能需要几个月甚至整整一年。

相反,Geordi 建议团队部署一个服务网格。服务网格将自动生成系统中每个请求的详细指标——所有这些都无需修改底层服务。运维团队可以为每个服务构建仪表板,查看哪些服务返回错误和运行缓慢。他们甚至可以设置警报阈值,以便在问题导致全面故障之前及时发现问题。

可靠性

在分布式系统中,往往会发生某种故障。构建可靠的分布式系统意味着在可能的情况下减少故障,并在故障不可避免发生时优雅地处理。

减少故障可能意味着实施健康检查,以便仅将流量发送到健康的服务。处理故障可能意味着重试失败的请求(见图 1-9),或实现超时,以便服务不会无限期等待响应。

在代码中实现这些技术既耗时又容易出错,并且在所有服务中以一致的方式进行这些操作非常困难。使用服务网格,代理可以为您的任何服务执行这些技术——您只需与控制平面进行交互。您还可以根据服务负载的变化实时调整设置。

第 1 章:服务网格基础知识 - 图7

可靠性案例研究

Miles 刚刚被 PagerDuty 应用程序的尖锐提示声唤醒。他正在值班,于是他昏昏沉沉地起床,拿出工作包里的笔记本电脑。警报显示主页宕机了,这可不好。

他自己加载主页,发现浏览器一直在转圈。他查看监控仪表板,发现主页服务的延迟超出了正常范围,每个请求需要五分钟!奇怪的是,主机主页的节点 CPU 负载看起来正常。他怀疑主页的某个依赖项是否运行缓慢?

他的公司运行着服务网格,因此他打开服务网格的用户界面,检查主页服务的状态。他立刻发现分析服务被列为主页服务的依赖项,并且其平均延迟达到了五分钟。这可能解释了宕机的原因!主页服务记录了每一个请求到分析服务。如果主页服务没有对请求到分析服务设置超时,它就会一直等待,直到该请求完成。

让人沮丧的是,请求分析服务甚至不是那么重要。公司更希望网站正常运行,而不是因分析数据未记录而导致整个网站宕机!

如果没有服务网格,Miles 需要唤醒主页开发人员,让他们在代码中实现超时设置,然后进行完全重新部署。幸运的是,服务网格让 Miles 可以通过控制平面控制任何服务的流量。Miles 推送了一个新的配置,将对分析服务的请求超时设置为 100 毫秒。他再次尝试加载主页:主页几乎瞬间加载完毕。Miles 向值班聊天频道发送消息,要求他们早上检查分析服务出了什么问题,然后回到床上继续睡觉。

流量控制

流量控制涉及控制服务之间流量的路由。流量控制解决了许多问题:

  • 实施部署策略,例如金丝雀发布,其中少量“金丝雀”流量被路由到服务的新版本,以检查它是否正常工作,然后才完全推出新版本。
  • 从单体到微服务的迁移,其中将服务从单体中分离出来,并将以前路由到单体的流量无缝地重定向到新的微服务。
  • 多集群故障转移,其中如果本地集群宕机,则将流量路由到其他健康集群中的服务。

流量控制案例研究

B’Elanna 很兴奋。她终于获得批准,将她负责的大型单体服务拆分成微服务。她所在的组织已经使用微服务很长时间,但她的服务是最老旧的,已经变得庞大且臃肿。

她的团队计划从单体中拆分出两个微服务,一个是成员服务,另一个是购物车服务,并且需要在没有任何停机的情况下完成。许多其他微服务依赖于单体,因此在拆分它时,他们需要确保那些微服务调用新拆分出的服务。

如果没有服务网格,B’Elanna 需要更新所有依赖服务,以调用新的成员和购物车服务,而不是单体。例如,请求 /members 端点现在需要转发到新的成员服务,请求 /cart 端点需要转发到新的购物车服务。这意味着需要在许多服务中更新大量代码。

幸运的是,B’Elanna 的公司运行着服务网格。如图 1-10 所示,她可以配置服务网格,将任何匹配 /members 的请求自动路由到新的成员服务,将 /cart 的请求路由到新的购物车服务。依赖服务可以继续正常发起请求,服务网格处理所有路由。这意味着 B’Elanna 可以专注于拆分单体,而不是更新所有依赖服务。

第 1 章:服务网格基础知识 - 图8

功能组合

现在你应该理解了服务网格在安全、可观察性、可靠性和流量控制方面提供的功能。单独来看,这些功能是有用的,但它们在结合使用时更具威力。

例如,服务网格提供的可观察性数据可以与可靠性和流量控制功能结合使用。如果网格检测到某个服务实例返回错误,它可以将流量重定向到健康的实例或完全不同的集群。或者,网格的安全功能可以与可观察性功能结合,以检测服务是否试图发起未授权的请求——这可能表明存在安全漏洞。当你自己部署 Consul 时,你会看到许多用例可以将服务网格的功能结合使用。

如果你的组织需要这些功能,你必须决定是否值得增加服务网格的复杂性,还是应该在服务代码中实现这些功能。回答这个问题的关键在于考察你的规模。

何时使用服务网格

毫无疑问,部署服务网格会增加额外的复杂性。你现在需要管理 sidecar 代理和服务网格控制平面。此外,你还需要更多的计算资源(CPU 和内存)来运行代理和控制平面,而且所有流量都需要经过本地 sidecar 代理,这会增加延迟。在代码中实现服务网格功能可以节省资源并减少基础设施复杂性(尽管这会增加代码复杂性)。要使服务网格具有价值,它必须为你的组织提供很多价值。

一个简单的公式来判断何时使用服务网格是,当你(a)需要解决前面提到的网络问题(安全、可观察性、可靠性和流量控制),并且(b)你的组织正处于或将很快处于一个规模,使得在服务代码中解决这些问题的成本过高时。

例如,假设你的组织正在转向所谓的零信任安全架构,其中所有内部流量都需要加密、认证和授权。如果你只运行两个微服务,你可以轻松地重新编写这些服务。然而,如果你运行的是 400 个微服务,那么在合理的时间内重新编写所有这些服务是不太可能的。在这种情况下,服务网格是非常合适的。

此外,在一定规模下,可能会有你想控制的服务和工作负载,而你实际上无法编辑它们的代码。例如,也许你正在部署一个打包的开源软件,或者你使用的是云管理数据库。理想情况下,你希望对这些工作负载有与你对其他服务一样的控制权。

最终,决定在何种规模下使用服务网格将取决于你的具体组织和你要解决的问题。我希望本书能帮助你理解服务网格解决了哪些问题,并帮助你评估在你的情况下是否合适。

共享库?

许多工程师在听到“我如何在每个服务中实现这个功能?”的问题时,立即想到共享库。共享库是一个适用于较小组织的好方案,但在达到一定规模后,它存在许多问题。

首先,必须有人编写共享库,或者从开源选项中选择一个好的共享库。如果你使用多种编程语言,你的库必须支持所有这些语言(以及任何未来的语言)。其次,你必须重新编写所有服务以使用该库,并重新部署它们。在大型组织中,这是一项庞大的任务。第三,如果你需要更新该库(而你肯定会需要),你还需要更新并重新部署每个服务。

在一些特定的用例中,共享库是一个不错的选择,但它们对于解决服务网格所解决的问题来说,效果并不好。

总结

在本章中,你了解了什么是服务网格,它是如何工作的,以及为什么要使用服务网格。

我介绍了服务网格的定义:

一个基础设施层,使你能够从单一的控制平面控制工作负载的网络通信。

我讨论了服务网格的两个组件——代理和控制平面——如何实现网络通信的控制。你通过一个实际的服务网格示例了解了其工作方式,并讨论了服务网格功能的四个类别:安全、可观察性、可靠性和流量控制。

最后,我讨论了何时应该使用服务网格:当你需要这些功能,并且你处于一个规模,使得在服务代码中实现这些功能的成本过高时。

到目前为止,本章讨论的内容适用于大多数服务网格,而不仅仅是 Consul。下一章将专门讨论 Consul。你将了解它的工作原理、架构、使用的协议,以及它的独特之处。