Welcome to the last lecture. Consider it the bonus lecture. WebAssembly is a strong (not-quite-anymore) newcomer to the field of cloud native. It is the right time to know what it is and to play around with a few usecases.
Last Updated: 2024-03-17
What if Kubernetes is too bloated for my tiny function and I just want this function to run anywhere. And securely in a sandbox. And irrespective of what language it's written in. And no matter what architecture.
We will cover the following topics:
By the end of today's lecture, you should have
Now: what on earth is webassembly, and why does it not really have anything to do with web?
Short answer: it came from the web, and the need to put more computationally expensive operations into browsers , so something was needed to combine with javascript ...
If you want to read the history of webassembly, please go through the TUW library to https://learning.oreilly.com/library/view/webassembly-the-definitive/9781492089834/
For our purposes, WASM is a framework to combine software elements built from different toolchains, over a share-nothing interface (WIT) , running in an architecture independent runtime, with an extremely small footprint.
We can export and import, and compose software. By default there is "shared nothing linking" so you need to explicitly expose your interfaces and they are strongly typed.
One of the most salient features of Webassembly is the component model, which is based on common open standards for runtime, type-interface, binary-format and build approach.
Not only can you interact with ANY wasm artefact, no matter what architecture it was built on. Also, you can run it on any runtime. There is something akin to Docker-Compose, and we will meet various runtimes, that you can either have directly on your machine, or on top of kubernetes, or somewhere else serverside as a PaaS.
In this KubeCon2023 talk, the authors demo the component model, by stating that no user should ever be able to get to the business logic without first going through the Auth middleware.
Also, we see the cli-import which is dealing with interacting with the WebAssembly host to e.g. get secrets from the environment.
Now, the Auth Middleware component has access to these cli-provided secrets, but the BusinessLogic Component does not (there is no wasi:cli/environment Import)
Constanze, will demo this live and you can do this as homework and get the code from:
<this assumes you have the whole setup of the later Hands-on labs working> git clone git@github.com:AustrianDataLAB/spin.git make componentmodeldemo
[2024-06-24T14:31:25Z WARN ] instance `wasi:cli/terminal-stderr@0.2.0-rc-2023-10-18` will be imported because a dependency named `wasi:cli/terminal-stderr@0.2.0-rc-2023-10-18` could not be found
composed component `service.wasm`
Finished building all Spin components
Logging component stdio to "example/.spin/logs/"
Serving http://127.0.0.1:3000
Available Routes:
example: http://127.0.0.1:3000 (wildcard)
no access token found in incoming request
no access token found in incoming request
no access token found in incoming request
authenticated
authenticated
You can find more runtimes in the github repo and you should play with wasmtime or deploy it to fermyon cloud https://github.com/fermyon/http-auth-middleware
Important to remember is, that you can deploy the *wasm binaries on any of the clouds and some of the standards are already on all runtimes (such as the key-value interface) , others (how to pass environment vars) are in the moment being standardized.
Can you run WASM on or in Kubernetes? Yes.
Can you convert a Docker Container to WASM ? Yes (but that's more of a hack)
Can you run WASM inside a Container? Yes
Can you run WASM inside WASM? Yes (but ... but .. but ...)
https://static.sched.com/hosted_files/colocatedeventseu2024/2f/WasmDay%20EU%20Keynote%202024.pdf
Lets look at some internals: if we compare it to (docker) containers, we see that it isnt a packaging format. Rather it is a compile target that runs on anything as long as whatever is on that computer is running the webassembly runtime (there are currently several). The WASM runtime then uses a system interface to talk to the OS of the underlying computer .
This immediately achieves a few things:
Lets see what projects are out there and how they differ
So : when should you use WASM?
Let's get all setup
In the following class, we ll need rust and several runtimes: you can natively install them OR put them into a container. . Or do as you please.
ALL of today's examples are in RUST, but you can switch them out for GO if you want to rewrite it all.
If you need rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add wasm32-wasi
(you can choose to put all of this into a rust container , then you dont have to clutter up your laptop docker run --rm -it rust:1-slim-buster )
https://developer.fermyon.com/cloud/quickstart
https://cloud.fermyon.com/add-app
At step3 , please choose Cloud Start as the sample application.
You may have to put `spin` into your PATH (container or native OS)
[OPTIONAL] docker run --rm -it rust:1-slim-buster curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash rustup target add wasm32-wasi ./spin login git clone https://github.com/fermyon/cloud-start && cd cloud-start ❯ ls Cargo.lock Cargo.toml LICENSE Makefile README.md spin.toml src target ❯ ../spin cloud deploy Uploading cloud_start version 0.1.0 to Fermyon Cloud... Deploying... Waiting for application to become ready......... ready View application: https://cloud-start-XXXXXX.fermyon.app/ Manage application: https://cloud.fermyon.com/app/cloud_start
We ll talk about what just happened,
Now, onto something slightly more interesting :
Cool, now you have a minimal UI and you can use it as a reminder app \o/
Once, you can deploy a new version of the cloud-start app, you should have a look at `lib.rs` and understand how the javascript is executed .
And WHERE
Running WASM on kubernetes
https://www.spinkube.dev/docs/overview/
https://www.spinkube.dev/docs/spin-operator/installation/installing-with-helm/
(unless you already have clone the AustrianOpenCloudCommunity/spin repo, clone it now.)
git clone git@github.com:AustrianDataLAB/spin.git make kind make spin-operator make application
git clone https://github.com/spinkube/spin-operator.git
It is possible (via spin) to push spin-apps to OCI registries, like ghcr.io
https://developer.fermyon.com/spin/v2/registry-tutorial , there are still many wasm apps around that don't support OCI.
From here we follow the tutorial here (it s all in your makefile)
https://www.spinkube.dev/docs/spin-operator/tutorials/assigning-variables/
Exercise:
Write you own app following the tutorial here
https://www.spinkube.dev/docs/spin-operator/tutorials/package-and-deploy/
WASM runtime and WASI
make wasmtime curl localhost:8080 Hello, wasi:http/proxy world!
If it complains about workspace conflicts with the Cargo.toml on the higher level, add line 9 : and empty workspace directive to the Cargo.toml of the hello-wasi-http
Adding standard components such as logs and DB
Lets install more stuff, yaya
Install `wash` https://wasmcloud.com/docs/installation - Run through the quickstart https://wasmcloud.com/docs/tour/hello-world - Add persistent storage and logging https://wasmcloud.com/docs/tour/adding-capabilities - Scale and distribute
wash new component hello --template-name hello-world-rust ... downloading nats ... starting wadm ... downloading wasmcloud cd hello wash build wasmtime serve -Scommon build/http-hello_world_s.wasm
wasm-tools component wit build/http-hello_world_s.wasm
Now , lets add some additional components
First, look at the wadm.yaml -> it should remind you strongly of something 🙂
wash app deploy wadm.yaml wash app list wash get inventory ❯ curl localhost:8080 Hello from Rust!
Exercise 2:
You can alternatively use the rust lib.rs from Constanze s git repo, since she had to make some more edits to the code to get it to work.
#![allow(clippy::missing_safety_doc)]
wit_bindgen::generate!();
use exports::wasi::http::incoming_handler::Guest;
use wasi::http::types::*;
struct HttpServer;
impl Guest for HttpServer {
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
// Get the path & query to the incoming request
let path_with_query = request
.path_with_query()
.expect("failed to get path with query");
// At first, we can assume the object name will be the path with query
// (ex. simple paths like '/some-key-here')
let mut object_name = path_with_query.clone();
let mut link_name = "default";
if let Some((path, query)) = path_with_query.split_once('?') {
object_name = path.to_string();
let query_params = query
.split('&')
.filter_map(|v| v.split_once('='))
.collect::<Vec<(&str, &str)>>();
if let Some((_, configured_link_name)) = query_params
.iter()
.find(|(k, _v)| k.to_lowercase() == "link_name")
{
link_name = configured_link_name;
}
}
wasi::logging::logging::log(
wasi::logging::logging::Level::Info,
"",
&format!("Greeting {link_name}"),
);
let bucket = wasi::keyvalue::store::open("").expect("failed to open empty bucket");
let count = wasi::keyvalue::atomics::increment(&bucket, &object_name, 1)
.expect("failed to increment count");
// Build & send HTTP response
let response = OutgoingResponse::new(Fields::new());
response.set_status_code(200).unwrap();
let response_body = response.body().unwrap();
response_body
.write()
.unwrap()
.blocking_write_and_flush(format!("Counter {object_name}: {count}\n").as_bytes())
.unwrap();
OutgoingBody::finish(response_body, None).expect("failed to finish response body");
ResponseOutparam::set(response_out, Ok(response));
}
}
export!(HttpServer);
package wasmcloud:hello;
world hello {
import wasi:keyvalue/atomics@0.2.0-draft;
import wasi:keyvalue/store@0.2.0-draft;
import wasi:logging/logging;
export wasi:http/incoming-handler@0.2.0;
}
docker run -d --name redis -p 6379:6379 redis wash build wash app delete rust-hello-world wash app list wash app deploy wadm.yaml ❯ curl localhost:8080/hiLVA Counter /hiLVA: 1
You can check in your redis, that the keys really are being incremented:
docker exec -it redis redis-cli KEYS '*' 1) "World" 2) "/hiLVA" 3) "Bob" docker exec -it redis redis-cli GET '/hiLVA' "1"
WASM runtime and WASI
make llm (by default it loads the gemma-2b-it-Q5 model) (if you havnt predownloaded it, it ll take a while)
You can find more examples here https://github.com/second-state/WasmEdge-WASINN-examples/blob/master/README.md
Fear not, we got you covered
Cant wait to see the finished products
Dont forget that you can make your audience play "a certain part" and make sure that the audience knows what your team did (most people have no idea what the other teams really worked on, so put an overview graphic somewhere)
Congratulations, you've successfully completed this training on WebAssembly and some different WASM runtimes