原文地址:https://mp.weixin.qq.com/s/543MsQkrtTdIfnfGd7LciA
同时也参考了另外一篇 InfoQ公众号推送:下一代的微服务架构基础是ServiceMesh?
微服务架构的核心技术问题
在微服务模式下,企业内部服务少则几个到几十个,多则上百个,每个服务一般都以集群方式部署,这时自然产生两个问题:
- 服务发现: 服务的消费方 (Consumer) 如何发现服务的提供方 (Provider)?
- 负载均衡: 服务的消费方如何以某种负载均衡策略访问集群中的服务提供方实例?
三种服务发现模式
在服务消费方 (Consumer) 和服务提供方 (Provider) 之间增加一层代理 Proxy,由代理 Proxy 负责服务发现和负载均衡功能,消费方 (Consumer) 通过代理间接访问目标服务 (Provider)。根据代理在架构上所处的位置不同,当前业界主要有三种不同的服务发现模式。
模式一:传统集中式代理
在服务消费者 (Consumer) 和生产者 (Provider) 之间,代理 Proxy 作为独立一层集中部署,由独立团队 (一般是运维或框架) 负责治理和运维。
常用的集中式代理有:
- 硬件负载均衡器 (如 F5)
- 软件负载均衡器 (如 Nginx)
- F5(4 层负载)+Nginx(7 层负载) 这种软硬结合两层代理也是业内常见做法
这种方式通常在 DNS 域名服务器的配合下实现服务发现,服务注册 (建立服务域名和 IP 地址之间的映射关系) 一般由运维人员在代理上手工配置,服务消费方仅依赖服务域名,这个域名指向代理 Proxy,由代理解析目标地址并做负载均衡和调用。
模式二:客户端嵌入式代理
这是很多互联网公司比较流行的一种做法,代理 (包括服务发现和负载均衡逻辑) 以客户库的形式嵌入在应用程序中。
这种模式一般需要独立的服务注册中心组件配合,服务启动时自动注册到注册中心并定期报心跳,客户端代理则发现服务并做负载均衡。
Netflix 开源的 Eureka(注册中心) 和 Ribbon(客户端代理) 是这种模式的典型案例,国内阿里开源的 Dubbo 也是采用这种模式。
模式三:主机独立进程代理
这种做法是上面两种模式的一个折中,代理既不是独立集中部署,也不嵌入在客户应用程序中,而是作为独立进程部署在每一个主机上,一个主机上的多个消费者应用可以共用这个代理,实现服务发现和负载均衡。
这个模式一般也需要独立的服务注册中心组件配合,作用同模式二。
谓的 ServiceMesh,其实本质上就是上面提到的模式三:主机独立进程模式。
-
上述模式一和二有一些固有缺陷
- 模式一相对比较重,有单点问题和性能问题;
- 模式二则有客户端复杂,支持多语言困难,无法集中治理的问题。
- 模式三是模式一和二的折中,弥补了两者的不足,它是纯分布式的,没有单点问题,性能也不错,应用语言栈无关,可以集中治理。
微服务化、多语言和容器化发展的趋势,企业迫切需要一种轻量级的服务发现机制,ServiceMesh 正是迎合这种趋势诞生。
模式三 (ServiceMesh) 也被形象称为边车 (Sidecar) 模式,如下图。
在模式三中,业务代码进程 (相当于主驾驶) 共享一个代理 (相当于边车),代理除了负责服务发现和负载均衡,还负责动态路由、容错限流、监控度量和安全日志等功能,这些功能是具体业务无关的,属于跨横切面关注点 (Cross-Cutting Concerns) 范畴。
在新一代的 ServiceMesh 架构中,如下图,服务的消费方 (Consumer) 和提供方 (Provider) 主机 (或者容器) 两边都会部署代理 SideCar。
ServiceMesh 比较正式的术语也叫数据平面 (DataPlane),与数据平面对应的还有一个独立部署的控制平面 (ControlPlane),用来集中配置和管理数据平面,也可以对接各种服务发现机制 (如 K8S 服务发现)。
上图左下角,每个主机上同时居住了业务逻辑代码 (绿色表示) 和代理 (蓝色表示),服务之间通过代理发现和调用目标服务,形成服务之间的一种网络状依赖关系,控制平面则可以配置这种依赖调用关系,也可以调拨路由流量。如果我们把主机和业务逻辑剥离,就出现一种网格状架构 (上图右下角),服务网格由此得名。
Istio
Istio 是 Google/IBM 等大厂支持和推进的一个 ServiceMesh 标准化工作组,上图是 Istio 给出的 ServiceMesh 参考架构。Istio 专注在控制平面的架构、功能、以及控制平面和数据平面之间 API 的标准化,它的控制平面功能主要包括:
- Istio-Manager:负责服务发现,路由分流,熔断限流等配置数据的管理和下发
- Mixer:负责收集代理上采集的度量数据,进行集中监控
- Istio-Auth:负责安全控制数据的管理和下发
Envoy 是目前 Istio 主力支持的数据平面代理,其它主流代理如 nginx/kong 等也正在陆续加入这个阵营。kubernetes 是目前 Isito 主力支持的容器云环境。
下图是最新的 Istio 架构,引用自 https://istio.io/docs/concepts/what-is-istio/
Service Mesh 是什么?
- 基础设施层:这是 Service Mesh 的定位;
- 服务间通讯:这是 Service Mesh 的功能和范围;
- 实现请求的可靠传递:是 Service Mesh 的目标;
- 轻量级网络代理:是 Service Mesh 的部署方式;
- 对应用程序透明:是 Service Mesh 的重要特性,零侵入,Service Mesh 的最大优势之一。
SOFAMesh 项目
SOFAMesh 是蚂蚁金服推出的 Service Mesh 开源产品,大家可以简单的理解为是 Istio 的落地增强版本。
- 跟随社区
- 实践检验
原则上:Istio 做好的地方,我们简单遵循,保持一致;Istio 做的不好或者疏漏的地方,我们努力改进和弥补。
SOFAMesh 架构继续延续 Istio 的数据平面和控制平面分离的方式,主要工作内容是:
- 用 Golang 开发 Sidecar,也就是我们的 SOFAMosn 项目,替代 Envoy。
- 集成 Istio 和 SOFAMosn,同时针对落地时的需求和问题进行扩展和补充,这是我们的 SOFAMesh 项目
在这个架构中,和 Istio 原版最大的不同在于我们没有选择 Istio 默认集成的 Envoy,而是自己用 Golang 开发了一个名为 SOFAMosn 的 Sidecar 来替代 Envoy。
在 Istio 和 Envoy 中,对通讯协议的支持,主要体现在 HTTP/1.1 和 HTTP/2 上,这两个是 Istio / Envoy 中的一等公民。而基于 HTTP/1.1 的 REST 和基于 HTTP/2 的 gRPC,一个是目前社区最主流的通讯协议,一个是未来的主流,Google 的宠儿,CNCF 御用的 RPC 方案,这两个组成了目前 Istio 和 Envoy(乃至 CNCF 所有项目)的黄金组合。
SOFAMosn 干了啥?
MOSN 的全称是 "Modular Observable Smart Network",正如其名所示,这是一个模块化可观察的智能网络。
而我们 SOFAMesh,在第一时间就遇到和 Istio/Envoy 不同的情况,我们需要支持 REST 和 gRPC 之外的众多协议:
- SOFARPC:这是蚂蚁金服大量使用的 RPC 协议 (已开源);
- HSF RPC:这是阿里集团内部大量使用的 RPC 协议 (未开源);
- Dubbo RPC: 这是社区广泛使用的 RPC 协议 (已开源);
- 其他私有协议:在过去几个月间,我们收到需求,期望在 SOFAMesh 上运行其他 TCP 协议,大部分是私有协议。
为此,我们需要考虑在 SOFAMesh 和 SOFAMosn 中增加这些通讯协议的支持,尤其是要可以让我们的客户非常方便的扩展支持各种私有 TCP 协议。
为什么不直接使用 Envoy?
Envoy 是 C++。
我们在选择之前,内部做过深入讨论,焦点在于:未来的新一代架构的底层平台,编程语言栈应该是什么?最终一致觉得应该是 Golang,配合部分 Java。
SOFAMesh 在落地期间遇到的典型问题
通讯协议扩展
前面也刚谈到过,就是我们会需要支持非常多的 TCP 协议,包括各种私有协议。
这个问题主要源于现在 Istio 的设计,按照 Istio 现在的方式,如果要添加一个新的通讯协议,是有几大块工作要做的:
- 添加协议的 Encoder 和 Decoder;
- 修改 Pilot 下发 Virtual Host 等配置;
- 修改 Sidecar 如 Envoy,MOSN 去实现请求匹配。
后两者是大量重复的,就技术实现而言,需要修改的内容和现有的东西差不多,但是必须要再改出一份新的来。因为我们协议比较多,由此带来的改动量非常大。根据我们之前的实践,以这样的方式加一个新的通讯协议可能需要几天的工作量,而且每次改动都重复大量代码。
在这里我们最后给出了一个名为 x-protocol 的通用解决方案。根据我们最新的验证情况,如果我们要添加一个新的通讯协议,大概就是一两百行代码,一两个小时就能完成。
平滑迁移传统架构
所谓传统架构指的是传统的 SOA 架构,如基于 Dubbo 的很多现有应用,我们希望它们能够在 Service Mesh 中直接跑起来,而不必一定要先进行微服务改造。
SOA 框架当中,通常是以接口为单位来做服务注册,也就是一个应用里面部署多个接口的,在运行时是一个进程里面有多个接口(或者说多个服务)。实际上是以接口为粒度,服务注册和服务发现,包括服务的调用都是以接口为粒度。
但是有个问题,部署到 Istio 中后,Istio 做服务注册是以服务为粒度来做服务注册,这个时候不管是注册模型,还是按接口调用的方式都不一致,就是说通过 Interface 调用是调不通的。
怎么解决这个问题?最正统的做法是,是先进行 微服务改造:把原有的 SOA 的架构改成微服务的架构,把现有应用拆分为多个微服务应用,每个应用里面一个服务(或者说接口),这样应用和服务的关系就会变成一对一,服务注册模型就可以匹配。
但是在执行时会有难处,因为微服务改造是一个比较耗时间的过程。我们遇到的实际的需求是:能不能先不做微服务改造,而先上 Service Mesh ?因为 Service Mesh 的功能非常有吸引力,如流量控制,安全加密。那能不能先把应用搬迁到 Service Mesh 上来,先让应用跑起来,后面再慢慢的来做微服务改造。
这就是我们实际遇到的场景,我们需要找到方案来解决问题:注册模型不匹配,原有用接口调用的代码调不通。
我们设计了一个名为 DNS 通用选址方案 的解决方案,用来支持 Dubbo 等 SOA 框架,容许通过接口名来调用服务。我们会在 DNS 中增加记录,我们会在 DNS 中把这个三个接口指向当前服务的 Cluster IP。k8s 的 Cluster IP 通常是一个非常固定的一个 IP,每个服务在 k8s 部署时都会分配。在增加完 DNS 记录之后,再通过 Interface 的方式去调用,中间在我们的 Service Mesh 里面,我们会基于 Cluster IP 信息完成实际的寻址,并跑通 Istio 的所有功能,和用服务名调用等同。
适配异构体系
异构体系指的是,当我们进行 Service Mesh 落地时,会存在新旧两条体系,比如说新的应用是基于 Service Mesh 开发的,而旧的应用是基于传统框架比如说 Dubbo 或者是 Spring Cloud。
当我们做应用迁移的时候,考虑到原来的存量应用会有很多的,比如上千个应用,这些应用肯定不可能说一个晚上全部切过去。中间必然会有一个过渡阶段,在这个过渡阶段新旧体系中的应用应该怎么通讯,如何才能做到最理想。
涉及到流量劫持的方案
Service Mesh 有一个很重要的特性,就是无侵入,而无侵入通常是通过流量劫持来实现的。通过劫持流量,在客户端服务器端无感知的情况下,可以将 Service Mesh 的功能插进去。
但 Istio 的流量劫持方案做的还不够好,目前 Istio 只给了一个方案就是 iptables。这个方案有比较多的问题,所以我们有几个思路:
- 优化 iptables
- 调研 IPVS 方案。我能告诉大家的是,到目前为止,蚂蚁金服内部的机器上,iptables 不仅仅是禁用,而是整个 iptables 模块都被卸载。原因是性能、安全、维护等大家周知的原因,总之我们蚂蚁金服内部是没有这个模块的。为了解决这个问题,我们现在正在调研 IPVS 的方案,准备用 IPVS 来替换 iptables。这块工作正在进行,目前方案已经验证,但是还有一些细节没有完善,后面有更多的消息也给大家继续介绍。
- 轻量级客户端的实践。另外还有一个实践是考虑不做流量劫持。比如说,最典型的 RPC 方案,因为 RPC 通常来说总是会有一个客户端的。在上 Service Mesh 之后,可以将原来的客户端的一些功能如服务发现、负载均衡、限流等精简,形成一个新的轻量级客户端,但此时终究还是有一个客户端在的。
但上面这三个都不是今天的重点,今天的重点是下面这个 Cilium + eBPF 的思路,这是我们目前最密切关注的一个方案。
Cilium 的这个思路是我们非常赞赏的,通过这样的方式减少服务和 Sidecar 之间的性能损失,可以解决 Service Mesh 中至关重要的一个问题:性能与架构的取舍。
熟悉 Service Mesh 技术的同学,应该多少都有这样的认知: Service Mesh 是一门中庸的艺术。
在性能与架构之间, Service Mesh 选择牺牲性能来换取架构。
- 在传统的侵入式框架中,客户端业务代码和框架代码之间是通过函数来进行调用的,速度非常快完全可以忽略。
-
而 Service Mesh 是强行把框架和类库剥离出来,将上述的方法调用变成一个远程调用,以牺牲了一次远程调用的开销为代价来换取整个架构的优化空间。
这是 Service Mesh 技术和传统侵入式框架的一个本质差异,也是 Service Mesh 技术和传统侵入式框架所有差异的源头。
服务间通讯范围的探索
Service Mesh 起初关注的是东西向通讯,即系统内部各个服务之间的通讯,而这通常都是同步的,走 REST 或者 RPC 协议。
在 Service Mesh 的实践过程中,我们发现,Service Mesh 可以提供的功能:
- 请求转发:如服务发现,负载均衡等;
- 路由能力:如强大的 Content Based Routing 和 Version Based Routing ;
- 服务治理:基于路由能力而来的灰度发布,蓝绿部署,版本管理和控制;
- 纠错能力:限流,熔断,重试,测试目的的错误注入;
- 安全类:身份,认证,授权,鉴权,加密等。
第一个探索的方向是 API Gateway,和东西向通讯直接对应的南北向通讯。
第二个探索的方向是 Serverless 和 Knative。
Cloud Native 云原生
云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。
Service Mesh 的归宿
Service Mesh 的归宿,或者说最终的形态,是下沉到基础设施!
从 Service Mesh 的发展看,从简单的 Proxy,到功能完善的 Sidecar(如 Linkerd 和 Envoy),再到以 Istio 为代表的第二代 Service Mesh,演进的方式如上图:
第一步:从应用剥离
通过将原有的方法调用改为远程调用,将类库的功能套上 Proxy 的壳子,Service Mesh 成功的将服务间通讯从程序中剥离出来,从此服务间通讯不再是应用程序的一部分。
第二步:下沉为抽象层
这些剥离出来的服务间通讯的能力,在剥离之后,开始下沉,在应用程序下形成一个单独的抽象层,成为 服务间通讯专用基础设施层。此时,这些能力以一个完成的形态出现,不再存在单独的类库或者框架形式。
第三步:融入基础设施
继续下沉,和底层基础设施密切联系,进而融为一体,成为平台系统的一部分,典型就是和 kubernetes 结合。
Istio 在这方面做了一个非常大的创新,Istio 的创新,不仅仅在于增加控制平面,也在于和 kubernetes 的结合。
Service Mesh 和 Spring Cloud / Dubbo 的本质差异,不仅仅在于将服务间通讯从应用程序中剥离出来,更在于一路下沉到基础设施层并充分利用底层基础设施的能力。