OCI Containers and Wasm

WebAssembly (Wasm) has a complete set of semantics and is designed to be version-free, feature-testable and backwards compatible on the web, but of course WebAssembly can run not only on browsers but also in non-web environments. In fact wasm is a small and fast loading binary format whose goal is to make full use of the hardware capabilities to achieve native execution efficiency, and in this article we will present a scenario of running a Wasm workload as a container image.

Introduction to Wasm

WebAssembly (Wasm for short) is a binary instruction set designed for stacked virtual machines.Wasm was designed to be a platform compilation target for high-level languages like C/C++/Rust, and was originally designed to address performance issues in JavaScript.Wasm is a Web standard being promoted by the W3C and supported by browser vendors such as Google, Microsoft and Mozilla. Of course, WebAssembly can run not only on browsers, but also in non-web environments.

Wasm is run efficiently, memory safe, has no undefined behaviour and is platform independent, and has been worked on by compiler and standardisation teams for many years and now has a mature community. For non-browser runtimes such as Wasmtime, Wamr, Wasm3, WasmEdge and Wasmer, which use the Wasm format, it demonstrates the flexibility of the Wasm specification, for example by executing Wasm3 as an interpreter, and the ability to support JIT and AOT compilation, plus various caching and optimisation features.

WASM Virtual Machine

  • WASMTIME

    WASMTIME is a WASM virtual machine promoted by the Bytecode Alliance that can be used as a CLI or embedded in other applications, such as IoT or cloud-native.

  • WAMR

    Also owned by the Bytecode Alliance, this virtual machine is more oriented towards the silicon scenario and, as its name suggests, is very small, starting at just 100 microseconds and consuming as little as 100KB of memory.

  • WASMER

    This is a product from a community that is independent of the Bytecode Alliance and trying to build its own ecosystem, featuring support for running WASM instances in more programming languages and its own package management platform, Wapm

  • SSVM

    This is a relatively niche runtime with targeted optimizations for cloud, AI, and blockchain

  • WasmEdge

    WasmEdge is a lightweight, high-performance virtual machine for severless cloud functions, SaaS, blockchain smart contracts, IoT, automotive real-time software applications and many other scenarios

Benefits of the WASM Virtual Machine

  1. Fast, efficient and portable: By taking advantage of common hardware capabilities, WASM code can run at near-native speeds on different platforms.
  2. Readable and debuggable: WASM is a low-order language, but allows code to be written, read and debugged by hand.
  3. Stay safe: WASM is restricted to run in a secure sandbox execution environment. Like other web code, it follows the browser’s same-origin policy and authorisation policy. 4.
  4. Don’t break the web: WASM is designed to work in harmony with other web technologies and remain backwards compatible.

Although Wasm relies heavily on a bridge between JavaScript and the Wasm runtime in the browser, the non-profit Bytecode Alliance (of which Cosmonic, Fermyon and Suborbital are members) has joined in the development of a system binding for Wasm. The Wasm System Interface (WASI) is a typical example, adding standardised support for interacting with system resources such as the filesystem, environment variables, clocks and random number generators.

WASI

WASI is a new API system, designed by the Wasmtime project, to design a set of engine-indepent, non-Web system-oriented APIs for WASM. Currently, the WASI Core API is working on APIs for modules covering files, networking, etc., but it is still a long way from being practical.

WASM and Runtime

Both WebAssembly and WASI are fairly new and therefore the standard for running Wasm workloads on the container ecosystem has not yet been defined. This article only describes one solution, but of course there are other viable options, such as replacing the Linux container runtime with Wasm-compatible components. For example, using Krustlet instead of the native kubelet. The limitation of this approach is that the user must choose between the Linux container runtime and the Wasm runtime; another solution is to use an image with the Wasm runtime and call the compiled binary, but this approach causes the container image to bloat with the runtime. This approach is not necessarily needed if the Wasm runtime is invoked on a low container runtime.

This article will describe how to configure the OCI runtime to run Linux containers and wasm compatible workloads.

Low-level Runtime Crun

Some of the problems discussed above can be easily solved by invoking Linux containers and Wasm containers through existing low-level OCI runtime implementations. This avoids problems such as relying on container images to host the Wasm runtime or introducing new layers to an infrastructure that only supports Wasm containers.

One of the container runtimes that can handle this task:Crun

Crun is fast, uses little memory and is a fully oci-compliant container runtime that can be used as a replacement for existing container runtimes. Crun was originally written to run Linux containers, but it also provides the ability to run arbitrarily extended programs in a container sandbox in a host fashion.

The following is an informal way of replacing an existing runtime with Crun, its just to show that Crun can replace an existing OCI runtime.

1
2
$ $ mv /path/to/exisiting-runtime /path/to/existing-runtime.backup
$ cp /path/to/crun /path/to/existing-runtime

