关于服务网格
介绍
如果您是一名软件工程师,工作在任何接近后端系统的地方,那么“服务网格”这个术语可能在过去几年的某个时候已经渗透到您的意识中了。多亏了一系列奇怪的事件,这句术语就像滚雪球一样越滚越大,在营销和宣传中不断地出现,而且没有任何迹象表明它会很快停止。
服务网格诞生于云原生生态-它浑浊且暗流涌动,这意味着大量的服务网格内容,从“毫无营养”到用一个技术术语来说就是:“基本上是扯淡”。但是,如果您能够排除所有的干扰,服务网格就具有一些实际的、具体的和重要的价值。
在本指南中,我将尝试提供一个诚实、深入、以工程师为中心的服务网格指南。我要讲的不仅是“是什么”,还有“为什么”和“为什么是现在”。最后,我将尝试描述为什么我认为这种特殊的技术吸引了如此疯狂的炒作,这本身就是一个有趣的故事。
我是谁
嗨。我是威廉·摩根。我是Linkerd的创造者之一,Linkerd是第一个服务网格项目,这个项目产生了服务网格这个术语。(对不起!)我也是Buoyant公司的CEO,这家公司开发出了像Linkerd和Dive这样很酷的服务网格项目。
可以想象,因利益相关,我是非常有偏见的,在这个问题上有一些强烈的意见。也就是说,我将尽我最大的努力把篇幅控制在最低限度(除了一个部分,“为什么人们对这件事谈论得这么多?”我将尽我最大的努力以一种尽可能客观的方式来写这篇指南。当我需要具体的例子时,我将主要依赖于Linkerd,但当我知道与其他网格实现的不同之处时,我将把它们指出来。
好的。时时候来点干货了!
什么是服务网格
服务网格在架构上非常简单。它只不过是一堆用户空间代理,被“粘在”你的服务上(我们稍后会讨论“下一步”是什么意思),加上一组管理流程。代理被称为服务网格的数据平面,管理过程被称为服务网格的控制平面。数据平面拦截服务之间的调用并“处理”这些调用;控制平面协调代理的行为,并为您(操作人员)提供一个API来操作和测量整个网格。
image.png这些代理是什么?它们是Layer 7-aware TCP代理(这个不知道怎么翻译,有点奇怪),就像haproxy和NGINX一样。代理的选择多种多样;Linkerd使用一个名为Linkerd-proxy的代理(使用Rust语言编写),它是我们专门为服务网格创建的。其他网格使用不同的代理;Envoy是一个的选择。但是不同的代理实现细节本身也不相同。
这些代理用何用途? 显然,它们代理对服务的调用和来自服务的调用。(严格来说,它们同时充当“代理”和“反向代理”的角色,处理入口和出口流量。)它们实现了一个关注服务间调用的特性集。服务之间的流量是服务网格代理与API网关或入口代理的区别所在,后者主要关注外网对整个集群的调用。
这就是数据平面。控制平面比较简单:它是一系列组件,提供数据平面以协调方式工作所需的任何机制,包括服务发现、TLS证书颁发、度量聚合等等。数据平面调用控制平面来通知其行为;控制平面反过来提供一个API,允许用户修改和检查数据平面的整体行为。
这是Linkerd的控制平面和数据平面的示意图。您可以看到控制平面有几个不同的组件,包括一个小型的Prometheus实例,该实例聚合来自代理的度量数据,以及诸如destination
(服务发现)、identity
(颁发证书)和public-api
(web和CLI)等组件。相比之下,数据平面只是应用程序实例旁边的一个linkerd-proxy。这只是一个逻辑图;在部署时,您可能会得到每个控制平面组件的三个副本,但会部署数百或数千个数据平面代理。
(图中的蓝色方框表示Kubernetes pod的边界。您可以看到,linkerd-proxy容器实际上与应用程序容器运行在同一个pod中。这种模式称sidecar容器。)
image.png服务网格有两个重要的含义。首先,由于代理特性集是为服务到服务调用而设计的,所以服务网格只有在应用程序构建为服务时才有意义。您可以将它与一个整体一起使用,但是它需要大量的机器来运行单个代理,并且这个特性集不太适合。
另一个后果是服务网格将需要大量的代理。实际上,Linkerd为每个服务的每个实例添加一个linkerd-proxy。(其他一些网格服务实现为每个节点/主机/虚拟机添加一个代理。两种方式都有很多。)大量使用代理本身有两个含义:
- 不管这些数据平面代理是什么,它们最好够快。您将为每个调用添加两个代理,一个在客户端,一个在服务器端。
- 此外,代理需要小而轻。每一个代理都将消耗内存和CPU,并且这些消耗将随应用程序线性增长。
- 您将需要一个用于部署和更新大量代理的管理系统,你肯定不会想要手动一个一个去做。
纵观全局,这似乎就是服务网格的全部内容:您部署了大量的用户空间代理来对内部的服务到服务的流量“做一些事情”,并且您使用控制平面来更改它们的行为并查询它们生成的数据。
现在让我们来谈谈服务网格产生的原因。
为什么服务网格是有意义的?
如果您是第一次接触服务网格的,那么如果您的第一反应是轻微的恐惧,那也是可以理解的。服务网格的设计意味着它不仅会增加应用程序的延迟,还会消耗资源并引入大量的机制。前一分钟你还在安装一个服务网格,下一分钟你就突然要运行成百上千个代理了。为什么有人想这么做?
答案有两点。首先,由于生态系统中正在发生的一些其他变化,部署这些代理的操作成本可以大大降低。后面会讲到更多。
更重要的一点是,这种设计实际上是向系统引入额外逻辑的一种好方法。这不仅是因为你可以在那里添加大量的功能,还因为你可以在不改变生态系统的情况下添加它们。实际上,整个服务网格模型都是基于这样的认知:在一个多服务系统中,无论单个服务实际做什么,它们之间的通信都是功能的理想插入点。
例如,Linkerd和大多数网格一样,有一个主要关注HTTP调用的第7层特性集,包括HTTP/2和gRPC。特征集很广,但可以归纳为以下三类:
- 可靠性特征。请求重试、超时、警告(流量切分)等。
- 可观测性特征。对每个服务或单个路由的成功率、延迟和请求量进行聚合、服务拓扑图的绘制等。
- 安全特性。双向TLS认证,访问控制等。
这些特性中有许多是在请求级别操作的(因此称为“L7代理”)。例如,如果服务Foo对服务Bar进行HTTP调用,那么Foo端上的link-proxy可以根据每个服务Bar的观察延迟,跨所有Bar实例智能调用负载均衡;它可以重试请求,如果它失败了,如果它是幂等的;它可以记录响应代码和延时;等等。类似地,如果不允许调用,或者超过了速率限制,Bar一侧的链接代理可以拒绝调用;它可以从它的角度记录延迟等等。
代理也可以在连接级别“做一些事情”。例如,Foo的linker-proxy可以发起TLS连接,Bar的linker-proxy可以终止连接,双方都可以验证对方的TLS证书。这不仅提供了服务之间的加密,而且还提供了一种加密安全形式的服务身份——Foo和Bar服务可以“证明”他们是他们所说的那个人。
无论它们是在请求级还是在连接级,需要注意的重要一点是,服务网格的特性本质上都是可操作的。Linkerd中没有任何关于转换请求有效负载语义的内容,例如将字段添加到JSON或转换protobuf。这是我们在讨论ESBs和中间件时再次提到的一个重要区别。
这就是服务网格可以提供的一组特性。但是为什么不直接在应用程序中实现它们呢?为什么要费尽心思使用代理呢?
为什么服务网格是一个好主意?
虽然特性集很有趣,但服务网格的核心价值实际上并不在特性中。毕竟,我们可以在应用程序本身中直接实现这些特性。(事实上,我们稍后将看到这就是服务网格的起源。) 如果只能用一句话描述,服务网格的价值可以归结为:服务网格为您提供了一些功能,这些功能对于以一种技术栈无关的应用程序代码解耦的方式运行现代服务器端软件是至关重要的。(这里翻译为技术栈无关是我个人见解,技术栈无关也是我认为服务网格很棒的一点)
让我们一点一点来。
如果您正在构建一个事务性的, 服务器端应用程序连接到公网,从外界接收请求, 返回一些碎片时间性质网页应用、API服务器,和现代的大部分服务器端软件如果你建立这个系统作为一个服务集合以同步的方式交谈,如果你不断地修改该软件添加更多的功能,如果您的任务是在修改系统的同时保持系统运行,那么恭喜您,您正在构建现代的服务器端软件。上面列出的所有这些出色的功能实际上对你来说是至关重要的。应用必须可靠;它必须是安全的;你必须能够观察到它在做什么。这正是服务网格所能帮助你到的。
(好吧,我在这里提出了一个观点:这种方法是构建服务器端软件的现代方法。当今世界上有些人正在建造“单体”或“响应式微服务”等不符合上述定义的东西,他们可能有不同的看法。反过来,我认为他们的观点是“错误的”——但无论如何,服务网格对他们都不是很有用。
在你的服务栈中保持一致。服务网格提供的功能并不仅仅是关键的,它们适用于应用程序中的每个服务,不管服务是用什么语言编写的、使用什么框架、谁编写的、如何部署的,或者开发或部署的任何其他细节。
与应用程序代码解耦。最后,服务网格不只是在整个服务栈中提供一致的特性,它还以一种不侵入代码的方式提供特性。服务网格的功能(包括配置、更新、操作、维护等的操作所有权)完全位于平台级,与应用程序代码无关。应用程序可以在不关注服务网格的情况下更改,而服务网格也可以在不关心应用程序的情况下更改。
简而言之:服务网格不仅提供了重要的特性,而且以一种全局的、统一的、独立于应用程序的方式提供这些特性。因此,虽然服务网格的特性可以在服务代码中实现(即使是作为链接到每个服务的库),但这种方法不能提供服务网格值支柱的核心的解耦和一致性。
你所要做的就是添加大量的代理!我保证我们将很快讨论添加所有这些代理的操作成本。但首先,我们需要停下来,从解耦的角度来审视这一观点。
谁能从服务网络中受益?
尽管有许多困难,但事实证明,为了让技术真正产生影响,它必须落地。那么谁会采用服务网格呢? 双有谁将从中受益?
如果您正在构建我上面所描述的现代服务器软件,那么您可以大致将您的团队分为两个部分:应用开发者(他们从事构建业务逻辑的业务-也就是业务开发)和平台开发者(他们构建运行这些服务的内部平台-可以理解为运维)。在小型组织中,这些人员可能是相同的,但是随着组织规模的扩大,这些角色通常会得到更多的定义,甚至进一步细分。(关于devops不断变化的本质,微服务对组织的影响等等,还有很多要说的。但现在,让我们把这些描述当作一个设定的条件。)
从这个角度来看,服务网格的直接受益者是平台开发者。毕竟,平台团队的目标是构建应用开发者可以在其上运行业务逻辑的内部平台,并以一种使应用开发者尽可能独立于操作化的详细信息的方式来实现。服务网格不仅提供了完成此任务的关键特性,而且还以一种不依赖于应用开发者的方式提供了这些特性。
应用开发者也会受益,尽管是以一种更间接的方式。应用开发者的目标是尽可能高效地构建业务逻辑,他们需要担心的操作机制越少,就越容易做到这一点。他们可以将注意力完全放在业务逻辑问题上,并相信平台会处理其余的问题,而不必关心实现诸如重试策略或TLS之类的问题上。这也是他们的一大优势。
平台和应用开发者之间解耦的组织价值怎么强调都不过分。事实上,我认为这可能是服务网格有价值的关键原因。
当我们最早的Linkerd采用者之一告诉我们他们为什么要采用服务网格时,我们得出一个结论:因为它允许他们“不必与人交谈”。这是一个大公司的平台团队迁移到Kubernetes。因为他们的应用程序需要处理敏感信息,所以他们希望加密集群上的所有通信。有数百个服务和数百个开发团队,他们并不期望说服每个开发团队将TLS添加到他们的路线图中。通过安装Linkerd,他们将该特性的所有权从开发人员手中转移到了平台团队手中,对开发人员来说,这是一种强加,而对平台团队来说,这是一种顶级优先级。Linkerd与其说为他们解决了一个技术问题,不如说它解决了一个组织问题。
简而言之,服务网格与其说是技术问题的解决方案,不如说是社会技术问题的解决方案。
服务网格是银弹吗?
当然不是!
如果您查看上面列出的三类特性(可靠性、安全性和可观察性),就会发现服务网格并不是这些领域的完整解决方案。当Linkerd知道请求是幂等的时候,它可以重试请求,但是当服务完全宕机时,它不能决定返回给用户什么——应用程序必须做出这些决定。虽然Linkerd可以报告成功率等,但它不能查看服务内部并报告内部度量指标——应用程序必须有仪表。虽然Linkerd可以“免费”实现诸如双向TLS认证之类的功能,但安全解决方案还有很多其他功能。
服务网格提供的这些域中的特性子集是平台特性。这里我指的是以下特征:
- 独立于业务逻辑。计算Foo和Bar之间调用的流量延迟直方图的方法完全独立于Foo最初调用Bar的原因。
- 难以正确实现。Linkerd的重试是用诸如重试预算之类复杂的东西参数化的,因为简单的重试方法肯定会导致“重试风暴”和其他分布式系统故障模式。
- 最有效的是统一实施。只有当每个人都在这样做时,双向TLS认证的机制才真正有意义。
因为这些特性是在代理层实现的,而不是在应用程序层,所以服务网格在平台级别而不是应用程序级别提供这些特性。不管服务是用什么语言编写的,也不管它们使用什么框架,也不管谁编写它们,也不管它们是如何实现的。独立于所有这些的代理功能,以及此功能的所有权(包括配置、更新、操作、维护等的操作所有权)完全位于平台级。
服务网格的示例特性
可观测性 | 可靠性 | 安全性 | ||
---|---|---|---|---|
服务网格 | 服务成功率 | 请求重试 | 双向TLS认证 | |
平台 | 日志聚合 | 多副本数据集 | 静态数据加密 | |
应用 | 内部特性使用的检测 | 整个组件停机时的故障处理 | 确保用户只能访问他们自己的数据 |
总而言之: 服务网格并不是可靠性、可观察性或安全性的一个完整解决方案。这些领域必然涉及应用开发者、ops和SRE团队以及组织的其他部分。服务网格只能提供每个领域的一个平台层“切片”。
为什么服务网格现在有意义?
此时,您可能会对自己说:好吧,如果这个服务网格这么好,为什么我们不在10年前就这么干呢?
对此有一个肤浅的回答,那就是十年前每个人都在做单体应用,所以没有人需要一个服务网格。这是真的,但我认为没有抓住重点。甚至在十年前,“微服务”作为构建大规模系统的一种可行方式的概念就被广泛讨论,并在Twitter、Facebook、谷歌和Netflix等公司公开付诸实践。一般的看法是,至少在我所接触到的行业中,微服务是构建大规模系统的“正确方式”,虽然这个过程真的有些痛苦。
当然,虽然十年前就有公司在运营微服务,但他们基本上没有到处安装代理来形成服务网格。但是,如果您仔细观察,就会发现它们在做一些类似的事情:许多这些组织强制要求使用特定的内部库进行网络通信(有时戏称为“胖客户端”)。Netflix有Hysterix,谷歌有Stubby,Twitter有Finagle。例如,Finagle对于Twitter上的每个新服务都是必需的,它处理连接的客户端和服务器端,并实现重试、请求路由、负载平衡和检测。它在整个Twitter服务栈中提供了一致的可靠性和可观察性,并且独立于服务本身。当然,它只适用于JVM语言,而且它有一个编程模型,你必须围绕这个编程模型构建整个应用程序,但它提供的操作特性几乎与服务网格完全一样
所以十年前,我们不仅有微服务,我们还有原始服务网格库,它们解决了许多与今天服务网格解决的问题相同的问题。但是我们没有服务网络。首先需要改变的是其他一些东西。
这就是更深层次的答案所在,隐藏在过去十年发生的另一个不同之处:部署微服务的成本大幅降低。我上面列出的10年前公开使用微服务的公司——Twitter、Netflix、Facebook、Google等都是拥有庞大的规模和资源的公司。他们不仅有需求而且有能力构建、部署和操作重要的微服务应用程序。在Twitter由单体应用向微服务转型的过程中,投入了大量的时间和精力,这让人难以想象,而这种基础设施的调整对小公司来说基本上是不可能的。
相比之下,今天的初创公司微服务与开发者的比例可能是5:1,甚至10:1,更重要的是,他们有能力应对这种情况。如果运行50个微服务对于一个5人的创业公司来说是一个可行的方法,那么很明显,微服务的使用成本降低了。
采用微服务的成本的显著降低是由于一件事的结果:容器和容器编制器技术的使用。这就是为什么要改变服务网格的深层原因。使服务在操作上可行的东西与使微服务在操作上可行的东西是一样的:Kubernetes和Docker。
但是,为什么? Docker解决了一个大问题: 服务打包问题。将应用程序及其(非网络)运行时依赖项打包到一个容器中,您的应用程序现在是一个插拔的单元,真正做到了一次打包,到处运行。于是我们可以轻松运行多语言的服务栈:因为执行的容器是一个原子单位, 部署和操作并不关心容器里面装的是什么,不管它是一个JVM应用Node、Go、Python还是Ruby应用。
Kubernetes解决了下一个问题:现在我有一堆“可执行的东西”,我也有一堆“可以执行这些可执行的东西”(也就是机器),我需要它们之间的映射。广义上说,你给Kubernetes一堆容器和一堆机器,它就能算出这个映射。(这当然是一个动态的、不断变化的东西,因为新的容器在系统中滚动、机器进进出出等等。但Kubernetes能解决这些问题。)
一旦Kubernetes启动,运行一个服务的部署时间成本与运行10个服务的部署时间成本没有太大差别,实际上与运行100个服务的部署时间成本也没有太大差别。将其与容器结合起来,作为鼓励使用多种编程语言开发实现的打包机制,结果是大量的新应用程序可以用各种语言编写成微服务——这正是服务网格最适合的环境。
最后,我们来看看为什么服务网格现在是可行的:Kubernetes为服务提供的完全相同的一致性直接适用于服务网格的操作挑战。你把代理打包在容器里,你告诉Kubernetes在哪使用代码,瞧!你得到了一个服务网格,所有的部署时机制都由Kubernetes为你处理。
总结:服务网格的原因现在是有意义的,而不是10年前,Kubernetes的崛起和Docker不仅大大增加了服务网格的需求, 通过提供部署及代理机制,使得构建多种开发语言的微服务架构变得很容易了, 而且还极大地降低了运行成本。
为什么人们总是在谈论服务网格?
高能预警:在本节中,我会“大放厥词”。
你只需要搜索“服务网格”,就会遇到一个卡夫卡式的狂热f场景,充满了令人困惑的项目,毫无营养的内容,扭曲的噪音。所有闪亮的新技术都有一定程度的这种情况,但服务网格似乎有一个特别糟糕的情况。这是为什么呢?
部分是我的错。我已经尽我最大的努力在传播Linkerd和服务网格,在无数的博客、播客和文章中,就像这篇一样。但我没有那么强大。要真正回答这个问题,我回到服务网格这个领域来讨论,要讨论这个话题就绕不开另外一个由谷歌、IBM和Lyft领衔开发的服务网格,名字叫做:Istio。
值得注意的是两件事。首先,谷歌,尤其是它背后的强大营销能力。据我估计,今天大多数了解服务网格的人都是通过Istio来了解它的。第二件值得注意的事是人们对它的接受程度有多差。很明显,我在这场竞赛中占了上风,但我尽量保持客观,在我看来,Istio在某种程度上引起了公众的强烈反对,这在开源项目中是很少见的(虽然也不是没有听说过)
抛开我个人的理论不谈,我相信谷歌的参与才是服务网格领域如此夸张的真正原因。具体来说,就是 1. 谷歌大力推广Istio; 2. 其实乏善可陈; 3. Kubernetes的迅速崛起至今仍在每个人的脑海中挥之不去,这一切结合在一起,形成了一种令人陶醉的环境,在这种环境中,理性思考的能力消失了,只剩下一种奇怪的、与生俱来的狂热。
当然,从Linkerd的角度来看,这是一件好事。我的意思是,现在的服务网格是一个“东西”,这很好,但在2016年Linkerd刚起步的时候并不是这样,而且真的很难引起任何人的注意。我们不再有那个问题了!但是糟糕的是,服务网格环境是如此混乱,甚至很难理解哪些项目是服务网格,更别提哪个项目最适合您了。那对每个人都不利。(当然,在某些情况下,Istio或者其它类似项目将是Linkerd之外的正确选择-但这远不是一个放之四海而皆准的解决方案。)
在Linkerd方面,我们的策略是忽略噪音,继续专注于为我们的社区解决真正的问题,基本上是等待整个事情结束。炒作的程度最终会消退,我们都可以继续我们的生活。
但与此同时,我们所有人都将一起经历这一切。
那么,作为一个谦虚的软件工程师,我应该关心服务网格吗?
作为软件工程师,是否需要服务网格,以下是我的基本准则:
如果您处于纯业务逻辑实现者的开发人员角色:不,您实际上不需要关心服务网格。我的意思是,你当然欢迎关心,但理想的服务网络不会直接影响你的生活。继续构建甜蜜的业务逻辑,让你身边的每个人都能得到报酬。
如果你在一个使用Kubernetes的组织中担任平台角色:是的,你100%应该关心。除非你是采用k8纯粹一个单体应用或批处理任务(在这种情况下,我会认真问你为什么要使用k8s),你会在一个情景,在这个情景中你有很多微服务,别人写的, 所有的交谈,都绑在一起形成成一个繁复的运行时依赖,你会需要一种方法来解决这个问题。由于您使用的是Kubernetes,所以您将有几种服务网格可选,应该选哪一个必须得有一个清晰的认知。(当然,我建议从从Linkerd开始。)
如果你在一个不使用Kubernetes,而是“做微服务”的组织中担任平台角色:是的,你应该关心,但这将是复杂的。当然,您可以通过在任何地方部署大量代理来获得服务网格的价值,但是Kubernetes的优点是部署模型,如果您必须亲自管理这些代理,您必须考虑投入与回报。
如果您的平台是一个单体应用,那你可能不需要关心。如果您正在操作一个大型单体应用,甚至是一个单体应用的集合,它具有定义良好且不经常变化的通信模式,那么服务网格将不会带来多少收益,您可以忽略它。
结论
这个服务网络可能并没有“世界上最被过度炒作的技术”的头衔——这个可疑的区别可能是比特币或人工智能。也许只是前五名。但是如果你能穿透这些噪音,对于任何在Kubernetes上构建应用程序的人来说,都有一些真正的价值。
最后,我希望您尝试一下Linkerd——安装到Kubernetes集群仅需要大约60秒的时间,甚至在您的笔记本上安装一个Minikube——您自己就可以清楚地看到我所说的内容。