Use docker to manage the web assembly program written in rust

Time:2021-7-26

Developers can deploy, manage and run lightweight web assembly applications in wasedge through docker tools such as dockerhub and cri-o.

WasmEdgeByManaged by CNCF (cloud native Computing Foundation)The web assembly runtime is the execution sandbox for edge computing applications.

Although web assembly was originally invented as the runtime of browser applications, its lightweight and high-performance sandbox design makes it an attractive choice for general-purpose application containers.

If we had wasm + Wasi in 2008, we wouldn’t need to start docker at all Solomon hykes, co-founder of docker

Compared with docker,Web assembly starts up a hundred times faster, occupies less memory and disk space, and has a better defined security sandbox. However, the disadvantage of webdocker as a tool chain is that it needs to be more limited than its own SDK environment. Web assemblies are increasingly used in edge computing scenarios where it is difficult to deploy dockers or where application performance is critical.

Docker’s advantage is its rich tool ecosystem. We hope to bring docker like tools to wasmedge developers. To achieve this, we created a file namedrunwReplace runner to load and run web assembly bytecode programs as if they were docker images.

Installing the webassembly runner in cri-o

To support web assemblies in cri-o, you just need to downloadrunwBinary code is published and installed into your cri-o.

becauserunwThe binary code already includes wasedge, and there is no need to install wasedge 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, seeHow to create for your operating systemrunwProject documentation.

sudo apt install -y llvm-10-dev liblld-10-dev

Make sure you installcri-ocrictlcontainernetworking-pluginsandbuildahperhapsdocker

Next, downloadrunw binary build

wget https://github.com/second-state/runw/releases/download/0.1.0/runw

Now you can installrunwEnter cri-o as an alternative to web assembly.

# 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 '[email protected]_runtime = "runc"@default_runtime = "runw"@' /etc/crio/crio.conf
sudo sed -i -e '[email protected]_image = "k8s.gcr.io/pause:3.2"@pause_image = "docker.io/beststeve/wasm-pause"@' /etc/crio/crio.conf
sudo sed -i -e '[email protected]_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"
EOF

Finally, restartcri-oSo that the new webassembly runner takes effect.

sudo systemctl restart crio

Building wasm applications with rust

The wasm application in the following case was written by rust. To make these programs work, make sure you install themRustandrustwasmcTool chain.

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 again with cri-o, you can skip this section.

The application source code is only onemain.rsFunction.Here.This application demonstrates how to use the standard rust API to access file systems and other operating system resources from wasedge.

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");
}

You can build the application into a wasm bytecode file through the following command line.

rustwasmc build

Wasm bytecode filead locum.

Build and publish a docker hub image for wasm app

You can now publish the entire wasm bytecode file to the docker hub as if it were a docker image.

First, inpkg/Create a dockerfile in the directory, as shown below.

FROM scratch
ADD wasi_example_main.wasm .
CMD ["wasi_example_main.wasm"]

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

Now you can use docker tools (for examplecrictl)Pull the published wasm file as an image. The following is an example of the wasm file image we published.

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

Start wasm app using cri-o

To start and run the wasm file, you need to create two profiles for cri-o. Create acontainer_wasi.jsonFile, 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"
        ]
      }
    }
  }
}

Next, create asandbox_config.jsonFile, as shown below. It defines the sandbox environment for running wasm applications.

{
  "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"
      }
    }
  }
}

Now you can create a cri-o pod as follows:

#When you create a pod, the output will be different from the example.
sudo crictl runp sandbox_config.json
7992e75df00cc1cf4bff8bff660718139e3ad973c7180baceb9c84d074b516a4

#Set an auxiliary variable for later use.
POD_ID=7992e75df00cc1cf4bff8bff660718139e3ad973c7180baceb9c84d074b516a4

Since pod, you can create a container to run wasm bytecode programs in isolation.

#Create a container instance, and the output will be different from the example.
sudo crictl create $POD_ID container_wasi.json sandbox_config.json
1d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f

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

#List containers. The status 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

#Start container
sudo crictl start 1d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f
1d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f

#Check the status of the container again# If the container does not finish working, you will see the running state# Because this example is very small. You may 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

#View container records 
sudo crictl logs 1d056e4a8a168f0c76af122d42c98510670255b16242e81f8e8bce8bd3a4476f

Test 1: print random numbers
Random number: 960251471

Test 2: print random bytes
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: call echo function
Printed from wasi: This is from a main function
This is from a main function

Test 4: print environment variables
The env vars are as follows.
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 ` contains content ` This is in a file`

Test 6: read content from previous file
File content is This is in a file

Test 7: delete previous files

next step

In this article, we saw how to start, run, and manage wasmedge applications using cri-o tools like docker.

The next step is to use kubernetes to manage the wasmedge container. To do this, you need to install a runner binary in kubernetes so that it can support both regular docker images and wasm bytecode images.