Developers can deploy, manage, and run lightweight WebAssembly applications in WasmEdge using Docker tools such as DockerHub and Cri-O.

WasmEdge is a WebAssembly Runtime hosted by CNCF (Cloud Native Computing Foundation), which is an execution sandbox for edge Computing applications.

Although WebAssembly was originally invented as a run-time browser application, its lightweight and high-performance sandbox design makes it an attractive choice for a general-purpose application container.

If we had WASM + WASI in 2008, we wouldn’t have started Docker at all. — Docker co-founder Solomon Hykes

Compared to Docker, WebAssembly starts up a hundred times faster, takes up less memory and disk space, and has a better-defined security sandbox. However, the downside is that WebAssembly requires its own language SDK and compiler toolchain, making it a more limited developer environment than Docker. WebAssembly is increasingly used in edge computing scenarios, often where deploying Docker is difficult or where application performance is critical.

One of Docker’s strengths is its rich tool ecosystem. We hope to bring Docker-like tools to WasmEdge developers. To achieve this, we created an alternative runner named Runw for Cri-O to load and run WebAssembly bytecode programs as if they were Docker images.

Install WebAssembly Runner in CRI-O

To support WebAssembly in Cri-O, you simply download the RUNW binary distribution and install it into your Cri-O.

Because the RUNW binary already includes WasmEdge, there is no need to install WasmEdge or any other WebAssembly VM separately.

First, make sure you are using Ubuntu 20.04 with LLVM-10 installed. If you are using a different platform, see how to create runw project documentation for your operating system.

sudo apt install -y llvm-10-dev liblld-10-dev
Copy the code

Make sure you have Cri-O, crictl, ContainerNetwork-plugins and Buildah or Docker installed.

Next, download the Runw binary build

Wget HTTP: / / https://github.com/second-state/runw/releases/download/0.1.0/runwCopy the code

You can now install Runw into CRI-O as an alternative to WebAssembly.

# Get the wasm-pause utility sudo crictl pull docker.io/beststeve/wasm-pause # Install runw into cri-o sudo cp -v runw /usr/lib/cri-o-runc/sbin/runw sudo chmod +x /usr/lib/cri-o-runc/sbin/runw sudo sed -i -e 's@default_runtime = Conf sudo sed -i -e 's@pause_image = "k8s.gcr. IO /pause:3.2"@pause_image  = "docker.io/beststeve/wasm-pause"@' /etc/crio/crio.conf sudo sed -i -e 's@pause_command = "/pause"@pause_command = "pause.wasm"@' /etc/crio/crio.conf sudo tee -a /etc/crio/crio.conf.d/01-crio-runc.conf <<EOF [crio.runtime.runtimes.runw] runtime_path = "/usr/lib/cri-o-runc/sbin/runw" runtime_type = "oci" runtime_root = "/run/runw" EOFCopy the code

Finally, the Cri-O is restarted so that the new WebAssembly Runner takes effect.

sudo systemctl restart crio
Copy the code

Build Wasm applications with Rust

The Wasm application in the following example was written by Rust. For these programs to work, make sure you have the Rust and RustwasMC toolchains installed.

You need the Rust compiler and RustwasMC to build the Rust source into a WASM bytecode file. If you already have a WASM bytecode program and just want to run it through with CRI-O, you can skip this section.

The application source code is just a main.rs function. At this point. This application demonstrates how to access file systems and other operating system resources from WasmEdge using the standard Rust API.

fn main() { println! ("Random number: {}", get_random_i32()); println! ("Random bytes: {:? }", get_random_bytes()); println! ("{}", echo("This is from a main function")); print_env(); create_file("/tmp.txt", "This is in a file"); println! ("File content is {}", read_file("/tmp.txt")); del_file("/tmp.txt"); } pub fn get_random_i32() -> i32 { let x: i32 = random(); return x; } pub fn get_random_bytes() -> Vec<u8> { let mut rng = thread_rng(); let mut arr = [0u8; 128]; rng.fill(&mut arr[..] ); return arr.to_vec(); } pub fn echo(content: &str) -> String { println! ("Printed from wasi: {}", content); return content.to_string(); } pub fn print_env() { println! ("The env vars are as follows."); for (key, value) in env::vars() { println! ("{}: {}", key, value); } println! ("The args are as follows."); for argument in env::args() { println! ("{}", argument); } } pub fn create_file(path: &str, content: &str) { let mut output = File::create(path).unwrap(); output.write_all(content.as_bytes()).unwrap(); } pub fn read_file(path: &str) -> String { let mut f = File::open(path).unwrap(); let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => s, Err(e) => e.to_string(), } } pub fn del_file(path: &str) { fs::remove_file(path).expect("Unable to delete"); }Copy the code

You can build a WASM bytecode file with the command line application below.

rustwasmc build
Copy the code

The WASM bytecode file is here.

Build and publish a Docker Hub image for the Wasm app

You can now publish the entire WASM bytecode file into the Docker Hub as if it were a Docker image.

First, create a Dockerfile in the PKG/directory, as shown below.

FROM scratch
ADD wasi_example_main.wasm .
CMD ["wasi_example_main.wasm"]
Copy the code

Create an image and publish it to the Docker Hub.

sudo buildah bud -f Dockerfile -t wasm-wasi-example
sudo buildah push wasm-wasi-example docker://registry.example.com/repository:tag

# Example: the following command publishes the wasm image to the public Docker hub under user account "hydai"
sudo buildah push wasm-wasi-example docker://docker.io/hydai/wasm-wasi-example:latest
Copy the code

Now you can use a Docker tool (such as Crictl) to pull and mirror published WASM files. Here is an example of the WASM file image we published.

sudo crictl pull docker.io/hydai/wasm-wasi-example
Copy the code

