wasm开发示例

news/2024/12/5 1:05:28/

envoy wasm plugins

envoy有着很强的可配置性,但总归有一些功能是没有实现的,那么如何对envoy进行扩展呢?

有2种做法:一个是直接修改envoy源码,另一个是通过Web Assembly插件。

直接修改源码的好处是envoy native,性能最好,但带来了可维护性的降低,不利于未来升级,另外也需要使用c++进行编码,对开发人员有一定的要求。

相对来说,Web Assembly的好处就比较多了,如下是banzaicloud总结的几个点:

  • Agility - filters can be dynamically loaded into the running Envoy process without the need to stop or re-compile.
  • Maintainability - we don’t have to change the Envoy’s codebase to extend its functionality.
  • Diversity - popular programming languages such as C/C++ and Rust can be compiled into WASM, thus developers can implement filters using their programming language of choice.
  • Reliability and isolation - filters are deployed into a VM (sandbox), therefore are isolated from the hosting Envoy process itself (e.g. when the WASM filter crashes it will not impact the Envoy process).
  • Security - since filters communicate with the host (Envoy Proxy) through a well-defined API, they have access to and can modify only a limited number of connection or request properties.

目前Envoy 提供了如下几种语言的 Web Assembly SDK:

  • C++
  • Rust
  • AssemblyScript
  • Go - still experimental

当然,Web Assembly也有其缺点:

  • 性能降低,只有原生c++的70%
  • 内存使用增加,毕竟运行了一个v8的虚拟机

assemblyscript SDK

下面介绍如何使用wasme,基于assemblyscript SDK ,制作wasm二进制文件。

wasme工具

wasme是solo.io公司开发的工具,可以很方便的用来开发和管理 envoy WASM filter。其设计理念和docker是类似的,会有针对本地的build,针对云端的push等;类似docker hub,solo.io也维护了Webassembly Hub作为插件市场。

命令行安装

wasme安装参考官网,步骤如下:

我这里安装的是0.0.33版本。

curl -sL https://run.solo.io/wasme/install| sh
exportPATH=$HOME/.wasme/bin:$PATH

初始化示例项目

wasme init 可以初始化一个针对WASM支持语言(cpp/rust/assemblyscript/tinyGo)的项目。这里示例使用的是assemblyscript。工具宣称目前只支持到istio 1.9,实测1.10版本也是支持的。

$ wasme init ./new-filter
✔ assemblyscript
✔ gloo:1.3.x, gloo:1.5.x, gloo:1.6.x, istio:1.5.x, istio:1.6.x, istio:1.7.x, istio:1.8.x, istio:1.9.x
INFO[0005] extracting 1812 bytes to/home/hubottle/wasm/new-filter

示例项目功能

初始化示例项目的功能为http response自动添加header:hello,value默认为world,如果用户设置了 configuration, 则值设置为 configuration的内容。

export * from"@solo-io/proxy-runtime/proxy";
import{ RootContext, Context, RootContextHelper, ContextHelper, registerRootContext, FilterHeadersStatusValues, stream_context } from"@solo-io/proxy-runtime";classAddHeaderRootextendsRootContext {configuration : string;onConfigure(): bool {let conf_buffer =super.getConfiguration();let result = String.UTF8.decode(conf_buffer);this.configuration = result;returntrue;}createContext(): Context {returnContextHelper.wrap(newAddHeader(this));}
}classAddHeaderextendsContext {root_context : AddHeaderRoot;constructor(root_context:AddHeaderRoot){super();this.root_context = root_context;}onResponseHeaders(a: u32): FilterHeadersStatusValues {constroot_context =this.root_context;if(root_context.configuration =="") {stream_context.headers.response.add("hello","world!");}else{stream_context.headers.response.add("hello", root_context.configuration);}returnFilterHeadersStatusValues.Continue;}
}registerRootContext(() => {returnRootContextHelper.wrap(newAddHeaderRoot()); },"add_header");

编译

npm install && npm run asbuild进行编译。编译得到的文件在build目录下,其中optimized.wasm文件较小,是优化后的二进制,而untouched.wasm较大,包含了debug信息。

注意,这里并没有使用 wasme build/push 进行管理,主要是考虑国内访问外网的问题,下面envoy filter将使用local模式而非remote模式。

$ npminstall&& npm run asbuild
> asbuild:untouched
> asc assembly/index.ts -b build/untouched.wasm --use abort=abort_proc_exit -t build/untouched.wat --validate --sourceMap --debug> asbuild:optimized
> asc assembly/index.ts -b build/optimized.wasm --use abort=abort_proc_exit -t build/optimized.wat --validate --sourceMap --optimize
$lsbuild/
optimized.wasm  optimized.wasm.map  optimized.wat  untouched.wasm  untouched.wasm.map  untouched.wat

