ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Istio服务网格Wasm过滤器

2021-12-31 21:07:00  阅读:359  来源: 互联网

标签:插件 虚拟机 Istio 网格 Wasm vm wasm config


背景信息

2019年前,Envoy是以静态编译的二进制文件方式来运行的,这意味着其所有扩展等均需要在构建阶段完成编译。因此其他工程(例如Istio)只能发布他们自己维护的自定义Envoy版本,一旦有更新或者Bug修复就不得不构建一个新的二进制版本、发布、重新部署到生产环境中。

上述问题虽然没有一个完美的解决方案,但是部分场景下可以通过C++的动态可加载性来实现,即在一种标准的二进制应用接口(ABI, Application Binary Interface)下编写、交付WebAssembly(WASM)模块,

WASM 本身是源自前端的技术,是为了解决日益复杂的前端 Web 应用以及有限的 JS 脚本解释性能而诞生的技术,通过该技术可以使用非 JavaScript 编程语言编写代码并且能在浏览器上运行。

随着WASM的发展,现在WASM不仅仅可以用于浏览器, 它已经被定义为一个可移植、体积小、加载快并且兼容 Web 的全新格式为一种可移植的二进制格式。

本文讨论的WASM用于以接近本机的速度在一个内存安全(memory-safe)的沙箱中执行多种语言编写的代码,在沙箱内有明确的资源限制和API来与内嵌的主机环境(例如Envoy)通信。

优点

  • 敏捷性:WASM可以动态加载到正在运行的Envoy进程中,而无需停止或重新编译

  • 可维护性:不必更改Envoy自身基础代码库即可扩展其功能

  • 多样性:可以将流行的编程语言(例如C/C++和TinyGo)编译为WASM,因此开发人员可以选择实现过滤器的编程语言

  • 可靠性和隔离性:过滤器会被部署到VM沙箱中,因此与Envoy进程本身是隔离的;即使当WasmFilter出现问题导致崩溃时,它也不会影响Envoy进程

  • 安全性:过滤器通过预定义API与Envoy代理进行通信,因此它们可以访问并只能修改有限数量的连接或请求属性

缺点

  • 内存消耗:WASM虚拟机的使用将锁定使用一部分内存

  • 性能损耗:报文数据在沙箱内外进行拷贝与转码

Proxy-wasm

Proxy-Wasm是WASM扩展模块与L4/L7代理之间的二进制应用接口(ABI)规范与标准,其明确定义了主机环境与Wasm虚拟机之间、函数调用、内存管理等通信接口。

当前Proxy-wasm提供了AssemblyScript SDKC++ SDKGo (TinyGo) SDKRust SDKZig SDK SDK,支持EnvoyIstio Proxy (Istio基于Envoy的扩展)、MOSN等代理主机环境中运行。

整体架构

在每个Envoy工作线程上(事件驱动),内置的WASM运行时将创建一个Wasm虚拟机,通过Proxy-Wasm规范来校验、实例化WASM模块(本地磁盘文件或控制面板XDS推送的方式)。

WASM模块通过扩展接口进行调用时,Proxy-Wasm通过一个垫片进行转码、翻译在Wasm虚拟机上运行。

: Envoy 使用单进程 - 多线程的架构模型。一个 master 线程管理各种琐碎的任务,而一些 worker 线程则负责执行监听、过滤和转发。当监听器接收到一个连接请求时,该连接将其生命周期绑定到一个单独的 worker 线程。

运行时

Envoy内嵌了基于LLVM的WAVMV8两个C/C++ Wasm 运行时,在WASM模块配置时可进行选择。

Proxy-wasm-go-sdk

Go (TinyGo) SDK是一种基于Tinygo语言的Proxy-Wasm实现。

本文基于该项目标签v0.14.0进行阐述。

TinyGo

TinyGo是一个Go编译器,旨在用于微控制器,WebAssembly(WASM)和命令行工具等小型场景。它重用了Go语言工具和LLVM一起使用的库,以提供编译用Go编程语言编写的程序的另一种方法。

