service mesh

发布时间 2023-12-13 16:08:04作者: matengfei

一  、云原生简介

云原生架构的应用程序应该是:采用开源堆栈(K8S+Docker)进行容器化,基于微服务架构提高灵活性和可维护性,借助敏捷方法、DevOps支持持续迭代和运维自动化,利用云平台设施实现弹性伸缩、动态调度、优化资源利用率。

 

云原生的四要素

微服务:几乎每个云原生的定义都包含微服务,跟微服务相对的是单体应用,微服务有理论基础,那就是康威定律,指导服务怎么切分,很玄乎,凡是能称为理论定律的都简单明白不了,大概意思是组织架构决定产品形态,不知道跟马克思的生产关系影响生产力有无关系。

微服务架构的好处就是按function切了之后,服务解耦,内聚更强,变更更易;另一个划分服务的技巧据说是依据DDD来搞。

容器化:Docker是应用最为广泛的容器引擎,在思科谷歌等公司的基础设施中大量使用,是基于LXC技术搞的,容器化为微服务提供实施保障,起到应用隔离作用,K8S是容器编排系统,用于容器管理,容器间的负载均衡,谷歌搞的,Docker和K8S都采用Go编写,都是好东西。

DevOps:这是个组合词,Dev+Ops,就是开发和运维合体,不像开发和产品,经常刀刃相见,实际上DevOps应该还包括测试,DevOps是一个敏捷思维,是一个沟通文化,也是组织形式,为云原生提供持续交付能力。

持续交付:持续交付是不误时开发,不停机更新,小步快跑,反传统瀑布式开发模型,这要求开发版本和稳定版本并存,其实需要很多流程和工具支撑。

 

二、Service Mesh 背景


随着微服务逐渐增多,系统最终可能会变为由成百上千个互相调用的服务组成的大型应用程序,服务与服务之间通过内部网络或外部网络进行通信。怎样管理这些容器或服务之间的通信,如何保持通信的无故障、安全、高可用和健壮(包括服务间的负载均衡、流量管理、路由、运行状况监视、安全策略及服务间身份验证),就成为云原生技术的巨大挑战。

在微服务使用过程中我们往往会遭遇以下现实问题:

1.多语言栈

微服务理念提倡不同业务使用最适合它的语言进行开发,一般大型互联网公司存在C/C++、Java、Golang、PHP、Python 等语言的项目,这就意味着每种语言都需要实现相应的服务治理功能框架。然而,服务框架的 SDK 通常实现都比较“重”,需要实现服务注册与发现、服务路由、负载均衡、服务鉴权、服务降级、服务限流、网络传输等功能,复杂度大、成本投入高、稳定健壮性隐患多。

2.产品交付

在服务治理组件的功能升级、bug 修复过程中,业务系统需要升级依赖的服务组件包,然而升级过程中还可能存在各种版本冲突,另外灰度验证过程也可能引入新的 bug,业务开发人员升级组件版本痛苦不堪,抵触情绪很大,往往一个组件包想要全覆盖升级,需要耗费相当长的时间,交付效率极其低下。随着公司业务的不断发展,业务的规模和交付的效率已经成为主要的矛盾,所以组件团队期望以更高的效率去研发基础设施,而不希望基础设施的迭代受制于这个组件的使用规模。

3.云原生

在云原生架构里,单个应用程序可能由数百个服务组成;每个服务可能有数千个实例;而且这些实例中的每一个都可能处于不断变化的状态,因为它们是由像 Kubernetes 一样的编排器动态进行调度的,所以服务间通信异常复杂,但它又是运行时行为的基础,且管理好服务间通信对于保证端到端的性能和可靠性来说是非常重要的。因此,需要一个稳定的高效的通信层来解决云原生微服务架构带来的问题。
 

而服务网格(Service Mesh)作为服务治理的基础设施层,可以解决上述问题。

 

三、什么是 service meish

 

一言以蔽之:Service Mesh 是微服务时代的 TCP/IP 协议。

 

服务开发模式和Service Mesh技术的演化过程

 

开发人员想象中,不同服务间通信的方式,抽象表示如下:

然而现实远比想象的复杂,在实际情况中,通信需要底层能够传输字节码和电子信号的物理层来完成,在TCP协议出现之前,服务需要自己处理网络通信所面临的丢包、乱序、重试等一系列流控问题,因此服务实现中,除了业务逻辑外,还夹杂着对网络传输问题的处理逻辑。

TCP

为了避免每个服务都需要自己实现一套相似的网络传输处理逻辑,TCP协议出现了,它解决了网络传输中通用的流量控制问题,将技术栈下移,从服务的实现中抽离出来,成为操作系统网络层的一部分。

第一代微服务

在TCP出现之后,机器之间的网络通信不再是一个难题,以GFS/BigTable/MapReduce为代表的分布式系统得以蓬勃发展。这时,分布式系统特有的通信语义又出现了,如熔断策略、负载均衡、服务发现、认证和授权、quota限制、trace和监控等等,于是服务根据业务需求来实现一部分所需的通信语义。

分布式微服务

为了避免每个服务都需要自己实现一套分布式系统通信的语义功能,随着技术的发展,一些面向微服务架构的开发框架出现了,如Twitter的Finagle、Facebook的Proxygen以及Spring Cloud等等,这些框架实现了分布式系统通信需要的各种通用语义功能:如负载均衡和服务发现等,因此一定程度上屏蔽了这些通信细节,使得开发人员使用较少的框架代码就能开发出健壮的分布式系统。

 

Service Mesh

分布式微服务模式看似完美,但开发人员很快又发现,它也存在一些本质问题:

  • 其一,虽然框架本身屏蔽了分布式系统通信的一些通用功能实现细节,但开发者却要花更多精力去掌握和管理复杂的框架本身,在实际应用中,去追踪和解决框架出现的问题也绝非易事;
  • 其二,开发框架通常只支持一种或几种特定的语言,回过头来看文章最开始对微服务的定义,一个重要的特性就是语言无关,但那些没有框架支持的语言编写的服务,很难融入面向微服务的架构体系,想因地制宜的用多种语言实现架构体系中的不同模块也很难做到;
  • 其三,框架以lib库的形式和服务联编,复杂项目依赖时的库版本兼容问题非常棘手,同时,框架库的升级也无法对服务透明,服务会因为和业务无关的lib库升级而被迫升级;

因此以Linkerd,Envoy,NginxMesh为代表的代理模式(边车模式)应运而生,这就是第一代Service Mesh,它将分布式服务的通信抽象为单独一层,在这一层中实现负载均衡、服务发现、认证授权、监控追踪、流量控制等分布式系统所需要的功能,作为一个和服务对等的代理服务,和服务部署在一起,接管服务的流量,通过代理之间的通信间接完成服务之间的通信请求,这样上边所说的三个问题也迎刃而解。

如果我们暂时略去服务,只看Service Mesh的单机组件组成的网络:

 

第二代Service Mesh

第一代Service Mesh由一系列独立运行的单机代理服务构成,为了提供统一的上层运维入口,演化出了集中式的控制面板,所有的单机代理组件通过和控制面板交互进行网络拓扑策略的更新和单机数据的汇报。这就是以Istio为代表的第二代Service Mesh。

 

只看单机代理组件(数据面板)和控制面板的Service Mesh全局部署视图如下:

Service Mesh的定义:

 

Service Mesh翻译为“服务网格”,作为服务间通信的基础设施层。轻量级高性能网络代理,提供安全的、快速的、可靠地服务间通讯,与实际应用部署一起,但对应用透明。应用作为服务的发起方,只需要用最简单的方式将请求发送给本地的服务网格代理,然后网格代理会进行后续的操作,如服务发现,负载均衡,最后将请求转发给目标服务。
服务网格是一个基础设施层,用于处理服务间通信。云原生应用有着复杂的服务拓扑,服务网格保证请求在这些拓扑中可靠地穿梭。在实际应用当中,服务网格通常是由一系列轻量级的网络代理组成的,它们与应用程序部署在一起,但对应用程序透明

 

Service Mesh目的是解决系统架构微服务化后的服务间通信和治理问题。 服务网格由Sidecar节点组成,这个模式的精髓在于实现了数据面(业务逻辑)和控制面的解耦。具体到微服务架构中,即给每一个微服务实例同步部署一个Sidecar。

 