wasm文件挂载

目标:将wasm文件挂载到目标Pod的istio-proxy容器中。

创建为configmap

将optimized.wasm创建为一个configmap。注意,生产上不要这么做,如果量比较大,有可能写满etcd,可以考虑将wasm文件上传到s3,然后将该文件挂载到sidecar容器,或者直接使用remote http的方式。

$ kubectl create cm example-filter --from-file=optimized.wasm

patch Annotation

为Deployment Template增加如下2个注解:

sidecar.istio.io/userVolume: '[{"name":"wasmfilters-dir","configMap": {"name": "example-filter"}}]'
sidecar.istio.io/userVolumeMount: '[{"mountPath":"/var/local/lib/wasm-filters","name":"wasmfilters-dir"}]'

istiod会依据这2个注解,将名为example-filter的configmap,挂载到istio-proxy 容器的/var/local/lib/wasm-filters目录下。

$ kubectl create deployment httpbin --image docker.io/kennethreitz/httpbin
$ kubectl patch deployment httpbin -p'{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/userVolume":"[{\"name\":\"wasmfilters-dir\",\"configMap\": {\"name\": \"example-filter\"}}]","sidecar.istio.io/userVolumeMount":"[{\"mountPath\":\"/var/local/lib/wasm-filters\",\"name\":\"wasmfilters-dir\"}]"}}}}}'

Pod重新创建后,在istio-proxy的/var/local/lib/wasm-filters下可以查看到optimized.wasm文件。

envoy wasm filter

接下来是创建 envoy wasm filter,通知envoy加载 wasm 插件。

创建envoy filter

其中:

  • proxyVersion与istio-proxy版本保持一致
  • root_id需要与 wasm registerRootContext保持一致
  • filename需要与前面Annotation保持一致
  • workloadSelector设置为目标Pod的label

如下envoy filter适应于 istio 1.10版本,Ref部分有多篇文章使用的是旧版本的配置,不适应于1.10版本(未验证是否适应于其他istio版本)。

apiVersion:networking.istio.io/v1alpha3
kind:EnvoyFilter
metadata:name:examplefilter
spec:configPatches:-applyTo:HTTP_FILTERmatch:context:SIDECAR_INBOUNDproxy:proxyVersion:'^1\.10.*'listener:portNumber:80filterChain:filter:name:envoy.filters.network.http_connection_managersubFilter:name:envoy.filters.http.routerpatch:operation:INSERT_BEFOREvalue:name:envoy.filters.http.wasmtyped_config:"@type":type.googleapis.com/udpa.type.v1.TypedStructtype_url:type.googleapis.com/envoy.extensions.filters.network.wasm.v3.Wasmvalue:config:root_id:add_headervm_config:code:local:filename:/var/local/lib/wasm-filters/optimized.wasmruntime:envoy.wasm.runtime.v8vm_id:"my_vm_id"allow_precompiled:falseworkloadSelector:labels:app:httpbin

验证

登录到其他服务网格上的容器,请求 httpbin/headers ,可以看到客户端请求时没有携带 hello header,但是应答的报文是携带了的,envoy filter生效了。

[root@debian-77dc9c5f4f-htvc2 /]# curl -i httpbin/headers
HTTP/1.1 200 OK
server: envoy
date: Wed, 21 Jul 2021 15:08:48 GMT
content-type: application/json
content-length: 526
access-control-allow-origin: *
access-control-allow-credentials:true
x-envoy-upstream-service-time: 5
hello: world!{"headers": {"Accept":"*/*","Host":"httpbin","User-Agent":"curl/7.29.0","X-B3-Parentspanid":"6b1605afd18ad4fe","X-B3-Sampled":"0","X-B3-Spanid":"33bc8d7277665508","X-B3-Traceid":"6ccb755e06d510386b1605afd18ad4fe","X-Envoy-Attempt-Count":"1","X-Forwarded-Client-Cert":"By=spiffe://cluster.local/ns/istio-demo/sa/httpbin;Hash=4de05d86f84ae38da23ff1f0961f43bb1dc94a45207ffc388dee687c2fb34837;Subject=\"\";URI=spiffe://cluster.local/ns/istio-demo/sa/default"}
}

Go SDK

上文描述了如何使用 assemblyscript SDK 来开发envoy WASM proxy,很多后端开发人员并不熟悉Type Script,使用起来多有不便。

