Istio的扩展性,主要体现在数据平面代理的扩展性。

WebAssembly是一种沙箱技术,可用于扩展Istio代理(Envoy)。 Proxy-Wasm 沙箱API取代了Mixer,成为Istio中的主要扩展机制。 Istio 1.6将为Proxy-Wasm插件提供统一的配置API。

WebAssembly 沙箱具备以下特点:

  • 高效-扩展插件在实现功能的前提下,只带来很低的延迟和少量的CPU和内存开销。
  • 功能-扩展插件可以执行策略,收集遥测和修改载荷。
  • 隔离-一个插件发生编程错误或崩溃不会影响其他插件。
  • 配置-使用与其他Istio API一致的API配置插件。扩展名可以动态配置。
  • 开发友好-该插件可以用几种编程语言编写,即支持wasm的语言几乎都可能用来编写插件。

架构

Istio扩展(Proxy-Wasm插件)具有几个方面:

  • Filter Service Provider Interface (SPI) for building Proxy-Wasm plugins for filters.
  • Sandbox V8 Wasm Runtime embedded in Envoy.
  • Host APIs for headers, trailers and metadata.
  • Call out APIs for gRPC and HTTP calls.
  • Stats and Logging APIs for metrics and monitoring.

为了使Envoy能够加载WebAssembly插件并能够对其进行调用并被该插件调用,我们需要一个稳定的接口。这是proxy-wasm的意义。它定义了一个应用程序二进制接口-从WebAssembly模块导出并在运行时可调用的一组函数。从本质上讲,这与动态链接库没有什么不同。

目前官方提供了RustC++两种语言的sdk,此外社区另外提供了TinyGoAssemblyScript 的实现,我们可以基于sdk更加方便地扩展Envoy。

Demo

我们的演示使用rust语言完成。大致包括以下三步:

  • 构建一段Rust代码,该代码实现并使用ABI
  • 将代码编译为WebAssembly
  • 将此部署到Envoy代理中并进行测试。

工具准备

确保你已经安装了最新版的rust。可以通过以下命令检查并查看具体版本:

$ rustc -V
rustc 1.46.0 (04488afe3 2020-08-24)

然后检查是否安装了wasm32-unknown-unknown编译目标:

$ rustup target list | grep wasm32
wasm32-unknown-emscripten
wasm32-unknown-unknown (installed)
wasm32-wasi (installed) 

如果大家没有安装,那么可以执行以下命令进行安装:

$ rustup update
$ rustup target add wasm32-unknown-unknown

创建库并设置

Wasm过滤器是从Rust库项目编译而成的,因此首先我们需要创建该项目:

cargo new --lib my-wasm-filter

生产的库是要被Envoy C++ 代码加载的,因此无需包含任何Rust特定的信息。故我们编辑Cargo.toml文件,将库的类型设置为"cdylib"。

同时我们需要引用proxy-wasm-rust SDK,故需要配置一下proxy-wasm-rust SDK依赖。

如下:

[package]
name = "my-wasm-filter"
version = "0.1.0"
authors = ["iyacontrol <gaohj2015@yeah.net>"]
edition = "2018"

[lib] 
crate-type = ["cdylib"]

[dependencies]
chrono = "0.4"
log = "0.4.8"
proxy-wasm = "0.1.0" # The Rust SDK for proxy-wasm

编写代码

src/lib.rs 代码如下:

use chrono::{DateTime, Utc};
use log::info;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
use std::time::Duration;

#[no_mangle]
pub fn _start() {
    proxy_wasm::set_log_level(LogLevel::Trace);
    proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HelloWorld) });
}

struct HelloWorld;

impl Context for HelloWorld {}

impl RootContext for HelloWorld {
    fn on_vm_start(&mut self, _: usize) -> bool {
        info!("Hello, World!");
        self.set_tick_period(Duration::from_secs(5));
        true
    }

    fn on_tick(&mut self) {
        let datetime: DateTime<Utc> = self.get_current_time().into();
        info!("It's {}", datetime);
    }
}