One of the handlers is crun-wasm-handler, which integrates the configured container image (Wasm-compatible image) into the crun sandbox and thus becomes part of the existing Wasm runtime. In this way, end users do not need to maintain the Wasm runtime themselves.

crun integrates with wasmedge, wasmtime and wasmer to support out-of-the-box functionality. crun can dynamically invoke these runtimes by detecting whether the image contains Wasm/WASI workloads.

For more information on building crun with Wasm/WASI support, see the crun repository on GitHub.

Users can use crun as the underlying OCI runtime on Podman and Kubernetes to create and run platform-independent Wasm images.

Buildah build images

Wasm/WASI-compatible images are special. They contain an annotation that helps an OCI runtime like crun to distinguish between a linux image in general and an image with a Wasm/WASI workload. Handlers are then called on demand.

Any container image builder can be used to create Wasm images, but for the purposes of this article, Buildah is used.

  1. Compile the .wasm module.

  2. Prepare a Dockerfile with the .wasm module.

    1
    2
    3
    
    FROM scratch
    COPY hello.wasm /
    CMD ["/hello.wasm"]
    
  3. Build the Wasm image using Buildah with the annotation module.wasm.image/varian=compat

    1
    
    $ buildah build --annotation "module.wasm.image/variant=compat" -t mywasm-image
    

    After building the image and configuring the container engine as crun, crun will automatically perform the required work and run the workload provided by the configured Wasm handler.

Podman runs WASM

Crun is Podman’s default OCI runtime. podman can take advantage of most crun features, including the crun Wasm handler. Once Wasm compatible images have been built, Podman can use them just like any other container image.

1
$ podman run mywasm-image:latest

Podman runs mywasm-image:latest using crun’s Wasm handler and returns the output of the execution.

1
hello world from the webassembly module !!!!

WASM and CRI

The following is how to configure the two common container runtimes.

CRI-O

cri-o is a lightweight CRI runtime that supports OCI and provides management of images, container process management, monitoring logging and resource isolation.

The default address for cri-o communication is at /var/run/crio/crio.sock.

how containers run in a k8s cluster

how containers run with a container engine

Configuring

  1. Configure crio to use crun instead of runc by editing the configuration in /etc/crio/crio.conf. The Red Hat OpenShift documentation contains more details on configuring crio.
  2. Restart crio with sudo systemctl Restart crio.
  3. CRI-O automatically propagates pod comments to the container spec.

Containerd

containerd is supposed to be the most popular CRI runtime at the moment. It implements CRI as a plug-in and is enabled by default. It listens for messages on unix sockets by default.

From version 1.2 onwards it supports a variety of low-level runtimes via runtime handlers. The runtime handler is passed through a field in the CRI, and based on this runtime handler, containerd runs shim’s application to start the container. This can be used to run runc and other low-level runtime containers such as gVisor, Kata Containers, etc. Runtime configuration is done in the Kubernetes API via RuntimeClass.

Containerd

Configuration

  1. Containerd supports switching the container runtime via a custom configuration defined in /etc/containerd/config.toml.
  2. configure containd to use crun by ensuring that the runtime binary points to crun. more details can be found in the included documentation.
  3. configure containd to allow Wasm annotations to be listed so that they can be propagated to the OCI specification by setting pod_annotations in the configuration:pod_annotations = ["module.wasm.image/ variable .*"].
  4. Restart the container using sudo systemctl start containerd. 5.
  5. containd should now propagate the wasmpod annotations to the container.

The following is an example of a Kubernetes pod specification that can use both crio and containerd:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-wasm-workload
  namespace: mynamespace
  annotations:
    module.wasm.image/variant: compat
spec:
  containers:
  - name: wasm-container
    image: myrepo/mywasmimage:latest

CRI was introduced in Kubernetes 1.5 as a bridge between the kubelet and the container runtime. The community expects the high-level container runtime integrated with Kubernetes to implement CRI. this runtime handles the management of images, supports Kubernetes pods, and manages containers, so a runtime that supports CRI must be a high-level runtime, according to the definition of a high-level runtime. Lower level runtimes do not have the above capabilities.

In many cases, pods come with sidecar. this means that Wasm integration for crun is not useful when the deployment contains sidecar and the sidecar container does not contain a Wasm entry point, such as an infrastructure using Service Mesh (e.g. Linkerd, Gloo and Istio) or a web proxy (e.g. Envoy).

Of course, we can solve this problem by adding two annotations to the Wasm handler: compat-smart and was-smart. these annotations act as selector switches to toggle the Wasm runtime only when the container needs it. Thus, when running deployments with sidecars, only containers containing valid Wasm workloads are executed by the Wasm handler. Regular containers are treated as regular containers and delegated to the host Linux container runtime.

Therefore, the annotation to use when building a container image is module.wasm.image/variant=compat-smart rather than module.wasm.image/variant=compat.