When Knative meets WebAssembly

Introduction: Knative can support a variety of containerized runtime environments, today we will explore the use of WebAssembly technology as a new serverless runtime.

Author: Yi Li

Knative is a technical framework for serverless computing based on Kubernetes, which can greatly simplify the development and operation and maintenance experience of Kubernetes applications. Become a CNCF Incubation Project in March 2022. Knative consists of two main parts: one is Knative Serving that supports HTTP online applications, and the other is Knative Eventing that supports CloudEvents and event-driven applications.

Knative can support a variety of containerized runtime environments, and today we will explore leveraging WebAssembly technology as a new serverless runtime.

From WASM, WASI to WAGI

WebAssembly (WASM for short) is an emerging W3C specification. It is a virtual instruction set architecture (virtual ISA) whose initial goal is that programs written in languages ​​such as C/C++ can run safely and efficiently in the browser. In December 2019, W3C officially announced that the core specification of WebAssembly became a Web standard, which greatly promoted the popularization of WASM technology. Today, WebAssembly is fully supported by streaming browsers like Google Chrome, Microsoft Edge, Apple Safari, Mozilla Firefox, and more. More importantly, WebAssembly, as a safe, portable, and efficient virtual machine sandbox, can safely run applications anywhere, on any operating system, and on any CPU architecture.

Mozilla proposed the WebAssembly System Interface (WASI) in 2019, which provides a standard API like POSIX to standardize the interaction of WebAssembly applications with system resources such as file systems and memory management. The emergence of WASI has greatly expanded the application scenarios of WASM, allowing it to run various types of server applications as a virtual machine. In order to further promote the development of the WebAssembly ecosystem, Mozilla, Fastly, Intel, and Red Hat have jointly established the Bytecode Alliance to jointly lead the WASI standard, WebAssembly runtime, tools, and more. Subsequent Microsoft, Google, ARM and other companies also became its members.

WebAssembly technology is still evolving rapidly. In April 2022, W3C released the first public working drafts of WebAssembly 2.0, which has become an important sign of its maturity and development.

As an emerging back-end technology, WASM/WASI has the characteristics of native security, portability, high performance and light weight, and is very suitable as a distributed application operating environment. Unlike containers, which are separate and isolated operating system processes, WASM applications can achieve security isolation within a process, support millisecond-level cold start time and extremely low resource consumption. As shown below:

1.png

Image credit: cloudflare

At present, WASM/WASI is still in the early stage of development, and there are still many technical limitations, such as not supporting threads, unable to support low-level socket network applications, etc., which greatly limits the application scenarios of WASM on the server side. The community is exploring an application development model that can fully adapt to WASM, making use of strengths and circumventing weaknesses. Engineers at Microsoft Deislabs drew inspiration from the history of HTTP server development and proposed the WAGI - WebAssembly Gateway Interface project [1]. Yes, the concept of WAGI comes from the ancient legend of the Internet, CGI.

CGI 是“公共网关接口”(Common Gateway Interface)的简称,是 HTTP 服务器与其它程序进行交互的一种规范。HTTP Server 通过标准输入、输出接口等与 CGI 脚本语言进行通信,开发者可以使用 Python/PHP/Perl 等各种实现来处理 HTTP 请求。

一个非常自然的推演,如果我们可以通过 CGI 规范来调用 WASI 应用,开发者就可以非常轻松地利用 WebAssembly 来编写 Web API 或者微服务应用了,而且无需在 WASM 中处理太多的网络实现细节。下图就是 CGI 与 WAGI 的概念架构图对比:

2.png

二者架构上高度相似,其不同之处是:传统 CGI 架构,每次 HTTP 请求会创建一个 OS 进程来进行处理,由操作系统的进程机制来实现安全隔离;而 WAGI 中 ,每次 HTTP 请求会在一个独立的线程来中调用 WASI 应用,应用之间利用 WebAssembly 虚拟机实现安全隔离。在理论上,WAGI 可以有比 CGI 更低的资源损耗和更快的响应时间。

本文不会对 WAGI 自身架构以及 WAGI 应用开发进行分析。有兴趣的小伙伴可以自行阅读项目文档。

进一步思考,如果我们可以将 WAGI 作为一个 Knative Serving 运行时,我们就可以建立起一座将 WebAssembly 应用于 Serverless 场景的桥梁。

WAGI 应用冷启动分析与优化

冷启动性能是 Serverless 场景的关键指标。为了更好了了解 WAGI 执行效率,我们可以利用 ab 做一个简单的压测:

$ ab -k -n 10000 -c 100 http://127.0.0.1:3000/

...