总结一下,Service Mesh具有如下优点:

  • 屏蔽分布式系统通信的复杂性(负载均衡、服务发现、认证授权、监控追踪、流量控制等等),服务只用关注业务逻辑;
  • 真正的语言无关,服务可以用任何语言编写,只需和Service Mesh通信即可;
  • 对应用透明,Service Mesh组件可以单独升级;

四  、Service Mesh主要功能

Service Mesh 的数据平面能够支撑很多服务治理逻辑,如服务发现、流量控制(路由、负载均衡)、请求熔断、安全通信、Metric 和链路追踪和重试功能。

1.服务发现
以微服务模式运行的应用变更非常频繁,应用实例的频繁增加减少带来的问题是如何精确地发现新增实例,以及避免将请求发送给已不存在的实例上。Service Mesh 可以提供简单、统一、平台无关的多种服务发现机制。

2.动态路由

动态路由是与静态路由相对的一个概念,指路由器能够根据路由器之间的交换的特定路由信息自动地建立自己的路由表,并且能够根据链路和节点的变化适时地进行自动调整。当网络中节点或节点间的链路发生故障,或存在其它可用路由时,动态路由可以自行选择最佳的可用路由并继续转发报文.


随着服务提供商以提供高稳定性、高可用性及高 SLA 服务为主要目标,为了实现所述目标,出现了各种应用部署策略,尽可能从技术手段上达到无服务中断部署,以此避免变更导致服务的中断和稳定性降低,例如 Blue/Green 部署、Canary 部署,但实现这些高级部署策略通常非常困难。如果可以轻松地将应用流量从一个版本切到另外一个版本,或者从一个数据中心到另外一个数据中心进行动态切换,甚至可以通过一个中心控制层控制多少比例的流量被切换,那么Service Mesh 提供的动态路由机制和特定的部署策略(如 Blue/Green 部署)结合起来,实现上述目标更加容易。

3.负载均衡
运行环境中微服务实例通常处于动态变化状态,可能经常出现个别实例不能正常提供服务、处理能力减弱、卡顿等现象。由于所有请求对 Service Mesh 来说是可见的,因此可以通过提供高级负载均衡算法来实现更加智能、高效的流量分发,降低延时,提高可靠性。

4.请求熔断
动态的环境中服务实例中断或不健康导致服务中断的情况可能会经常发生,这就要求应用或其他工具具有快速监测并从负载均衡池中移除不提供服务实例的能力,这种能力也称为熔断,以此使得应用无须消耗更多不必要的资源而不断地尝试,而是快速失败或降级,甚至这样可避免一些潜在的关联性错误。Service Mesh 可以很容易地实现基于请求和连接级别的熔断机制。

5.安全通信
无论何时,安全在整个公司、业务系统中都有着举足轻重的位置,也是非常难以实现和控制的部分。在微服务环境中,不同的服务实例间的通信变得更加复杂,那么如何保证这些通信在安全、授权情况下进行非常重要。通过将安全机制如 TLS 加解密和授权实现在 Service Mesh 上,不仅可以避免在不同应用中的重复实现,而且很容易在整个基础设施层更新安全机制,甚至无须对应用做任何操作。

6.Metric和链路追踪
Metric 是指服务各种运行指标信息,如调用量、成功率、耗时等;而链路追踪是指服务调用的整体链路信息,如链路拓扑、服务依赖。

Service Mesh 对整个基础设施层的可见性使得它不仅可以暴露单个服务的运行数据,而且可以暴露整个集群的运行数据,因此可以很轻易地从 Service Mesh 中获取服务的 Metric 统计信息和服务调用的链路信息。

7.重试
网络环境的异常复杂和服务质量的多变性,服务调用总会存在超时、失败的情况,在这些情况下,重试是能够增加服务质量的一种措施。
Service Mesh 中的重试功能,不仅可以避免将其嵌入业务代码,而且该重试逻辑还可以配置最后期限,使得应用允许一个请求的最长生命周期,防止无限期重试

 

五、Service Mesh的原理


Service Mesh的核心是数据平面Sidecar与控制平面Control Plane:

 

数据平面

