1 Punkte von GN⁺ 2023-10-24 | 1 Kommentare | Auf WhatsApp teilen
  • 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-demo ausgefü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 libc oder andere Low-Level-Bibliotheken voraus
  • 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-server verwendet, und der Gast nutzt eine vom Host bereitgestellte Funktion
    • 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

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-bindgen generierte 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 wasmi von wit-bindgen nicht unterstützt wurde, wurde wit-bindgen erweitert, um mit wasmi arbeiten zu können
  • 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

 
GN⁺ 2023-10-24
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

    • RedHat schaut sich seit 2018 Linux-as-unikernel an: https://research.redhat.com/blog/article/unikernel-linux-ukl...
      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
    • Wenn man von der Linux-Familie ausgeht, kann man zuerst eine statisch kompilierte Anwendung bauen, sie als einzige Datei in ein initramfs legen, sie einfach /init nennen, an den Kernel binden und booten
      Dann 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
    • Unikraft ist ebenfalls einen Blick wert: https://unikraft.org
      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
    • Für OCaml gibt es das Framework MirageOS: https://mirage.io/
      Wenn man OCaml lernen möchte und zugleich einen Unikernel will, ist das ein möglicher Weg
    • Es gibt im Wesentlichen drei Wege, einen Unikernel zu bauen: ein bestehendes allgemeines Betriebssystem minimieren, das Betriebssystem umgehen oder von Grund auf neu bauen
      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

    • So schwer es heute zu glauben ist: In den 90ern wurden Webbrowser meist als Navigatoren für Hypertext-Dokumente gesehen, nicht als Ersatz für Betriebssysteme
      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“
    • Ganz naiv hoffe ich, dass sich das Web in sandboxed WASM-Apps und Dokumentinhalte aufspaltet, die nicht einmal JS brauchen
      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
    • Ich denke, damit das funktioniert, brauchte es JavaScript oder etwas Ähnliches. Sonst wäre das Ökosystem vermutlich von etwas wie Java infiziert worden
  • 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?

    • Streng genommen vermutlich nie. Im sehr engen Sinn ist WASM dafür einfach nicht ausreichend spezifiziert.
      Aber etwas in der Art von „außen wasm, darunter in Wirklichkeit RISC-V“ könnte wohl jemand bauen.
    • Irgendjemand wird so etwas sicher bauen. Es gab schließlich auch Lisp-Maschinen und dedizierte JVM-CPUs.
      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?

    • Zu WASM sage ich lieber nichts. Sonst verfalle ich wahrscheinlich in den „die Jugend von heute“-Modus.
      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.
    • Micro-VMs können bei manchen Aufgaben mit Linux-Containern konkurrieren und haben den Vorteil, dass sie weniger vertrauenswürdigem Code nicht den Linux-Kernel aussetzen.
      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.
    • Es scheint dazu zu dienen, die Einsatzorte von WASM zu erweitern. Nach Browsern und Docker-Containern nun auch leichtgewichtige Betriebssysteme, die sich auf eingebetteten Geräten einsetzen lassen.
  • 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...

    • Die Logik des Videos war, dass der Browser ohnehin ein einzelner Prozess sei und, wenn alles in diesem Prozess läuft, eine solche Trennung nicht nötig wäre
      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
    • Die Entwicklung von JavaScript/WASM verläuft von einer Auslegung für im Browser laufende Apps → zum Schreiben von Desktop- und Server-Apps → zum Schreiben von Betriebssystemen oder Kerneln
      Schwer, das exakt zu benennen, aber es kommt mir irgendwo bekannt vor. Der Hinweis ist, dass auch das mit „J“ anfängt
    • JavaStation war ein Netzwerkcomputer, den Sun Microsystems zwischen 1996 und 2000 entwickelte und der nur Java-Anwendungen ausführen sollte
      https://en.wikipedia.org/wiki/JavaStation
    • Virtueller Speicher und Paging sind nicht nur für Schutz, Sicherheit und Prozessisolation da. Sie liefern auch einen Satz von Abstraktionen für die effiziente Nutzung des physischen Speichers und das Speichermanagement
      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
    • Die Unterstützung für Virtual-Memory-Mapping zu entfernen ergibt umso weniger Sinn, je länger man darüber nachdenkt
      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