官方的Go编译器无法产生Proxy-Wasm所兼容的二进制文件,并且TinyGo相比较于Go另一个最主要的的差异是二进制尺寸。

根据TinyGo官方描述,最简单的"Hello world"程序,在strip命令加持下(移除所有符号标志与调试信息),Go编译器产生837kb大小的二进制,而TinyGo则为10kb,接近于1%的尺寸缩减效率。

使用TinyGo也存在一些限制和约束:

  • 部分Go库不可用(可导入,但运行时异常)

  • 部分系统调用不可用,如crypto/rand包

  • 不支持反射

  • 部分语言特性不支持,如recovergoroutine

虽然不支持创建协程,但是Proxy-Wasm定义了OnTick函数,类似于定时器触发函数,可用于处理一些异步调用任务。

术语

  • 虚拟机(Wasm VM): Envoy中Wasm虚拟机是在每个工作线程中被创建的,并且相互之间隔离

  • 插件(Plugin): Envoy中的过滤器类型(Http Filter, Network(Tcp) Filter, 和 Wasm Service)。每个扩展模块都可以进行配置,单个虚拟机中相同的Wasm模块经过不同的配置便形成了多个插件

    • Http Filter: 在工作线程虚拟机中处理HTTP协议,可操作Http请求的头、正文、尾部内容

    • Network Filter: 在工作线程虚拟机中处理Tcp协议,可操作Tcp数据帧,连接信息

    • Wasm Service: 运行于Envoy主线程的单例虚拟中,可用于并发处理一些额外的任务如集成metrics,日志等

Envoy配置

Envoy中Wasm过滤器配置如下:

 vm_config:
   vm_id: "foo"
   runtime: "envoy.wasm.runtime.v8"
   configuration:
     "@type": type.googleapis.com/google.protobuf.StringValue
     value: '{"my-vm-env": "dev"}'
   code:
     local:
       filename: "example.wasm"
 configuration:
   "@type": type.googleapis.com/google.protobuf.StringValue
   value: '{"my-plugin-config": "bar"}'
字段描述
vm_config配置Wasm虚拟机
vm_config.vm_id虚拟机的id,可用于配置跨虚拟机通信
vm_config.runtimeWasm 运行时类型,如: envoy.wasm.runtime.v8.
vm_config.configuration虚拟机配置,可在运行时动态读取以便配置不同的虚拟机上下文
vm_config.codeWasm二进制文件位置
configuration插件配置,可在运行时动态读取以便配置不同的插件上下文

字段vm_config所有属性为相同值时,则多个插件共享一个Wasm虚拟机,这在资源使用及启动时延上会有一定影响。

Http Filter

处理HTTP事件,即http协议流量,插件引用为envoy.filter.http.wasm,示例如下:

 http_filters:
 - name: envoy.filters.http.wasm
   typed_config:
     "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
     config:
       vm_config: { ... }
       # ... plugin config follows
 - name: envoy.filters.http.router

Network Filter

处理TCP事件,即所有tcp流量(包括http流量),插件引用为envoy.filter.network.wasm,示例如下:

 filter_chains:
 - filters:
     - name: envoy.filters.network.wasm
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm
         config:
           vm_config: { ... }
           # ... plugin config follows
     - name: envoy.tcp_proxy

: Http FilterNetwork Filter之间的差别仅仅是通过不同的配置,分别作用于TCP流或HTTP流。

Wasm Service

工作于主线程,配置在bootstrap_extensions中,插件引用为envoy.bootstrap.wasm,示例如下:

 bootstrap_extensions:
 - name: envoy.bootstrap.wasm
   typed_config:
     "@type": type.googleapis.com/envoy.extensions.wasm.v3.WasmService
     singleton: true
     config:
       vm_config: { ... }
       # ... plugin config follows

其中singleton属性通常配置为true代表重用主线程的虚拟机,此时主线程不会阻塞工作线程中插件的运行。

Go SDK API

环境上下文(Contexts)