Sidecar,与服务部署在一起的轻量级网络代理,用于实现服务框架的各项功能(如,服务发现、负载均衡、限流熔断等),让服务回归业务本质。

数据平台可以认为是将Spring Cloud、Dubbo等相关的微服务框架中通信和服务治理能力独立出来的一个语言无法的进程,并且更注重通用性和扩展性。在Service Mesh中,不再将数据平面代理视为一个个独立的组件,而是将这些代理连接在一起形成一个全局的分布式网格。

在传统的微服务架构中,各种服务框架的功能(如,服务发现、负载均衡、限流熔断等)代码逻辑或多或少的都需要耦合到服务实例的代码中,给服务实例增加了很多无关业务的代码,同时带来了一定的复杂度。

有了SideCar之后,服务节点只做业务逻辑自身的功能,服务之间的调用只需交给SideCar,由SideCar完成注册服务、服务发现、请求路由、熔断限流、日志统计等业务无关功能。

在这种新的微服务架构中,所有的SideCar组成在一起,就形成了服务网格。那么这个大型的服务网格并不是完全自治的,它还需要一个统一的控制节点Control Plane。

 

控制平面

是用来从全局的角度上控制SideCar,相当于Service Mesh架构的大脑,控制着 SideCar来实现服务治理的各项功能。比如,它负责所有SideCar的注册,存储统一的路由表,帮助各个 SideCar进行负载均衡和请求调度;它收集所有 SideCar的监控信息和日志数据。



六、sidecar模式

什么是Sidecar模式

 

中文译名为:边车/挎斗模式。这个名字为直译,挎斗就是这样的一种摩托车:

Sidecar 模式是 Service Mesh 中习惯采用的模式,就会明白这个名字其实取得特别好。Sidecar 模式就是指在原来的业务逻辑上再新加一个抽象层。这种模式很好的印证了那个计算机的名言:

“计算机科学领域的任何问题都可以通过增加一个简介的中间层来解决。”
“Any problem in computer science can be solved by another layer of indirection.”

如果一个抽象层不够,那来两个。这种模式也不是近些年新发明的,我们可以理解 Nginx 的反向代理其实也算一种 sidecar 模式。只是近些年,随着微服务和容器化在实践中越来越多,这种模式的使用范围也更广了。

类似lnmp架构的Nginx层.

 

应用程序的功能划分为单独的进程可以被视为 Sidecar 模式。如图所示,Sidecar 模式允许您在应用程序旁边添加更多功能,而无需额外第三方组件配置或修改应用程序代码。

在软件架构中, Sidecar 连接到父应用并且为其添加扩展或者增强功能。Sidecar 应用与主应用程序松散耦合。它可以屏蔽不同编程语言的差异,统一实现微服务的可观察性、监控、日志记录、配置、断路器等功能

Istio与Envoy使用的模型 也是用的这种模型

Sidecar的使用

  • 使用 Sidecar 模式部署服务网格时,无需在节点上运行代理,但是集群中将运行多个相同的 Sidecar 副本

  • 在Sidecar部署方式中,会为每个应用的容器部署一个伴生的容器

  • 对于Service Mesh,Sidecar接管进出应用程序容器的所有网络流量

  • 在 Kubernetes 的 Pod 中,在原有的应用容器旁边注入一个 Sidecar 容器,两个容器共享存储、网络等资源,可以广义的将这个包含了 Sidecar 容器的 Pod 理解为一台主机,两个容器共享主机资源。

 

七、Istio架构

 

istio是⼀个Service Mesh形态的⽤于服务治理的开放平台。(治理的是服务间的访问)

Service Mesh 是一个专门处理服务通讯的基础设施层。它的职责是在由云原生应用组成服务的复杂拓扑结构下进行可靠的请求传送。

Istio是最初由IBM,Google和Lyft开发的服务网格的开源实现。它可以透明地分层到分布式应用程序上,并提供服务网格的所有优点,例如流量管理,安全性和可观察性。

它旨在与各种部署配合使用,例如本地部署,云托管,Kubernetes容器以及虚拟机上运行的服务程序。尽管Istio与平台无关,但它经常与Kubernetes平台上部署的微服务一起使用。

从根本上讲,Istio的工作原理是以Sidcar的形式将Envoy的扩展版本作为代理布署到每个微服务中:

