/wow

A WebAssembly container runtime for the Apache OpenWhisk serverless platform

Primary LanguageRust

WebAssembly-flavored OpenWhisk

A WebAssembly-based container runtime for the Apache OpenWhisk serverless platform.

Why?

The cold start problem is well-known in serverless platforms. The first invocation of a function requires provisioning all the resources necessary for its execution. Typically that means starting a docker container, i.e. a node.js container to then inject some JavaScript code for execution. This process can take hundreds of milliseconds and is thus unacceptable for latency-critical tasks. This project is an evaluation of using WebAssembly instead of Docker containers to implement serverless functions. The simple idea is that WebAssembly is a lighter-weight sandboxing mechanism so the cold start latency should be much smaller. It builds on top of Apache OpenWhisk as the serverless platform. There are three repositories relevant to this:

  • This repository, which implements the WebAssembly-based container runtime.
  • The OpenWhisk fork that implements the runtimes support on the OpenWhisk side
  • The diploma thesis repository, which contains the thesis describing the motivation, background, design and evaluation of this idea.

Crates Overview

The project is split into multiple crates, which are:

  • ow-common contains common types such as the WasmRuntime trait or types that represent OpenWhisk payloads.
  • ow-executor implements the actual container runtime and the OpenWhisk runtime protocol.
  • ow-wamr implements the WasmRuntime trait for the WebAssembly Micro Runtime.
  • ow-wasmer implements the WasmRuntime trait for Wasmer.
  • ow-wasmtime implements the WasmRuntime trait for Wasmtime.
  • ow-wasm-action contains abstractions for building WebAssembly serverless functions ("actions" in jOpenWhisk terminology) and has a number of example actions.
  • ow-wasmtime-precompiler implements Ahead-of-Time compilation for wasmtime (which is part of the runtime as of version 0.26), making this crate obsolete once ow-wasmtime is migrated to that version.
  • ow-evaluation contains some tests used for evaluating the performance of the system, such as concurrency tests and cold start tests.

Tutorial with Wasmtime

As a small tutorial, let's build the wasmtime executor and run one of the examples.

  1. To build the executor with wasmtime run the following command from the root of this repository:
cargo build --manifest-path ./ow-executor/Cargo.toml --release --features wasmtime_rt
  1. Next we build the add example for the wasm32-wasi target with:
cargo build --manifest-path ./ow-wasm-action/Cargo.toml --release --example add --target wasm32-wasi --no-default-features --features wasm

# Optional step to optimize the compiled Wasm if `wasm-opt` is installed
# On Ubuntu it can be installed with `sudo apt install binaryen`
wasm-opt -O4 -o ./target/wasm32-wasi/release/examples/add.wasm ./target/wasm32-wasi/release/examples/add.wasm
  1. Precompile the example for efficient execution with wasmtime:
cargo run --manifest-path ./ow-wasmtime-precompiler/Cargo.toml --release --bin wasmtime ./target/wasm32-wasi/release/examples/add.wasm
  1. Install wsk-cli from https://github.com/apache/openwhisk-cli/releases/tag/1.2.0

  2. Clone the openwhisk repo, checkout the appropriate branch and run OpenWhisk in a separate terminal:

git clone git@github.com:PhilippGackstatter/openwhisk.git
git checkout invoker-wasm
./gradlew core:standalone:bootRun

This will print something like the following:

[ WARN  ] Configure wsk via below command to connect to this server as [guest]

wsk property set --apihost 'http://172.17.0.1:3233' --auth '23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP'

Execute this command.

  1. Run the executor in a separate terminal. OpenWhisk will forward execution requests for Wasm to it:
./target/release/executor
  1. Upload the example zip to OpenWhisk:
wsk action create --kind wasm:0.1 add ./target/wasm32-wasi/release/examples/add-wasmtime.zip
  1. Run the action. We need to provide the correct parameters for this action, which are defined in our action source file ow-wasm-action/examples/add.rs. Let's see what the result of 2+2 is:
wsk action invoke --blocking add --param param1 2 --param param2 2

which prints:

{
  "activationId": "b67ca8877a494fcdbca8877a494fcd73",
  "annotations": [
    {
      "key": "path",
      "value": "guest/add"
    },
    {
      "key": "waitTime",
      "value": 23
    },
    {
      "key": "kind",
      "value": "wasm:0.1"
    },
    {
      "key": "timeout",
      "value": false
    },
    {
      "key": "limits",
      "value": {
        "concurrency": 1,
        "logs": 10,
        "memory": 256,
        "timeout": 60000
      }
    },
    {
      "key": "initTime",
      "value": 3
    }
  ],
  "duration": 5,
  "end": 1655918974484,
  "logs": [],
  "name": "add",
  "namespace": "guest",
  "publish": false,
  "response": {
    "result": {
      "result": {
        "result": 4
      },
      "status": "success",
      "status_code": 0,
      "success": true
    },
    "size": 73,
    "status": "success",
    "success": true
  },
  "start": 1655918974479,
  "subject": "guest",
  "version": "0.0.1"
}

And we get 4 as a result. And that's it, we've run a serverless function with WebAssembly! Try running one of the other examples in ow-wasm-action/examples/, the appropriate compilation commands and features can be found in the justfile.

Building

We can also use just to make building a bit easier. The recipes are defined in the justfile. If you don't want to install this tool, you can copy the commands from there into your terminal.

The just commands generally run operations for all three runtimes, but if not all tools for all Wasm runtimes are installed, some of the steps can be skipped.

To build the executor with the wasmtime runtime, run just build-amd64 wasmtime_rt. Other possible values for the runtime argument are wasmer_rt and wamr_rt to use wasmer or wamr respectively.

To build example actions use just build-wasm-examples. This requires binaryen's wasm-opt to be present in your PATH, which optimizes the produced wasm. For convenience you can also skip this part. To precompile the examples use just precompile, which requires wasmer and wamrc to be installed or in your PATH. Of course only one of the precompilation steps is necessary to run a given module for a certain runtime, so you can skip some of these steps as well.