Skip to content

Commit

Permalink
docs: add APISIX support WASM blog (#754)
Browse files Browse the repository at this point in the history
  • Loading branch information
SylviaBABY authored Nov 19, 2021
1 parent 3f9cc8e commit c4619d0
Show file tree
Hide file tree
Showing 2 changed files with 365 additions and 0 deletions.
183 changes: 183 additions & 0 deletions website/blog/2021/11/19/apisix-support-wasm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
---
title: "Apache APISIX embraces the WASM ecosystem"
author: "Zexuan Luo"
authorURL: "https://github.com/spacewander"
authorImageURL: "https://avatars.githubusercontent.com/u/4161644?v=4"
keywords:
- Apache APISIX
- WASM
- WebAssembly
- Ecosystem
- Plugin
description: Support for WASM will be added in the upcoming Apache APISIX version (2.11.0)! By reading this article you will learn how Apache APISIX deploys the support and development of this feature from 0 to 1.
tags: [Technology]
---

> Support for WASM will be added in the upcoming Apache APISIX version (2.11.0)! By reading this article you will learn how Apache APISIX deploys the support and development of this feature from 0 to 1.
<!--truncate-->

In the upcoming release of Apache APISIX 2.11.0, we will add support for WASM! You can develop plugins in WASM, Lua, Java, Go, Python, JavaScript with Apache APISIX 2.11.0.

![Support WASM](https://static.apiseven.com/202108/1637289637179-ab74d38f-acd4-4401-908f-e1d310a33583.png)

WASM, known as [WebAssembly](https://webassembly.org/), differs from the specific programming language runtimes mentioned above, is a set of bytecode standards specifically designed to be used nested in a host environment.
If a programming language provides the ability to compile to WASM bytecode, applications written in that language can be compiled to WASM bytecode and run in some WASM-enabled host environment.

Doesn't it sound like you can run any application like an operating system as long as the host environment supports WASM?

But there is a limitation here. Just like an operating system needs to implement a specific standard syscall, in order to run a specific application, you need to implement the API required for that application.

Take JavaScript for example, although it is also JavaScript code, JS modules written for browsers can't be used directly in npm packages, because the APIs are different.

So just putting WASM into Apache APISIX doesn't work. To allow developers to run WASM on Apache APISIX, we also need to provide a special API.

## Why Proxy WASM

We weighed two options on how to provide this API.

1. Implement the corresponding WASM version of the API by referring to the lua-nginx-module interface
2. Implement Proxy WASM as a set of standards

[Proxy WASM](https://github.com/proxy-wasm/spec) is Envoy's WASM API standard. So the above question is really equivalent to, do we make our own API standard or do we reuse Envoy's existing standard?

> The WASM API standard can be broken down into two aspects:
1. Host, which is responsible for providing the implementation of the API
2. SDK, which is responsible for implementing a set of glue layers in a different programming language in order to call the provided APIs in that language

If we follow Envoy's standards, the advantage is that we can reuse Envoy's existing WASM SDK (Proxy WASM SDK), while the disadvantage is that this set of standards is developed by Envoy in conjunction with its own situation, and if we follow the implementation, tailoring it to meet our own demands is difficult.

After some community discussion, we finally decided to adopt the Proxy WASM standard. "Doing the hard and right thing" is naturally the hard thing to do, but we believe it is the right thing to do, and through community collaboration and building together, we can build a more prosperous ecosystem.

## How to use WASM in Apache APISIX

Apache APISIX now has initial support for WASM, which can be used to write parts of the fault-injection plugin. You can try it in Apache APISIX 2.11.0 at the end of this month, so stay tuned!

In the following, we will talk about how to use WASM to inject custom responses in conjunction with [proxy-wasm-go-sdk](https://github.com/tetratelabs/proxy-wasm-go-sdk/).

### Step 1: Write code based on proxy-wasm-go-sdk

The implementation code (including go.mod and others) can be found at [here](https://github.com/apache/apisix/tree/master/t/wasm).

It should be explained that although the proxy-wasm-go-sdk project carries the Go name, it actually uses tinygo instead of native Go, which has some problems supporting WASI (which you can think of as a non-browser WASM runtime interface), see [here](https://github.com/tetratelabs/proxy-wasm-go-sdk/blob/main/doc/OVERVIEW.md#tinygo-vs-the-official-go-compiler) for more details.

### Step 2: Build the corresponding WASM file

```shell
tinygo build -o ./fault-injection/main.go.wasm -scheduler=none -target=wasi ./fault-injection/main.go
```

### Step 3: Refer to this file in `config.yaml` in Apache APISIX

```yaml
apisix:
...
wasm:
plugins:
- name: wasm_fault_injection
priority: 7997
file: t/wasm/fault-injection/main.go.wasm
```
By doing so, you can use this WASM plugin as if it were a Lua plugin. For example:
```yaml
---
uri: "/wasm"
plugins:
wasm_fault_injection:
conf: '{"body":"hello world", "http_status":200}'
upstream:
type: roundrobin
nodes:
127.0.0.1:1980: 1
```
Note that the configuration of the WASM plugin is a string under the conf field, which is parsed by the corresponding plugin itself.
## Cross-sectional evaluation
Apache APISIX has evolved to the point where there are three ways to write plugins:
1. Native Lua way, running inside of APISIX.
2. External plugin runner for multiple languages, where the plugin logic runs outside of APISIX.
3. Compile multiple languages into WASM, still running inside of APISIX.
![APISIX ecosystem](https://static.apiseven.com/202108/1637289637159-f2fd1f09-4be6-4cd4-88a0-9c3a23c4f405.png)
These three approaches are very different in various aspects such as ecology and maturity. It just so happens that we can use them all for fault-injection, so we can compare them.
### Step 1: Configure Routing
The Lua way of fault-injection, naturally, uses the built-in fault-injection plugin.The Runner way fault-injection implementation can be found [here](https://github.com/apache/apisix-go-plugin-runner/blob/master/cmd/go-runner/plugins/fault_injection.go).
Let's configure different routes for each of them.
```yaml
---
uri: "/wasm"
plugins:
wasm_fault_injection:
conf: '{"body":"hello world", "http_status":200}'
upstream:
type: roundrobin
nodes:
127.0.0.1:1980: 1
---
plugins:
ext-plugin-pre-req:
conf:
- name: fault-injection
value: '{"body":"hello world", "http_status":200}'
upstream:
nodes:
127.0.0.1:1980: 1
type: roundrobin
uri: /ext-plugin
---
plugins:
fault-injection:
abort:
body: hello world
http_status: 200
upstream:
nodes:
127.0.0.1:1980: 1
type: roundrobin
uri: /fault-injection
```
### Step 2: The actual pressure test
Next, try to use wrk pressure. The specific data comparison is shown in the following chart:
![Pressure test results](https://static.apiseven.com/202108/1637289637162-6d2ef1d6-9de8-410c-8ca6-e264205c1be1.png)
As you can see from the results, the performance of the WASM version is somewhere between that of the external plugin and the native Lua.
The reason why the WASM version performs so much better than the external plugin is that the fault-injection functionality is simple, so the performance loss caused by the external plugin RPC is too obvious. Considering that we haven't made any optimizations to the WASM implementation, we are satisfied with this situation.
Another benefit of WASM is that we have multilingual support at once (thanks to the Proxy WASM SDK). Details can be found in the following documentation:
- [Fault-injection(Rust)](https://gist.github.com/spacewander/0357198ea21e022003c407fd23155f79)
- [Fault-injection(AssemblyScript)](https://gist.github.com/spacewander/64773a706f1dc758aecc7f28aff7555d)
## Advantages and disadvantages
With all these benefits of WASM, aren't you a bit excited? But it is not a perfect solution at the moment, WASM/Proxy WASM still has some immature areas. For example:
- **Programming language support to be improved**: native Go's WASM support is mainly based on the browser environment, so we had to use tinygo to implement it. However, tinygo is a young project and still has a lot of limitations.
- **Proxy WASM ecosystem needs to be mature**: AssemblyScript version of the fault injection implementation, and there is no JSON decode part. This is because the AssemblyScript SDK is based on AssemblyScript version 0.14.x, and several open-source AssemblyScript JSON libraries are implemented for higher versions of AssemblyScript, and can not be used on the older AssemblyScript 0.14.
- **WASM doesn't have a built-in concurrent process**: WASM does not have a built-in concurrent process, so it cannot be scheduled by the host's scheduling system.
While there are several shortcomings listed above, we believe the future of this technology stack is bright:
1. Open source projects, including Apache APISIX and Envoy, have a strong interest in WASM, and there are many startups and large enterprises adding to the WASM ecosystem, which means that difficulties such as the stagnant AssemblyScript SDK will only be temporary. In the long run, the Proxy WASM ecosystem will flourish.
2. WASM has a bright future as the darling of serverless and edge computing. The implementation and optimization of many practical scenarios will solve the technical deficiencies more quickly.
## Come and join the project
Apache APISIX is a project that keeps up with the technology trend, Apache APISIX support for WASM is a long-term process.
"A journey of a thousand miles begins with a single step",Apache APISIX has launched the [wasm-nginx-module](https://github.com/api7/wasm-nginx-module) open source project to support WASM.
Interested readers can follow the progress of the project, we look forward to your joining us and creating the world's top projects together.
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
---
title: "重磅功能!Apache APISIX 拥抱 WASM 生态"
author: "罗泽轩"
authorURL: "https://github.com/spacewander"
authorImageURL: "https://avatars.githubusercontent.com/u/4161644?v=4"
keywords:
- Apache APISIX
- WASM
- WebAssembly
- 生态
- 插件
description: 在即将发布的 Apache APISIX 版本(2.11.0)中将会新增对于 WASM 的支持!通过阅读本文你将了解到 Apache APISIX 如何从 0 到 1 部署这项功能的支持与开发。
tags: [Technology]
---

> 在即将发布的 Apache APISIX 版本(2.11.0)中将会新增对于 WASM 的支持!通过阅读本文你将了解到 Apache APISIX 如何从 0 到 1 部署这项功能的支持与开发。
<!--truncate-->

在即将发布的 Apache APISIX 版本(2.11.0)中,我们新增了对于 WASM 的支持!现在开发者除了可以使用 Lua、Java、Go、Python、JavaScript 等多种编程语言开发 APISIX 的插件之外,也可以用 WASM 来开发插件。

![拥抱 WASM 生态](https://static.apiseven.com/202108/1637289637179-ab74d38f-acd4-4401-908f-e1d310a33583.png)

WASM 全称 [WebAssembly](https://webassembly.org/),与上述具体编程语言运行时的不同之处在于,它是一套字节码标准,专门设计成可以在宿主环境中嵌套使用。

如果某种编程语言提供编译成 WASM 字节码的功能,就可以把该语言的应用编译成 WASM 字节码,运行在某个支持 WASM 的宿主环境中。

听起来,是不是只要某个宿主环境支持 WASM,就能像操作系统一样运行任意应用呢?

但其实这里有个限制,就像操作系统需要实现特定标准的 syscall 一样,要想运行特定的应用,也需要实现该应用所需的 API。

以 JavaScript 为例,虽然同样是 JavaScript 代码,但是针对浏览器写的 JS 模块不能直接用在 npm 包里面,因为两个的 API 不一样。

所以仅仅把 WASM 放到 Apache APISIX 里面并行不通,要想让开发者在 Apache APISIX 上运行 WASM,我们还需要提供一套专门的 API。

## 为什么选择 Proxy WASM

对于如何提供这套 API,我们曾经权衡过两套方案:

1. 参考 lua-nginx-module 的接口,实现对应的 WASM 版 API
2. 实现 Proxy WASM 这一套标准

[Proxy WASM](https://github.com/proxy-wasm/spec) 是 Envoy 的 WASM API 标准。所以上述问题其实等价于,我们是自己搞一套 API 标准还是复用 Envoy 已有标准呢?

> WASM API 标准可以拆成两个方面来看:
1. Host,负责提供 API 的实现
2. SDK,要想在不同的编程语言里面调用提供的 API,需要使用该语言来实现一套胶水层

如果我们遵循 Envoy 的标准,优势在于可以复用 Envoy 现有的 WASM SDK(Proxy WASM SDK),而不足之处在于这套标准是 Envoy 结合自己情况制定的,如果我们跟着实现,没有自己量身定制那么轻松。

经过社区的讨论后,我们最终决定采用 Proxy WASM 标准。「做难且正确的事」,实现 Proxy WASM 自然是难的事,但我们相信这是正确的事,通过社区的合作和共建,可以构建更加繁荣的生态。

## 如何在 Apache APISIX 中使用 WASM

Apache APISIX 目前已初步支持 WASM,可以使用 WASM 来编写 fault-injection 插件的部分功能。感兴趣的读者可以在本月底的 Apache APISIX 2.11.0 版本中尝尝鲜,敬请期待!

下面我们将结合 [proxy-wasm-go-sdk](https://github.com/tetratelabs/proxy-wasm-go-sdk/) 来讲讲怎么用 WASM 实现注入自定义响应的功能。

### 步骤一:基于 proxy-wasm-go-sdk 编写代码

实现代码(包含 `go.mod` 和其他)具体细节可[点击此处](https://github.com/apache/apisix/tree/master/t/wasm)查阅。

这里需要解释下,虽然 proxy-wasm-go-sdk 这个项目带了 Go 的名字,但它其实用的是 tinygo 而不是原生的 Go。因为原生的 Go 在支持 WASI (你可以认为它是非浏览器的 WASM 运行时接口)时会有一些问题,详情可[点击此处](https://github.com/tetratelabs/proxy-wasm-go-sdk/blob/main/doc/OVERVIEW.md#tinygo-vs-the-official-go-compiler)查阅。

### 步骤二:构建对应的 WASM 文件

```shell
tinygo build -o ./fault-injection/main.go.wasm -scheduler=none -target=wasi ./fault-injection/main.go
```

### 步骤三:在 Apache APISIX 的 config.yaml 引用该文件

```yaml
apisix:
...
wasm:
plugins:
- name: wasm_fault_injection
priority: 7997
file: t/wasm/fault-injection/main.go.wasm
```
通过以上操作,你可以像用 Lua 插件一样用这个 WASM 插件,比如:
```yaml
uri: "/wasm"
plugins:
wasm_fault_injection:
conf: '{"body":"hello world", "http_status":200}'
upstream:
type: roundrobin
nodes:
127.0.0.1:1980: 1
```
注意 WASM 插件的配置都是 conf 字段下面的一条字符串,由对应的插件自己去做解析。
## 横向测评——条条大道通罗马
Apache APISIX 发展到现在,已经有三种编写插件的方式:
1. 原生的 Lua way,跑在 APISIX 里面
2. 多种语言的外部插件 runner,插件逻辑跑在 APISIX 外面
3. 把多种语言编译成 WASM,依然跑在 APISIX 里面
![APISIX 生态支持](https://static.apiseven.com/202108/1637289637159-f2fd1f09-4be6-4cd4-88a0-9c3a23c4f405.png)
这三种方式在诸如生态、成熟度等各个方面都差异很大。正巧我们都可以用它们来实现 fault-injection,所以可以比比看。
### 步骤一:配置路由
Lua way 的 fault-injection,自然是使用内置的 fault-injection 插件。Runner way 的 fault-injection 实现具体可[点击此处](https://github.com/apache/apisix-go-plugin-runner/blob/master/cmd/go-runner/plugins/fault_injection.go)查阅。
接下来让我们分别给它们配置不同的路由:
```yaml
---
uri: "/wasm"
plugins:
wasm_fault_injection:
conf: '{"body":"hello world", "http_status":200}'
upstream:
type: roundrobin
nodes:
127.0.0.1:1980: 1
---
plugins:
ext-plugin-pre-req:
conf:
- name: fault-injection
value: '{"body":"hello world", "http_status":200}'
upstream:
nodes:
127.0.0.1:1980: 1
type: roundrobin
uri: /ext-plugin
---
plugins:
fault-injection:
abort:
body: hello world
http_status: 200
upstream:
nodes:
127.0.0.1:1980: 1
type: roundrobin
uri: /fault-injection
```
### 步骤二:实际压测
接下来试着用 wrk 进行压测,具体数据对比如下:
![压测结果对比](https://static.apiseven.com/202108/1637289637162-6d2ef1d6-9de8-410c-8ca6-e264205c1be1.png)
从上述结果可以看到,WASM 版本的性能介于外部插件和原生 Lua 之间。
WASM 版本的性能之所以比外部插件好那么多,是因为 fault-injection 功能简单,所以外部插件 RPC 带来的性能损耗过于明显。考虑到我们还没有对 WASM 实现做任何优化,这种情况已经让我们感到满意了。
而 WASM 的另一个好处,就是让我们一下子拥有多语言的支持(这也托了 Proxy WASM SDK 的福)。具体细节可参考下方文档:
- [Rust 版本 fault-injection](https://gist.github.com/spacewander/0357198ea21e022003c407fd23155f79)
- [AssemblyScript 版本 fault-injection](https://gist.github.com/spacewander/64773a706f1dc758aecc7f28aff7555d)
## 道路曲折,但前途光明
说了这么多 WASM 的好处,是不是有点心动呢?但它目前并非是一个完美的解决方案, WASM/Proxy WASM 还是有一些不够成熟的地方。比如:
- **编程语言支持待完善**:原生 Go 的 WASM 支持,主要是基于浏览器环境的,所以我们不得不用 tinygo 来实现。但是 tinygo 作为一个年轻的项目,还是有不少局限性。
- **Proxy WASM 生态有待成熟**:AssemblyScript 版本的 fault injection 实现,并没有 JSON decode 的部分。这是因为 AssemblyScript SDK 是基于 AssemblyScript 0.14.x 版本实现的,而几个开源的 AssemblyScript JSON 库都是针对高版本 AssemblyScript 实现的,没办法用在较为陈旧的 AssemblyScript 0.14 上。
- **WASM 没有内置协程**:WASM 目前尚未内置协程,所以没办法很好地被宿主的调度系统给调度起来。
虽然上面列举了几点不足之处,但是我们相信这个技术栈的前景是光明的:
1. 包括 Apache APISIX 和 Envoy 等开源项目对于 WASM 都很看重,有许多初创公司和大企业为 WASM 生态添砖加瓦,这意味着诸如 AssemblyScript SDK 停滞不前这样的困难,只会是暂时。长久看,Proxy WASM 的生态会枝繁叶茂。
2. WASM 作为 serverless 和 edge computing 的宠儿,有着光明的未来。在众多实际场景的落地和优化,会更快的解决技术上的不足。
## 写在最后
Apache APISIX 是个紧跟技术潮流的项目,“好风凭借力,送我上青天”,Apache APISIX 支持 WASM 是个长期的过程。
“千里之行,始于足下”,Apache APISIX 为了支持 WASM,已经发起了 [wasm-nginx-module](https://github.com/api7/wasm-nginx-module) 这个开源项目。感兴趣的读者可以关注该项目的进展,“独行者速,众行者远”,期待你的加入,一起创造世界顶级项目。

0 comments on commit c4619d0

Please sign in to comment.