service mesh的定制化与性能考量

SOURCE: The Enterprise Path to Service Mesh CHAPTER 4 Customization and Integration

说到服务网格的"可扩展性",这可不是一个空泛的概念,它体现在很多具体的地方。比如,

  • 可以根据自己的需求,换掉默认的Sidecar代理。
  • 遥测数据要去哪里?用什么方式授权?这些都可以通过适配器来灵活配置
  • 身份认证,不一定非得用网格自带的,可以接入你自己的证书颁发机构。
  • 数据平面过滤器,这是实现精细化流量控制的关键

当然,不同服务网格的设计理念不同。像Linkerd、Kuma这些,追求的是轻量和易用,开箱即用是它们的强项,但想做深度定制就没那么容易了。而像Istio这样的,则把可扩展性放在了非常重要的位置,提供了丰富的接口让你去玩转。

现在我们聚焦到数据平面,特别是它的核心组件------Sidecar代理。你可以把它想象成每个服务旁边的一个智能小助手,默默地处理着所有进出这个服务的网络流量。它负责转发请求、做负载均衡、在服务挂掉时进行熔断,甚至还能做限流。最关键的是,这一切对你的应用代码来说都是透明的,你不需要改动一行代码就能享受到这些好处。所以,Sidecar代理的可扩展性直接决定了整个服务网格的灵活性。怎么扩展它呢?方法很多,比如你想记录更详细的访问日志,或者想用特定的指标插件,或者需要一套自定义的认证授权流程,甚至想用更高级的限流算法,都可以通过扩展Sidecar代理来实现。

我们今天重点看看如何通过自定义流量过滤器来增强它的能力。那么,具体有哪些技术可以用来给Sidecar代理注入新的智能呢?主要有两大方式:Lua 和 WebAssembly

  • Lua是一种非常轻量级的脚本语言,它嵌入性好,写起来也快,配合LuaJIT还能跑得飞快。如果需要快速验证一个想法,或者做一些简单的逻辑处理,Lua是个不错的选择。
  • WebAssembly,简称WASM,一个开放标准,目标是让各种编程语言编写的代码能在不同的环境中高效运行,有点像浏览器里的JavaScript。通过WASI,WASM模块能跟宿主环境安全地交互。这意味着你可以用你最熟悉的语言,比如Rust、C++、Go,来编写高性能的扩展,然后打包成WASM模块,让数据平面去执行。对于需要高性能和复杂逻辑的场景,WASM是更强大的武器。

我们来看一个具体的例子:NGINX 加 Lua。NGINX支持动态模块,可以根据需要在运行时加载或卸载这些模块,非常灵活。而ngx_lua模块就是NGINX的魔法棒,它允许你在NGINX里直接嵌入Lua脚本。Lua本身就很轻量,而且支持多种编程范式。通过ngx_lua,你可以把很多原本需要后端服务处理的逻辑,比如请求的初步判断、内容的修改、路由的决策,都交给NGINX这个智能数据平面来处理。这大大减轻了后端服务的压力,也让整个系统更加灵活高效。你可以链式调用多个Lua脚本,在NGINX处理请求的不同阶段执行不同的操作。

接下来看看Envoy和WebAssembly的组合。Envoy作为Istio默认的Sidecar代理,其性能和可扩展性至关重要。

  • WASM的引入,为Envoy带来了强大的扩展能力。
  • WASI定义了WASM如何与外部世界沟通。Envoy通过暴露一套特定的API,也就是所谓的WASM ABI,让WASM模块能够像原生代码一样,作为过滤器来处理流量。
  • Google的V8引擎,那个在Chrome浏览器里跑JavaScript的超级引擎,也被整合到了Envoy中,专门用来运行WASM。

这意味着你可以用C++、Rust这些高性能语言,编写出接近原生速度的过滤器,然后打包成WASM模块,让Envoy在运行时加载并执行。这不仅性能高,而且因为WASM有沙箱机制,安全性也更有保障。这对于构建复杂的、需要高性能的网络功能来说,简直是如虎添翼。

既然有了Lua和WASM两种选择,那到底该怎么选呢?对比如下

Lua WASM
运行时大小 极小 (约 4KB) 相对较大 (包含编译器和优化器)
性能 JIT 加速,接近原生速度 编译为机器码,接近原生速度,但有开销
复杂度 高 (编译器、优化器、WASI)
特性 Lua (LuaJIT) WebAssembly (WASM)
内存模型 垃圾回收 (GC) 扁平内存,无 GC
语言支持 Lua 多种语言 (Rust, C++, Go, etc.)
适用场景 轻量级、快速原型、简单逻辑 高性能、复杂逻辑、高性能过滤器
  • 从运行时大小看,LuaJIT极其小巧,只有几KB,而WASM因为包含了编译器和优化器,体积会大一些。
  • 性能方面,两者都通过JIT或编译优化,能达到接近原生的速度,但WASM由于需要额外的编译步骤,可能会有百分之十到百分之二十的开销。
  • 复杂度上,Lua简单直接,而WASM涉及编译器、优化器、WASI接口,相对复杂。
  • 内存管理方面,Lua依赖垃圾回收,而WASM使用扁平内存模型,没有内置GC,需要自己管理。
  • 语言支持上,Lua只支持Lua,而WASM支持几十种语言。

