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擴展。咱們能夠輕鬆選擇本身熟悉的語言實現諸如度量,可觀察性,轉換,數據丟失預防,合規性驗證或其餘功能。