Skip to content

ashokspeelyaal/kubecon2024-eu-wasm-workshop

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 

Repository files navigation

👋 Hello wasmtime

This section of the tutorial introduces wasmtime, a WebAssembly (Wasm) runtime and one of the Bytecode Alliance's reference implementations for the WebAssembly System Interface (WASI) standards1.

In this workshop, we're going to:

  • Clone an example repository,
  • Build a WebAssembly component
  • Serve web traffic with our component using wasmtime.

📦 1. Setup

Note

This first example is Rust-focused, but we'll move onto working with other languages (Go, TypeScript, Python) in a later tutorial stage.

1.1 🐳 Set up docker container

To get started quickly, if you have docker installed, you can start a container:

docker run --rm -it rust:1-slim-buster

Once your container has loaded up, install the basic dependencies for the demo:

apt update && apt install -y curl pkg-config libssl-dev git

If you'd like to use our setup.Dockerfile, run the following (once you've checked out this repository):

docker build --tag wasm-workshop --file setup.Dockerfile .
docker run --rm -it wasm-workshop

1.1 (optional) 🦀 Install Rust toolchain natively

This step is only necessary if you're not using docker

As this demo will be working primarily in Rust, you'll need to install the Rust language toolchain.

You can find out how to install Rust from rust-lang.org.

1.2 ⬇️ Clone Dan Gohman's sunfishcode/hello-wasi-http Repository

You can clone the repository with git:

git clone https://github.com/sunfishcode/hello-wasi-http.git

1.3 🏗️ Install wasmtime and related tools

Before we can build WebAssembly components in Rust, we'll need to install some Rust ecosystem tooling:

Here is some information on the tools we'll be installing

Tool Purpose
wasmtime Leading WebAssembly runtime implementation, developed by the Bytecode Alliance, which supports basic WebAssembly and many more advanced standards.
wasm-tools Tooling for manipulating and modifying WebAssembly binaries and more.
cargo-component Rust CLI for building WebAssembly components with Rust

We can install all the tooling we'll need with cargo, the package ("crate") manager of the Rust toolchain:

cargo install wasmtime-cli wasm-tools cargo-component

2. ⁉️What the WIT

Here we'll learn about the WebAssembly Interface Types specification, which helps us build and connect components with declarative, high level types.

🗺️ A brief introduction to WIT

A brief introduction to WIT:

package local:demo; # <namespace>:<package>

interface host {
  log: func(msg: string);
}

This is a WIT interface that defines a namespace (local), and a package (demo) which contains one interface (host).

The host interface consists of one function (log) which accepts a single argument (msg, of type string) and does not return anything.

2.1 Define the WIT

From the hello-wasi-http repository you cloned locally, take a look at the WebAssembly Interface Types in wit/world.wit:

package sunfishcode:hello-wasi-http;

world target-world {
  include wasi:http/proxy@0.2.0;
}

This WIT definition defines a world called target-world which sets all of the interfaces (and functions) that the component we're about to build will import and export.

To enable our component to handle incoming HTTP requests, we're includeing the wasi:http/proxy interface (<namespace>:<package>/<interface>), at version 0.2.0. This interface includes a shareable implementation for handling HTTP requests.

WIT information (the world, interfaces, etc) is embedded into every WebAssembly component you build; you can inspect Wasm components to see exactly what interfaces they implement are before running them.

Tip

🔐 Gamechanger for security

Interfaces are like the concept of capabilities, whch we can use with fine-grained security controls to make our execution environments safe.

By inspecting interfaces, we can understand a component without seeing or executing the code. Think of the tools we have to inspect containers, their contents, and what they do, it's very difficult to inspect binaries and containers for what they'll do at runtime before running them.

Feel free to take a look in src/lib.rs as well, where you can find the implementation code for this component directly using the WASI interface.

// ...imports

impl bindings::exports::wasi::http::incoming_handler::Guest for Component {
    fn handle(_request: IncomingRequest, outparam: ResponseOutparam) {
        let hdrs = Fields::new();
        let resp = OutgoingResponse::new(hdrs);
        let body = resp.body().expect("outgoing response");

        ResponseOutparam::set(outparam, Ok(resp));

        let out = body.write().expect("outgoing stream");
        out.blocking_write_and_flush(b"Hello, wasi:http/proxy worldddd!\n")
            .expect("writing response");

        drop(out);
        OutgoingBody::finish(body, None).unwrap();
    }
}

This looks pretty similar in each language, and the use of the interface directly here is a good learning exercise.2

🛠️Build your component

Building your component is similar to building a Rust binary, simply run:

cargo component build

This will create a component in target/wasm32-wasi/debug, you can use the wasm-tools CLI to inspect its wit:

wasm-tools component wit target/wasm32-wasi/debug/hello_wasi_http.wasm

You should see output like the following (without syntax highlighting):

package root:component;

world root {
  import wasi:io/error@0.2.0;
  import wasi:io/streams@0.2.0;
  import wasi:cli/stdout@0.2.0;
  import wasi:cli/stderr@0.2.0;
  import wasi:cli/stdin@0.2.0;
  import wasi:http/types@0.2.0;

  export wasi:http/incoming-handler@0.2.0;
}

As you can see, this component **import**s standard libraries for IO and standard output/error/in, and **export**s the HTTP incoming handler.

Based on only this information we know that this component will never be able to access files, make web requests of its own, run arbitrary commands, etc without ever looking at the source code.

👟 Run your component

You can run your component using wasmtime serve, which provides the implementation for wasi:http, wasi:io, wasi:cli and others to the WebAssembly component:

wasmtime serve -Scommon ./target/wasm32-wasi/debug/hello_wasi_http.wasm

In another terminal, you can run curl and see the hello statement we've written:

curl localhost:8080

Footnotes

Footnotes

  1. jco, a NodeJS runtime and JavaScript tooling project, is another reference implementation for WASI.

  2. There are multiple tools that abstract this interface usage, like wasm-http-tools which supports generating a component from OpenAPI / Swagger specifications.

About

WebAssembly workshop given at Kubecon 2024

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dockerfile 100.0%