WebAssembly(简称为 Wasm)的诞生源自前端,是一种为了解决日益复杂的 Web 前端应用以及有限的 JavaScript 性能而诞生的技术。它本身并不是一种语言,而是一种字节码标准。WASM 字节码和机器码非常接近,因此可以非常快速的装载运行。任何一种语言,都可以被编译成 WASM 字节码,然后在 WASM 虚拟机中执行,理论上,所有语言,包括 JavaScript、C、C++、Rust、Go、Java 等都可以编译成 WASM 字节码并在 WASM 虚拟机中执行。当然不仅可以嵌入浏览器增强 Web 应用,也可以应用于其他的场景。
WebAssembly
WebAssembly 是为下列目标而生的:
对于 Istio 来说,WebAssembly 也使得 Istio 的扩展能力得到了极大的提升,Isstio 从 1.12 版本开始引入 WASM 扩展 Envoy,当你需要添加 Envoy 或 Istio 不支持的自定义功能时,那么我们就可以使用 Wasm 插件,比如使用 Wasm 插件来添加自定义验证、认证、日志或管理配额等等。
Envoy 架构
首先我们再回顾下 Envoy 的过滤机制,Envoy 通过过滤器来实现各种功能,比如路由、负载均衡、TLS、认证、日志、监控等等。Envoy 提供了进程外架构、支持 L3/L4 filter、HTTP L7 filter,过滤器包括侦听器过滤器(Listener Filters)、网络过滤器(Network Filters)、HTTP 过滤器(HTTP Filters)三种类型。
侦听器过滤器在初始连接阶段访问原始数据并操作 L4 连接的元数据。例如,TLS 检查器过滤器标识连接是否经过 TLS 加密,并解析与该连接关联的 TLS 元数据;HTTP Inspector Filter 检测应用协议是否是 HTTP,如果是的话,再进一步检测 HTTP 协议类型 (HTTP/1.x or HTTP/2) ,这两种过滤器解析到的元数据都可以和 FilterChainMatch 结合使用。
网络过滤器访问和操作 L4 连接上的原始数据,即 TCP 数据包。例如,TCP 代理过滤器将客户端连接数据路由到上游主机,它还可以生成连接统计数据。此外,MySQL proxy、Redis proxy、Dubbo proxy、Thrift proxy 等都属于网络过滤器。
HTTP 过滤器在 L7 上运行,由网络过滤器(即 HTTP 连接管理器,HTTP Connection Manager)创建。这些过滤器用于访问、操作 HTTP 请求和响应,例如,gRPC-JSON 转码器过滤器可以为 gRPC 后端提供一个 REST API,并将请求和响应转换为相应的格式。此外,还包括 JWT、Router、RBAC 等多种过滤器。
有很多编程语言都支持编写 WASM 插件,比如 C、C++、Rust、Go、Java 等等,这里我们以 Go 语言为例来编写一个简单的 WASM 插件,编写 WASM 的工具有 Solo.io 团队的 wasme、tinygo等,目前应用比较多是 tinygo,tinygo 支持的包可以查看 https://tinygo.org/docs/reference/lang-support/stdlib/ 进行了解。
TinyGo 是 Go 编程语言规范的一个编译器实现,为什么不使用官方的 Go 编译器?目前官方编译器无法生成可以在浏览器外部运行的 WASM 二进制文件,因此也无法生成与 Proxy-Wasm 兼容的二进制文件。
Proxy-Wasm是开源社区针对「网络代理场景」设计的一套 ABI 规范,定义了网络代理和运行在网络代理内部的 Wasm 虚拟机之间的接口,属于当前的事实规范。当前支持该规范的网络代理软件包括 Envoy、MOSN 和 ATS(Apache Traffic Server),支持该规范的 Wasm 扩展 SDK 包括 C++、Rust 和 Go。采用该规范的好处在于能让 Wasm 扩展程序在不同的网络代理产品上运行,比如 MOSN 的 Wasm 扩展程序可以运行在 Envoy 上,而 Envoy 的 Wasm 扩展程序也可以运行在 MOSN 上。
Proxy-Wasm 规范定义了宿主机与 Wasm 扩展程序之间的交互细节,包括 API 列表、函数调用规范以及数据传输规范这几个方面。其中,API 列表包含了 L4/L7、property、metrics、日志等方面的扩展点,涵盖了网络代理场景下所需的大部分交互点。目前实现该规范的 Wasm 扩展 SDK 包括 AssemblyScript、C++、Rust 和 Go:
为了方便,我们也直接选择已有的 proxy-wasm-go-sdk 这个 SDK 进行开发。这个 Proxy-Wasm Go SDK 是用于使用 Go 编程语言在 Proxy-Wasm ABI 规范之上扩展网络代理(例如 Envoyproxy)的 SDK,有了这个 SDK,每个人都可以轻松地生成与 Proxy-Wasm 规范兼容的 Wasm 二进制文件,而无需了解低级且对于没有专业知识的人来说难以理解的 Proxy-Wasm ABI 规范。
首先安装 tinygo 工具,前往 https://github.com/tinygo-org/tinygo/releases/tag/v0.30.0 下载对应的版本,比如我们这里是 Linux 系统,可以使用下面的命令进行安装:
# linux$ wget https://github.com/tinygo-org/tinygo/releases/download/v0.30.0/tinygo0.30.0.linux-amd64.tar.gz$ tar -xvf tinygo0.30.0.linux-amd64.tar.gz$ export PATH=$PATH:~/tinygo/bin$ tinygo versiontinygo version 0.30.0 linux/amd64 (using go version go1.17 and LLVM version 16.0.1)
当然我们也可以直接使用 docker 镜像来进行编译。
接下来我们就可以来编写一个 WASM 插件,这里我们将包含一个 Envoy 过滤器,将来自 http://service/banana/X 的请求重定向到 http://service/status/X,这里我们使用 Go 语言来编写插件,首先初始化项目:
$ mkdir wasm-go-demo && cd wasm-go-demo$ go mod init github.com/cnych/wasm-go-demo$ go get github.com/tetratelabs/proxy-wasm-go-sdk
然后创建 main.go 文件,内容如下:
package mainimport ( "regexp" "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm" "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types")type vmContext struct { // 嵌入默认的 VM 上下文,这样我们就不需要重新实现所有方法 types.DefaultVMContext}func (ctx *vmContext) NewPluginContext(contextID uint32) types.PluginContext { return &pluginContext{}}type pluginContext struct { // 嵌入默认的插件上下文,这样我们就不需要重新实现所有方法 types.DefaultPluginContext pattern string replaceWith string configData string // 保存插件的一些配置信息}// 注入额外的 Headervar additionalHeaders = map[string]string{ "who-am-i": "go-wasm-demo", "injected-by": "istio-api!", "site": "youdianzhishi.com", "author": "阳明", // 定义自定义的header,每个返回中都添加以上header}// NewHttpContext 为每个 HTTP 请求创建一个新的上下文。func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext { return &httpRegex{ contextID: contextID, pluginContext: ctx, }}// OnPluginStart 在插件被加载时调用。func (ctx *pluginContext) OnPluginStart(pluginCfgSize int) types.OnPluginStartStatus { proxywasm.LogWarnf("regex/main.go OnPluginStart()") // 获取插件配置 data, err := proxywasm.GetPluginConfiguration() if data == nil { return types.OnPluginStartStatusOK } if err != nil { proxywasm.LogWarnf("failed read plug-in config: %v", err) return types.OnPluginStartStatusFailed } proxywasm.LogWarnf("read plug-in config: %s/n", string(data)) // 插件启动的时候读取配置 ctx.configData = string(data) ctx.pattern = "banana/([0-9]*)" ctx.replaceWith = "status/$1" return types.OnPluginStartStatusOK}// OnPluginDone 在插件被卸载时调用。func (ctx *pluginContext) OnPluginDone() bool { proxywasm.LogWarnf("regex/main.go OnPluginDone()") return true}type httpRegex struct { // 嵌入默认的 HTTP 上下文,这样我们就不需要重新实现所有方法 types.DefaultHttpContext // contextID 是插件上下文的 ID,它是唯一的。 contextID uint32 pluginContext *pluginContext}// OnHttpResponseHeaders 在收到 HTTP 响应头时调用。func (ctx *httpRegex) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { proxywasm.LogWarnf("%d httpRegex.OnHttpResponseHeaders(%d, %t)", ctx.contextID, numHeaders, endOfStream) // 添加 Header for k, v := range additionalHeaders { if err := proxywasm.AddHttpResponseHeader(k, v); err != nil { proxywasm.LogWarnf("failed to add response header %s: %v", k, err) } } //为了便于演示观察,将配置信息也加到返回头里 proxywasm.AddHttpResponseHeader("configData", ctx.pluginContext.configData) return types.ActionContinue}// OnHttpRequestHeaders 在收到 HTTP 请求头时调用。func (ctx *httpRegex) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { proxywasm.LogWarnf("%d httpRegex.OnHttpRequestHeaders(%d, %t)", ctx.contextID, numHeaders, endOfStream) re := regexp.MustCompile(ctx.pluginContext.pattern) replaceWith := ctx.pluginContext.replaceWith s, err := proxywasm.GetHttpRequestHeader(":path") if err != nil { proxywasm.LogWarnf("Could not get request header: %v", err) } else { result := re.ReplaceAllString(s, replaceWith) proxywasm.LogWarnf("path: %s, result: %s", s, result) err = proxywasm.ReplaceHttpRequestHeader(":path", result) if err != nil { proxywasm.LogWarnf("Could not set request header to %q: %v", result, err) } } return types.ActionContinue}func (ctx *httpRegex) OnHttpStreamDone() { proxywasm.LogWarnf("%d OnHttpStreamDone", ctx.contextID)}func main() { proxywasm.LogWarnf("regex/main.go main() REACHED") // 设置 VM 上下文,这样我们就可以在插件启动时读取配置。 proxywasm.SetVMContext(&vmContext{})}
在上面的代码中,我们主要关注 pluginContext 和 httpRegex 这两个结构体,其中 pluginContext 结构体主要用于插件的初始化,而 httpRegex 结构体主要用于处理 HTTP 请求,这里我们主要关注 OnHttpRequestHeaders 和 OnHttpResponseHeaders 这两个方法,这两个方法分别用于处理 HTTP 请求头和响应头,我们在这两个方法中添加了一些自定义的 Header,然后在 Istio 中就可以看到这些 Header 了。
代码编写完成后,我们就可以使用 tinygo 来编译了,执行以下命令:
tinygo build -o main.wasm -scheduler=none -target=wasi main.go
上面的命令会生成一个 main.wasm 文件,这个文件就是我们的 WASM 插件,接下来我们就可以将这个插件部署到 Istio 中了。
部署 WASM
我们可以将这个 main.wasm 文件放到一个 ConfigMap 中,然后挂载到 Envoy 中,这样就可以在 Envoy 中使用了,比如我们可以使用下面的命令来创建一个 ConfigMap:
kubectl create configmap new-filter --from-file=new-filter.wasm=main.wasm
然后接下来我们以 httpbin 为例来测试下,这里我们需要修改下 httpbin 的部署文件,将 ConfigMap 挂载到 Envoy 中。
部署 WASM
修改后的部署文件如下所示:
# httpbin.yamlapiVersion: v1kind: ServiceAccountmetadata: name: httpbin---apiVersion: v1kind: Servicemetadata: name: httpbin labels: app: httpbin service: httpbinspec: ports: - name: http port: 8000 targetPort: 80 selector: app: httpbin---apiVersion: apps/v1kind: Deploymentmetadata: name: httpbinspec: selector: matchLabels: app: httpbin version: v1 template: metadata: labels: app: httpbin version: v1 annotations: # 不能在容器上使用 volume 挂载,因为它来自 injector。 # NOTE: 我们这个示例始终挂在 "new-filter" ConfigMap 到 /var/local/wasm/new-filter.wasm sidecar.istio.io/userVolume: '[{"name":"new-filter","configMap":{"name":"new-filter"}}]' sidecar.istio.io/userVolumeMount: '[{"mountPath":"/var/local/wasm","name":"new-filter"}]' spec: serviceAccountName: httpbin containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: httpbin ports: - containerPort: 80
直接应用上面的资源对象即可:
kubectl apply -f httpbin.yaml# 当然要需要创建 VirtualService,在 Istio 根目录下面kubectl apply -f samples/httpbin/httpbin-gateway.yaml
部署完成后接下来我们先访问下 httpbin 服务,看下是否正常:
$ export GATEWAY_URL=$(kubectl get po -l istio=ingressgateway -n istio-system -o 'jsnotallow={.items[0].status.hostIP}'):$(kubectl get svc istio-ingressgateway -n istio-system -o 'jsnotallow={.spec.ports[?(@.name=="http2")].nodePort}')$ curl -v http://$GATEWAY_URL/status/418> GET /status/418 HTTP/1.1> User-Agent: curl/7.29.0> Host: 192.168.0.20:31896> Accept: */*>< HTTP/1.1 418 Unknown< server: istio-envoy< date: Fri, 08 Dec 2023 07:28:57 GMT< x-more-info: http://tools.ietf.org/html/rfc2324< access-control-allow-origin: *< access-control-allow-credentials: true< content-length: 135< x-envoy-upstream-service-time: 2< -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, /_;`"---"`|// | ;/ /_ _/ `"""`
上面我们编写的插件逻辑是当我们访问 http://service/banana/X 时,会将请求重定向到 http://service/status/X,所以我们可以使用下面的命令来测试下:
$ curl -v http://$GATEWAY_URL/banana/418> GET /banana/418 HTTP/1.1> User-Agent: curl/7.29.0> Host: 192.168.0.20:31896> Accept: */*>< HTTP/1.1 404 Not Found< server: istio-envoy< date: Fri, 08 Dec 2023 07:30:19 GMT< content-type: text/html< content-length: 233< access-control-allow-origin: *< access-control-allow-credentials: true< x-envoy-upstream-service-time: 9<<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>404 Not Found</title><h1>Not Found</h1><p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
从结果可以看到,我们定义的插件并没有生效,其实也能预料到,现在我们只是将编译后的 WASM 文件挂载到了 Envoy 中,但是 Envoy 并不知道这个文件是用来做什么的,或者说 Envoy 并不知道要将这个文件当成 WASM 插件来使用。
$ kubectl get pods -l app=httpbinNAME READY STATUS RESTARTS AGEhttpbin-55db5999b4-qtlcg 2/2 Running 0 8m23s$ kubectl exec -it httpbin-55db5999b4-qtlcg -c istio-proxy -- ls /var/local/wasmnew-filter.wasm
wasm 插件
这个时候我们还需要使用到一个名为 EnvoyFilter 的 CRD 资源对象,EnvoyFilter 提供了一种机制,可以自定义 Istio Pilot 生成的 Envoy 配置,使用 EnvoyFilter 可以修改某些字段的值、添加特定的过滤器,甚至添加全新的监听器、集群等。需要注意的是这个功能必须小心使用,因为不正确的配置可能会导致整个网格不稳定。与其他 Istio 网络对象不同,EnvoyFilters 是附加应用的。对于特定命名空间中给定工作负载来说,可以存在任意数量的 EnvoyFilters,这些 EnvoyFilters 的应用顺序如下:首先是配置根命名空间中所有的 EnvoyFilters,然后是工作负载所在命名空间中匹配到的所有 EnvoyFilters。
比如我们可以使用下面的配置来将我们的 WASM 插件挂载到 Envoy 中:
# httpbin-wasm-filter.yamlapiVersion: networking.istio.io/v1alpha3kind: EnvoyFiltermetadata: name: httpbin-wasm-filterspec: workloadSelector: labels: app: httpbin configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND # 仅对入站流量进行过滤 listener: filterChain: filter: name: envoy.filters.network.http_connection_manager subFilter: name: envoy.filters.http.router patch: operation: INSERT_BEFORE # 在 router 之前插入 value: name: mydummy typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: configuration: "@type": type.googleapis.com/google.protobuf.StringValue value: dummy root_id: "regex_replace" vm_config: code: local: filename: /var/local/wasm/new-filter.wasm runtime: envoy.wasm.runtime.v8 vm_id: myvmdummy
上面的 EnvoyFilter 对象中,首先我们通过 workloadSelector 来指定要对哪些 Pod 进行过滤,这里我们指定了 app: httpbin,表示只对 httpbin 这个 Pod 进行过滤,然后主要关注 configPatches 字段,这个字段用于配置 Envoy 的过滤器,其中的 match 字段用于匹配 Envoy 的过滤器,这里我们匹配的是 envoy.filters.network.http_connection_manager,然后 patch 字段用于指定要挂载的 WASM 插件,在 value.config 中指定了插件的配置信息以及 WASM 插件的路径。
然后直接应用上面的这个资源对象即可:
kubectl apply -f httpbin-wasm-filter.yaml
部署完成后我们可以先查看 httpbin 应用的 sidecar 日志:
$ kubectl logs -f httpbin-55db5999b4-qtlcg -c istio-proxy# ......2023-12-08T07:48:03.605802Z warning envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1151 wasm log: regex/main.go main() REACHED thread=252023-12-08T07:48:03.606902Z warning envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1151 wasm log regex_replace myvmdummy: read plug-in config: dummy thread=252023-12-08T07:48:03.729847Z info Readiness succeeded in 1.025422807s2023-12-08T07:48:03.730148Z info Envoy proxy is ready
正常现在就可以看到上面我们在插件中添加的一些日志了,然后我们再来测试下 httpbin 服务,访问 http://service/banana/X,看下是否能够正常重定向到 http://service/status/X:
$ curl -v http://$GATEWAY_URL/banana/418> GET /banana/418 HTTP/1.1> User-Agent: curl/7.29.0> Host: 192.168.0.20:31896> Accept: */*>< HTTP/1.1 418 Unknown< server: istio-envoy< date: Fri, 08 Dec 2023 08:11:17 GMT< x-more-info: http://tools.ietf.org/html/rfc2324< access-control-allow-origin: *< access-control-allow-credentials: true< content-length: 135< x-envoy-upstream-service-time: 10< who-am-i: go-wasm-demo< injected-by: istio-api!< site: youdianzhishi.com< author: 阳明< configdata: dummy< -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, /_;`"---"`|// | ;/ /_ _/
从上面的结果可以看到,我们的插件已经生效了,当我们访问 http://service/banana/X 时,会将请求重定向到 http://service/status/X,并且在响应头中添加了我们定义的一些 Header。到这里我们就实现了一个简单的 WASM 插件,当然这个插件只是一个简单的示例,实际上我们可以实现更加复杂的逻辑,比如可以实现自定义认证、自定义日志、自定义路由等等。
需要注意的是这里我们的部署方式是创建一个包含已编译的 Wasm 插件的 ConfigMap,将 ConfigMap 挂载到 Pod 的 Envoy Sidecar 中去,然后通过 EnvoyFilter 配置 Envoy,从本地文件加载 Wasm 插件。这种方法的确可以实现我们的需求,但是配置 EnvoyFilter 对象有点复杂,功能丰富的 Wasm 插件可能超出 ConfigMap 1MB 的大小限制。为了解决这个问题,Istio 便引入了一个新的用于自定义 Wasm 插件对 Istio 代理功能进行扩展的新顶层 API - WasmPlugin CRD,不再需要使用 EnvoyFilter 资源向代理添加自定义 Wasm 模块,取而代之的是使用 WasmPlugin 资源:
apiVersion: extensions.istio.io/v1alpha1kind: WasmPluginmetadata: name: your-filterspec: selector: matchLabels: app: server phase: AUTHN priority: 10 pluginConfig: someSetting: true someOtherSetting: false youNameIt: - first - second url: docker.io/your-org/your-filter:1.0.0
WasmPlugin 和 EnvoyFilter 之间有不少相似的地方,但也存在一些不同之处。比如在上面的示例中是将 Wasm 模块部署到与 selector 字段匹配的所有工作负载 —— 这与 EnvoyFilter 是完全相同的。
接下来的字段是 phase,该字段决定了 Wasm 模块将被注入到代理过滤器链中的哪个位置。我们为其定义了四个不同的阶段:
pluginConfig 字段用于 Wasm 插件的具体配置。在此字段中输入的任何内容都将通过 JSON 格式进行编码并传递到过滤器中,我们可以在 Proxy-Wasm SDK 的配置回调中访问它,比如在 Go SDK 中的 OnPluginStart 回调中可以获取这些配置信息。
url 字段指定了 Wasm 模块的拉取位置,这里的 url 是一个 docker URI,除了通过 HTTP、HTTPS 和本地文件系统 (使用 file://)方式加载 Wasm 模块之外,还可以使用 OCI 镜像格式作为分发 Wasm 插件,这也是推荐的方式。
接下来我们按照上面的要求在代码根目录中新建一个 Dockerfile,用来将我们的 Wasm 插件打包到 Docker 镜像中:
# Dockerfile for building "compat" variant of Wasm Image Specification.# https://github.com/solo-io/wasm/blob/master/spec/spec-compat.mdFROM scratchCOPY main.wasm ./plugin.wasm
当然也可以将构建的动作放到 Dockerfile 中,进行多阶段构建:
FROM tinygo/tinygo as buildWORKDIR /srcCOPY . .RUN go env -w GOPROXY=https://goproxy.cn,directRUN tinygo build -o main.wasm -scheduler=none -target=wasi ./main.goFROM scratchCOPY --from=build /src/main.wasm ./plugin.wasm
直接使用 docker build 命令来构建镜像即可,构建完成后推送到镜像仓库:
docker build -t cnych/wasm-go-demo:v0.1 .docker.io/cnych/wasm-go-demo:v0.1
接下来就可以使用 WasmPlugin 资源对象来部署我们的 WASM 插件了,创建如下所示的 WasmPlugin 资源对象:
# httpbin-wasm-plugin.yamlapiVersion: extensions.istio.io/v1alpha1kind: WasmPluginmetadata: name: httpbin-wasm-plugin namespace: defaultspec: selector: matchLabels: app: httpbin url: oci://docker.io/cnych/wasm-go-demo:v0.1 pluginConfig: testConfig: abcd website: youdianzhishi.com listconfig: - abc - def
先删除之前的 EnvoyFilter 资源:
$ kubectl delete cm new-filterconfigmap "new-filter" deleted$ kubectl delete envoyfilter httpbin-wasm-filterenvoyfilter.networking.istio.io "httpbin-wasm-filter" deleted
记得将 httpbin.yaml 中的 sidecar.istio.io/userVolume 和 sidecar.istio.io/userVolumeMount 字段删除:
apiVersion: apps/v1kind: Deploymentmetadata: name: httpbinspec: selector: matchLabels: app: httpbin version: v1 template: metadata: labels: app: httpbin version: v1 # annotations: # 去掉这里的注解 spec: serviceAccountName: httpbin containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: httpbin ports: - containerPort: 80
然后直接应用上面的 WasmPlugin 资源对象:
$ kubectl apply -f httpbin-wasm-plugin.yaml$ kubectl get wasmpluginsNAME AGEhttpbin-wasm-plugin 5s
部署完成后我们可以先查看 httpbin 应用的 sidecar 日志:
$ kubectl logs -f httpbin-86869bccff-6wqdc -c istio-proxy# ......2023-12-08T08:43:11.360127Z warning envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1151 wasm log: regex/main.go main() REACHED thread=252023-12-08T08:43:11.360757Z warning envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1151 wasm log: regex/main.go OnPluginStart() thread=252023-12-08T08:43:11.360895Z warning envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1151 wasm log: read plug-in config: {"listconfig":["abc","def"],"testConfig":"abcd","website":"youdianzhishi.com"} thread=252023-12-08T08:43:12.692961Z info Readiness succeeded in 6.774251572s2023-12-08T08:43:12.693263Z info Envoy proxy is ready
可以看到我们的插件已经生效了,然后我们再来测试下 httpbin 服务,访问 http://service/banana/X,看下是否能够正常重定向到 http://service/status/X:
$ curl -v http://$GATEWAY_URL/banana/418> GET /banana/418 HTTP/1.1> User-Agent: curl/7.29.0> Host: 192.168.0.20:31896> Accept: */*>< HTTP/1.1 418 Unknown< server: istio-envoy< date: Fri, 08 Dec 2023 08:45:29 GMT< x-more-info: http://tools.ietf.org/html/rfc2324< access-control-allow-origin: *< access-control-allow-credentials: true< content-length: 135< x-envoy-upstream-service-time: 8< who-am-i: go-wasm-demo< injected-by: istio-api!< site: youdianzhishi.com< author: 阳明< configdata: {"listconfig":["abc","def"],"testConfig":"abcd","website":"youdianzhishi.com"}< -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, /_;`"---"`|// | ;/ /_ _/ `"""`
从上面的结果可以看到结果是符合我们的预期的,证明我们的插件已经生效了。
参考阿里云性能测试结果(仅供参考):
1000 并发 1000QPS 持续 10 秒钟 | 基准 | WASM | LUA |
平均延迟 | 0.6317 secs | 0.6395 secs | 0.7012 secs |
延迟 99%分布 | 0.9167 secs | 0.9352 secs | 1.1355 secs |
QPS | 1541 | 1519 | 1390 |
Total | 16281 | 16109 | 1390 |
相对于基准版本,增加 Wasm 插件的两个版本,平均延迟多出几十个到几百个毫秒,增加耗时比为:
可以看出 WASM 版本的性能明显优于 LUA 版本。
本文链接:http://www.28at.com/showinfo-26-43326-0.html使用 WebAssembly 对 Istio 进行扩展
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 一文带你彻底弄懂线程池
下一篇: 深入解析HTML的<a>标签