所以没有绝对的好坏,只有适用场景。如果你追求极致轻量和快速迭代,Lua是首选;如果需要高性能、复杂逻辑和跨语言能力,WASM更适合。

在Envoy这边,如果你想添加自定义过滤器,主要有两种途径。

  • 原生集成,就是把你的代码直接写进Envoy的C++源码里,然后重新编译一个Envoy版本。这种方式性能最高,因为你的代码直接跑在Envoy的执行引擎上,没有中间层。但缺点也很明显,你需要维护自己的Envoy版本,每次Envoy官方更新,你都得跟着改,升级维护成本很高。
  • WASM集成。你可以用C++、Rust、Go等语言写好你的过滤器逻辑,编译成WASM模块。Envoy可以在运行时动态加载这个模块,甚至在不停止服务的情况下更新它。这种方式的好处是显而易见的:热更新、易于维护、无需频繁编译Envoy。当然,代价是性能上会有一些损耗,大概在百分之十到百分之二十之间。

所以再次强调,选择哪种方式,取决于你对性能和维护性的权衡。WASM之所以吸引人,很大程度上是因为它的动态可重载特性。这意味着什么?意味着你可以在不重启任何服务的情况下,实时更新你的网络逻辑!这对于生产环境来说简直是福音。你可以随时调整策略,修复bug,甚至上线新的功能。更重要的是,这种动态加载的能力,使得我们可以将一些原本属于业务逻辑的东西,比如根据用户的订阅计划来限制请求速率,或者根据用户类型将请求路由到不同的服务集群,或者进行复杂的多变量测试,这些都可以通过编写WASM模块,动态地注入到数据平面中去执行。这极大地扩展了服务网格的能力边界,让它不再仅仅是网络层的工具,而是可以参与到业务逻辑的决策中。

我们再回到Sidecar代理的选择。虽然Envoy是Istio的默认选择,但并不是唯一的。为什么会有其他选择?因为不同的组织有不同的需求和背景。

  • 开发者可能更关心代理是否支持他们常用的协议,比如gRPC、NATS、Kafka,以及是否方便集成到现有的云原生生态里,比如OpenTelemetry、Prometheus。
  • 运维人员则更关注性能、稳定性、可观测性、配置管理的便捷性以及与现有基础设施的兼容性。

一个好的Sidecar代理,应该具备高性能、低延迟、高可扩展性、小内存占用、全栈可观测性、程序化配置以及良好的文档支持。所以,选择哪个代理,是一个综合考量的结果。这体现了服务网格设计中的一个重要原则:可替换性。为了让不同的代理能够更好地协同工作,业界也在推动标准化。

这里提到了UDPA,通用数据平面API。它的目标是成为类似SDN中OpenFlow那样的标准,让不同厂商的数据平面代理能够用统一的API进行配置和管理。Envoy的xDS API就是这个方向的一个重要实践。它定义了一整套接口,用于服务发现、负载均衡、路由规则、监听器配置、密钥分发、健康检查等等。有了稳定且标准化的API,就意味着我们可以更容易地将不同的控制平面和数据平面组合起来,或者将一个代理替换为另一个。这大大降低了集成的复杂度和成本。事实上,早在Istio早期,就已经有项目尝试将Istio的控制平面与Linkerd或NGINX的代理结合起来,这就是可替换性的一个体现。

具体来说,在Istio生态里,除了Envoy之外,还有哪些代理可以作为Sidecar呢?

  • 首先是NGINX,通过NGINX Service Mesh项目,它使用了NGINX Plus作为Sidecar代理,带来了缓存、Web应用防火墙WAF等额外功能。
  • Citrix Service Mesh提供的CPX也是一个不错的选择。
  • 来自阿里巴巴的MOSN,它也是一个可以部署在Istio数据平面的代理,特别适合那些使用Go语言栈,并且需要高度定制化或者支持多种私有协议的场景。

所以,选择哪个代理,很大程度上取决于你的团队对哪个代理更熟悉,你的业务需要哪些特定功能,以及你现有的技术栈和基础设施。这种选择的自由度,正是服务网格可扩展性的一个重要方面。

这张图展示了Istio和nginMesh的一个典型架构。可以看到,Istio的控制平面组件,比如Pilot、Citadel、Mixer,它们负责生成配置和策略。而在数据平面,我们用的是NGINX,而不是Envoy。关键在于,有一个translator agent,它扮演了桥梁的角色,接收来自Istio控制平面的配置,然后翻译成NGINX能够理解和使用的配置格式。这样,NGINX就能按照Istio的指令去处理流量了。这个例子清晰地说明了,即使使用不同的Sidecar代理,只要它们能够理解和执行来自控制平面的配置,就可以无缝集成到Istio生态中。

