Introduzione
Sempre più spesso alcuni colleghi mi chiedono perché io insista molto sul fatto che WebAssembly potrebbe arrivare a sostituire Docker. Se proprio devo essere onesto, in realtà non so nemmeno io se questo succederà; sono però convinto che WebAssembly ci riservi numerose interessanti prospettive. Andiamo con ordine e vediamo insieme di cosa si tratta.
Perché Docker?
Nella distribuzione delle moderne applicazioni, abbiamo sempre bisogno di sicurezza. Dobbiamo essere certi che le applicazioni che consumiamo, e quelle che sviluppiamo, non possano in nessun modo intaccare il sistema che le ospita. In questo, non c’è ombra di dubbio, i container svolgono il loro lavoro egregiamente.
Molto più flessibili e veloci all’avvio rispetto alle Virtual Machine, ci garantiscono il necessario isolamento fra un processo e un altro e ci permettono di mantenere viva un’applicazione anche se un processo si blocca per qualsiasi motivo. Insomma, distribuire applicazioni a microservizi tramite container ha cambiato un po’ la vita di noi sviluppatori.
Però, come ogni nuova tecnologia, anche Docker ha sicuramente contribuito a risolvere alcuni problemi, ma ha portato con sé nuove sfide. Per essere onesti, se proprio volessimo una completa autonomia di ogni singolo processo, dovremmo pubblicare ogni singola funzione in un container separato;, ma, siamo sinceri, sarebbe un lavoro un po’ da pazzi, assolutamente possibile, ma poco pratico.
Attenzione, non sto parlando di qualche processo che si possa racchiudere in una lambda su AWS, o una function su Azure, ma di un’intera applicazione in cui ogni singola funzione sia un container. Nella realtà un container è più un’API, che poi, per chi applica Domain-Driven Design, è l’espressione di un Bounded Context, quindi, proprio micro non lo è.
Se poi vogliamo che veramente ogni singolo processo sia indipendente, allora bisogna anche che la comunicazione fra i container sia asincrona, e questo aggiunge complessità alla nostra infrastruttura. Per non parlare del fatto che il sogno di ogni sviluppatore è quello di scrivere un solo codice per tutte le piattaforme, e non è un segreto che i container non sono affatto agnostici alla piattaforma. Possiamo creare container per Windows o per Linux, ma non container “universali”.
Insomma, servirebbe una soluzione che ci possa garantire affidabilità e sicurezza, ma che sia un po’ più semplice da applicare, una via di mezzo fra isolamento ed efficienza.
I moduli WebAssembly
Sarebbe possibile avere una soluzione che ci possa garantire portabilità e aggiungere efficienza senza ricorrere a Virtual Machine e/o Container per avere garantito il giusto isolamento grazie alla sandbox che questi strumenti ci mettono a disposizione? In realtà lo è, e questa soluzione si chiama WebAssembly.
Abbiamo visto nel precedente articolo che il formato WebAssembly è un file binario che ha bisogno di un runtime per funzionare; ma attenzione, alcuni potrebbero pensare che anche i linguaggi del .NET framework, o i programmi scritti in Java, in fin dei conti, una volta compilati, sono dei file binari che girano sfruttando i rispettivi framework, quindi che vantaggi porta il formato WebAssembly?
Innanzitutto il formato WebAssembly è più simile a una virtual machine che non ad un binario in senso lato, così come siamo abituati a pensare per l’IL del .NET framework di Microsoft; è più una “stack based virtual machine”. La differenza? IL è un binario OOP, con tanto di interfacce, garbage collector, reflection, high level types, e tutto quello che serve per un’astrazione ad alto livello.
WebAssembly, da parte sua, è un binario molto più a basso livello, senza tutte le astrazioni presenti nel IL, senza tutti i pattern della OOP; è molto più simile all’Assembler, e questo binario è in grado di girare all’interno di tutti i browser moderni, senza ulteriori framework di mezzo. È agnostico alla piattaforma.
Certamente, qualcuno potrebbe obiettare che il binario WebAssembly giri all’interno di un browser. Ma anche questa affermazione è vera a metà. WebAssembly può girare anche all’esterno dei browser, e qui le cose si fanno interessanti. Per poter girare out-of-the-browser, WebAssembly ha bisogno di un host, che potrebbe anche essere un host custom scritto da chi distribuisce la soluzione.
Per nostra fortuna ne esistono di pronti all’uso, e li abbiamo già citati sempre nel precedente articolo: Wasmtime [1] e WAMR [2]. Grazie a questi host, possiamo compilare i nostri programmi direttamente in WebAssembly, e ormai la maggior parte dei linguaggi supporta questa soluzione, a partire da Rust, particolarmente indicato, sino ad arrivare a C#, Cobol, Java, Python e molti altri.
WASI
Quindi tramite questi host che ci permettono di far girare applicativi WebAssembly al di fuori dei browser, possiamo accedere alle risorse del sistema che ci ospita? Non esattamente; abbiamo detto che la forza di WebAssembly è proprio la presenza di una sandbox che ci garantisce tutto l’isolamento necessario. E allora?
Abbiamo bisogno di una API agnostica alla piattaforma che ci consenta di ottenere il tanto agognato obiettivo “Write Once, Run Everywhere”, e questa API esiste ed ha un nome: WASI (WebAssembly Sistem Interface). WASI [3] è il confine fra l’applicazione che gira nella sandbox e il sistema sottostante e fornisce tutta la protezione necessaria al server che ci ospita, praticamente molto simile a un container.
WAGI
È chiaro che il progetto WebAssembly, così come era stato pensato all’inizio, è andato ben oltre le aspettative, ed è quindi lecito chiedersi cosa possiamo ulteriormente fare. Ad esempio, perché non costruire un web server WebAssembly in grado di intercettare ogni richiesta e gestirla in un processo separato, una sandbox isolata?
Per questo abbiamo a disposizione WAGI (WebAssembly Gateway Interface) [4]. I dev un po’ più attempati forse avranno già notato una somiglianza con un amico de passato, ossia CGI (Common Gateway Interface). L’idea alla base è esattamente la stessa: il Web Server riceve la richiesta HTTP e avvia un processo per gestirla, in questo caso un processo isolato, con tutto ciò che ne consegue; un eventuale malfunzionamento non compromette la stabilità della soluzione.
Il valore aggiunto di WAGI è che è costruito su Wasmtime, e questo, oltre a garantirci una sandbox isolata, ci permette di non avere ulteriori framework di mezzo, rendendo il tutto estremamente snello ed efficace.
WebComponents
Se è vero che l’appetito vien mangiando, perché fermarsi proprio ora che ci stiamo divertendo ad avere WebAssembly ovunque? Perché non definire uno standard WASM-based per costruire componenti agnostici al linguaggio e isolati nella loro sandbox da poter riciclare?
Anche in questo caso ci sarà chi riconosce una soluzione simile del passato: COM, Component Object Model. Proprio come si faceva con COM, ossia costruire oggetti in C++ da utilizzare in VisualBasic, oppure condividere fogli Excel all’interno di Word, allo stesso modo l’idea è costruire componenti WebAssembly nel linguaggio preferito, e più indicato allo scopo, e consumarli ovunque.
Qui la strada è solo tracciata, nel senso che una soluzione definitiva è ancora in divenire. Al momento attuale abbiamo bisogno di un SDK per poter costruire componenti condivisibili. A tal proposito segnalo spin [5], un SDK di Fermyon, startup molto attiva nel mondo WebAssembly.
WebAssembly vs Docker
Giunti a questo punto è lecito chiedersi se WebAssembly sostituirà Docker in un futuro più o meno immediato. Ovviamente no, la sola ed unica risposta degna di un dev è: “Dipende!”
Qual’è la prima differenza rispetto a Docker? Un modulo WebAssembly non viene fornito con un filesystem preconfezionato (Linux o Windows), e nemmeno con altre primitive di basso livello. Variabili d’ambiente, file, directory, timer e altre risorse di sistema possono invece essere collegate direttamente al nostro modulo.
Più che a un container possiamo dire che un modulo WebAssembly è più simile ad un Pod di Kubernetes, nel senso che filesystem e variabili d’ambiente sono legati a un container nel momento in cui questo viene avviato. La differenza fondamentale sta nel fatto che questa è una caratteristica fondamentale di WASI (WebAssembly System Interface), uno degli standard di WebAssembly stesso. Fa parte di un runtime, non di un sistema di orchestrazione come nel caso di Kubernetes.
Possiamo concludere che, così come i container non hanno sostituito le Virtual Machine, Wasm non sostituirà i container. In ogni caso, ci sono caratteristiche di Wasm che lo rendono particolarmente appetibile negli ambienti serverless (cloud edge). Tra queste caratteristiche:
- Wasm è simile a un Container, ma con un’astrazione a un livello più alto;
- Wasm è agnostico alla piattaforma;
- Wasm gira in una sandbox isolata;
- sarà possibile costruire componenti Wasm-based
Quello che possiamo affermare è che WebAssembly ormai è uno strumento in più che abbiamo a disposizione, non si tratta di un qualcosa che sarà disponibile in futuro, e per questo non lo possiamo più ignorare. A dimostrazione di questo, molte novità sono attese in autunno anche da parte di Microsoft con il rilascio della versione 7 del .NET framework, in cui sarà possibile compilare applicazioni Blazor direttamente in WebAssembly e creare applicazioni WebAssembly out-of-browser grazie a .NET MAUI.
Cosa ne pensa Docker?
Sul sito di Docker è apparso recentemente un interessante articolo [6] proprio sul rapporto tra Docker e WebAssembly. Amici o nemici? Consiglio la lettura completa dell’articolo, ma già il titolo è in grado di dirci molto: Why Containers and WebAssembly Work Well Together.
Riferimenti
[1] Wasmtime. A fast and secure runtime for WebAssembly
https://wasmtime.dev/
[2] WebAssembly Micro Runtime (WAMR)
https://github.com/bytecodealliance/wasm-micro-runtime
[3] WASI. The WebAssembly System Interface
https://wasi.dev/
[4] WAGI. WebAssembly Gateway Interface
https://github.com/deislabs/wagi
[5] spin, SDK open source di Fermyon
https://github.com/fermyon/spin
[6] Tyler Charboneau, Why Containers and WebAssembly Work Well Together. 01/07/2022
https://t.ly/pRxF
Sono fondamentalmente un eterno curioso. Mi definisco da sempre uno sviluppatore backend, ma non disdegno curiosare anche dall'altro lato del codice. Mi piace pensare che "scrivere" software sia principalmente risolvere problemi di business e fornire valore al cliente, e in questo trovo che i pattern del DDD siano un grande aiuto. Lavoro come Software Engineer presso intré, un'azienda che sposa questa ideologia; da buon introverso trovo difficoltoso uscire allo scoperto, ma mi piace uscire dalla mia comfort-zone per condividere con gli altri le cose che ho imparato, per poter trovare ogni volta i giusti stimoli a continuare a migliorare.
Mi piace frequentare il mondo delle community, contribuendo, quando posso, con proposte attive. Sono co-founder della community DDD Open e Polenta e Deploy, e membro attivo di altre community come Blazor Developer Italiani.