如果你是后端软件工程师,那么在过去几年里,service mesh这个词很可能已经深入你的潜意识了。各种事情神奇地汇聚在一起,结果就是service mesh就像Katamari ball一样在业界滚来滚去,不断地获取更大的市场份额和声望,同时丝毫没有短期内消停的迹象。
service mesh诞生于云原生生态的昏暗激流之中。这就意味着service mesh的大量内容是很悲剧地从“低营养”到——用学术名词来说就是——“基本一无是处”。然而,如果能够拨开这些迷雾,你会发现service mesh也有一些切实且重要的价值。
在本文中,我会努力提供一份诚实的、有深度的、聚焦于工程师的service mesh指南。我要谈的不仅有service mesh是什么(What),而且有service mesh为什么会产生(Why) 以及为什么在现在这个时间(Why Now)爆发。最后,我会努力阐述清楚为什么我认为service mesh这项技术能够赢得如此狂热的关注——而这本身也是一个很有趣的故事。
我是William Morgan,是Linkerd的创始人之一。 Linkerd是第一个service mesh项目,也是从这个项目开始,service mesh这个称呼才正式诞生。同时我也是Buoyant的CEO。 Buoyant是一家初创企业,主要构建如Linkerd和Dive这一类便捷的service mesh工具。
因此你可能已经想到了,我对于service mesh这个话题是有些偏见和固执看法的。既然这样,我就要尽量刨除春秋笔法(除去“为什么大家讨论如此热烈”一章。在这章我会阐述我自己的观点),并用尽可能客观的方式来撰写本文。需要有理有据的时候,我主要拿Linkerd来举例子;然而在Linkerd和其他service mesh实施方案有所不同的时候,我会直接指出。
好了,我们书归正传。
尽管名声在外,service mesh从架构上来说十分简单。service mesh不过就是一堆“紧挨着”各项服务的用户代理(userspace proxies),外加一组任务管理流程(a set of management processes)。关于何为“紧挨着”,我们之后将稍作解释。代理在service mesh中被称为数据层(data plane),管理流程被称为控制层(control plane)。数据层截获不同服务之间的调用并对其进行“处理”;控制层协调代理的行为,并且为你(也就是操作者)提供API,使你能够操控和测量整个网格。
这些代理是什么?他们是Layer 7的TCP代理,就像是haproxy和NGINX。代理的选择多种多样,Linkerd采用的是简称为linkerd-proxy的Rust代理,这是我们专为service mesh而创建的。其他的网格使用了不尽相同的代理;Envoy是个常见的选择。当然了,代理的选择是个纯粹的实施细节了,和service mesh本身无关。
代理都做些什么?他们当然是代理出入服务的各项调用了。(严格地说,他们同时充当了“代理”和“反向代理”的角色,同时处理了呼入和呼出的调用。)他们同时还实现了聚焦于不同服务之间调用的特性集。聚焦于服务之间流量的特性使得service mesh区别于API网关或者入口代理,后两者整体上聚焦于从外部进入集簇的调用。
以上就是数据层。控制层更加简单:就是一堆提供数据层有序运行所需的所有机制的组件集合,这些机制包括服务发现、TLS证书颁发、度量聚合……诸如此类。数据层调用控制层以通知其行为;控制层反过来提供API以让用户整体上调整和检测数据层的行为。
上图就是Linkerd的控制层和数据层的示意图。可以看到,控制层拥有数个不同的组件,包括小型的从代理汇聚度量数据的Prometheus用例和诸如destination(服务发现)、identity(证书确认)和public-api(Web和CLI端点)的组件。作为比较,数据层只是挨着应用用例的单个Linkerd-proxy。这个只是逻辑示意图;一旦部署,每个控制层组件会有三个副本,每个数据层代理会有成百上千个。
示意图中的蓝色方框表示Kubernetes池子的边界线。可以看到,Linkerd-proxy容器实际上和应用容器运行在同样的池子里。这一模式即sidecar容器。
service mesh的架构有几重重大内涵。首先,因为代理特性集(proxy featureset)是设计于用在服务到服务的调用的,service mesh只有在应用是建立在服务之上的时候才有意义。你可以在单体应用上使用service mesh,但是那就需要费很大劲儿去运行单个的代理,同时特性集并不适用。
第二个后果是,service mesh需要很多很多的代理。实际上,Linkerd为每个服务用例都增加一个Linkerd-proxy 代理。(有些其他网格实施时为每个节点/主机/VM 增加一个代理。总之都会有很多代理。)如此大量的使用代理本身也是有几方面内涵的:
然而,这也就是从目力所及之处,我们能看到的service mesh为何物了:部署大量的用户代理来对内部的、服务到服务的流量做管理,同时用控制层来管理其行为并对代理产生的数据做查询。
前面说的是 What,下面开始讲 Why。
如果你是第一次听说service mesh的理念,那你的第一反应是微微惊慌也就不足为奇了。service mesh的设计就表明,它不仅为应用增加延迟,而且还消耗资源,同时引入了一大堆组件(machinery)。前一分钟你安装了service mesh,下一分钟你突然陷入要管理成百上千个代理的境地。有谁会对这种事情乐此不疲呢?
答案包含两个方面。第一方面是,部署这些代理的成本可以大幅降低,这要归功于云原生生态的发展。稍后详述。
第二个方面更加重要,service mesh的这种设计实际上是把传统逻辑引入到系统当中的绝佳方法。你不仅可以直接增加许多功能,而且增加功能时可以不改变整个生态。实际上,整个service mesh模型都是基于这一视角的:在多服务系统中,不论各个服务的功能是什么,服务间的流量都是插入新功能的理想位置。
以Linkerd为例——其他大多数网格也都是如此,Linkerd拥有一套包括HTTP/2和gRPC[1]的特性集,其主要聚焦于HTTP调用。这个特性集十分广泛,不过可以分为以下三类:
这里的许多功能都直接在请求层(request level)进行操作,也就是“L7代理”。例如,如果服务Foo向服务Bar发起了HTTP请求,Foo这一侧的Linkerd-proxy可以负载均衡,这一均衡可以基于Bar的每个用例的延迟观测来智能调用;如果失败了并且是幂等的,则可以重新发起请求;他可以记录相应代码和等待时间,诸如此类。类似的,Bar这一侧的Linkerd-proxy可以拒绝未经允许的调用,或者超过频率限制的调用;他可以从自己的视角来纪录等待时间;诸如此类。
代理在连接层(connection level)也可以“有所作为”。例如,Foo这一侧的Linkerd-proxy可以初始化一个TLS链接,Bar这一侧的Linkerd-proxy可以终止这个链接,同时双方都可以验证对方的TLS证书[2]。这样不仅能加密不同的服务,而且能加密每个服务的身份—— Foo和Bar都能够“证明我是我”。
不论是在请求层还是连接层,不得不提的就是,service mesh的特性本质上是操作层面的。在Linkerd中,请求负载的语法不需要任何更改,比如向JSON添加新的字段或者转换 protobuf。这是与ESB和中间件的显著不同。
以上就是service mesh能够提供的特性。那为什么不直接把这些实施到应用里呢?为什么要费力折腾这么多代理呢?
service mesh的特性集很有意思,不过其核心价值却不在这里。当然了,我们可以直接把这些功能实施到应用里面。(实际上,稍后我们会看到这正是service mesh的缘起)。如果只能用一句话来描述的话,service mesh的价值是:service mesh 提供了对于运行现代服务端软件(modern server-side software)至关重要的特性集,从而使其在整个技术栈是统一的并且是与应用代码解耦的。
我们一点点解释前面这句话。
对于运行服务端软件至关重要的特性集。如果你是在开发一款服务端交易系统,这个系统连接了公网、从外部接受请求并在短时间内回复请求——想想 web 应用、API 服务器和大量的现代服务端软件——如果你正在把这个系统打造为以同步的方式相互通讯的服务集合,如果你正在持续修订这个软件以增加更多功能,如果你接到任务说要在修改系统的同时保持系统运行——恭喜你,你就是在开发现代服务端软件。前面列出的所有的功能对你来说都是不可或缺的。应用必须可靠、必须安全、必须能够让我们监测到所有行为。这一切恰恰都是service mesh大展宏图之地。
(我说一点个人保留意见。这是打造服务端软件的现代方法。现在世界上还有不少人在建造单体软件或者“响应式微服务”或者其他东西,那些都不适合上述定义。有人对此持有不同意见。我反过来也不赞同他们的“不同意见”,我认为他们的观点是“错的”——但是不管怎样,service mesh对他们都没什么用。)
在整个技术栈是统一的。service mesh提供的功能不仅是很重要的,而且这些功能适用于你的应用的所有服务,而无关乎这些服务是用什么语言写的、在采用什么框架、谁写的代码、如何部署的或者其他的开发与部署细节。
是与应用代码解耦的。最终,service mesh不仅仅提供了跨栈统一的功能,而且是以一种不要求任何应用更改的方式实现的。service mesh功能的全部所有权——包括配置、更新、运营(operation)、维护等等的运营权限——都是仅存在于平台层面的,是独立于应用之外的。应用可以在不卷入service mesh的情况下进行更改,service mesh也可以在不卷入应用的情况下进行更改。
简言之:service mesh不仅提供了至关重要的功能,而且是采用全局的、统一的和独立于应用的方式提供的。尽管的确service mesh的功能可以在服务的代码中实施(甚至是作为连接到每个服务的库),但这种方式无法提供作为service mesh价值核心的解耦性和统一性。
你要做的全部工作也仅仅是增加大量的代理。我保证,我们马上就会谈到增加那么多代理的操作成本。不过我们首先要稍作驻足,从人的视角来检验一下这个解耦的观点。
再好的技术,要想真正发挥作用,必须得到人类的采用才可以,这可能是听起来有点儿别扭。那么,谁会采用service mesh技术?谁又会从中收益?
如果你正在开发我于前文描述的现代服务端软件,你大致可以把团队分为两类,一类是服务开发者( service owners),主要开发业务逻辑;一类是平台开发者( platform owners),主要开发这些服务得以运行的内部平台。在小型组织中,这两类可能是同一批人,但是随着组织壮大,一般这些角色会更加细分甚至进一步再分。(关于 devops 的性质变迁、微服务对组织的影响等都可以展开论述,不过我们现在先把那些论述作为已知条件来对待。)
从这个角度来看,首先能从service mesh受益的是平台开发者。毕竟,平台开发团队的目标是打造服务开发者可以运行其服务逻辑的内部平台,而且是从组织运作角度让服务开发者尽可能地保持独立。service mesh不仅提供了实现这一目标的关键特性,而且是以一种不会反过来影响服务开发者独立性的方式实现的。
服务开发者同样受益,不过方式更加隐蔽。服务开发者的目标是尽可能高效地开发业务逻辑服务。在操作机制上花的精力越少,他们的工作就越简单。不用为诸如重试策略或者 TLS 这样的实施花心思,相信平台会解决后顾之忧后,他们就可以集中精力在业务逻辑问题上了。这对于他们来说也已经是很大的收益了。
在平台开发者和服务开发者之间解耦的组织价值(organizational value)再夸大都不为过。实际上,我认为这是service mesh有价值的核心原因。
在Linkerd最早的采用者之一告诉我们他们为什么要用service mesh的时候,我们就学习到了这个:service mesh使得他们“不用非得和人沟通”。他们是一家大公司的平台团队,正在向Kubernetes迁移。因为他们的 APP 处理的是敏感信息,所以他们希望在集群上加密所有通信。公司有上百个服务和上百个开发者团队,他们也没有寄希望于说服每个开发团队在路线图中增加 TLS。通过安装Linkerd,他们把这些功能的所有权从开发者手中转移到了平台团队。对于开发团队,增加 TLS 完全是份儿外的工作;对于平台团队,这就是最高优先级的事情。Linkerd更多解决的是组织问题(an organizational problem),而不是技术问题。
简言之,service mesh更像是社会-科技问题(a socio-technical problem)的解决之道,而不是纯技术问题的解决方法。[3]
当然……不能了。
看看前面列举的三类特性——可靠性、安全性和监测性——你就明白service mesh对其中任何一类都不是包治百病的。在已知服务是幂等的情况下,Linkerd可以重试请求;如果服务器完全下线了,Linkerd无法决定向用户返回何种信息——必须由应用来做出那些决定。Linkerd可以回报成功率等信息,但是却无法深入服务内部并汇报内部参数——应用必须建立自己的指标。Linkerd可以“免费”做双向 TLS 之类的事情,但是安全方案可远不止于此。
service mesh提供的是前述三类功能中属于平台特性的子功能。具体到这个,我指的是以下功能:
因为这些特性是在代理层而非应用层部署的,service mesh在平台层而非应用层提供这些特性。这样service mesh就无关乎服务使用何种语言编写、采用了什么框架、谁写的代码或者服务如何运行等事项。这些代理独立于前述所有事项运行,而这些功能的所有权仅存在于平台层面——这些功能包括配置、升级、操作和维护等的操作所有权。
service mesh特性实例
监测性 | 可靠性 | 安全性 | |
---|---|---|---|
service mesh | 服务成功率 | 请求重试 | 所有服务的双向 TLS |
平台(非service mesh) | 日志聚合 | 数据集的多副本 | 空闲时的数据加密 |
应用 | 应用内特性使用情况的检测 | 整个组件失效时处理故障 | 确保用户仅对自身数据有权限 |
总结一下:service mesh并非可靠性、监测性或者安全性的完整解决方案。 这些领域的主导权涉及的范围更广,必然包含服务开发者、运维和SRE团队以及组织的其他部门。service mesh可以提供的仅仅是这三个领域在平台层的“切片”。
此时你可能会问自己:如果service mesh这么牛,我们为什么不是十年前就在技术栈中添加上百万的代理呢?
最浅显的回答就是,十年前大家都在开发单体应用,所以没人需要service mesh。这是真的,但我觉得这个说法不在点上。甚至在十年前,微服务的概念作为一种构建高扩展系统的弹性方式都已经在广泛讨论,并且在诸如 Twitter、Facebook、Google 和 Netflix 的公司公开地大规模实践了。普遍的观点——至少在我接触到的部分是,微服务是构建高扩展系统的“正确方式”,尽管其过程极其痛苦。
当然了,尽管十年前就有公司在运用微服务,他们大概率没有到处安装代理从而形成service mesh。如果细细观察,你就会发现他们在做相关的事情:其中许多公司都授权使用了一种用于网络通信的内部库(有时称为“胖客户端”库)。Netflix 的是 Hysterix 库,Google 的是 Stubby 库,Twitter 的是 Finagle 库。例如 Finagle 是 Twitter 的每个新建服务强制使用的,同时处理客户端和服务端的连接、实施重试、请求路径、负载均衡和检测。他提供了对整个 Twitter 技术栈的连续的可靠性和监测性层,独立于这些服务本身在做的事情。当然了,它仅支持 JVM 语言,并且你必须围绕它的编程模式构建整个 APP,但是他提供的操作特性几乎和service mesh的完全一致。[4]
所以,十年前不仅有微服务,而且有很多类似service mesh的库,现在的service mesh解决的问题大部分都能用那些库来解决。但那时我们没有service mesh。有些东西必须首先革新。
这就是更深层的答案所在,这个答案深埋在过去十年发生的巨变之中:部署微服务的成本已经大幅下降了。前面我列出的十年前在公开使用微服务的公司——Twitter、Netflix、Facebook、Google——都有着极大的规模和极其丰富的资源。他们不仅有需求,而且也有能力去构建、部署和维护大量的微服务应用。单单是 Twitter 从单体应用向微服务迁移过程的工程时间和产能消耗都是超乎想象的[5],这种类型的基础设施操作对于小公司是根本不可能的。
今非昔比,今天你可以看到微服务与开发者比例是 5:1 甚至 10:1 的创业公司——不仅如此,他们还能够玩得转。如果 5 个人的初创企业运行 50 个微服务是值得称赞的方式,那么肯定是有什么东西降低了采用微服务的成本。
运营微服务的成本的大幅下降是容器和容器编排大量采用的结果。这也就是“哪些变化让service mesh得以实现”的问题的深层答案。让service mesh切实可行的正是让微服务切实可行的东西:Kubernetes和Docker。
原因何在?Docker解决了一大难题,也就是打包问题。Docker使得我们可以将 APP 及其(非网络)的运行时依赖项打包到一个容器中,APP 现在就变成一个任意安放和随处运行的可替代单元。也是基于同样的原因,Docker使得运行 apolyglot 堆栈变得及其简单:容器是最小的执行单元,所以对于部署和操作目的而言,容器内部是什么、他是 JVM APP 还是 Node APP 还是 Go 的或者 Python 的或者 Ruby 的,就根本无关紧要了。
Kubernetes解决了下一步问题:现在我有了一堆“可执行的东西”,同时我也有一堆“可以执行这些东西的东西”(也就是机器),那我就需要二者之间的映射。在更广义的层面上,你给Kubernetes一堆容器和一堆机器,Kubernetes就会计算出这个映射。(这个映射当然是动态的并且一直在变化的,因为新的容器不断涌入系统,运行过程也会不断增减机器,诸如此类。但是Kubernetes可以解决。)
一旦你在运行Kubernetes,运行一个服务的部署时间成本和运行 10 个服务的差别不大,并且实际上和运行 100 个的差别也不大。将Kubernetes与容器结合,容器相当于鼓励 polyglot 实施的打包机制,则结果就是一堆用各种语言编写的、作为微服务来实施的新应用——这正是service mesh最适合运行的环境。
最终,我们得出了为什么service mesh现在才可行的原因:Kubernetes为服务提供的一致性也正是可以直接适用于service mesh面临的操作挑战的。你将代理打包到容器中,告诉Kubernetes处处跟踪他们,然后你就得到service mesh了,Kubernetes为你解决了所有的部署时间机制。[6]
总结一下:为什么service mesh在当下而不是在十年前可行的原因是:Kubernetes和Docker的崛起不仅大幅增加了运行service mesh的需求,而且大幅降低了运行service mesh的成本。前一方面通过让构建 polyglot 微服务架构的应用更加简单来实现,后一方面通过提供部署和维护 sidecar 代理编组的机制来实现。
内容警告:此部分内容会有推测、猜想、内幕和个人意见。
只需要搜索“service mesh”,任何人都会发现一幅卡夫卡式的狂热全景,其中满是迷惑不清的项目、低价值的重复内容和通常都是回音室般的歪理邪说。
好吧,这种情况部分归咎于我。我已经尽了最大的努力,抓住每个机会,在不计其数的博文和 podcast 和像这篇一样的文章里,大讲特讲Linkerd和service mesh。但是我也不是特别厉害。真要回答这个问题,我必须聊聊service mesh全景。如果避开了一个特别的项目,Istio,那就无法讨论这个全景。Istio是一款被视为Google、IBM和Lyft合作项目的开源的service mesh。[7]
Istio引人注目的有两个方面。第一,他背后有 Google 的大量的市场力量在推动。据我估计,今天知道service mesh的大部分人都是通过Istio知道这个概念的。第二,Istio的接受情况实在太惨了。显然我司产品也是service mesh竞赛中的一员,不过我尽量保持客观,在我看来Istio已经引发了对于开源项目很不常见(尽管并非从未听说[8])的明显的公众反噬。[9]
关于正在发生的事情,撇开我的个人理论不谈,我相信 Google 的介入才是service mesh如火如荼的真正原因。特别是,以下三点的结合形成了一种沉重的、缺氧的环境,在这种环境中,理性思考的空间所剩无几,同时对于云原生的诡异的“郁金香狂热”持续存在着:
当然了,从Linkerd的角度看,这个……我觉得我会将之描述为福祸兼具。我指的是,service mesh现在终于是个“东西”了——这可不是 2016 年Linkerd刚问世的时候了,你要知道吸引人们的注意力是非常困难的。我们不再有这个问题了。但不好的是,service mesh全景是如此让人分辨不清,要明白哪些项目是service mesh是如此困难,更不用说哪个service mesh与你的用例最匹配了。这对所有人都是有百害而无一利的。(当然有些场合下Istio或者其他项目是比Linkerd更合适的选择——远没有万全之策。)
在Linkerd这一方,我们的战略是忽视噪音,继续专注于为社区解决真正的问题,然后基本就是等着时过境迁了。炒作终将消散,我们继续生活。
但与此同时,我们所有人不得不共同经历这一切。
如果你是软件工程师,关于你是否应该关注service mesh,我的基本建议如下。
如果你是单纯的业务逻辑实施开发角色(a pure business-logic-implementin’ developer role): 不用,你真的不用关注service mesh。我说的是,当然欢迎你关注service mesh,但是service mesh不会对你的工作有任何直接的影响。坚持开发那些让周围人付费的甜甜美美的业务逻辑吧。
如果你是一名在采用Kubernetes的机构中的平台开发人员:是的,你百分百应该关注。除非你是在采用Kubernetes来运行单体应用或者运行批处理过程(这种情况下,我要郑重地问一下为什么要用Kubernetes),否则你将会陷入如此境地:大量的微服务、各个微服务全都是由他人编码的、微服务之间相互通信、微服务绑定在一起形成一大团运行时依赖项。你需要一种方式来解决这种困境。因为你在运行Kubernetes,所以你就有好几个service mesh选项,这时候你需要周全考虑后决定选择哪个service mesh或者是否选择其中任何一个。(先从Linkerd开始吧。)
如果你是一名没采用Kubernetes的机构中的平台开发人员,但是在“采用微服务”:是的,你应该关注,但是情况很复杂。当然了,你需要到处部署大量代理才能获得service mesh的价值,但是Kubernetes的好的地方就在于部署模型。如果你不得不自己部署这些代理,那你的ROI方程将会看上去大不相同。
如果你是一名在开发单体应用的机构中的平台开发人员:不用,你完全不用关注。如果你在运行单体应用,甚至是一堆定义良好并且通信模式不经常变化的“单体应用集合”,那么service mesh并不能增加多少东西,你也可以忽视他并随他远去。
对于“世界上炒得最热的技术”一称,service mesh很可能名不副实——可能比特币或者 AI 现在更加火热,而service mesh估计能排进前五名吧。但是如果你能透过噪音看本质,对于在Kubernetes上构建应用的任何人来说,service mesh都是提供了真正价值的。
最终,我还是希望你能试试Linkerd——在Kubernetes集群上甚至是笔记本电脑的 Minikube 上,Linkerd大概只要 60 秒就能安装完毕——你就可以亲自检验下我通篇都在讲的是什么了。
很不幸,service mesh就在这里,不离不弃。
那就别用。不过请看看我的前述导引确定下你是否需要领会service mesh。
service mesh集中于操作逻辑而非业务逻辑。这就是企业服务的失败之处,保持二者的分离对于service mesh避免相同命运而言至关重要。
这样的文章汗牛充栋。Google 一下会不会?
不是,Envoy 不是service mesh。Envoy 是代理,他可以用来组建service mesh(以及其他很多事情。Envoy 是通用目的的代理)。但是它本身不是service mesh。
不是。尽管名称里有service mesh,Networkservice mesh并不是service mesh。(市场宣传很搞笑,对吧?)
service mesh在基于响应式异步消息队列的系统能用吗?
不能,service mesh无能为力。
Linkerd。哈哈。
请把本文链接分享给你所有的朋友,这样他们就能够看到这篇文章有多烂/我有多不行了。
你可能已经从标题里猜到了,这篇文章是受 Jay Krep 关于 Logs 的经典好文 The Log: What every software engineer should know about real-time data’s unifying abstraction 的启发而来的。我在大概十年前 LinkedIn 面试时认识了 Jay,从那时起他就是我的灵感源泉。
尽管我喜欢自称Linkerd维修工,事实上我基本上是维护Linkerd的 README.md 文档的。如今Linkerd是许多、许多、许多、许多人的共同作品,并且没有社区的众多贡献者和采纳者也是无法实现的。
最后,特别致谢Linkerd的发明人 Oliver Gould , 许多年前我们一同纵身一跃从此上了service mesh的贼船。
领取专属 10元无门槛券
私享最新 技术干货