Start the Wasm app with CLI-O

To start and run the WASM files, you need to create two configuration files for CRI-O. Create a container_wasi.json file as shown below. It tells the Cri-O runtime where to extract the WASM file image from the Docker repository.

{
  "metadata": {
    "name": "podsandbox1-wasm-wasi"
  },
  "image": {
    "image": "hydai/wasm-wasi-example:latest"
  },
  "args": [
    "wasi_example_main.wasm", "50000000"
  ],
  "working_dir": "/",
  "envs": [],
  "labels": {
    "tier": "backend"
  },
  "annotations": {
    "pod": "podsandbox1"
  },
  "log_path": "",
  "stdin": false,
  "stdin_once": false,
  "tty": false,
  "linux": {
    "resources": {
      "memory_limit_in_bytes": 209715200,
      "cpu_period": 10000,
      "cpu_quota": 20000,
      "cpu_shares": 512,
      "oom_score_adj": 30,
      "cpuset_cpus": "0",
      "cpuset_mems": "0"
    },
    "security_context": {
      "namespace_options": {
        "pid": 1
      },
      "readonly_rootfs": false,
      "capabilities": {
        "add_capabilities": [
          "sys_admin"
        ]
      }
    }
  }
}
Copy the code

Next, create a sandbox_config.json file as shown below. It defines the sandbox environment in which wASM applications run.

{ "metadata": { "name": "podsandbox12", "uid": "redhat-test-crio", "namespace": "redhat.test.crio", "attempt": 1}, "hostname" : "crictl_host", "log_directory" : ""," dns_config ": {" searches" : [" 8.8.8.8 "]}, "port_mappings" : [], "resources": { "cpu": { "limits": 3, "requests": 2 }, "memory": { "limits": 50000000, "requests": 2000000 } }, "labels": { "group": "test" }, "annotations": { "owner": "hmeng", "security.alpha.kubernetes.io/seccomp/pod": "unconfined" }, "linux": { "cgroup_parent": "pod_123-456.slice", "security_context": { "namespace_options": { "network": 0, "pid": 1, "ipc": 0 }, "selinux_options": { "user": "system_u", "role": "system_r", "type": "svirt_lxc_net_t", "level": "s0:c4,c5" } } } }Copy the code

You can now create a Cri-O pod as follows:

# create POD. The output will be different from the example. Sudo crictl runp sandbox_config. Json e75df00cc1cf4bff8bff660718139e3ad973c7180baceb9c84d074b516a4 # 7992 set an instrumental variables for later use. POD_ID=7992e75df00cc1cf4bff8bff660718139e3ad973c7180baceb9c84d074b516a4Copy the code

From POD, you can create a container to run wASM bytecode programs in isolation.

Create a container instance. The output will be different from the example. sudo crictl create $POD_ID container_wasi.json sandbox_config.json 1d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476fCopy the code

Finally, start the container and view the output of the WASM application.

# list containers, The STATE should be 'Created' sudo crictl ps -a CONTAINER IMAGE Created STATE NAME ATTEMPT POD ID 1d056e4a8a168 Hydai/WasM-wasi-example: Latest About a minute ago Created Podsandbox1-WasM-wasI 0 7992e75DF00cc # sudo crictl start  1d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f D056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f # 1 check the container status again. If the container didn't finish its work, you'll see the runtime status. # Because this example is small. You might see Exited at this point. sudo crictl ps -a CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID 1d056e4a8a168 hydai/wasm-wasi-example:latest About a Minute ago Running Podsandbox1 - wASM-wasI 0 7992e75DF00cc # When the container is finished. You can see that the status changes to Exited. sudo crictl ps -a CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID 1d056e4a8a168 hydai/wasm-wasi-example:latest About a Minute ago Exited Podsandbox1 -wasm-wasi 0 7992e75DF00cc 1 d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f Test 1: printing a Random number of Random number: 960251471 Test 2: Print Random bytes: [50, 222, 62, 128, 120, 26, 64, 42, 210, 137, 176, 90, 60, 24, 183, 56, 150, 35, 209, 211, 141, 146, 2, 61, 215, 167, 194, 1, 15, 44, 156, 27, 179, 23, 241, 138, 71, 32, 173, 159, 180, 21, 198, 197, 247, 80, 35, 75, 245, 31, 6, 246, 23, 54, 9, 192, 3, 103, 72, 186, 39, 182, 248, 80, 146, 70, 244, 28, 166, 197, 17, 42, 109, 245, 83, 35, 106, 130, 233, 143, 90, 78, 155, 29, 230, 34, 58, 49, 234, 230, 145, 119, 83, 44, 111, 57, 164, 82, 120, 183, 194, 201, 133, 106, 3, 73, 164, 155, 224, 218, 73, 31, 54, 28, 124, 2, 38, 253, 114, 222, 217, 202, 59, 138, 155, 71, 178, 113] Test 3: Printed from wasi: This is from a main function Printing environment variable The env vars are as follows. The PATH: / usr/local/sbin, / usr/local/bin: / usr/sbin, / usr/bin: / sbin/bin TERM: xterm HOSTNAME: crictl_host PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin The args are as follows. /var/lib/containers/storage/overlay/006e7cf16e82dc7052994232c436991f429109edea14a8437e74f601b5ee1e83/merged/wasi_example _main.wasm 50000000 Test 5: Create file '/tmp. TXT' This is in a file 'Test 6: File content is This is in a File Test 7: Delete the previous FileCopy the code

What’s next

In this article, we saw how to start, run, and manage WasmEdge applications using a Docker-like Cri-O tool.

Our next step is to use Kubernetes to manage the WasmEdge container. To do this, we need to install a Runner binary in Kubernetes so that it can support both regular Docker images and WASM bytecode images.