首先,我们定义了一个名为_start的特殊函数,该函数是ABI的一部分(我们使用no_mangle宏保留名称),然后让我们初始化事情。在其中,我们设置日志级别以跟踪和注册稍后定义的HttpContext。 HTTP上下文是可用的三种上下文类型之一,用于构建HTTP Filter以及RootContext和StreamContext,可分别用于配置和使用计时器以及TCP Filter。您可以阅读可用的API,它们非常简单。

其余代码定义了我们的HelloWorld扩展,实现了必需的Context特性和HttpContext特性。

编译

执行如下语句:

cargo build --target wasm32-unknown-unknown --release

生成结果如下:

主要是 my_wasm_filter.wasm 文件。后续我们会使用到该文件。

运行

配置envoy启动文件,如下:

# envoy-bootstrap.yml
admin:
    access_log_path: /dev/null
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 19000
static_resources:
    listeners:
      - name: listener_0
        address:
          socket_address: { address: 0.0.0.0, port_value: 8080 }
        filter_chains:
          - filters:
              - name: envoy.http_connection_manager
                config:
                  codec_type: AUTO
                  stat_prefix: ingress_http
                  route_config:
                    name: test
                    virtual_hosts:
                      - name: httpbin.com
                        domains: ["*"]
                        routes:
                          - match: { prefix: "/" }
                            route:
                              cluster: static-cluster
                              auto_host_rewrite: true
                  http_filters:
                  - name: envoy.filters.http.wasm
                    config:
                      config:
                        name: "my_wasm_filter"
                        root_id: "my_wasm_filter"
                        vm_config:
                          runtime: "envoy.wasm.runtime.v8"
                          code:
                            local:
                              filename: "/etc/my_wasm_filter.wasm"
                          allow_precompiled: true
                  - name: envoy.router
    clusters:
      - name: static-cluster
        connect_timeout: 0.25s
        type: LOGICAL_DNS
        lb_policy: ROUND_ROBIN
        dns_lookup_family: V4_ONLY
        hosts:
          - socket_address:
              address: httpbin.org
              port_value: 80
              ipv4_compat: true 

通过envoy.filters.http.wasm配置项,将我们编写的my_wasm_filter.wasm 加入到envoy filter中。

为了简单,我们直接基于istio官方proxy镜像打一个新的镜像。Dockerfile如下:

FROM istio/proxyv2:1.7.3
ADD ./target/wasm32-unknown-unknown/release/my_wasm_filter.wasm /etc/my_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/proxyv2:1.7.3 .

运行构建好的镜像:

$ docker run iyacontrol/proxyv2:1.7.3

...

[2020-10-30 02:04:14.174][6][debug][upstream] [external/envoy/source/common/upstream/logical_dns_cluster.cc:153] DNS refresh rate reset for httpbin.org, refresh rate 5000 ms
[2020-10-30 02:04:14.283][15][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1009] wasm log my_wasm_filter : It's 2020-10-30 02:04:14.283022 UTC
[2020-10-30 02:04:14.294][18][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1009] wasm log my_wasm_filter : It's 2020-10-30 02:04:14.294866 UTC
[2020-10-30 02:04:14.311][16][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1009] wasm log my_wasm_filter : It's 2020-10-30 02:04:14.311076 UTC
[2020-10-30 02:04:14.318][17][info][wasm] [external/envoy/source/extensions/common/wasm/context.cc:1009] wasm log my_wasm_filter : It's 2020-10-30 02:04:14.317969 UTC

....

通过打印的日志可以看出,我们的插件已经完美运行。

结论

虽然目前主要用来编写遥测组件,但是后续的发展过程中,我们可以使用我们熟悉的语言,通过wasm实现一些功能插件,从而实现无需修改envoy主体代码,即可扩展我们所需的功能的目的。

Solo.io 推出了一个名为WebAssembly Hub的wasm插件存储服务(使用OCI镜像),让我们可以用来存储和分发wasm 插件。

同时也推出一个名为wasme的cli工具,大大简化了扩展的过程。


iyacontrol
1.4k 声望2.7k 粉丝

专注kubernetes,devops,aiops,service mesh。