WebAssembly + Dapr = next generation cloud native runtime?

Introduction: cloud computing has become a key infrastructure supporting the development of digital economy. Cloud computing infrastructure is also evolving. From IaaS to container as a service (CaaS), to Serverless container and function PaaS (fPaaS or FaaS), new computing forms have emerged one after another. Cloud native technology represented by container and Serverless is reshaping the whole application life cycle.

Author Yi Li
Source | Alibaba cloud official account

Cloud computing has become a key infrastructure supporting the development of digital economy. Cloud computing infrastructure is also evolving. From IaaS to container as a service (CaaS), to Serverless container and function PaaS (fPaaS or FaaS), new computing forms have emerged one after another. Cloud native technology represented by container and Serverless is reshaping the whole application life cycle.

In Gartner's analysis report, the development path of cloud computing infrastructure is also a process of gradual enhancement of cloud native characteristics. Its specific performance is as follows:

  • Modularity is getting higher and higher - more fine-grained computing units, such as containers and Serverless functions, are more suitable for the application delivery of microservice architecture, and can make more full use of the capabilities of the cloud and improve the agility of the architecture.
  • Increasingly programmable - automatic management and operation and maintenance can be realized through declarative API s and policies, and the certainty of distributed application operation and maintenance can be further improved through Immutable Infrastructure.
  • More and more elastic efficiency - VM can realize minute scale expansion; Container and Serverless container can realize second level capacity expansion; With the help of scheduling optimization, the function can achieve millisecond capacity expansion.
  • Higher and higher toughness - Kubernetes provides strong automatic editing ability and improves the self-healing of application system. Serverless further reduces the system level complexity such as stability, scalability and security to infrastructure. Developers only need to pay attention to their own business application logic, which further releases productivity and improves the recoverability of the system.

Distributed cloud is another important trend in the development of cloud computing. Public cloud services can be extended to different physical locations to make computing closer to customers. Distributed cloud not only allows customers to enjoy the convenience of cloud computing, but also meets the demands of real-time computing and security compliance. This also promotes the change of Enterprise Application Architecture - applications should be able to deploy and migrate in different environments and provide services in an optimized way.

Further, with the emergence of new technologies such as mobile Internet, AI and IoT, ubiquitous computing has become a reality. At the same time, this is also giving birth to the diversity of computing power. The era of X86 architecture dominating the world has passed. New chip forces such as ARM/RISC-V are not only dominating the field of mobile communications and embedded devices, but also launching attacks on the edge computing and data center market. Developers even need to make applications support different CPU architectures. For example, we can deploy an image recognition application to run on devices with different environments and architectures such as edge or IoT.

Under the new cloud computing scenarios such as distributed cloud, edge computing and cloud integration, what characteristics will the next generation of cloud native application runtime have?

Next generation cloud native application runtime

1. Ubiquitous computing gives birth to the next generation of portable, high-performance and lightweight security sandbox

Container applications adopt a self-contained packaging method - container image, which contains application code and dependent system components, which can decouple applications from infrastructure, enable applications to be deployed, operated and maintained in a consistent manner in different operating environments such as public cloud and private cloud, and simplify elasticity and migration. In addition, Docker image specification supports multi arch image, which can simplify the construction and distribution of application images of different CPU architectures (such as x86, ARM, etc.).

The function application only contains the code package for event response, which improves the application delivery format from the native binary file to the high-level language level. This also brings more imagination to the portability of applications. In theory, it can even shield the differences of CPU architecture in the execution environment. For example, scripts such as Python/NodeJS or Java applications that do not rely on local code can run on different CPU architectures such as x86 or ARM without modification.

However, the portability of PAS is ideal.

  • Many script codes still need to call native code to realize data processing and call middleware (such as database driver), but compiling native code needs to be consistent with the target execution environment to ensure compatibility. For example, AWS Lambda / alicloud function computing requires binary native code to rely on the specified kernel and libc version. Therefore, more and more function PaaS services support container image as a carrier to simplify function application packaging and dependency management.
  • Function applications usually rely on back-end services (BaaS) to achieve data access, computing and processing capabilities. Since there are no standards in BaaS, it is difficult to transplant function applications developed on AWS Lambda to Alibaba cloud function computing services.

In Serverless computing, the existing mainstream technology is to use sandbox container technology, such as AWS firecracker or Alibaba cloud sandbox container, to achieve a strong isolated secure execution environment, but it also brings greater resource consumption. Although Alibaba cloud sandbox containers are now optimized to achieve a cold start speed of 300ms, which is close to that of OS containers such as Docker, it still cannot meet the millisecond start requirements of function PaaS. At present, a certain standby instance needs to be reserved through the scheduling strategy, but this also introduces more resource consumption.