除了Sidecar代理,服务网格还需要处理大量的遥测数据,比如追踪、日志、指标。这些数据通常需要被收集起来,然后发送到外部的监控系统,比如Prometheus、Jaeger、ELK等。这就引出了适配器的概念。

  • 有些架构中,数据平面代理直接把原始的遥测数据发送给控制平面,然后由控制平面的适配器来负责收集、转换、并最终推送到外部系统。这种情况下,适配器就位于控制平面。

  • 而另一种架构,是将遥测数据的生成和初步处理放在数据平面,再通过适配器发送出去。

无论哪种方式,适配器的存在都使得服务网格能够灵活地对接不同的监控工具,满足多样化的可观测性需求。我们来看一下遥测数据处理架构的演进。

早期的Istio版本,比如1.5之前是数据平面代理把原始的遥测数据直接发送给控制平面的Mixer组件,由Mixer来处理和转发。但是,随着服务规模的增长,这种模式下Mixer成为了性能瓶颈,因为它需要处理海量的遥测数据。

Istio从1.5版本开始,引入了Mixerless架构遥测数据的生成和处理更多地放在了数据平面。数据平面代理内部集成了WASM模块,这些模块可以作为智能网络过滤器,直接生成和处理遥测数据,然后通过数据平面适配器发送给外部的监控系统。这种架构的转变,主要是为了性能优化,把计算压力从控制平面转移到了数据平面,从而显著提升了遥测数据的处理效率。

我们反复提到了性能,这确实是一个绕不开的话题。服务网格提供了这么多强大的功能,比如精细化的流量控制、统一的可观测性、安全策略等等,但这些功能都不是免费的午餐。它们都会在一定程度上增加网络延迟,降低吞吐量,也就是引入性能开销。所以,一个关键的问题是:我们如何在享受服务网格带来的便利和能力的同时,尽可能地减少这种性能损耗?我们需要找到一个平衡点。这就需要我们去量化评估服务网格的性能影响,比如具体的延迟增加了多少?吞吐量下降了多少?然后,通过各种优化手段,比如选择合适的代理、优化配置、利用WASM等高效扩展技术,来尽可能地降低这个开销。这也是为什么业界正在努力制定Service Mesh Performance SMP规范,希望能给大家提供一个统一的基准和评估方法,帮助大家更好地理解和优化服务网格的性能。

服务网格性能的重要性 (SMP):

  • 性能开销,服务网格会引入额外的性能开销,影响应用性能。
  • 性能平衡,需要在服务网格提供的功能和性能开销之间找到平衡点。
  • 性能评估,量化评估服务网格的性能开销,例如延迟、吞吐量等。
  • 性能优化,通过性能优化手段,降低服务网格的性能开销,提升用户体验。

服务网格本身还在不断发展演进,未来它将在提升开发者体验、增强可观测性、实现更智能的流量管理以及促进跨网格互操作性等方面发挥更大的作用。展望未来,服务网格会朝着哪些方向发展呢?我们可以期待一些更酷炫的功能。

  • 开发者体验方面,可能会有更强大的分布式调试工具,甚至可以直接通过配置来实现用户或租户的隔离,而无需在应用代码里写一堆逻辑。
  • 可观测性方面,不仅仅是分布式追踪,而是更全面的全栈APM,能深入到业务逻辑层面。
  • 流量管理方面,可能会有更智能的自动金丝雀发布、API文档自动生成和发现等功能。
  • 应用场景也会更加广泛,比如更好地支持多租户、多集群、跨网格的场景,更紧密地集成性能管理工具,提供更精细的故障注入和熔断降级策略,以及支持更灵活的证书管理。

总而言之,服务网格的未来充满想象空间,它将继续深刻地改变我们构建和管理现代应用的方式。

相关推荐
幽络源小助理4 分钟前
SpringBoot框架开发网络安全科普系统开发实现
java·spring boot·后端·spring·web安全
俺不是西瓜太郎´•ﻌ•`35 分钟前
二维差分数组(JAVA)蓝桥杯
java·开发语言·蓝桥杯
cainiao08060540 分钟前
Java大数据可视化在城市空气质量监测与污染溯源中的应用:GIS与实时数据流的技术融合
java·开发语言·信息可视化
2685725942 分钟前
Java 23种设计模式 - 行为型模式11种
java·开发语言·设计模式
编程点滴路1 小时前
LinkedList源码解析
java·开发语言
花花鱼1 小时前
spring boot lunar 农历的三方库引用,获取日期的农历值
java·前端·spring boot
一切皆有迹可循1 小时前
Spring Boot 基于 Cookie 实现单点登录:原理、实践与优化详解
java·spring boot·后端
喂我花生(๑•̀ㅂ•́)و✧1 小时前
JAVA中ArrayList的解析
java·开发语言·算法
forestsea2 小时前
Java 基础面试题
java·开发语言
从int开始2 小时前
在过滤器中获取body中的json数据并且使得后续的controller层也能获取使用
java·filter