Envoy 是专为大型现代 SOA(面向服务架构)架构设计的 L7 代理和通信总线

进程外架构:Envoy 是一个独立进程,设计为伴随每个应用程序服务运行。所有的 Envoy 形成一个透明的通信网格,每个应用程序发送消息到本地主机或从本地主机接收消息,但不知道网络拓扑。在服务间通信的场景下,进程外架构与传统的代码库方式相比,具有两大优点:

  • Envoy 可以使用任何应用程序语言。Envoy 部署可以在 Java、C++、Go、PHP、Python 等之间形成一个网格。面向服务架构使用多个应用程序框架和语言的趋势越来越普遍。Envoy 透明地弥合了它们之间的差异。
  • 任何做过大型面向服务架构的人都知道,升级部署库可能会非常痛苦。Envoy可以透明地在整个基础架构上快速部署和升级。

Data Plane: 数据传输,完成服务之间的交互

 

Control Plane:管理配置、控制路由流量、执行策略

 

 

ISTIO 有四大功能,分别是流量管理,安全,策略,观测。 

连接:Istio 通过集中配置的流量规则控制服务间的流量和调⽤,实现负载均衡、熔断、故障注⼊、重试、重定向等服务治理功能。

保护:Istio 提供透明的认证机制、通道加密、服务访问授权等安全能⼒,可增强服务访问的安全性。

控制:Istio 通过可动态插拔、可扩展的策略实现访问控制、速率限制、配额管理、服务计费等能⼒。

观测:动态获取服务运⾏数据和输出,提供强⼤的调⽤链、监控和调⽤⽇志收集输出的能⼒。配合可视化⼯具,可⽅便运维⼈员了解服务的运⾏状况,发现并解决问题。

 

简单描述一个 istio 中的应用流量案例:当服务 A 访问服务 B 时,服务 A 出发的请求会被 Proxy 劫持,Proxy 根据请求的特征匹配相应的路由规则,并上报相关的度量信息到 Mixer ,且询问 Mixer 本次通行的策略,接着才将流量访问到服务 B ,而服务 B 的入口流量也会被 Proxy 劫持,然后再交给实际的服务 B 。

下图描绘了该场景下,架构示意。

组件:

Mixer

  • 执行策略,策略控制,比如:服务调用速率限制
  • 收集遥测数据、日志,Mixer组件也可以收集各个服务上的日志,从而可以进行监控

Pilot 

  • 服务发现
  • 智能路由

Envoy

  • 代理转发, 处理服务的流量
  • 负载均衡 
  • 熔断
  • 流量控制
  • 故障注入

Citadel

  • 权限管理
  • 认证服务、证书和密钥,起到安全作用,维护了服务代理通信需要的证书和密钥。

Galley

  • 配置管理

Istio的工作机制

 

 

 

自动注入:指在创建应用程序时自动注入 Sidecar 代理。在 Kubernetes 场景下创建 Pod 时,Kube-apiserver 调用管理面组件的 Sidecar-Injector 服务,自动修改应用程序的描述信息并注入 Sidecar。在真正创建 Pod 时,在创建业务容器的同时在 Pod 中创建 Sidecar 容器。

流量拦截:在 Pod 初始化时设置 iptables 规则,当有流量到来时,基于配置的iptables 规则拦截业务容器的 Inbound 流量和 Outbound 流量到 Sidecar 上。应用程序感知不到 Sidecar 的存在,还以原本的方式进行互相访问。如上图中,流出frontend 服务的流量会被 frontend 服务侧的 Envoy 拦截,而当流量到达forecast 容器时,Inbound 流量被 forecast 服务侧的 Envoy 拦截。


服务发现:服务发起方的 Envoy 调用管理面组件 Pilot 的服务发现接口获取目标服务的实例列表。在上图中,frontend 服务侧的 Envoy 通过 Pilot 的服务发现接口得到 forecast 服务各个实例的地址,为访问做准备。

负载均衡:服务发起方的 Envoy 根据配置的负载均衡策略选择服务实例,并连接对应的实例地址。在上图中,数据面的各个 Envoy 从 Pilot 中获取 forecast 服务的负载均衡配置,并执行负载均衡动作。