Web assembly (WASM) is a new W3C specification. It is a general, open, efficient and secure underlying virtual machine abstraction. Its original intention is to solve the performance problem of JavaScript and make web applications close to the performance of native applications. Existing programming languages, such as C/C++, Rust, etc., can be compiled into the bytecode of WASM and run in a sandbox environment in the browser.

WASM decouples the application development technology from the runtime environment, which greatly promotes code reuse. Mozilla also launched the web assembly system interface (WASI) in 2019, which provides standard API s like POSIX to standardize the interaction abstraction between web assembly and system resources, such as file system access, memory management, etc. The emergence of WASI expands the application scenario of WASM, which can run various types of server applications as a virtual machine. WASM/WASI brings new hope to the portability of applications. In order to further promote the ecological development of WebAssembly, Mozilla, Fastly, Intel and red hat have jointly established the Bytecode Alliance to jointly lead the WASI standard, WebAssembly runtime, tools and other work.

WebAssembly has the characteristics of security, portability, high efficiency and lightweight, which brings a new idea to the development of application sandbox. WASM can easily achieve millisecond cold start time and extremely low resource consumption. At the same time, WASM bytecode has a higher security level than native machine code. In addition, WASI implements a fine-grained capability based security model, which follows the principle of minimum permission. During execution, WASI applications can only access the exact resource set specified by dependency injection. Compared with the traditional coarse-grained operating system level isolation, this method further converges the security attack surface.

For this reason, WASM/WASI has received extensive attention from Serverless, IoT / edge computing and other communities. Fastly, Cloudflare and other manufacturers have successively released more lightweight Serverless services based on WebAssembly technology.

However, the application of web assembly on the server side is still full of thorns. First of all, WASI's capabilities are still very early, and some key capabilities are still missing. The first is the lack of standardized network access capabilities: https://github.com/WebAssembly/WASI/issues/315.

At present, WASI applications can only do some computing tasks, and basically can not realize distributed applications, nor can they call diverse back-end services and application middleware such as Redis, MySQL and Kafka. This greatly limits the application scenarios of WASI.

When the ideal collides with reality, is it a broken head or a desperate survival?

2. When the next generation of portable applications are running, the programming interface is accelerated to move up, and the ability of application infrastructure is sinking

Dapr is Microsoft's open source distributed application runtime for cloud native applications. Its goal is to enable all developers to easily build elastic, event driven and portable microservice applications in any language and framework.

Dapr implements a series of design patterns for building high-performance, scalable and highly available distributed applications, such as providing service discovery and service invocation capabilities, and also implements a simple and consistent programming model to support event driven application architecture.

In addition, Dapr shields the technical details of application access to back-end services through infrastructure, such as resource binding, security management, observability and so on. This is very important for Serverless applications. On the one hand, it couples development and deployment, so that developers and operation and maintenance teams can simplify system complexity through separation of concerns; On the one hand, the short life cycle and stateless Serverless application logic can be decoupled from the long-term operation of database connection pool management and the access ability of stateful middleware, which improves the scalability and operation efficiency of Serverless application.

"Any language, any framework, anywhere" is an important design goal of dapr. Dapr provides an abstraction layer between applications and back-end services through Sidecar, and realizes the portability of applications and the replaceability of back-end services through standardized HTTP/gRPC API.

Towards poetry and distance

We can combine WebAssembly and dapr to realize a portable, strong isolation and lightweight microservice application architecture. Dapr sidecar is deployed with WASM virtual machine. WASI applications access the local dapr service endpoint through HTTP/gRPC, and dapr proxy connects various back-end services or realizes inter service communication.

This architecture design makes the security boundary of WASI applications very clear and conforms to the WASI security model. WASI applications can only access external services through Dapr sidecar. At the same time, in this architecture, only WASM virtual machine and Dapr run with native machine code as trusted environment. The application is a portable WASM bytecode, which greatly improves the portability and security of the architecture.

Radu Matei from Microsoft Deis Labs recently provided an experimental project to add HTTP support to WASI. See: https://deislabs.io/posts/wasi-experimental-http/ .

On this basis, we build a minimum prototype to verify the technical feasibility of the combination of WebAssembly and Dapr.

1. Dapr environmental preparation

We first follow https://docs.dapr.io/getting-started/ Process:

$ dapr init
⌛  Making the jump to hyperspace...
✅  Downloading binaries and setting up components...
✅  Downloaded binaries and completed components set up.
ℹ️  daprd binary has been installed to /Users/yili/.dapr/bin.
ℹ️  dapr_placement container is running.
ℹ️  dapr_redis container is running.
ℹ️  dapr_zipkin container is running.
ℹ️  Use `docker ps` to check running containers.
✅  Success! Dapr is up and running. To get started, go here: https://aka.ms/dapr-getting-started


