2021年1月12日,Envoy 1.17.0 正式 released!本次更新值得你们关注的功能:html
本文主要小试Wasm filter。git
Envoy Wasm扩展是一种Filter,可经过Wasm ABI 将Envoy内部 C++ API ”翻译“ 到 Wasm 运行时。 目前Envoy 支持如下4种Wasm 运行时:github
Name | Description |
---|---|
envoy.wasm.runtime.v8 | V8-based runtime |
envoy.wasm.runtime.wasmtime | Wasmtime runtime |
envoy.wasm.runtime.wavm | WAVM runtime |
envoy.wasm.runtime.null | Compiled modules linked into Envoy |
默认状况下,Envoy发行镜像中不包含Wasmtime和WAVM运行时。web
ABI 定义了Wasm扩展两侧的函数约定:主机公开的函数和Wasm模块实现的函数。主机公开的函数被“import”到Wasm模块中。docker
固然咱们在真正编写一个Wasm Filter的时候,咱们并非直接操做ABI。咱们通常使用的是SDK,SDK是ABI的一种特定于语言的实现,它使使用该语言编写Wasm Filter变得更加容易。api
理论上,咱们可使用各类支持Wasm的语言编写Filter。不过目前官方实现了C++和 Rust 两种语言的SDK。安全
Envoy 支持 Wasm Network Filter 和 HTTP Filter。dom
Wasm是沙箱技术。从安全的角度来看,这是很是理想的,但它对内存模型有影响。 Envoy和Wasm VM之间的任何交互都将从Envoy内存复制到Wasm内存,而后再返回。curl
在本示例中,咱们基于 proxy-wasm-rust-sdk 实现一个Wasm 扩展 ,该扩展主要实现功能:当请求的Header中path属性值为“/hello”的时候,Envoy代理直接返回“Hello, World”。socket
1:建立库并设置
Wasm过滤器是从Rust库项目编译而成的,所以首先咱们须要建立该项目:
cargo new --lib my-http-wasm-filter
生产的库是要被Envoy C++ 代码加载的,所以无需包含任何Rust特定的信息。故咱们编辑Cargo.toml文件,将库的类型设置为"cdylib"。
同时咱们须要引用proxy-wasm-rust SDK,故须要配置一下proxy-wasm-rust SDK依赖。
以下:
[package] name = "my-http-wasm-filter" version = "0.1.0" authors = ["iyacontrol <gaohj2015@yeah.net>"] edition = "2018" [lib] crate-type = ["cdylib"] [dependencies] log = "0.4.8" proxy-wasm = "0.1.3" # The Rust SDK for proxy-wasm
2:编写代码
src/lib.rs 代码以下:
use log::trace; use proxy_wasm::traits::*; use proxy_wasm::types::*; #[no_mangle] pub fn _start() { proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) }); } struct HttpHeadersRoot; impl Context for HttpHeadersRoot {} impl RootContext for HttpHeadersRoot { fn get_type(&self) -> Option<ContextType> { Some(ContextType::HttpContext) } fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> { Some(Box::new(HttpHeaders { context_id })) } } struct HttpHeaders { context_id: u32, } impl Context for HttpHeaders {} impl HttpContext for HttpHeaders { fn on_http_request_headers(&mut self, _: usize) -> Action { for (name, value) in &self.get_http_request_headers() { trace!("#{} -> {}: {}", self.context_id, name, value); } match self.get_http_request_header(":path") { Some(path) if path == "/hello" => { self.send_http_response( 200, vec![("Hello", "World"), ("Powered-By", "proxy-wasm")], Some(b"Hello, World!n"), ); Action::Pause } _ => Action::Continue, } } fn on_http_response_headers(&mut self, _: usize) -> Action { for (name, value) in &self.get_http_response_headers() { trace!("#{} <- {}: {}", self.context_id, name, value); } Action::Continue } fn on_log(&mut self) { trace!("#{} completed.", self.context_id); } }
3:编译
执行以下语句:
cargo build --target wasm32-unknown-unknown --release
生成的结果以下:
主要是my_http_wasm_filter.wasm文件。后续咱们会使用到该文件。
4: 运行
配置envoy启动文件,以下:
static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 8000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: - "*" routes: - match: prefix: "/" route: cluster: web_service http_filters: - name: envoy.filters.http.wasm typed_config: "@type": type.googleapis.com/udpa.type.v1.TypedStruct type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm value: config: name: "my_plugin" root_id: "my_root_id" configuration: "@type": "type.googleapis.com/google.protobuf.StringValue" value: | {} vm_config: runtime: "envoy.wasm.runtime.v8" vm_id: "my_vm_id" code: local: filename: "/lib/my_http_wasm_filter.wasm" configuration: {} - name: envoy.filters.http.router typed_config: {} clusters: - name: web_service connect_timeout: 0.5s type: LOGICAL_DNS lb_policy: round_robin dns_lookup_family: V4_ONLY load_assignment: cluster_name: httpbin endpoints: - lb_endpoints: - endpoint: address: socket_address: address: httpbin.org port_value: 80 ipv4_compat: true
经过envoy.filters.http.wasm配置项,将咱们编写的my_http_wasm_filter.wasm加入到envoy filter中。
为了简单,咱们基于Envoy 1.17 官方镜像打一个新的镜像,Dockerfile以下:
FROM envoyproxy/envoy:v1.17.0 ADD ./target/wasm32-unknown-unknown/release/my_http_wasm_filter.wasm /lib/my_http_wasm_filter.wasm ADD ./envoy.yaml /etc/envoy.yaml ENTRYPOINT /usr/local/bin/envoy -c /etc/envoy.yaml -l debug --service-cluster proxy
使用如下命令构建咱们新的镜像:
docker build -t iyacontrol/envoy:1.17.0 .
运行构建好的镜像:
docker run iyacontrol/envoy:1.17.0
正常状况下,咱们的Envoy正常运行。
5:验证
exec 到Envoy容器中,安装curl来测试。若是响应内容以下,则咱们的Wasm扩展预期工做。
# curl http://127.0.0.1:8000/hello Hello, World! # curl http://127.0.0.1:8000 <!DOCTYPE html> <html lang="en"> ...
相信之后官方会支持愈来愈多的语言编写Envoy的Wasm扩展。咱们能够轻松选择本身熟悉的语言实现诸如度量,可观察性,转换,数据丢失预防,合规性验证或其余功能。