2016 年前后,"服务网格"这个词出现在微服务、云计算和 DevOps 的领域。Buoyant 团队在 2016 年用这个词来解释他们的产品 Linkerd。服务网格的到来主要是由于 IT 领域内的一场风暴。开发人员开始使用多语言(polyglot)方法构建分布式系统,并需要动态服务发现。运维部门开始使用短暂的基础设施,并希望优雅地处理不可避免的通信故障和执行网络策略。平台团队开始接受像 Kubernetes 这样的容器编排系统,并希望使用现代 API 驱动的网络代理(如 Envoy)在系统中和周围动态地路由流量。
随着消除特定语言的通信库编译到单个服务、处理服务发现、路由和应用层肺功能通信需求、Kubernetes等容器编排系统的流行,服务网格出现,并试图解决一下问题:
(1) 消除了将特定语言的通信库编译到单个服务中的需求,以处理服务发现、路由和应用层(第 7 层)非功能通信要求。
(2) 外部化服务通信配置,包括外部服务的网络位置、安全凭证和服务质量目标。
(3) 提供对其他服务的被动和主动监测。
(4) 提供可观察性的默认值,并使相关数据的收集标准化。
需要说明的是,服务网格主要侧重于处理传统上被称为"东西向"的基于远程过程调用(RPC)的流量:请求/响应类型的通信,源自数据中心内部,在服务之间传播。这与 API 网关或边缘代理相反,后者被设计为处理"南北向"流量,来自外部的通信,进入数据中心内的一个终端或服务。
服务网格是一种在分布式(如微服务)软件系统内管理所有服务对服务(东西向)流量的基础设施层。服务网格负责构成现代云原生应用的复杂服务拓扑来可靠地交付请求。服务网格既提供以业务为重点的功能操作,如路由,也提供非功能支持,如执行安全策略、服务质量和速率限制。它通常(尽管不是唯一的)使用 sidecar 代理来实现,所有服务都通过 sidecar 代理进行通信。sidecar 作为轻量级网络代理,与应用程序代码部署在一起,对应用程序来说无需感知其存在。
在微服务架构发展的早期,微服务间通信主要借助SDK,也就是微服务自身要解决服务间通信问题,如服务发现、负载均衡、流量治理、安全、服务可观测性等。但是,按照关注点分离原则,服务间通信的治理是一个独立的过程,完全可以从服务中解耦出来。为实现服务通信治理从应用中解耦出来,基于网络代理的架构应运而生。
因为这些网络代理基为服务创建了一个网格,因此得名服务网格。通过这些网络代理,服务网格能够控制服务到服务通信的各个方面,从而解决了分布式计算的八个谬论。
仅仅引入网络代理,无法解决服务上线、下线等问题,为此,还要引入控制单元,以监控各服务实例。因此常见的服务网格架构主要由两部分组成:数据平面和控制平面。
控制平面(Control Plane),控制和管理数据平面中的网络代理(也称为sidecar),完成配置分发、服务发现、流量路由、授权鉴权等功能,以达到对数据平面的统一管理。注意,控制平面不接触服务中的任何数据包。相反,控制平面允许运维人员为网格中所有正在运行的数据平面提供策略和配置。控制平面还能够收集和集中数据平面的遥测数据,供运维人员使用。
数据平面(Data Plane),由整个网格内的 Sidecar 代理组成,这些代理以 Sidecar 的形式和应用服务一起部署。这些代理负责协调和控制应用服务之间的所有网络通信,服务只需要在 localhost 之间发送和接收消息,而无需了解网络拓扑。每一个 Sidecar 会接管进入和离开服务的流量,并配合控制平面完成流量控制等方面的功能。
控制平面和数据平面的结合提供了两方面的优势,即策略可以集中定义和管理,同时,同样的政策可以以分散的方式,在 Kubernetes 集群的每个 pod 中本地执行。这些策略可以与安全、路由、断路器或监控有关。
更多服务网格数据平面和控制平面的介绍,可以参考 Matt Klein,Envoy Proxy 的作者,写的服务网格数据平面与控制平面一文。
引入控制平面和数据平面后,网络拓扑结构变成如下形式:
接下来将从Traffic Management(流量管理)、Observability(可观测性)和Security(安全)三个方面介绍下服务网格。
服务网格的基本功能之一是流量管理,这包括动态服务发现和路由。流量管理还包括一些高级场景,如基于流量拆分实现灰度发布。此外,由于所有服务到服务的通信都是由服务网格处理,所以服务网格还承担了一些可靠性功能,如重试、超时、限流、断路器等。这些开箱即用的故障恢复功能使服务间通信更加可靠。
可观测性是处理分布式系统复杂性的基本要求。由于服务网格处理所有通信,因此需要基于服务网格提供可观测性功能,如提供分布式跟踪的信息。服务网格可以生成大量监控指标,例网络延迟、流量信息、错误等。此外,服务网格还可以生成访问日志,为每个请求提供完整记录。这些对于理解单个服务以及整个系统的行为非常有用。
服务网格通常还处理服务到服务通信的安全方面。这包括通过双向TLS(mTLS)强制实施流量加密、通过证书验证提供身份验证以及通过访问策略确保授权。此外,还可以实现网络分段,允许某些服务进行通信,同时禁止其他服务。此外,服务网格可以为审计要求提供精确的历史信息。
作为专用的基础设置,选择服务网格时,需要在功能性(流量管理)和非功能性(可观测性、安全、可靠性等)等方面进行考量。当前,开源的服务网格有很多,如 Istio、Linkerd、Maesh、Kuma等,这里重点对比下使用广泛度较高的 Istio 和 Linkerd,其他服务网格还请自行学习。
特性 | 细分场景 | Istio | Linkerd |
---|---|---|---|
平台无关性 | 跨平台 | 目前只支持Kubernetes,后续会实现平台无关 | 支持多种平台,如 Kubernetes、VM 等 |
流量管理 | 流控 | 支持 | 支持 |
流量管理 | 流量拆分 | 支持 | 支持 |
流量管理 | 负载均衡 | 支持 | 支持 |
可靠性 | 熔断 | 支持 | 没有 |
可靠性 | 重试和超时 | 支持 | 支持 |
可靠性 | 故障注入 | 支持 | 支持 |
可靠性 | 延迟注入 | 支持 | 不支持 |
可观测性 | 访问日志生成 | 支持 | 不支持 |
可观测性 | 日志跟踪 | 支持 | 支持 |
可观测性 | 可视化 | 支持 | 支持 |
安全 | TLS | 支持 | 支持,但不适用于TCP |
安全 | 授权规则 | 支持 | 不支持 |
易用性 | 使用难易度 | 复杂 | 简单 |
可以看到,在易用性和平台无关性方面,Linkerd表现出了优越性,而在可靠性、可观测性、安全等方面Linkerd表现不如Istio。在企业或组织选用服务网格时,要根据自身的业务需要,选择合适的服务网格。需要说明的是,尽管Istio在易用性和平台无关性方面表现不如Linkerd,但是其使用广泛度要更高,这主要来源于Istio在可靠性、安全、可观测性等方面的优势。目前 Istio 已经成为服务网格的事实标准。
在微服务架构发展的早期,多服务间的通信通过SDK的方式实现,如 Spring Cloud 框架就是这种实现。但是基于SDK的实现,因为依赖特定语言的实现,无法做到微服务的异构性,且这种实现方式对开发者并不友好,有一定的侵入性。服务网格就是在上述背景下产生,实现了将网络通信从服务中分离出来。
接下来将以 Istio 和 Spring Cloud 为例,介绍下服务网格与微服务SDK的差异。
基于Spring Cloud的微服务架构,所有的微服务都会先连接注册中心(如Eureka),进行服务注册,然后在服务访问时,去注册中心进行服务发现,得到待访问的目标服务的实例列表,使用客户端负载均衡(如Ribbon)选择一个服务实例发起访问。
而在服务网格Istio中,不需要服务注册的过程,只需要从运行平台Kubernetes中获取服务和实例的关系,在服务访问时,数据面代理Envoy拦截到流量,选择一个目标实例发送请求。
可以看到无论是基于Spring Cloud的微服务架构,还是服务网格Istio,都是基于服务发现数据进行客户端负载均衡,差别是服务发现数据来源不同,负载均衡的执行体不同。
基于Spring Cloud的微服务架构的熔断是在SDK中Hystrix执行,而在服务网格Istio中,数据面proxy执行。Hystrix因为在业务代码中,允许用户通过编程做一些控制。
无论是基于Spring Cloud的微服务架构,还是服务网格Istio,熔断的模型是一样的,差别是处理熔断的执行体不同。
(1) 多语言问题
在企业应用开发下,一个业务使用统一的开发框架是非常合理常见的,很多开发团队为了提升效率,经常还会维护有自己公司或者团队的通用开发框架。当然因为大部分业务系统都是基于Java开发,所以Spring Cloud开发框架,或者衍生于Spring Cloud的各种开发框架使用的尤其广泛。
但是在云原生场景下,业务一般更加复杂多样,特别是涉及到很多即存的老系统。不能要求为了微服务化,将在用的一组成熟服务用Spring Cloud重写下。用户非常希望有一种方式不重写原来的系统也能对其进行应用层服务访问管理。
而且,对于一些新增业务,如AI领域相关的业务,可能基于Python等语言开发更具优势,所以需要框架实现语言无关性。
而基于服务网格,业务和治理的数据面无需运行在同一个进程里,也无需一起编译,因此也没有语言和框架上的绑定。无论什么语言开发的服务,只要有一个对外可以被访问和管理的一定应用协议上的端口,都可以被网格进行管理。通过统一的网格控制面,下发统一的治理规则给统一的网格数据面执行,进行统一的治理动作,包括灰度发布、流量管理、安全、可观察性等。
所以,相比Spring Cloud是基于Java语言,服务网格的语言无关性无疑跟具有优势。
(2) 服务发现不及时
基于Spring Cloud的服务发现是基于各个微服务先向注册中心进行服务注册的数据来实现的,当微服务部署在VM上,服务动态变化要求没有那么高,至多只会出现个别实例运行不正常,但通过服务发现健康检查就可以解决。但是在Kubernetes场景下,服务实例动态迁移是非常正常场景。如果该情况频繁发生,会出现注册中心数据维护不及时,导致服务发现和负载均衡到旧的服务实例上,从而引起访问失败的情况。
将Spring cloud服务在Kubernetes运行时,关于原有的服务注册和发现不及时的问题,其根因是两套服务发现导致的不一致问题,那么解决办法也比较简单,统一服务发现即可。由于Kubernetes已经在Pod调度的同时维护有服务和实例间的数据,那么就没有必要再单独搞一套名字服务的机制,直接复用即可。
(3) 同步升级问题
对基于Spring Cloud的微服务架构来说,在每次升级开发框架时,需要要求业务团队来同步升级自己的服务。经常会出现Spring Cloud 自身修改测试工作量不大,但却要制定很长周期的升级计划,来对上千个基于这个SDK开发的服务分组重新编译,打包,升级,而且经常要陪着业务团队在夜间变更。业务团队因为自身没有什么改动,考虑到这个升级带来的工作量和线上风险,一般也没有什么动力。
对于SDK自身升级导致业务全部重新升级的问题,解决办法就是把服务治理的公共能力和业务解耦。在服务网格中,将治理能力下沉到基础设施后,业务的开发、部署、升级都和服务治理的基础设施解耦了。业务开发者专注自己的业务部分。只要没有修改业务代码,就无需重新编译和上线变更。
当治理能力升级时,只需基础设施升级即可,基础设施的升级对用户业务完全没有影响。
(4) 渐进式微服务
马丁福勒在著名的文章将单体到微服务的拆分中提到了对渐进微服务化的倡议,如何能从业务上将一个大的业务分割,解耦,然后逐步微服务化。马丁福勒强调 “解耦的是业务能力不是代码”,大神将代码的解耦留给了开发者。
但是站在开发者的角度讲渐进的微服务不是一个容易的事情。以基于Spring cloud框架进行微服务开发为例,为了所有的微服务间进行统一的服务发现、负载均衡,消费和执行同样的治理策略,必须要求所有的微服务基于同样的,甚至是统一版本的SDK来开发。
当然在这种情况下也有基于API层面做适配,将原有的未微服务化的和已微服务化的并存,使用这种类似于灰度方式,实际操作非常麻烦。
曾经有客户问过有没有不用费劲搞两套,是否可以直接有些大的单体微服务化,另外一些单体很长时间内完全不动,直到有时间或者认为安全想动它的时候去动。
关于渐进微服务化的问题,使用服务网格Isito可以非常完美的解决。Istio治理的是服务间的访问,只要一个服务被其他服务访问,就可以通过Istio来进行管理,不管是微服务还是单体。Istio接管了服务的流量后,单体和微服务都可以接收统一的规则进行统一的管理。从而尽量避免一次大的重构带来的工作量和业务迁移的风险,真正做到马丁富勒倡导的渐进微服务化的实践。
API 网关负责管理进出集群的入口,即南北向流量;服务网格的首要功能是管理集群内部服务间的流量,即东西向流量,而有些服务网格自带 API 网关,如 Istio 内置的基于 Envoy 的 API 网关,Istio 中可以声明 Gateway 对象并与 VirtualService 绑定来履行网关职能,这样的服务网格可以接管进出集群及集群内部所有服务的流量。限于服务网格中内置的 Sidecar 主要作为代理而存在,而未对作为 API 网关的功能作定制开发,可能在功能上逊色于传统的 API 网关。
API 网关和服务网格并非二选一,两者可以同时存在。如果已经有稳定成熟的微服务中间件,对于集群内服务的安全性、可观察性要求不高,可以继续沿用传统的 API 网关。如果已经开始迁移到服务网格,并且希望构建零信任网络,对集群内服务进行全方位的可观察性分析,可以继续使用API 网关和服务网格,或逐步将API 网关迁移到服务网格中。
采用Istio Ingress Gateway加上Load balancer的方式可以将Kubernetes Cluster中的应用服务暴露给外部客户端。这种方式比较灵活,基本可以满足大部分应用的需要。但如果需要在入口处提供更强大的功能,如有更高的效率要求,需求进行安全认证,日志记录,或者需要一些应用的定制逻辑,则需要考虑采用微服务架构中的API 网关模式,采用一个更强大的API 网关来作为应用的流量入口。
采用 Gateway 和 VirtualService 实现的 Istio Ingress Gateway 提供了网络入口处的基础通信功能,包括可靠的通信和灵活的路由规则。但对于一个服务化应用来说,网络入口除了基础的通讯功能之外,还有一些其他的应用层功能需求,例如:第三方系统对 API 的访问控制、用户对系统的访问控制、修改请求/返回数据、服务 API 的生命周期管理、服务访问的 SLA、限流及计费等。
采用Istio Ingress Gateway加上Load balancer一起作为服务网格的流量入口,然后让Ingress和API 网关对接的方式,既能够通过对网关进行定制开发满足产品对 API 网关的各种需求,又可以在网络入口处利用服务网格提供的灵活的路由能力和分布式跟踪,策略等管控功能,是服务网格产品入口网关的一个理想方案。
性能方面的考虑:采用该方案后,外部请求的处理流程在入口处增加了Istio Ingress Gateway这一跳,因此该方式会带来少量的性能损失,但该损失是完全可以接受的。
对于请求时延而言,在服务网格中,一个外部请求本来就要经过较多的代理和应用进程的处理,在Istio Ingress Gateway 处增加一个代理对整体的时延影响基本忽略不计,而且对于绝大多数应用来说,网络转发所占的时间比例本来就很小,99% 的耗时都在业务逻辑。如果系统对于增加的该时延非常敏感,则建议重新考虑该系统是否需要采用微服务架构和服务网格。
对于吞吐量而言,如果入口处的网络吞吐量存在瓶颈,则可以通过对API 网关 + Istio Ingress Gateway 组成的整体进行水平扩展,来对入口流量进行负荷分担,以提高网格入口的网络吞吐量。
服务网格自身不提供服务注册与服务发现能力,需要对接第三方服务注册和发现平台。使用服务网格后,因为 Kubernetes 已成为容器编排系统的事实标准,所以这里介绍下服务网格(特指Istio)与Kubernetes结合实现服务注册与服务发现的过程。
Kubernetes 使用 DNS 作为服务注册表,为了满足这一需要,每个 Kubernetes 集群都会在 kube-system 命名空间中用 Pod 的形式运行一个 DNS 服务,通常称之为集群 DNS。然后应用到Kubernetes中的Service会自动注册到集群 DNS 之中,注册过程大致如下:
(1) 向 API Server 用 POST 方式提交一个新的 Service 定义;
(2) 这个请求需要经过认证、鉴权以及其它的准入策略检查过程之后才会放行;
(3) Service 得到一个 ClusterIP(虚拟 IP,VIP),并保存到集群数据仓库;
(4) 在集群范围内传播 Service 配置;
(5) 集群 DNS 服务得知该 Service 的创建,据此创建必要的 DNS 记录。
在 Kubernetes 中 DNS 是一种 Addon (附加组件)。从 v1.13 开始 CoreDNS 成为默认 DNS 服务,CoreDNS 的特点是效率更高,资源占用率更小。CoreDNS 实现了一个控制器,会对 API Server 进行监听,一旦发现有新建的 Service 对象,就创建一个从 Service 名称映射到 ClusterIP 的域名记录。这样 Service 就不必自行向 DNS 进行注册,CoreDNS 控制器会关注新创建的 Service 对象,并实现后续的 DNS 过程。DNS 中注册的名称就是 metadata.name,而 ClusterIP 则由 Kubernetes 自行分配。
Istio 本身并不具备服务注册的功能,它需要通过平台适配器和特定的平台结合才能具有完整的服务发现的功能。这里介绍下Istio如何对接Kubernetes实现服务发现。Istio在控制面,依赖Pilot组件实现服务发现。Pilot架构图如下所示:
一个 Pilot 主要由Platform Adapter(平台适配器)、Abstract Model(抽象模型)、用于配置和调用 Envoy 的"Envoy API"和用于用户指定流量管理规则的"Rules API"所组成。
服务发现流程如下:
(1) 平台适配器从 Kubernetes API Server 中获取 Pod 的注册信息。
(2) 用户通过 Pilot 的 Rules API 对所有被发现的服务的 Envoy 进行各种高级特性(包括路由规则、HTTP层的流量管理等)的配置。
(3) 用户配置的这些规则被抽象模型翻译成低级配置。
(4) Envoy API 将这些翻译好的低级配置通过 Discovery API 分发到每个微服务上的 Envoy实例中。
https://jimmysong.io/kubernetes-handbook/cloud-native/from-kubernetes-to-cloud-native.html 云原生应用之路——从 Kubernetes 到云原生
https://zhuanlan.zhihu.com/p/420686115 服务网格终极指南第二版——下一代微服务开发
https://jimmysong.io/kubernetes-handbook/usecases/service-mesh.html 服务网格(Service Mesh )
https://zhuanlan.zhihu.com/p/358891699 【IstioCon 2021】最佳实践:从Spring Cloud 到 Istio
https://www.cnblogs.com/xishuai/p/microservices-and-service-mesh.html 微服务(Microservices)和服务网格(Service Mesh)架构概念整理
https://www.baeldung.com/ops/istio-service-mesh Service Mesh Architecture with Istio
https://developer.aliyun.com/article/867274 全方位解读服务网格(Service Mesh)的背景和概念
https://www.vmware.com/cn/topics/glossary/content/network-segmentation.html 什么是网络分段?
https://zhuanlan.zhihu.com/p/102453287 史上最详测评:6 大 Service Mesh 横向对比!
https://cloud.tencent.com/developer/article/1892701 九种开源服务网格比较
https://cloud.tencent.com/developer/article/1821295 构建基于 Spring Cloud 向 Service Mesh 框架迁移的解决方案及思路
https://www.jianshu.com/p/ee82c9f0965c Spring Cloud迁移Service Mesh(Istio)
https://cloudnative.to/blog/how-to-pick-gateway-for-service-mesh/ 如何为服务网格选择入口网关?
https://developer.aliyun.com/article/886724 Istio:服务发现和Pilot的架构机制
https://juejin.cn/post/7138964546672132103 云原生架构之服务发现与注册-Kubernetes中服务注册发现
https://zhuanlan.zhihu.com/p/393138613 kubernetes第八章:服务发现
https://www.infoq.cn/article/T9wjTI2rPegB0uafUKeR 服务网格 Istio 初探 -Pilot 组件
https://zhuanlan.zhihu.com/p/54123996 K8s为何需要Istio?较为深入地讨论 Istio——其历史发展、设计理念、核心功能原理及运行流程