Go SDK 中接口的集合,一共有四种类型的上下文: VMContext, PluginContext, TcpContext and HttpContext。其关系表如下:

                     Wasm Virtual Machine
                       (.vm_config.code)
 ┌────────────────────────────────────────────────────────────────┐
 │  Your program (.vm_config.code)                TcpContext      │
 │          │                                  ╱ (Tcp stream)     │
 │          │ 1: 1                            ╱                   │
 │          │         1: N                   ╱ 1: N               │
 │      VMContext  ──────────  PluginContext                      │
 │                                (Plugin)   ╲ 1: N               │
 │                                            ╲                   │
 │                                             ╲  HttpContext     │
 │                                               (Http stream)    │
 └────────────────────────────────────────────────────────────────┘
  • VMContext: 对应于配置中的.vm_config.code, 每个虚拟机中仅有一个VMContext ,作为PluginContexts的父级可以创建任意多个PluginContexts

  • PluginContext: 对用于配置中的.configuration,负责具体插件的实例化,作为TcpContexHttpContext的父级可以创建多个TcpContexHttpContext

  • TcpContext: 处理Tcp数据流

  • HttpContext: 处理Http数据流

虚拟机上下文VMContext源码定义如下:

 // VMContext 相当于Wasm虚拟机的配置,是扩展网络代理的入口点。其生命周期与Wasm虚拟机相同
 type VMContext interface {
     // 当Wasm虚拟机创建时, OnVMStart 被调用,期间API GetVMConfiguration 可用来检索配置中的 vm_config.configuration 属性
     // 这个函数主要用于Wasm虚拟机级别的初始化
     OnVMStart(vmConfigurationSize int) OnVMStartStatus
 ​
     // 根据插件配置来创建 PluginContext
     NewPluginContext(contextID uint32) PluginContext
 }

插件上下文PluginContext源码定义如下:

 // PluginContext 相当于每个不同的插件配置(config.configuration)
 // 每个配置通常在一个监听器的 http/tcp 过滤器中创建,因此 PluginContext 相当于创建网络过滤器实例
 type PluginContext interface {
     // 在 OnVmStart调用发生之后, OnPluginStart 将被调用,期间API GetPluginConfiguration 可用来检索配置中的 config.configuration 属性
     OnPluginStart(pluginConfigurationSize int) OnPluginStartStatus
 ​
     // 插件在主机中结束运行时, onPluginDone 被调用
     // 返回 false 代表着其出于 pending 状态,还有一些遗留工作需要完成
     // 这种情况下,必须调用方法 PluginDone() 来告诉主机工作已完成可以清除上下文
     OnPluginDone() bool
 ​
     // 当插件调用API RegisterQueue 后,其他插件将数据入队列, 本插件的 OnQueueReady 被调用
     OnQueueReady(queueID uint32)
 ​
     // 当通过API SetTickPeriodMilliSeconds 设置了定时周期并且时间已到时, 本插件的 OnTick 被调用
     // 本方法可以用于流处理期间并行的处理其他任务
     OnTick()
 ​
     // 开发者必须实现下面两者中的出于实际流数据的扩展入口点
     //
     // NewTcpContext 用来创建 TcpContext, 返回 nil 代表本插件不适用于 TcpContext
     NewTcpContext(contextID uint32) TcpContext
     // NewHttpContext 用来创建  HttpContext, 返回 nil 代表本插件不适用于 HttpContext.
     NewHttpContext(contextID uint32) HttpContext
 }

HttpContextTcpContext不再具体展开,可通过context.go查看详情。

主机调用API

主机调用API是Proxy-Wasm提供一系列方法用于与网络插件交互,例如在HttpContext中可以调用GetHttpRequestHeaders API来获取Http请求头数据, LogInfo API可以用来在日志中添加打印信息。

所有可用的API可通过hostcall.go查看详情。

入口点

当Envoy创建Wasm虚拟机时,在他创建VMContext 前它将调用程序中main函数,因此必须在main函数中实现自定义的VMContext

