通过WASM和Rust扩展Envoy

如果您已经知道Istio,Envoy,WASM和Rust的共同点,并且只想开始构建您的过滤器,可以直接跳到第2部分-构建过滤器。

第1部分:网格的演变

正如我在过去几年中多次提到的那样,服务网格是云原生基础架构发展的下一阶段。

Istio可扩展性的历史

尽管如此,与Istio一样,每种网格实现仍以自己的速度继续发展,由于Google和IBM支持Istio,所以Istio在与功能相关的所有方面均领先于竞争对手。但是可惜-在版本1.5之前,Istio还因项目开发初期做出的许多体系结构决策而导致的性能问题而闻名。性能瓶颈的主要根源之一是名为Mixer的组件。它在网状网络中的主要职责包括执行流量策略和收集遥测。 Mixer在Adapters的帮助下完成了所有这一切,Adapters是一种扩展机制,可以方便地使Istio与第三方策略和遥测系统集成。不利的一面是,Mixer驻留在每个网络请求的数据路径中,并在过载时导致无法忍受的延迟。请查看此基准测试报告,以了解1.5版之前的Istio如何在高流量下发挥作用。

Istio团队一直在努力解决性能问题。他们必须做出的艰难决定之一是将Mixer移除,并将遥测和策略设置功能转移到网状代理中。在Istio的情况下,这些代理是Envoy实例。但是Mixer适配器呢?为了不放弃支持吞吐量的可扩展性,必须设计一种新的解决方案。Envoy代理现在允许加载WebAssembly模块以在其网络过滤器链中使用。

WASM 与 Rust

我不会浪费您的时间描述什么是WebAssembly,以及安全轻量级计算工作负载的未来。关于此还有其他博客文章。我只是说,Envoy现在支持WASM,这意味着人们可以使用C++,AssemblyScript或Rust编写过滤器。这是让我最兴奋的最后一个选择-因为我非常喜欢Rust,并且一直在寻找一个可以使用它的实用项目。

关于WebAssemblyHub

如果您想快速开始创建自己的基于WASM的Envoy过滤器,Solo.io的优秀人员已经提供了一些工具来帮助您起步。他们的WebAssemblyHub和wasme命令行实用程序允许创建过滤器,打包过滤器并将其部署到Gloo,Istio或独立的Envoy。但是碰巧的是-他们仍然没有内置Rust支持。

总的来说,Rust的proxy-sdk似乎落后于C++和AssemblyScript。在尝试使基于Rust的过滤器正常工作时,我甚至以为SDK尚未准备就绪。但是多亏了Victor Charypar,他非常高兴地回应了我的Github问题,我终于意识到这是我的Envoy版本存在问题。

无论如何-最终,我的过滤器开始工作了,这篇文章的第2部分接着介绍了构建基于WASM的Envoy过滤器所需采取的步骤:

第2部分:构建过滤器

准备好工具箱

您使用Rust的程度可能会有所不同-因此,以防万一您仍然没有安装Rust和Cargo(出色的Rust包管理器),请继续进行操作。在Linux和macOS系统上,此操作如下:

curl https://sh.rustup.rs -sSf | sh

如果一切顺利,您会看到以下内容:

Rust is installed now. Great!

请记住-我们在这里处理一些前沿功能,因此基本的Rust安装是不够的。我们还需要安装Rust nightly工具链和对WASM编译目标的支持。幸运的是,这非常容易。您只需要运行:

rustup toolchain install nightly
rustup target add wasm32-unknown-unknown

现在我们已经准备好所有工具-让我们初始化项目:

创建库

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

cargo new --lib my-wasm-filter

这将在“ my-wasm-filter”目录中创建一个模板库项目。您将在src/目录中找到一个lib.rs文件,以及一个Cargo.toml文件,该文件告诉Cargo如何构建您的项目。

设置库类型

生成的库是从Envoy的C++代码加载的,因此无需在其中包含任何Rust特定的信息。因此,我们将按照此处的定义将库类型设置为“cdylib”。这将产生较小的二进制文件。为此,请打开您的Cargo.toml文件,然后在[lib]部分下添加:

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

Proxy WASM SDK

Envoy过滤器必须基于proxy-wasm项目提供的SDK。特别是proxy-wasm-rust SDK的第一个版本已在此处Crates.io上发布。因此,我们需要将其作为依赖项添加到我们的Cargo.toml中:

[dependencies] 
proxy-wasm =  "0.1.0"

现在我们可以编写代码了。

编写代码

在撰写本文时-不存在用于编写Envoy WASM过滤器的详细文档。我的代码基于proxy-sdk项目中的示例,以及基于envoy-wasm项目中基于CPP的过滤器的示例。

基本上,我们需要做的是:

  • 为我们的过滤器实现基本的上下文特征。
  • 实现一个HttpContext特征,该特征继承基本上下文特征。
  • 覆盖上下文API方法以处理来自主机的相应初始化和http标头/事件。
  • 通过调用proxy_wasm::set_http_context初始化上下文。