envoy WASM是支持 GO SDK 的,该SDK由 tetrate 开发,项目为proxy-wasm-go-sdk。

proxy-wasm-go-sdk 依赖 TinyGo编译器,而不是普通的Go编译器。与服务端使用的Go编译器不同,TinyGo主要面向嵌入式系统和WASM。

下面介绍下使用方法。

安装tinyGo

参照官网。我这里是ubuntu。

wget https://github.com/tinygo-org/tinygo/releases/download/v0.18.0/tinygo_0.18.0_amd64.deb
sudodpkg -i tinygo_0.18.0_amd64.deb

示例编写

这里直接使用 proxy-wasm-go-sdk 的example http_headers,它会replace http header。

func (ctx *httpHeaders) OnHttpRequestHeaders(numHeadersint, endOfStream bool) types.Action {err := proxywasm.ReplaceHttpRequestHeader("test","best")iferr != nil {proxywasm.LogCriticalf("failed to set request header: test, %v", err)}hs, err := proxywasm.GetHttpRequestHeaders()iferr != nil {proxywasm.LogCriticalf("failed to get request headers: %v", err)}for_, h := range hs {proxywasm.LogInfof("request header --> %s: %s", h[0], h[1])}returntypes.ActionContinue
}

从代码上来看,go sdk要比较方便一些,主要是比较熟悉,可以直接查看 proxy-wasm-go-sdk 提供的接口。

编译

在http_headers目录下执行tinygo编译,生成p.wasm。

tinygo build -o p.wasm -scheduler=none -target=wasi -no-debug

应用与验证

接下来的步骤和 assemblyscript SDK 类似,也是创建configmap、挂载到istio-proxy容器、创建envoy wasm filter,不再赘述。


http://www.ppmy.cn/news/62495.html

相关文章

Vue-cli搭建项目(包含Node.js安装和ElementUI安装)

目录 一、vue-cli 二、Node.js npm: Node.js安装: 测试: 三、Vue-cli搭建项目 使用HBuildex 创建一个vue.js项目 创建的自己的组件: 组件路由: 四、ElementUI安装 1.ElementUI下载: 2.在main.js中…

100种思维模型之每日评估思维模型-58

曾子曰:吾日三省吾省,为人谋而不忠乎?与朋友交不信乎?传不习乎? 曾国藩,坚持每日写复盘日记,最后他用自己的实践经历向我们证明:一个智商很平庸、出身很普通且有着各种毛病的人&…

米哈游的春招实习面经,问的很基础

米哈游的春招实习面经,主要考察了java操作系统mysql网络,这四个方面。 面试流程,共1小时,1min自我介绍,20min写题,剩下问题基础知识。 Java String,StringBuilder, StringBuffer区…

案例2:东方财富股吧文本分析----code函数封装版

案例2:东方财富股吧文本分析----code函数封装版 1.加载第三方包及全局设定2.东方财富贴吧文本信息爬取2.1爬取并保存数据2.2读取已爬数据并进行预处理 3.股吧信息分析3.1 热门话题分析3.2 投资情绪分析3.2 发帖时间分析3.4 热点主题关联分析3.5 用户行为分析3.6 调用…

【Linux】浅谈eloop机制

目录 1.eloop 机制 2.eloop结构体 2.1.eloop_data结构体 2.2 Socket事件结构体 2.3 Timeout事件结构体 2.4 Signal事件结构体 3.eloop_init 4.eloop_run 4.1 signal事件 4.2 socket事件 4.3 timeout事件 1.eloop 机制 主线程中启动事件监听机制,对不同的…

C++11实现线程池

1.所有权的传递 适用移动语义可以将一个unique_lock赋值给另一个unique_lock,适用move实现。 void myThread1() {unique_lock<mutex> myUnique (testMutex1,std::defer_lock);unique_lock<mutex>myUnique1(std::move(myUnique));//myUnique 则实效 myUnique1 相当…

进程替换函数组介绍exec*

目录 前述 execl execlp execle execv execvp execvpe 前述 介绍后缀的意义&#xff1a; l &#xff08;list&#xff09;&#xff1a;表示参数采用列表。 v&#xff08;vector&#xff09;&#xff1a;参数同数组表示。 p&#xff08;path&#xff09;&#xff1a;自…

IP协议基础

文章目录 基本概念IP和TCP分别解决什么问题 以下过程都是在网络层完成的网段划分路由路由转发过程路由表 基本概念 主机: 配有IP地址, 但是不进行路由控制的设备。 路由器: 即配有IP地址, 又能进行路由控制。 节点: 主机和路由器的统称。 IP和TCP分别解决什么问题 TCP解决…