$ dapr run --app-id myapp --dapr-http-port 3500
WARNING: no application command found.
ℹ️  Starting Dapr with id myapp. HTTP Port: 3500. gRPC Port: 63734
ℹ️  Checking if Dapr sidecar is listening on HTTP port 3500
...
ℹ️  Checking if Dapr sidecar is listening on GRPC port 63734
ℹ️  Dapr sidecar is up and running.
✅  You're up and running! Dapr logs will appear here.

2. Use Redis as the state storage of WASI application

Let's use Dapr's Get Started example and Redis as the state storage of WASI applications. The specific logic is shown in the figure below.

Note: the following applications require # Rust and AssemblyScript # environment configuration. Please complete it yourself.

We fork a version based on the Radu project. First, download the code and build it.

$ git clone https://github.com/denverdino/wasi-experimental-http
$ cd wasi-experimental-http
$ cargo build
...
    Finished dev [unoptimized + debuginfo] target(s) in 3m 02s

We use AssemblyScript to implement this test application. The test code is as follows:

$ cat tests/dapr/index.ts
// @ts-ignore
import { Console } from "as-wasi";
import { DaprClient, StateItem } from "./dapr";
import { JSON } from "assemblyscript-json";


Console.log("Testing Dapr API ....")

let dapr = new DaprClient()
dapr.saveState("statestore", "weapon", JSON.Value.String("Death Star"))

let o = JSON.Value.Object()
o.set("name", "Tatooine")
o.set("test", 123)
let item = new StateItem("planets", o)
let items: StateItem[] = [item]
dapr.saveBulkState("statestore", items)

let testObj = dapr.getState("statestore", "planets")
let testStr = dapr.getState("statestore", "weapon")

if (testStr.toString() == "Death Star" && testObj.isObj && (<JSON.Integer>(<JSON.Obj>testObj).getInteger("test")).valueOf() == 123) {
    Console.log("Test successfully!")
} else {
    Console.log("Test failed!")
}

The code logic is very simple, that is, create a Dapr client, and then manage the state of Dapr through the REST API. We can verify it quickly.

$  cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.19s
     Running `target/debug/wasi-experimental-http-wasmtime-sample`
Testing Dapr API ....
POST http://127.0.0.1:3500/v1.0/state/statestore with [{"key":"weapon","value":"Death Star"}]
POST http://127.0.0.1:3500/v1.0/state/statestore with [{"key":"planets","value":{"name":"Tatooine","test":123}}]
GET http://127.0.0.1:3500/v1.0/state/statestore/planets
GET http://127.0.0.1:3500/v1.0/state/statestore/weapon
Test successfully!
module instantiation time: 333.16637ms

3. Analysis of key points

The WASI experimental HTTP} project implements an extension on the Wasmtime (a WASM implementation from Bytecode Alliance) virtual machine to support access to HTTP services in WASI applications. It also provides an HTTP Client implementation of AssemblyScript.

Wasi experimental HTTP} project: https://github.com/deislabs/wasi-experimental-http/.

On this basis, we provide a Dapr package for AssemblyScript. See: https://github.com/denverdino/wasi-experimental-http/blob/main/tests/dapr/dapr.ts.

// @ts-ignore
import { Console } from "as-wasi";
import { Method, RequestBuilder, Response } from "../../crates/as";

import { JSONEncoder, JSON } from "assemblyscript-json";

export class StateItem {
  key: string
  value: JSON.Value
  etag: string | null
  metadata: Map<string, string> | null

  constructor(key: string, value: JSON.Value) {
    this.key = key
    this.value = value
    this.etag = null
    this.metadata = null
  }
}

...

export class DaprClient {
  port: i32
  address: string

  constructor() {
    this.address = "127.0.0.1"
    this.port = 3500
  }

  stateURL(storeName: string): string {
    return "http://" + this.address + ":" + this.port.toString() + "/v1.0/state/" + storeName
  }

  saveState(storeName: string, key: string, value: JSON.Value): boolean {
    let item = new StateItem(key, value)
    let items: StateItem[] = [item]
    return this.saveBulkState(storeName, items)
  }

  saveBulkState(storeName: string, items: StateItem[]): boolean {
    // Handle field
    let encoder = new JSONEncoder();

    // Construct necessary object
    encoder.pushArray(null);
    for (let i = 0, len = items.length; i < len; i++) {
      let item = items[i]
      encoder.pushObject(null);
      encoder.setString("key", item.key)
      encodeValue(encoder, "value", item.value)
      if (item.etag != null) {
        encoder.setString("etag", <string>item.etag)
      }
      encoder.popObject()
    };
    encoder.popArray();
    // Or get serialized data as string
    let jsonString = encoder.toString();
    let url = this.stateURL(storeName);
    Console.log("POST " + url + " with " + jsonString);
    let res = new RequestBuilder(url)
      .method(Method.POST)
      .header("Content-Type", "application/json")
      .body(String.UTF8.encode(jsonString))
      .send();
    let ok = res.status.toString() == "200"
    res.close();
    return ok
  }

