使用wasm扩展envoy
envoy wasm 介绍
WebAssembly是一种沙盒技术,可用于扩展Istio代理(Envoy)。Proxy-Wasm沙箱API取代了Mixer作为Istio中的主要扩展机制。
WebAssembly沙箱目标:
功能 -扩展可以执行策略,收集遥测和执行有效载荷突变。
隔离 -一个插件中的编程错误或崩溃确实会影响其他插件。
配置 -使用与其他Istio API一致的API配置插件。扩展名可以动态配置。
Operator -可以扩展扩展并将其部署为仅日志,失败打开或失败关闭。
istio社区基于官方envoy的基础上fork 了https://github.com/istio/envoy
,在wasm分支以实现istio wasm支持,当前官方envoy暂未支持wasm
架构
筛选器服务提供程序接口(SPI),用于为筛选器构建Proxy-Wasm插件。
Envoy中嵌入了Sandbox V8 Wasm Runtime。
headers, trailers 和 metadata的host api。
Stats和Logging API,用于度量和监视
代码实现
使用 solo.io提供的proxy-runtime 通过js来实现wasm逻辑
git clone https://github.com/solo-io/proxy-runtime
mkdir wasm-addheader
npm install --save-dev assemblyscript
npx asinit .
修改package.json
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --validate --use abort=abort_proc_exit --sourceMap --debug",
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --validate --use abort=abort_proc_exit --sourceMap --optimize",
修改依赖为 proxy-runtime的本地路径
"@solo-io/proxy-runtime": "file:/root/proxy-runtime"
该示例判断配置的header value是否存在,不存在设置则添加 hello: world
header, 设置则为 hello: configvalue
通过逻辑实现:
export * from "@solo-io/proxy-runtime/proxy"; // this exports the required functions for the proxy to interact with us.
import { RootContext, Context, RootContextHelper, ContextHelper, registerRootContext, FilterHeadersStatusValues, stream_context } from "@solo-io/proxy-runtime";
class AddHeaderRoot extends RootContext {
configuration : string;
createContext(context_id: u32): Context {
return ContextHelper.wrap(new AddHeader(context_id, this));
}
}
class AddHeader extends Context {
root_context : AddHeaderRoot;
constructor(context_id: u32, root_context:AddHeaderRoot){
super(context_id, root_context);
this.root_context = root_context;
}
onResponseHeaders(a: u32): FilterHeadersStatusValues {
const root_context = this.root_context;
if (root_context.getConfiguration() == "") {
stream_context.headers.response.add("hello", "world!");
} else {
stream_context.headers.response.add("hello", root_context.getConfiguration());
}
return FilterHeadersStatusValues.Continue;
}
}
registerRootContext((context_id: u32) => { return RootContextHelper.wrap(new AddHeaderRoot(context_id)); }, "add_header");
编译
配置envoy
我们配置了一个返回admin接口的listener,在filter中添加了我们的 add_header wasm plugin
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 }
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
stat_prefix: ingress_http
use_remote_address: true
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: some_service }
http_filters:
- name: envoy.filters.http.wasm
config:
config:
name: "add_header"
root_id: "add_header"
configuration: "what ever you want"
vm_config:
vm_id: "my_vm_id"
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: /media/optimized.wasm
allow_precompiled: false
- name: envoy.router
clusters:
- name: some_service
connect_timeout: 0.25s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 9901
测试访问
我们将编译好的文件通过volume挂在进容器
docker run -it --net=host --entrypoint=bash -v /media:/media istio/proxyv2:latest
envoy -c /media/t.yaml
通过curl测试访问:
curl http://172.16.233.129:10000/ -I
HTTP/1.1 200 OK
content-type: text/html; charset=UTF-8
cache-control: no-cache, max-age=0
x-content-type-options: nosniff
date: Fri, 05 Jun 2020 16:00:57 GMT
server: envoy
x-envoy-upstream-service-time: 0
hello: what ever you want
transfer-encoding: chunked
可以看到返回的header已经包含 hello: what ever you want