This project aims to build a unikernel that has a sole purpose: run WebAssembly applications.
Unikernels offer a limited attack surface and theoretically can provide better performance compared to running the same application on top of a regular operating system. However, running applications on top of unikernels is not an easy tasks. Some might just work out of the box, others will require quite some efforts to be ported.
WebAssembly is a binary application format. A developer write their application using their favorite programming language, then compile it using the WebAssembly target. This produces a Wasm module, that can be run on top of a WebAssembly runtime. The runtime abstracts the underlying operating system and architecture, making the Wasm module capable of running everywhere. The WebAssembly runtime is also in charge of exposing a series of capabilities that can be leveraged by the WebAssembly guest. These capabilities could be system-related (like interacting with the filesystem, this is what WASI does) or could be functionalities provided by a specific platform (like accessing Key/Value stores, implementing http endpoints,... this is what platforms like Spin or Spiderlightning offer).
Why should we have a unikernel that runs WebAssembly:
- To improve security. Each WebAssembly runtime executes the WebAssembly code inside of a dedicated sandbox. This isolates the Wasm workloads from each other and from the host. However this is all done in software. By using a unikernel, we will be running the application on a host (the unikernel) that as a limited attack surface, plus it will be placed inside of a traditional VM that offers better isolation capabilities.
- To go fast. We could just place a regular WebAssembly runtime inside of a VM and call it a day (like running that inside of kata for example). However, having a super limited use case, I think we can build a really optimized system (à la firecraker)
Why I think this has higher chances of adoption compared to traditional "wanna run everything" unikernels:
- WebAssembly modules have clear boundaries. The set of capabilities offered is set by the platform (Spin, Spiderlightning) is limited. That reduces the amount of scenarios the unikernel must support.
- A better developer experience. A developer will build an application targeting a certain platform (Spin, Spiderlightning). This will produce a Wasm module that can be run, unchanged, by a regular WebAssembly runtime, including this unikernel. Both the regular WebAssembly runtime and the unikernel will implement the same set of capabilities. The WebAssembly guest won't even notice where its being ran.
Why am I doing this
I've always been fascinated by unikernels and I'm deeply into WebAssembly. I think there's a purpose for the two things to play together, as described above.
State of the project
I've a running POC. The unikernel is based on rusty-hermit, a unikernel written in Rust.
I've a unikernel application that is capable of running the unmodified WebAssembly module produced by the Spiderlightning key-value demo.
The unikernel is running inside of qemu, using kvm.
Goal for this Hackweek
Main goal - run WebAssembly
The main goal is be able to run the http server demo from Spiderlightning. I don't care about being able to run WASI modules, right now I just want to target some of the interfaces offered by Spiderlightning.
I don't need external help to achieve this goal.
Virtualization goals - HELP NEEDED
As an additional goal, I would like to replace the usage of qemu with something that is custom built.
We need a stripped down hypervisor that:
- Offers a network stack based on virtio - this is what hermit supports
- [Optional] offers a virtio disk backed by virtiofs - that's the only type of disk supported by hermit unikernel
We could leverage the Rust VMM libraries (the ones used by firecraker and crosvm) to build it, use cloud-hypervisor or other things you have in mind. It would be great to have all of that written in Rust, because it would allow me to reuse other Wasm/OCI-related things I'm already using inside of other projects I maintain/contribute to.
Have a cli program that works in this way:
<our binary> run <unikernel image>
This would setup the network stack and run the unikernel image provided by the cli.
In this case we don't care about the unikernel being run. It could be the "hello world" example from hermit or it could be the wasm unikernel I'm building. In the latter case, the unikernel image would also bundle inside of itself the actual WebAssembly module.
Have a cli program that works in this way:
<our binary> run <module.wasm>
The binary will now bundle the wasm unikernel application, hence it's no longer required to specify that on the cli. Instead, on the cli the user will point us to the WebAssembly module to run. The module will somehow be injected into the VM and consumed by the unikernel. How to do that, I have some ideas, but you're the virtualization expert, I rely on you to help me with that .
This makes the unikernel image generic, because there's no need to bundle the actual WebAssembly module to execute inside of it.
[comment]: # (Please link to sources and other data here.) [comment]: # (Prefer public repositories, such as GitHub!)
I've still to upload the POC on GitHub. In the meantime these are the Virtualization related links:
- Intro to hermit unikernel
- A hypervisor built by the hermit people: this is not meant to be used in production
- hermit wiki: it has useful resources, especially this one
- Bootloader to be used with qemu
- VMM reference implementation
[comment]: # (After creating the project, please add some keywords:) [comment]: # (* What type of project mates are you looking for, which skills do you need or lack?) [comment]: # (* Which keywords will help other people to find your project?)
This project is part of:
Hack Week 22