  getState(storeName: string, key: string): JSON.Value {
    let url = this.stateURL(storeName) + "/" + key;
    Console.log("GET " + url);
    let res = new RequestBuilder(url)
      .method(Method.GET)
      .send();
    let ok = res.status.toString() == "200"
    let result = <JSON.Value> new JSON.Null()
    if (ok) {
      let body = res.bodyReadAll();
      result = <JSON.Value>JSON.parse(body)
    }
    res.close();
    return result
  }
};

The main function of the test application will create a Wasmtime runtime environment, add it as an HTTP extension, and load the WASM bytecode for executing the test application: https://github.com/denverdino/wasi-experimental-http/blob/main/src/main.rs.

fn main() {
    let allowed_domains = Some(vec![
        "http://127.0.0.1:3500".to_string(),
    ]);
    let module = "tests/dapr/build/optimized.wasm";
    create_instance(module.to_string(), allowed_domains.clone()).unwrap();
}

/// Create a Wasmtime::Instance from a compiled module and
/// link the WASI imports.
fn create_instance(
    filename: String,
    allowed_domains: Option<Vec<String>>,
) -> Result<Instance, Error> {
    let start = Instant::now();
    let store = Store::default();
    let mut linker = Linker::new(&store);

    let ctx = WasiCtxBuilder::new()
        .inherit_stdin()
        .inherit_stdout()
        .inherit_stderr()
        .build()?;

    let wasi = Wasi::new(&store, ctx);
    wasi.add_to_linker(&mut linker)?;
    // Link `wasi_experimental_http`
    let http = HttpCtx::new(allowed_domains, None)?;
    http.add_to_linker(&mut linker)?;

    let module = wasmtime::Module::from_file(store.engine(), filename)?;

    let instance = linker.instantiate(&module)?;
    let duration = start.elapsed();
    println!("module instantiation time: {:#?}", duration);
    Ok(instance)
}

The road is blocked and long, and the line is coming

WASM/WASI provides a good foundation for lightweight, portable and default safe application runtime. WebAssembly has been widely used in blockchain and other fields. However, for general server-side applications, the gap between WASM/WASI is still very obvious. Due to the lack of standardized network programming interface such as berkeley socket, it can only be supplemented by expanding WASM virtual machine. In addition, the multithreading capability of WASM has not been standardized. The current HTTP call adopts blocking synchronous call, which can not achieve efficient and stable network communication.

In addition, another weakness of WASM/WASI is development efficiency and ecological construction. At present, although many programming languages have gradually begun to provide support for web assembly, for ordinary developers, a scripting language such as AssemblyScript is a more appropriate choice. AssemblyScript reuses the syntax of TypeScript. Compared with Rust/C + +, it greatly reduces the learning curve and provides a very good experience of IDE tools, such as VS Code. However, unlike TypeScript, which is translated into JavaScript for execution, AssemblyScript applications are compiled into WASM bytecode for execution. AssemblyScript is essentially a statically typed compiled language, which is very different from the dynamically typed interpreted language such as JS/TS. There are also some differences in syntax between the two. For example, at present, AssemblyScript lacks support for common functions such as closure and regex, which still has a certain technical threshold for developing WASM applications.

In addition, compared with the strong ecology of NPM, the AssemblyScript community is also very young. Many functions need to be built from scratch, such as serialization and deserialization of JSON. We chose_ https://github.com/nearprotocol/assemblyscript-json_ However, there is still a certain gap between its ease of use and performance and the mature JSON class library. Of course, we also see the rapid growth of AssemblyScript, and more and more developers begin to contribute to the AssemblyScript code base, such as regex support and so on.

The emergence of Dapr brings another ray of light for the development of general distributed applications for WASM/WASI, especially for portable and Serverless applications. However, Dapr is not perfect: API standardization not only improves the portability of back-end services, but also hinders the support of differentiation. Sidecar architecture increases deployment and management complexity while improving flexibility.

As a rational optimist, any technology has its green era. We look forward to the joint efforts of the community to make the ideal of ubiquitous computing and accessible innovation a reality.

Original link

This article is the original content of Alibaba cloud and cannot be reproduced without permission.

Keywords: Web Development Cloud Native

Added by Brendan Nolan on Fri, 18 Feb 2022 01:27:12 +0200