流量治理:Envoy 从 Pilot 中获取配置的流量规则,在拦截到 Inbound 流量和Outbound 流量时执行治理逻辑。在上图中,frontend 服务侧的 Envoy 从 Pilot 中获取流量治理规则,并根据该流量治理规则将不同特征的流量分发到 forecast 服务的v1 或 v2 版本。当然,这只是 Istio 流量治理的一个场景。


访问安全:在服务间访问时通过双方的 Envoy 进行双向认证和通道加密,并基于服务的身份进行授权管理。在上图中,Pilot 下发安全相关配置,在 frontend 服务和forecast 服务的 Envoy 上自动加载证书和密钥来实现双向认证,其中的证书和密钥由另一个管理面组件 Citadel 维护。


服务遥测:在服务间通信时,通信双方的 Envoy 都会连接管理面组件 Mixer 上报访问数据,并通过 Mixer 将数据转发给对应的监控后端。在上图中,frontend 服务对 forecast 服务的访问监控指标、日志和调用链都可以通过这种方式收集到对应的监控后端。


策略执行:在进行服务访问时,通过 Mixer 连接后端服务来控制服务间的访问,判断对访问是放行还是拒绝。在上图中,Mixer 后端可以对接一个限流服务对从 frontend服务到 forecast 服务的访问进行速率控制。


外部访问:在网格的入口处有一个 Envoy 扮演入口网关的角色。在上图中,外部服务通过 Gateway 访问入口服务 frontend,对 frontend 服务的负载均衡和一些流量治理策略都在这个Gateway上执行。

 

 

 

八、Istio 与 Kubernetes

从场景来看,Kubernetes已经提供了非常强大的应用负载的部署、升级、扩容等运行管理能力。Kubernetes 中的 Service 机制也已经可以做服务注册、服务发现和负载均衡,支持通过服务名访问到服务实例。
从微服务的工具集观点来看,Kubernetes本身是支持微服务的架构,在Pod中部署微服务很合适,也已经解决了微服务的互访互通问题,但对服务间访问的管理如服务的熔断、限流、动态路由、调用链追踪等都不在Kubernetes的能力范围内。那么,如果想提供一套从底层的负载部署运行到上层的服务访问治理端到端的解决方案,目前,最完美的答案就是在 Kubernetes 上叠加 Istio 这个好帮手。

如果说K8s管理的对象是Pod,那么ServiceMesh管理的对象就是Service,实际上就是K8s来管理微服务在上层应用ServiceMesh。

 

九、service mesh的坑

资源上的损耗


mesh 本质上相当于寄生在业务机器上。使用的是业务机器的资源。实际上的发现,由于采用了 go 实现的 mesh 对于内存的消耗比较可控,默认情况下只占用几 M,在高并发下一般也只会上升至几十 M。这对于正常情况下 8G/16G 内存的应用机器来说基本可以忽略不计。所以内存的额外占用这个问题可以基本忽略。但是其对于 cpu 资源的消耗则较大,一般会趋近于业务正常使用的 cpu 资源量。这意味着,加入了 mesh 之后,有可能业务能使用的 cpu 资源只剩下本来的一半。这就是一个比较大的问题了.

不过有人认为,由于正常业务机器的资源使用率不到 10%,所以这部分的额外占用在实际情况下并不会对业务造成实质性的影响,反而可以让我们更好地利用上闲置资源,避免浪费。业务与 mesh 互利共赢。

性能上的损耗

 

性能上的损耗是回避不了的一个问题。由于多进行了一次转发以及要进行服务治理,所以性能天然地比直连 RPC 的方式的性能要差。基于性能测试结果,其相比于直连,mesh 方式的性能会退化 20-50%左右,这还是在不采用 iptables 这种更耗性能的方式下进行的测试。当然,这个增加的延时在毫秒级,对于大部分的业务要求来说,其实是可接受的。对业务性能的影响微乎其微.

但是对于一些高并发的业务场景,可能本身延时就低(毫秒级),且对延时敏感,加上一次调用链路可能会有七八次甚至十次以上的 RPC 调用,如果以这种方式进行改造,则可能导致这类业务性能退化严重,甚至可能引起比如超时、线程池打满等问题.