- Während der SUSE Hackweek 22 wurde ein Unikernel-POC erstellt, der WebAssembly-Module ausführt; der Implementierungsprozess wird in mehreren Teilen beschrieben
- Um gewöhnliche Anwendungen direkt auf einen Unikernel zu portieren, müssen auch die Abhängigkeiten passen, aber bei einer WebAssembly-Plattform sind die Funktionsgrenzen, die die Runtime bereitstellen muss, deutlich klarer
- Eine Spiderlightning-Anwendung benötigt nur Funktionen wie Key/Value; ob der Host das mit Redis oder Azure Cosmos DB implementiert, ist für dasselbe
.wasm-Modul transparent - Als Basis dient der Rust-Unikernel RustyHermit; da sich Wasmtime und Wasmer nicht bauen ließen, fiel die Wahl auf die reine Rust-Runtime wasmi
- Um das Component Model und WIT anzupassen, wurde wit-bindgen um Unterstützung für wasmi erweitert; danach wurde die hostseitige Key/Value-Funktionalität aufgesetzt und
keyvalue-demoausgeführt
Hackweek-Projekt und Ziel
- Während der Hackweek 22 von SUSE wurde das Projekt Build a unikernel that runs WebAssembly durchgeführt
- Der gesamte Implementierungsprozess wäre für einen einzelnen Artikel zu lang, deshalb wurde er auf mehrere Beiträge aufgeteilt; dieser Beitrag ist der erste Teil
- Der POC-Code ist an anderer Stelle öffentlich verfügbar, im bereitgestellten Text ist jedoch keine tatsächliche Link-URL enthalten
Warum Unikernel und WebAssembly zusammen nutzen
- Für Anwendungsentwickler ist das Portieren auf einen Unikernel mit großem Aufwand verbunden
- Die Anwendung und alle ihre Abhängigkeiten müssen den Ziel-Unikernel unterstützen
- Möglicherweise sind Patches im gesamten Application Stack nötig
- Auch Unikernel-Maintainer müssen viel Energie darauf verwenden, beliebige Anwendungen reibungslos ausführbar zu machen
- Denn es ist schwer vorherzusagen, welche Systemprimitiven Benutzeranwendungen verwenden werden
- Zielt man stattdessen auf WebAssembly-Plattformen wie Spin oder Spiderlightning, wird die Menge der Funktionen, die die Runtime bereitstellen muss, klarer
- Im Spiderlightning-Szenario kann eine Anwendung von der Runtime eine Key/Value-Storage-Funktion anfordern
- Ob der Host diese Funktion mit Redis oder mit Azure Cosmos DB implementiert, ist für die Anwendung transparent
- Dasselbe
.wasm-Modul kann auf unterschiedlichen Host-Implementierungen ausgeführt werden
Zielarchitektur
- Wenn eine Unikernel-Anwendung WebAssembly-Module ausführt und die Spiderlightning-API-Sammlung unterstützt, kann dieselbe Spiderlightning-Anwendung sowohl auf der normalen
slight-Runtime als auch auf diesem Unikernel laufen - Anwendungsentwickler müssen keine zusätzliche Arbeit leisten, und das Wasm-Modul muss nicht wissen, wo es ausgeführt wird
- Die Komplexität konzentriert sich auf die Unikernel-Entwickler, aber der zu implementierende Umfang ist deutlich klarer als „Ausführung aller Anwendungen unterstützen“
Implementierung auf Basis von RustyHermit
- Als Grundlage wurde RustyHermit gewählt
- Ein in Rust geschriebener Unikernel
- Es ist in Rust nightly enthalten und bietet damit eine Entwicklungserfahrung, die dem Schreiben gewöhnlicher Rust-Anwendungen ähnelt
- Das Bauen von RustyHermit-Anwendungen ist vergleichsweise geradlinig
- Die Dokumentation ist zwar etwas verstreut, aber qualitativ gut, und die Beispiele helfen sehr
- Man kann nicht erwarten, dass alle Rust-Crates unverändert auf RustyHermit funktionieren; diese Einschränkung beeinflusste die Entwicklung des POC
Auswahl der WebAssembly-Runtime
- Das bevorzugte Wasmtime ließ sich auf RustyHermit nicht bauen
- Viele Abhängigkeiten setzen
libcoder andere Low-Level-Bibliotheken voraus
- Viele Abhängigkeiten setzen
- Bei wasmer bestand dasselbe Problem
- Auch die WebAssembly Micro Runtime wurde in Betracht gezogen, letztlich fiel die Wahl aber auf eine in Rust geschriebene Runtime, um die „vollständige RustyHermit-Erfahrung“ beizubehalten
- Am Ende wurde die reine Rust-WebAssembly-Runtime wasmi gewählt
- Sie funktioniert gut auf RustyHermit
- Das Design ist von Wasmtime inspiriert, sodass sich viel vorhandenes Wissen wiederverwenden ließ
WebAssembly Component Model und WIT
- Spiderlightning nutzt den Vorschlag für das WebAssembly Component Model
- Damit werden Funktionen für WebAssembly-Gäste bereitgestellt
- Und der Host kann Funktionen konsumieren, die der WebAssembly-Gast bereitstellt
- Die Kommunikation zwischen Host und Gast verwendet Typen, die mit Wasm Interface Type definiert sind
- Die Demo nutzt das Component Model in folgendem Ablauf
- Der Gast fordert den Host auf, einen HTTP-Server zu starten, und übergibt die zu registrierenden HTTP-Routen sowie die Namen der internen Handler-Funktionen
- Dabei wird der Typ
http-serververwendet, und der Gast nutzt eine vom Host bereitgestellte Funktion
- Dabei wird der Typ
- Der Host verarbeitet eingehende HTTP-Anfragen anhand der vom Gast bereitgestellten Routing-Informationen
- Der HTTP-Handler ist eine Funktion, die vom WebAssembly-Gast exportiert wird
- Der Server konsumiert eine vom Gast bereitgestellte Funktion und kommuniziert über den Typ
http-handler
- Einige HTTP-Handler interagieren mit einem Key/Value-Storage
- Auch in diesem Fall nutzt der Gast eine vom Host bereitgestellte Funktion, definiert durch den Typ
keyvalue
- Auch in diesem Fall nutzt der Gast eine vom Host bereitgestellte Funktion, definiert durch den Typ
- Der Gast fordert den Host auf, einen HTTP-Server zu starten, und übergibt die zu registrierenden HTTP-Routen sowie die Namen der internen Handler-Funktionen
wit-bindgen erweitern und die Demo ausführen
- Für jeden WIT-Typ werden sowohl gastseitiger SDK-artiger Code als auch hostseitiger Implementierungscode benötigt
- wit-bindgen ist ein CLI-Tool, das aus
.wit-Dateien Host-/Gast-Code generiert - In diesem POC musste nur die hostseitige Schnittstelle innerhalb des Unikernels implementiert werden
- Der von
wit-bindgengenerierte Code nutzt eine WebAssembly-Runtime, um Low-Level-Aufgaben auszuführen- Der generierte Code hängt von der Programmiersprache und der hostseitigen WebAssembly-Runtime ab
- Da
wasmivonwit-bindgennicht unterstützt wurde, wurdewit-bindgenerweitert, um mit wasmi arbeiten zu können- Der Code befindet sich in einem Fork im wasmi-Branch
- Anschließend wurde hostseitiger Code für die Key/Value-Funktionalität aufgesetzt und eine einfache Implementierung des Host-Traits ergänzt
- Der Host-Code gab im Wesentlichen Debug-Informationen aus
- In diesem Zustand ließ sich das keyvalue-demo aus dem Spiderlightning-Projekt ohne Änderungen ausführen
Ausblick auf den nächsten Teil
- Es gibt eine Aufzeichnung, in der die Unikernel-Anwendung die Spiderlightning-
http-server-Demo ausführt - Im nächsten Teil sollen Rust async, Redis und einige merkwürdige Fehler behandelt werden
1 Kommentare
Hacker-News-Kommentare
Muss man da nicht sofort an https://www.destroyallsoftware.com/talks/the-birth-and-death... denken?
Wenn jemand, der kein Betriebssystem-Hacker ist, ein Unikernel will, was wäre dann der vollständigste Ansatz?
Mir fallen als Optionen ein: die Anwendung als Linux-Kernel-Modul bauen und auf einem normalen Kernel laufen lassen, den Userspace ignorieren; Linux radikal abspecken und den eigenen Code anhängen; mit einem Unikernel-Projekt auf GitHub anfangen; oder ein anderes Betriebssystem wie FreeBSD zurechtstutzen
Mir gefällt die Vorstellung, dass auf einer VM, die an eine Netzwerkkarte angeschlossen ist, eine x64-Maschine wie eine universelle Rechenressource arbeitet und Aufgaben durch das Senden von Daten übers Netzwerk zugewiesen bekommt. Bisher war das im Vergleich zu einem Userspace-Daemon noch zu umständlich und daher nicht besonders lohnend, aber wenn ich irgendwann Zeit habe, frage ich mich, wo man mit Hacking auf Betriebssystemebene am besten anfängt
Unikernel Linux (UKL) begann als Versuch, die Konfigurierbarkeit von Linux zu nutzen, und zielt auf einen Kernel ab, der vom allgemeinen Betriebssystem bis zu anwendungs- und hardwarespezifischen Unikernels reicht. Verwandte Bereiche wie io_uring und eBPF werden ebenfalls erwähnt; io_uring verteilt die Kosten von Systemaufrufen, und eBPF ist eine weitere Möglichkeit, wenn auch eingeschränkt, Code im Kernel-Space auszuführen
Code: https://github.com/unikernelLinux/ukl
UKL ist ein kleiner Patch für Linux und glibc, mit dem sich viele Programme ohne Änderungen als Unikernel bauen lassen. Das Programm wird mit dem Linux-Kernel und dem finalen vmlinuz gelinkt und läuft im Kernel-Space, kann auf Bare Metal oder in einer VM booten und fast alle Funktionen und Treiber von Linux nutzen
/initnennen, an den Kernel binden und bootenDann ist die App PID 1 und praktisch der einzige Prozess, und abgesehen von ein paar Kernel-Threads kann man im Grunde tun, was man will
Es unterstützt mehrere Sprachen und Apps, x86/ARM64, QEMU/Firecracker, und kann auch unter Linux gebaute ELF-Dateien als Unikernel ausführen: https://unikraft.org/guides/bincompat
Discord gibt es unter https://unikraft.org/discord
Wenn man OCaml lernen möchte und zugleich einen Unikernel will, ist das ein möglicher Weg
Mehr dazu steht in der Unikraft-Dokumentation: https://unikraft.org/docs/concepts/design-principles#approac...
Tolles Projekt. WASM gefällt mir, weil es von Anfang an mit Blick auf Sandboxing und Portabilität entworfen wurde
Ich wünschte, in den 90ern wäre statt JavaScript WASM erschienen, und ich habe das Gefühl, dass WASM die Welt erobern wird. Am meisten hoffe ich auf Beständigkeit. Es gibt heute viele Programme, die sich nicht mehr ausführen lassen, alte Spiele sind ein typisches Beispiel. Eine einfache Spezifikation hat eine größere Chance, lange zu überleben, daher bin ich bei neuen Features etwas skeptisch, aber die Zukunft von Binärdateien wirkt spannend
Dass JS anfangs auf einfaches Scripting wie Klick-Handler oder Formularvalidierung beschränkt war, hatte seinen Grund. Dass daraus etwas viel Größeres wurde, lag nicht nur an Designfehlern von JS, sondern auch an Anwendungsfällen, in die es hineingezwängt wurde. Den Browser als Auslieferungsmechanismus für solche Apps zu verwenden, ist weit entfernt von dem, was Tim Berners-Lee oder Marc Andreesen sich vorgestellt hatten
Das Lager „Das Netzwerk ist der Computer“ brachte damals für reichhaltigere Apps dünne X-Clients heraus: https://en.wikipedia.org/wiki/Network_Computer
Bei WASM habe ich gemischte Gefühle. Im Moment liegt ein großer Schleier aus Hype und Neuheit darüber. Wenn Webbrowser nur noch als Viewport für die jeweilige Sprachfantasie behandelt werden, die UI-Designer und Entwickler gerade bevorzugen, hat das oft schlechte Folgen für Bereiche wie Barrierefreiheit und Screenreader
Auch die Bewegung, WASM außerhalb des Browsers als universelle VM zu behandeln, ist ein Weg, den man schon vor 30 Jahren gegangen ist. Genau das sollte die JVM leisten, aber offenbar gilt das heute nicht mehr als „cool“
Ich weiß nicht genau, wie ein Mittelweg aussehen sollte oder warum man ihn wollen sollte. Realistisch gesehen wird WASM aber wohl auch Dokumentinhalte verschlingen, und dann könnten Werbeblocker und Lesemodus am Ende erledigt sein
Gefällt mir wirklich sehr. Unter den verlinkten Technologien waren auch einige, die ich noch nicht gesehen hatte, also habe ich sie alle als Lesezeichen gespeichert.
Als Nächstes würde ich gern eine WireGuard-Verbindung zum Hypervisor einrichten. Der Verbindungsaufbau könnte auch über so etwas wie Tailscale laufen.
Dann würde das WebAssembly auf dieser Maschine direkt mit dem WebAssembly auf jener Maschine sprechen. Also nicht so, dass ein Prozess eine TCP-Verbindung zu einem beliebigen Ort öffnet, sondern eine Struktur, die auf übergebener Konfiguration und Berechtigungen basiert.
Etwas spät, aber hat schon einmal jemand darüber nachgedacht, Zephyr als Unikernel auszuführen? https://docs.zephyrproject.org/latest/boards/x86/acrn/doc/in...
Wie lange wird es wohl dauern, bis dedizierte WASM-Hardware erscheint?
Aber etwas in der Art von „außen wasm, darunter in Wirklichkeit RISC-V“ könnte wohl jemand bauen.
Ich denke aber, dass solche Hardware immer eine Nische bleiben wird. WASM auf allgemeiner Standardhardware auszuführen wird meistens schneller sein. WASM selbst wurde dafür entworfen, auf vorhandener Hardware schnell zu laufen, und die Skaleneffekte bei Allzweckprozessoren sind deutlich besser.
Die International Conference on Functional Programming war anfangs auch eine Konferenz zu Functional Programming and Computer Architecture, aber später fand man Wege, lazy ausgewertete funktionale Sprachen wie Haskell effizient für bestehende Hardware zu kompilieren.
Bei Lisp- und Java-Maschinen war es ähnlich. Ein Grund, warum man solche Dinge heute kaum noch sieht, ist, dass die Compiler-Technik aufgeholt hat.
Was wären Anwendungsfälle für Unikernel und WASM?
Den Wert von Unikernels sehe ich in 1) Performance: Unnötiges wegwerfen und das Nötige in „ring 0“ ziehen, um noch ein paar Zyklen herauszuholen, 2) Vereinfachung: Unnötige Teile entfernen und dadurch möglicherweise die Komplexität reduzieren, 3) Sicherheit: ebenfalls durch weniger Überflüssiges die Angriffsfläche verändern.
Ich glaube allerdings nicht, dass das gut zu der Art von Microservices oder Web-Apps passt, die viele in diesem Forum schreiben. Der Einsatzbereich liegt eher beim Bau von Infrastruktur-Komponenten wie Datenbanken oder Load Balancern.
Deshalb wandeln manche Edge-Cloud-Anbieter Docker-Images beim Ausführen in Micro-VMs um.
Allerdings könnte WASM innerhalb von Micro-VMs am Edge Schwierigkeiten haben, mit dem sandboxierten WASM des Edge-Anbieters zu konkurrieren. Aus Sicht des Anbieters ist Letzteres wahrscheinlich leichter um nützliche Boundary-Funktionen und Integrationen zu erweitern.
Wie vor langer Zeit in Birth & Death of Javascript „angekündigt“, war die Vorstellung, dass es irgendwann ein Unikernel geben würde, das eine sichere Garbage-Collection-Runtime im Kernel-Space ausführt, und man dann die Unterstützung für Virtual-Memory-Mapping in der CPU entfernen könnte, um alles schneller zu machen
2014 hatte der Autor JS und asm.js im Blick, aber heute sieht es so aus, als wäre WASM dieser Weg. Spannend, haha
https://www.destroyallsoftware.com/talks/the-birth-and-death...
Später haben wir allerdings gelernt, dass ein Single-Process-Browser ein Sicherheitsalptraum ist, und heutige Browser sind für richtiges Sandboxing längst keine Single-Process-Systeme mehr
Trotzdem ist es interessant, wie nah das Video an der richtigen Antwort war, und auch, auf welche Weise es danebenlag
Schwer, das exakt zu benennen, aber es kommt mir irgendwo bekannt vor. Der Hinweis ist, dass auch das mit „J“ anfängt
https://en.wikipedia.org/wiki/JavaStation
Die virtuelle Nutzung eines Prozesses kann RSS auch ohne Swapping überschreiten, und Betriebssystem und Allocator arbeiten zusammen und handhaben das im Normalfall ziemlich geschickt
Deshalb ist es schwer zu sagen, dass das Abschaffen davon automatisch Leistungsgewinne bringt. Besonders dann, wenn man zusätzlich noch durch eine ziemlich langsame WASM-VM-Schicht geht
Für manche Anwendungen, etwa Datenbanken, kann es ein großer Vorteil sein, als Unikernel zu laufen oder näher am Kernel zu sitzen und direkt auf die MMU zuzugreifen: https://github.com/tuhhosg/exmap & https://github.com/viktorleis/vmcache & https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/_my_direct_up...
Aber bei allgemeinen Anwendungen, die den POSIX-Standard voraussetzen oder annehmen, dass die Laufzeitumgebung wie ein moderner Allzweckrechner aussieht, bin ich skeptisch. Am Ende müsste man wohl vieles von dem, was die VMM-Schicht erledigt hat, in User-Code neu schreiben
JS-Engines hängen von der VMM ab, und auch WASM tut das auf verschiedene Weise. Fast jedes nicht-triviale Programm außerhalb des Embedded-Bereichs setzt stillschweigend eine VMM voraus. Auch manche VM-Techniken rund um Micro-VMs nutzen VMM, und Unikernel ergeben ihren eigentlichen Sinn erst, wenn sie als VM verwendet werden