2021年1月12日,Envoy 1.17.0 正式 released!本次更新值得大家关注的功能:
- wasm filter
- xds 资源 ttl
- skywalking tracer
本文主要小试Wasm filter。
Wasm扩展概述
Envoy Wasm扩展是一种Filter,可通过Wasm ABI 将Envoy内部 C++ API ”翻译“ 到 Wasm 运行时。 目前Envoy 支持以下4种Wasm 运行时:
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运行时。
ABI 定义了Wasm扩展两侧的函数约定:主机公开的函数和Wasm模块实现的函数。主机公开的函数被“import”到Wasm模块中。
当然我们在真正编写一个Wasm Filter的时候,我们并不是直接操作ABI。我们一般使用的是SDK,SDK是ABI的一种特定于语言的实现,它使使用该语言编写Wasm Filter变得更加容易。
理论上,我们可以使用各种支持Wasm的语言编写Filter。不过目前官方实现了C++和 Rust 两种语言的SDK。
Envoy 支持 Wasm Network Filter 和 HTTP Filter。
Wasm是沙箱技术。从安全的角度来看,这是非常理想的,但它对内存模型有影响。 Envoy和Wasm VM之间的任何交互都将从Envoy内存复制到Wasm内存,然后再返回。
扩展示例
在本示例中,我们基于 proxy-wasm-rust-sdk 实现一个Wasm 扩展 ,该扩展主要实现功能:当请求的Header中path属性值为“/hello”的时候,Envoy代理直接返回“Hello, World”。
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扩展。我们可以轻松选择自己熟悉的语言实现诸如度量,可观察性,转换,数据丢失预防,合规性验证或其他功能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。