Proxywasm 包中的SetVMContext正是创建VMContext的入口点,借助于Proxywasm 包提供的DefaultVMContextmain函数一般如下:

 func main() {
     proxywasm.SetVMContext(&vmContext{})
 }
 ​
 type vmContext struct {
     // 嵌入默认提供的虚拟机上下文,这样就不必实现VMContext接口中的所有方法
     types.DefaultVMContext
 }
 ​
 // 覆盖DefaultVMContext中的NewPluginContext方法
 func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
     return &pluginContext{}
 }
 ​
 type pluginContext struct {
     // 嵌入默认提供的插件上下文
     types.DefaultPluginContext
 }
 // 覆盖DefaultPluginContext中的NewTcpContext方法
 func (ctx *pluginContext) NewTcpContext(contextID uint32) types.TcpContext {
     return &networkContext{}
 }
 ​
 type networkContext struct {
     // 嵌入默认提供的Tcp上下文
     types.DefaultTcpContext
 }
 ​
 // 覆盖DefaultTcpContext的方法OnNewConnection
 func (ctx *networkContext) OnNewConnection() types.Action {
  ... ...   

跨虚拟机通信

上文中提到,在每个Envoy工作线程上内置的WASM运行时将创建一个Wasm虚拟机,某些特定场景下我们可能需要在当前虚拟机中与其他虚拟机通信,例如集成状态信息、缓存数据等。

当前提供了两种方案来实现跨虚拟机通信。

共享数据

共享数据(Shared Data)是一种基于键值对存储以跨虚拟机或跨线程共享数据的方案。

共享数据适用于场景如:

  • 通过一个全局的请求计数器来计数多个Wasm虚拟机中的请求

  • 多个Wasm虚拟机需要共享缓存数据

一份共享存储区通过vm_config.vm_id配置来创建,这意味着同一份wasm二进制文件(vm_config.code指定)不是必要条件。

 

如上图所示,两个虚拟机虽然使用hello.wasmbye.wasm两个二进制文件,由于其使用同一个vm_id (foo),但是他们却共享同一份数据存储区。

共享数据可用的API如下:

 // GetSharedData 用于检索指定的键
 // 返回的 "cas" 是方法 SetSharedData 中设置的用于保证线程安全更新的值
 func GetSharedData(key string) (value []byte, cas uint32, err error) 
 ​
 // SetSharedData 用于在共享存储区设置键值对
 // 若 CAS 值未匹配上当前值,则返回 ErrorStatusCasMismatch, 这意味着有其他Wasm虚拟机已经在这个键上设置了一个值,因此当前 CAS值递增更新,因此在变成逻辑中添加重试逻辑是非常有必要的
 // 设置 cas 为0时代表不进行CAS值比对,永远返回成功
 func SetSharedData(key string, data []byte, cas uint32) error

API相对简单,其使用了Compare-And-Swap方案来确保线程安全。

共享队列

共享队列(Shared Queue)是一种先进先出(FIFO, First-In-First-Out)的队列。

共享队列适用于场景如:

  • 并行的集成多个Wasm虚拟机中的metrics信息

  • 推送跨虚拟机的及成套信息给远程主机

一个共享队列通过配置中的vm_config.vm_id和一个队列名称(vm_id, name)来创建,通过这两个产生可产生一个队列ID(queue_id)用于出/入队列。

共享队列可用的API如下:

 // ResolveSharedQueue 通过 vm_id 与 queue name 来产生ququeID, 用于 Enqueue/DequeueSharedQueue方法
 func ResolveSharedQueue(vmID, queueName string) (ququeID uint32, err error)
 ​
 // 通过 queueID 入队列
 func EnqueueSharedQueue(queueID uint32, data []byte) error 
 ​
 // 通过 queueID 出队列
 func DequeueSharedQueue(queueID uint32) ([]byte, error) 
 ​
 // RegisterSharedQueue 用于在插件上下文注册一个共享队列
 // 注册意味着当有数据入队列时,当前插件上下文的 OnQueueReady 方法被调用
 func RegisterSharedQueue(name string) (ququeID uint32, err error)

通常情况下, RegisterSharedQueueDequeueSharedQueue 被"消费者"调用,ResolveSharedQueueEnqueueSharedQueue为"生产者"使用:

  • RegisterSharedQueue是"消费者"通过vm_idname来创建一个共享队列,通常在PluginContext调用

  • ResolveSharedQueue 是"生产者"解析出队列ID之后用来将数据入队列

因此两个方法都返回了队列ID。

环境上下文一节中提到,PluginContext中包含一个API OnQueueReady,这正是当有数据入队列时用于通知"消费者"的机制,当其他插件将数据入队列, 本插件的 OnQueueReady 被调用。

建议在单例的Wasm Service中(如Envoy的主线程)创建共享队列,否则当 OnQueueReady发生调用时,将阻塞当前工作线程Tcp/Http流处理。

 

如上图所示,主线程Wasm虚拟机(vm_id="foo", my-singleton.wasm)通过 RegisterQueue创建、注册了两个共享队列(分别命名为"http"与"tcp")。两个共享队列的"生产者"分别在 各自工作线程的Wasm虚拟机中实例化了HttpContextTcpContext用于处理Http与Tcp数据流。当他们往各自队列入队数据时,主线程中的PluginContext自动调用OnQueueReady方法用于获取队列数据。

样例测试

由于Wasm过滤器支持的功能较为丰富,本节仅进行简单的数据流打印测试,验证其在Tcp数据流中的作用。

部署样例服务

部署以下几个服务并且注入边车,验证服务正常运行:

  • 简单的sleep服务,用于模拟发送http请求

  • goserver服务端,用于响应http请求,容器内的人8081端口映射到Service的9091端口

  • dubbo协议下定时发生调用的生产者与消费者,此时使用ServiceEntry代替Service资源

 [root@linux ~]# kubectl -nwasm get po
 NAME                            READY   STATUS    RESTARTS   AGE
 goserver-7c5cc7cf6-lslcz        2/2     Running   2          4d2h
 sleep-558cdddbdb-g4wwd          2/2     Running   2          3d6h
 [root@linux ~]# kubectl -nwasm get svc
 NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                       
 goserver       NodePort    10.107.169.228   <none>        9091:16814/TCP
 sleep          ClusterIP   10.96.185.32     <none>        80/TCP   
 [root@linux ~]# kubectl -nwasm exec -it goserver-7c5cc7cf6-lslcz -c goserver --  bash
 bash-4.4# curl 127.0.0.1:8081/healthz
 {"status":"healthy","hostName":"goserver-7c5cc7cf6-lslcz"}
 ​
 [root@linux ~]# kubectl -ndubbo get po -owide
 NAME                                        READY   STATUS          IP               
 dubbo-sample-consumer-6958b44b75-lf7sr      2/2     Running        10.244.104.60
 dubbo-sample-provider-v1-cfdcf7768-ptbld    2/2     Running        10.244.122.140   
 [root@linux ~]# kubectl -ndubbo get se dubbo-samples-demoservice -o yaml
 apiVersion: networking.istio.io/v1beta1
 kind: ServiceEntry
 ...
 spec:
   addresses:
   - 240.240.0.5
   endpoints:
   - address: 10.244.122.140
     ports:
       tcp-dubbo: 20880 
 [root@linux ~]# kubectl -ndubbo logs -f deploy/dubbo-sample-consumer
 ...
 Hello Aeraki, response from dubbo-sample-provider-v1-cfdcf7768-ptbld/10.244.122.140

构建Wasm过滤器

根据web-assembly-hub教程安装wasm客户端工具,并且初始化工程

 [root@linux ~]# wasm init --language tinygo --platform istio --platform-version 1.9.x tcp-stream-data
 INFO[0000] extracting 1416 bytes to /path/tcp-stream-data
 [root@linux ~]# tree .
 .
 ├── go.mod
 ├── go.sum
 ├── main.go
 └── runtime-config.json
 ​
 0 directories, 4 files

修改go.mod,使用最新版SDK:

 go 1.16
 ​
 require github.com/tetratelabs/proxy-wasm-go-sdk v0.14.0

修改其默认生成的main.go,使得其符合新版SDK的用法(默认应用v0.1.0版本),添加自定义打印函数:

 ...
 // 打印tcp连接属性
 func (ctx *networkContext) PrintConnectionAttrs() error {
     addr, err := proxywasm.GetProperty([]string{"source", "address"})  
     ......
     proxywasm.LogInfof("source address: %s", string(addr))
 ​
     dest, err := proxywasm.GetProperty([]string{"destination", "address"})
     ......
     proxywasm.LogInfof("destination address: %s", string(dest))
 ​
     return nil
 }
 ​
 // 打印envoy上游属性
 func (ctx *networkContext) PrintUpstreamAttrs() error {
     addr, err := proxywasm.GetProperty([]string{"upstream", "address"})  
     ......
     proxywasm.LogInfof("upstream address: %s", string(addr))
     
     return nil
 }

分别在OnDownstreamData(客户端请求数据)、OnUpstreamData(服务端响应数据)中引用上述自定义函数进行打印:

 func (ctx *networkContext) OnDownstreamData(dataSize int, endOfStream bool)types.Action{
     ......
     _ = ctx.PrintConnectionAttrs()
     _ = ctx.PrintUpstreamAttrs()
 ​
     data, err := proxywasm.GetDownstreamData(0, dataSize)
     ......
     
     proxywasm.LogInfof(">>>>>> downstream data received >>>>>>\n%s", string(data))
     return types.ActionContinue
 }
 ​
 func (ctx *networkContext) OnUpstreamData(dataSize int, endOfStream bool) types.Action {
     ......
     _ = ctx.PrintConnectionAttrs()
     _ = ctx.PrintUpstreamAttrs()
 ​
     data, err := proxywasm.GetUpstreamData(0, dataSize)
     ......
 ​
     proxywasm.LogInfof("<<<<<< upstream data received <<<<<<\n%s", string(data))
     return types.ActionContinue
 }

其中,Envoy支持引用的环境上下文属性参见其官网

将代码在linux环境下编译生成二进制文件:

 # 根据需要配置网络代理
 [root@linux ~]# export http_proxy=proxyIP:proxyPort
 [root@linux ~]# export GOPROXY=https://goproxy.cn,https://goproxy.io,direct
 # 编译 (在容器镜像quay.io/solo-io/ee-builder:0.0.33内完成编译,映射编译结果到本地磁盘)
 [root@linux ~]# wasm build tinygo . -t tcp-steam-data:test  --store ./build/
 Building with tinygo...go: downloading github.com/tetratelabs/proxy-wasm-go-sdk v0.14.0
 INFO[0007] adding image to cache...                      filter file=/tmp/wasme551366072/filter.wasm tag="tcp-steam-data:test"
 INFO[0007] tagged image                                  digest="sha256:fc1563eb463aeb31119104a923509d4e885063ad0bd64fcfd2f6dd4da79c2196" image="docker.io/library/tcp-steam-data:test"
 [root@linux ~]# ls -l build/79ada3a6417713a07a6c89d400f62306/
 -rw-r--r--. 1 root root  225 Aug  9 16:57 descriptor.json
 -rw-r--r--. 1 root root 255K Aug  9 16:57 filter.wasm
 -rw-r--r--. 1 root root   37 Aug  9 16:57 image_ref
 -rw-r--r--. 1 root root  126 Aug  9 16:57 runtime-config.json

应用Wasm过滤器

为简单起见,此处以hostPath方式挂载存储,使得sleep服务边车容器能访问得到本地生成的Wasm二进制文件:

 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: sleep
 ......
   template:
     metadata:
       annotations:
         sidecar.istio.io/userVolume: '[{"name":"host","host": {"path":"/host/path"}}]'
         sidecar.istio.io/userVolumeMount: '[{"mountPath":"/mount/path","name":"host"}]'

修改客户端边车容器日志级别为Info,默认为Warn,而上文代码中使用了proxywasm.LogInfof输入Info日志:

 [root@linux ~]# kubectl -nwasm exec deploy/sleep -- curl -X POST http://localhost:15000/logging?level=info
 active loggers:
   admin: info
   ......
   wasm: info
 [root@linux ~]# kubectl -ndubbo exec deploy/dubbo-sample-consumer -- curl -X POST http://localhost:15000/logging?level=info
 active loggers:
   admin: info
   ......
   wasm: info

由于本文将Wasm模块应用到SIDECAR_OUTBOUND环境,因此需要将客户端边车日志级别做调整。

编写EnvoyFilter资源,使得Wasm过滤器插入到最后一条过滤器envoy.filters.network.tcp_proxy之前:

 apiVersion: networking.istio.io/v1alpha3
 kind: EnvoyFilter
 metadata:
   name: goserver-wasm
   namespace: wasm
 spec:
   configPatches:
     - applyTo: NETWORK_FILTER
       match:
         context: SIDECAR_OUTBOUND
         listener:
           name: 10.107.169.228_9091     # (goserver) svcIP_svcPort
           filterChain:
             filter:
               name: envoy.filters.network.tcp_proxy  # tcp流量
       patch:
         operation: INSERT_BEFORE
         value:
           name: envoy.filters.network.wasm
           typed_config:
             '@type': type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasm
             config:
               name: tcp-stream-data
               configuration:
                 '@type': type.googleapis.com/google.protobuf.StringValue
                 value: "empty string"
               vm_config:
                 vm_id: "same_vm_id"
                 runtime: "envoy.wasm.runtime.v8"
                 code:
                   local:
                     filename: "/mount/path/filter.wasm"

Dubbo协议的EnvoyFilter配置除了listener.name需要更换为ServiceEntry.spec.addresses,其他几乎一致,此处不在展开。

http流量

进入sleep服务,发送一个http请求:

 [root@linux ~]# kubectl -nwasm exec -it deploy/sleep -c sleep --  sh
 / # curl goserver:9091/healthz
 {"status":"healthy","hostName":"goserver-7c5cc7cf6-lslcz"}

注意,此时需要使用svcName:svcPort的方式进行调用,因为根据Envoy规则将根据请求的Host消息头进行主机域名匹配。

此时观察sleep服务边车容器日志:

 

根据上述结果可知,在OnDownstreamData(客户端请求数据)、OnUpstreamData(服务端响应数据)中打印的各方地址及端口数据是一致的,由于Http流量也属于Tcp流量,因此Http请求中的消息头与正文均被完整的打印了出来。

dubbo(tcp)流量

由于部署的客户端是定时请求服务端数据,因此不需要手动触发请求发送。

 

在dubbo协议流量中请求与响应数据也被正常打印,由于dubbo自定义的协议头等为非纯文本字符格式,因此数据中出现部分乱码。

但是正文部分仍然能看到请求中的接口、方法、消息类型等数据。

小结

上文对Wasm过滤器在Istio服务网格中的应用,从其架构原理、标准使用规范到样例测试,完整的展现了其在Envoy代理中的可插拔、可扩展的特性。

不过在实践过程中仍然会发现其在Istio运用中的问题:

  • Wasm过滤器在INBOUND流量中不能正常运行,尤其是将其类型定义为Network Filter时边车容器将会报错

  • Envoyfilter生效之后,若服务实例pod发生重启,可能导致服务不可用

相信之后官方会支持愈来愈多的语言编写Envoy的Wasm扩展。我们能够轻松选择本身熟悉的语言实现诸如度量,可观察性,转换,数据丢失预防,合规性验证或其余功能。

标签:插件,虚拟机,Istio,网格,Wasm,vm,wasm,config
来源: https://blog.csdn.net/a605692769/article/details/122262262

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有