Service Mesh 已经在云原生界火了很多年,大家的探索热情依然不减。而最近一段时间 Proxyless Service Mesh 也开始进入大家的视野,比如:“Istio 官宣支持 gRPC Proxyless Service Mesh”,“Dubbo 3.0 引入 Proxyless Service Mesh 架构”。
那么,什么是 Proxyless Service Mesh?它和原来的 Proxy Service Mesh 有什么区别和优缺点?落地场景又有哪些呢?本文将结合 Proxyless Service Mesh 在百度的落地实践,带你一探究竟。
来看下 Proxy Service Mesh,也就是最常见的 Service Mesh 架构,一般如下所示:
而 Proxyless Service Mesh 则是如下的架构:
如果简单对比上述架构,不难得出 Proxyless 和 Proxy 模式的优缺点:
以上仅是一些直观的分析,但当真正落地 Proxyless Service Mesh 的时候,会发现情况并不是我们想的那么简单。
Proxyless 第一阶段百度从 2018 年开始引入 Service Mesh,一开始是 Proxy 模式。到了 2020 年,我们在落地一些业务线的时候,发现 Proxy 模式很难在整个业务线全面铺开:
因此我们开始引入如下的 Proxyless Service Mesh 模式:
这种方式的好处是:
但是,第一阶段方案,存在一些明显的问题:
因此,到了 2021 年,我们设计并实现了一套 Proxyless/Proxy 统一架构的 Service Mesh:
无论是哪种模式,Envoy 都负责转换配置,bRPC 负责配置执行,因此,所有的代码都可以在 Proxy 和 Proxyless 模式下复用。
从业务场景上:
服务治理能力提升:
这个架构彻底解决了 Envoy 这个历史包袱,让 Service Mesh 轻装上阵。
让我们回过头看一下前面说的 Proxyless 模式的两个缺点是怎么被解决的:
似乎这两个缺点在我们的场景里都没有了:)
Proxyless 模式的真正优势是性能吗?一开始我们都是这么认为的,但是随着我们落地 Proxyless 模式的过程,我们才逐渐发现 Proxyless 模式的更多优势。让我们来看看一些场景吧:
比如我们要在 Service Mesh 中实现一致性哈希负载均衡,原来没有 Service Mesh 的时候,用户通过 RPC 框架提供的一个 set_request_code() 方法来设置请求级别哈希码,RPC 框架可以确保同一个 request_code 的请求被调度到同一个后端实例。
在 Service Mesh 中如何实现这个需求呢?如果是 Proxy 模式,负载均衡是在 Sidecar 做的,Sidecar 需要获取到这个 request_code。总不能让用户改代码吧?那么只能修改 RPC 框架,让框架把 request_code 传给 Sidecar。如图所示:
怎么传给 Sidecar 呢?得在协议里的某个字段中传过去,比如 HTTP 协议可以在 header 中传过去,那么其它协议呢?每个协议都得找一个地方来传这个字段,每个协议都得实现一遍这个逻辑。
所以在 Proxy 模式下,传参这个事的实现成本 =(RPC 框架发送参数 +Sidecar 接收参数)* 协议数量。
那么在 Proxyless 模式下呢?这个参数本来 RPC 框架就能拿到,所以传参的实现成本 =0。
除了一致性哈希,类似的场景还有很多,比如请求级别超时控制、请求级别路由参数等,在这些场景下,Proxyless 模式完胜。
比如用户想实现一个自定义重试策略,RPC 框架在访问一次后端服务之后,调用用户代码来判断是否需要重试。一个典型的流程如下所求:
现在业务想要接入 Service Mesh,如果用 Proxy 模式,该如何实现用户自定义重试策略呢?考虑以下方案:
在 Proxy 模式下,无论采用哪种方案,问题都很大。在 Proxyless 模式下呢?由于该功能 RPC 框架本来就支持,成本 =0。除了自定义重试策略,类似的场景还很多,比如自定义负载均衡策略、自定义 NamingService 等,在这些场景下,Proxyless 模式完胜。
先解释一下什么叫动态多分片。多分片在搜索、推荐类业务是一个典型场景,也就是说一个服务的一个实例并不加载全量的数据,只加载一个分片的数据。客户端调用此类服务时,需要将请求同时发送给不同分片的后端服务实例,获取到每个分片的结果再进行汇总处理。动态多分片是指多个分片的服务组成一个分组,每次请求可以动态地选择一组分片。之所以需要多个分组,一种场景是因为数据发生扩容时,分片数会发生变化,为了保证流量平滑迁移,会同时存在不同分片数量的分组;另一种场景是由于业务需要,不同的分组加载了不同业务属性的数据,需要根据请求来动态确定调用哪个分组的服务。如下图所示,分组 A 有 2 个分片,分组 B 有 3 个分片,而 k1、k2 则代表了不同的业务属性。
在动态多分片场景下,业务代码和 RPC 框架的一个简化的交互流程如下:
之所以需要分两个阶段调用 RPC 框架,是因为业务需要根据最终选定的分组和分片数量来拼装业务的请求。
现在业务想要接入 Service Mesh,让我们看看在 Proxy 模式下支持动态多分片的方案:
现在,让我们看看 Proxyless 模式下支持动态多分片的方案:
虽然这个方案也有一定复杂性,但实现成本也不是很高,比 Proxy 模式的实现成本低多了。所以,在动态多分片场景,Proxyless 模式完胜。
服务可观测是 Service Mesh 的重点场景,我们来看一下这个场景下的一些需求吧:
为什么会出现这些情况呢?实现服务可观测的最好方法就是深入服务内部,对于 Sidecar 来说,服务就是个黑盒,当然不好实现了。
所以,对于服务可观测场景,Proxyless 模式完胜。
诚然,Sidecar 是 Service Mesh 的一大卖点,比如方便支持多语言、和应用解耦,这可以让用户更方便地接入 Service Mesh。但是我们更应该关注 Service Mesh 给用户带来了什么价值,如果用户用了 Service Mesh 没有收益,即使接入成本为 0,用户也会不接入的。
从用户角度看,Service Mesh 带来的是以下价值:
因此我认为,Service Mesh 就是能让服务间通信更可靠、更快、更透明、更灵活、更安全、更便于管理的基础设施。
至于这个基础设施是 Proxyless 还是 Proxy 模式,其实不是很重要。从实际业务场景出发,哪种模式更容易满足业务需求,就用哪种方案。两种模式各有自己的优势场景,结合起来可以提供更好的服务。
有一种观点认为,Proxyless 模式不就是 RPC 框架 + 配置中心吗?Proxy 模式不就是一个 7 层代理 + 配置中心吗?这玩意很多年前就有了。
但是,Service Mesh 的控制平面,不能简单看作配置中心。配置中心仅仅是简单地将配置文件或者配置项进行下发,并不感知配置的实际含义。
而以 Istio 为代表的控制平面,实际上是定义了 Service Mesh 的能力标准,比如各种路由、负载均衡策略等。如果只是做了一个简单的 RPC 框架,暴露了几个超时参数到配置中心来控制,那不叫 Service Mesh,因为没有实现 Service Mesh 的标准能力。即使 RPC 框架把 Service Mesh 的标准能力都实现了,但是没有统一的协议和配置格式,不同的框架的配置方式五花八门,通信协议互相割裂,那也不能算 Service Mesh。
所以说,Service Mesh 是一组服务间通信的能力标准,实现了这些标准,就可以称之为 Service Mesh。
从目前趋势看来,Istio 仍然会作为 Service Mesh 控制平面的首选。尽管 Istio 的 CRD 对用户也不是很友好,但是 Istio 定义的 Service Mesh 标准体系目前仍是最完整的,也获得了最广泛的数据平面的支持。
而数据平面,则可能出现百花齐放的场景,毕竟业务场景非常多样,有的看重灵活性,有的看重性能,有的看重安全,那么就能催生不同的数据平面实现方案。Envoy 仍然会作为 Proxy 模式的主流选择,但各 RPC 框架也不甘于只做瘦客户端,将会继续发展自己的 Proxyless 方案,让 Service Mesh 能落地到更多的业务场景之中。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8