注意:我们的示例仅处理HTTP标头。如果我们要处理其他流和初始化事件-我们需要相应地实现StreamContext或RootContext。

实现的filter功能

我决定实现一个非常简单的假想方案,其中发送到我们服务的每个请求都需要通过发送令牌进行授权,然后由过滤器检查令牌的有效性。如果令牌已验证-请求将传递到服务。否则-403响应返回给调用方。

令牌的有效性检查非常笨拙-我们检查令牌是否为质数。为了做到这一点-让我们在Cargo.toml中添加另一个依赖项:

[dependencies] 
proxy-wasm =  "0.1.0"  
primes  =  "0.3.0"

具体实现:

让我们创建上下文:

struct  PrimeAuthorizer  {    
    context_id: u32,    
}

并为其实现Context类:

impl  Context  for  PrimeAuthorizer  {}

注意-我们不需要在Context中实现任何方法,因为我们在L7级别上进行了所有工作-仅处理HTTP标头。但是我们仍然需要在代码中包含它,因为我们的上下文必须实现基本的上下文特征。

现在开始实际工作-让我们实现HttpContext。实际上,我们只需要实现一种方法:HttpContext :: on_http_request_headers-在标头到达时对其进行验证:

rust01.jpg

如您所见-该方法调用self.get_http_request_header来查找名为“ token”的标头,然后检查其优先级。如果令牌是素数,则self.resume_http_request()将请求进一步传递到目标群集。否则,将返回403响应,提示“禁止访问”。该方法返回一个Action枚举,该枚举告诉Envoy是继续请求处理还是暂停并等待下一个请求。

测试Filter

虽然构建过滤器非常容易-验证它的工作证明有些痛苦。官方的Envoy二进制文件仍然没有内置的WASM支持。但是我假设如果我在启用WASM的情况下构建自己的Envoy二进制文件-过滤器应该可以正常加载。我构建的二进制文件基于此github存储库,但是可惜-Envoy因“由于缺少导入而无法加载WASM模块而崩溃:env.proxy_get_configuration”

下一步-我决定使用wasme CLI工具使用的Envoy镜像来测试构建的过滤器。这是一个wrapper:https://quay.io/repository/so...

但是这次Envoy崩溃了,“ WASM缺少malloc/free”。在尝试解决此问题时,我在proxy-wasm-rust项目上打开了一个github问题。多亏了Victor Charypar,我终于意识到我应该使用Istio官方代理镜像https://hub.docker.com/r/isti...。特别是1.5.0标签。一旦我以此为基础构建了镜像-一切都OK了!万岁,我的filter做了我所期望的!

在docker-compose文件中完成测试,我的最终项目可以在这里找到:

https://github.com/otomato-gh/proxy-wasm-rust

所有你需要做的是:

  • 克隆代码仓库
  • cargo +nightly build –target=wasm32-unknown-unknown –release

    这将在<您的克隆目录> /target/wasm32-unknown-unknown中创建文件“myenvoyfilter.wasm”。 WASM文件的名称由Cargo.toml中的此配置定义:

    [lib]  
    name  =  "myenvoyfilter"
  • docker-compose up –build

    这将基于istio/proxyv2构建一个新的envoy 镜像,并拉取hasicorp/ http-echo 镜像,该镜像用作目标服务。

    您应该看到类似以下内容:

proxy_1        | [2020-04-17 13:15:36.931][14][debug][wasm] [external/envoy/source/extensions/common/wasm/wasm.cc:285] Thread-Local Wasm created 4 now active


proxy_1        | [2020-04-17 13:15:36.935][14][debug][upstream] [external/envoy/source/common/upstream/cluster_manager_impl.cc:1084] membership update for TLS cluster web_service added 1 removed 0

请注意,Envoy已设置为侦听主机的端口18000。而且我们的新建过滤器是从我们的构建目标中加载的:

  proxy:  
   ..  
    volumes:  
      -  ./envoy/envoy.yaml:/etc/envoy.yaml  
      -  ./target/wasm32-unknown-unknown/release/myenvoyfilter.wasm:/etc/myenvoyfilter.wasm  
    ..  
   ports:  
    -  "18000:80"

现在转到另一个提示,并通过发送curl请求来验证一切正常:

 curl -H "token":"323232" 0.0.0.0:18000 
 Access forbidden. 
 curl -H "token":"32323" 0.0.0.0:18000 
 "Welcome to WASM land"

随意尝试使用其他素数和非素数数字或字符串。让我知道您是否成功实现。

总结:

  • WASM过滤器允许扩展Envoy功能
  • 我们可以使用Rust为Envoy构建WASM过滤器
  • 当前,需要Istio1.5 +中的Envoy二进制文件才能成功执行这种过滤器。
阅读 345

推荐阅读
kubernetes solutions
用户专栏

专注k8s,serverless,service mesh,devops

1952 人关注
187 篇文章
专栏主页