Server Software:
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      100
Time taken for tests:   7.632 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1510000 bytes
HTML transferred:       120000 bytes
Requests per second:    1310.31 [#/sec] (mean)
Time per request:       76.318 [ms] (mean)
Time per request:       0.763 [ms] (mean, across all concurrent requests)
Transfer rate:          193.22 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.6      0       9
Processing:     8   76  29.6     74     214
Waiting:        1   76  29.6     74     214
Total:          8   76  29.5     74     214

Percentage of the requests served within a certain time (ms)
  50%     74
  66%     88
  75%     95
  80%    100
  90%    115
  95%    125
  98%    139
  99%    150
 100%    214 (longest request) 
复制代码

image.gif

3.png

我们可以看到 P90 请求响应时间在 115ms,就这?这个和我们对 WASM 应用轻量化的认知不同。利用火焰图,我们可以快速定位到问题所在:prepare_wasm_instance 函数消耗了整体应用运行 80% 的时间。

经过对代码的分析,我们发现在每次响应 HTTP 请求过程中,WAGI 都要对已经编译过的 WSM 应用,重新连接 WASI 以及 wasi-http 等扩展和并进行环境配置。这消耗了大量的时间。定位了问题,解决思路就非常简单了,重构执行逻辑,让这些准备工作只在初始化过程中执行一次,无需在每次 HTTP 请求过程中重复执行。具体可参考优化过的实现[2]

我们重新运行一遍压力测试:

$ ab -k -n 10000 -c 100 http://127.0.0.1:3000/

...


Server Software:
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      100
Time taken for tests:   1.328 seconds
Complete requests:      10000
Failed requests:        0
Keep-Alive requests:    10000
Total transferred:      1510000 bytes
HTML transferred:       120000 bytes
Requests per second:    7532.13 [#/sec] (mean)
Time per request:       13.276 [ms] (mean)
Time per request:       0.133 [ms] (mean, across all concurrent requests)
Transfer rate:          1110.70 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.6      0       9
Processing:     1   13   5.7     13      37
Waiting:        1   13   5.7     13      37
Total:          1   13   5.6     13      37

Percentage of the requests served within a certain time (ms)
  50%     13
  66%     15
  75%     17
  80%     18
  90%     21
  95%     23
  98%     25
  99%     27
 100%     37 (longest request)
复制代码

4.png

在经过优化过的实现中,P90响应时间已经下降到 21ms,其中 prepare_wasm_instance 所占运行时间已经下降到 17%。整体冷启动效率有了很大的提升!

注:本文利用 flamegraph[3] 进行的性能分析。

利用 Knative 运行 WAGI 应用

为了让 WAGI 可以作为 Knative 应用运行,我们还需在 WAGI 上增加了对 SIGTERM 信号的支持,让 WAGI 容器支持优雅下线。具体细节不再赘述。

Knative 的环境准备可以参考 Knative 安装文档[4],利用 Minikube 创建本地测试环境。

注:前提是需要有一定的网络能力,因国内无法访问在 gcr.io 中的 Knative 镜像。

一个更加简单的方式是直接使用阿里云 Serverless 容器服务 ASK[5] 上 Serverless K8s 集群。ASK 内建了 Knative 支持[6],无需复杂的配置安装过程即可以开发和使用 Knative 应用。

首先我们利用 WAGI 来定义一个 Knative 服务:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: autoscale-wagi
  namespace: default
spec:
  template:
    metadata:
      annotations:
        # Knative concurrency-based autoscaling (default).
        autoscaling.knative.dev/class: kpa.autoscaling.knative.dev
        autoscaling.knative.dev/metric: concurrency
        # Target 10 requests in-flight per pod.
        autoscaling.knative.dev/target: "10"
        # Disable scale to zero with a min scale of 1.
        autoscaling.knative.dev/min-scale: "1"
        # Limit scaling to 100 pods.
        autoscaling.knative.dev/max-scale: "10"
    spec:
      containers:
      - image: registry.cn-hangzhou.aliyuncs.com/denverdino/knative-wagi:0.8.1-with-cache
复制代码

其中:

  • 容器镜像 knative-wagi 包含了 WAGI 网关和一些示例的 WASI 应用,更多细节可以参考项目[7]。

  • autoscale-wagi 服务可以根据请求数进行弹性伸缩

    $ kubectl apply -f knative_test.yaml

    k u b e c t l g e t k s v c a u t o s c a l e w a g i N A M E U R L L A T E S T C R E A T E D L A T E S T R E A D Y R E A D Y R E A S O N a u t o s c a l e w a g i h t t p : / / a u t o s c a l e w a g i . d e f a u l t . 127.0.0.1. s s l i p . i o a u t o s c a l e w a g i 00002 a u t o s c a l e w a g i 00002 T r u e kubectl get ksvc autoscale-wagi NAME URL LATESTCREATED LATESTREADY READY REASON autoscale-wagi http://autoscale-wagi.default.127.0.0.1.sslip.io autoscale-wagi-00002 autoscale-wagi-00002 True curl autoscale-wagi.default.127.0.0.1.sslip.io Oh hi world $ curl autoscale-wagi.default.127.0.0.1.sslip.io/hello hello world

大家也可以进行一些压测,学习一下 Knative 的弹性伸缩能力。

后记

本文介绍了 WAGI 这个项目,它可以将 HTTP 服务器的网络处理细节,与 WASM 应用逻辑实现解耦。这样可以轻松将 WASM/WASI 应用与 Knative 这样的 Serverless 框架相结合。一方面我们可以复用 Knative/K8s 带来的弹性和大规模资源调度能力,一方面我们可以发挥 WebAssembly 带来的安全隔离、可移植、轻量化等优势。

一脉相承的思考,在之前一篇文章《WebAssembly + Dapr = 下一代云原生运行时?》 中,我介绍了一个思路是将 WASM 应用与外部服务依赖通过 Dapr 实现解耦,来解决可移植性与多样化的服务能力之间的矛盾。

当然这些工作还是简单的玩具,只是用来验证技术的可能性边界。主要目的还是抛砖引玉,听到大家关于下一代分布式应用框架和运行时环境的思考和设想。

文章书写过程中,忽然回忆起在 90 年代与师兄们一起根据 RFC 规范来实现 HTTP Server 与 CGI Gateway 的岁月,那是一种非常简单而单纯的快乐。在这里,也祝每一位技术人永葆好奇,享受每一天的编程时光。

点击此处,了解阿里云 Serverless 容器服务 ASK 更多详情!

原文链接:click.aliyun.com/m/100034865…

本文为阿里云原创内容,未经允许不得转载。

Guess you like

Origin juejin.im/post/7119424352969293861