1 pontos por GN⁺ 2023-10-24 | 1 comentários | Compartilhar no WhatsApp
  • Durante a Hackweek 22 da SUSE, foi criado um POC de um unikernel que executa módulos WebAssembly, e o processo de implementação foi organizado em várias partes
  • Para portar uma aplicação comum diretamente para um unikernel, é preciso compatibilizar até as dependências, mas uma plataforma WebAssembly define com muito mais clareza os limites das funcionalidades que o runtime deve oferecer
  • Uma aplicação Spiderlightning exige apenas recursos como Key/Value, e mesmo que o host implemente isso com Redis ou Azure Cosmos DB, o mesmo módulo .wasm não precisa saber da diferença
  • A base usada é o unikernel em Rust RustyHermit e, como Wasmtime e Wasmer não compilavam, a escolha foi o runtime totalmente em Rust wasmi
  • Para alinhar com o Component Model e o WIT, foi adicionado suporte a wasmi no wit-bindgen; depois disso, foi feito o scaffolding da funcionalidade Key/Value no lado do host para executar o keyvalue-demo

Projeto da Hackweek e objetivo

  • Durante a Hackweek 22 da SUSE, foi realizado o projeto Construindo um unikernel que executa WebAssembly
  • Como todo o processo de implementação seria longo demais para caber em um único texto, ele foi dividido em vários artigos, e este é a primeira parte
  • O código do POC está publicado em outro lugar, mas o texto fornecido não inclui a URL real do link

Por que usar unikernel com WebAssembly

  • Para desenvolvedores de aplicações, portar para unikernel é um fardo grande
    • A aplicação e todas as dependências precisam oferecer suporte ao unikernel de destino
    • Pode ser necessário aplicar patches em toda a stack da aplicação
  • Quem mantém o unikernel também precisa gastar muita energia para fazer aplicações arbitrárias rodarem sem atrito
    • Isso acontece porque é difícil prever quais primitivas de sistema uma aplicação de usuário vai usar
  • Em contrapartida, ao mirar plataformas WebAssembly como Spin ou Spiderlightning, o conjunto de funcionalidades que o runtime precisa fornecer fica claro
  • No cenário do Spiderlightning, a aplicação pode solicitar ao runtime recursos de armazenamento Key/Value
    • Seja o host implementando isso com Redis ou com Azure Cosmos DB, isso é transparente para a aplicação
    • O mesmo módulo .wasm pode ser executado sobre diferentes implementações de host

Arquitetura alvo

  • Se a aplicação unikernel conseguir executar módulos WebAssembly e oferecer suporte ao conjunto de APIs do Spiderlightning, a mesma aplicação Spiderlightning poderá rodar tanto no runtime slight comum quanto nesse unikernel
  • O desenvolvedor da aplicação não precisa fazer trabalho adicional, e o módulo Wasm também não precisa saber onde está sendo executado
  • A complexidade fica concentrada no desenvolvedor do unikernel, mas o escopo a implementar é muito mais claro do que “dar suporte à execução de qualquer aplicação”

Implementação baseada em RustyHermit

  • A base escolhida foi o RustyHermit
    • É um unikernel escrito em Rust
    • Está incluído no Rust nightly, oferecendo uma experiência de desenvolvimento parecida com a de criar aplicações Rust comuns
  • O build de aplicações RustyHermit é relativamente direto
    • A documentação está um pouco espalhada, mas é de boa qualidade, e os exemplos ajudam bastante
  • Não dá para esperar que todo crate Rust funcione no RustyHermit sem mudanças, e essa limitação influenciou o desenvolvimento do POC

Escolha do runtime WebAssembly

  • O preferido, Wasmtime, não compilava sobre o RustyHermit
    • Muitas dependências esperam libc ou outras bibliotecas de baixo nível
  • O wasmer tinha o mesmo problema
  • O WebAssembly Micro Runtime também foi considerado, mas a decisão foi usar um runtime escrito em Rust para manter a “experiência RustyHermit completa”
  • No fim, a escolha foi o wasmi, um runtime WebAssembly totalmente em Rust
    • Ele funciona bem sobre o RustyHermit
    • Seu design foi inspirado no Wasmtime, o que permitiu reaproveitar bastante conhecimento prévio

WebAssembly Component Model e WIT

  • O Spiderlightning usa a proposta WebAssembly Component Model
    • Ela fornece funcionalidades para guests WebAssembly
    • E permite que o host consuma funcionalidades fornecidas pelos guests WebAssembly
  • A comunicação entre host e guest usa tipos definidos pelo Wasm Interface Type
  • A demo usa o Component Model no seguinte fluxo
    • O guest pede ao host para iniciar um servidor HTTP e passa as rotas HTTP a registrar e os nomes das funções internas de handler
      • Usa o tipo http-server, no qual o guest consome uma funcionalidade oferecida pelo host
    • O host processa as requisições HTTP recebidas com base nas informações de roteamento fornecidas pelo guest
      • O handler HTTP é uma função exposta pelo guest WebAssembly
      • O servidor consome uma funcionalidade oferecida pelo guest e se comunica pelo tipo http-handler
    • Alguns handlers HTTP interagem com o armazenamento Key/Value
      • Também nesse caso o guest consome uma funcionalidade oferecida pelo host, definida pelo tipo keyvalue

Extensão do wit-bindgen e execução da demo

  • Para cada tipo WIT, são necessários códigos com papel de SDK no lado do guest e de implementação no lado do host
  • O wit-bindgen é uma ferramenta CLI que gera código de host/guest a partir de arquivos .wit
  • Neste POC, bastava implementar apenas a interface do lado do host dentro do unikernel
  • O código gerado pelo wit-bindgen usa o runtime WebAssembly para realizar tarefas de baixo nível
    • O código gerado depende da linguagem de programação e do runtime WebAssembly no lado do host
  • Como o wasmi não era suportado pelo wit-bindgen, ele foi estendido para lidar com wasmi
  • Depois disso, foi feito o scaffolding do código do lado do host para a funcionalidade Key/Value e adicionada uma implementação simples do trait do host
    • O código do host ficava no nível de apenas imprimir informações de debug
  • Nesse estado, foi possível executar sem modificações o keyvalue-demo do projeto Spiderlightning

Prévia da próxima parte

  • Há uma gravação da aplicação unikernel executando a demo http-server do Spiderlightning
  • Na próxima parte, serão abordados Rust async, Redis e alguns erros estranhos

1 comentários

 
GN⁺ 2023-10-24
Opiniões no Hacker News
  • Não vem imediatamente à mente https://www.destroyallsoftware.com/talks/the-birth-and-death...?

  • Para alguém que não é hacker de sistemas operacionais e quer um unikernel, qual seria a abordagem mais completa?
    As opções que vêm à cabeça são transformar a aplicação em um módulo do kernel Linux e carregá-la em um kernel comum, ignorando o espaço de usuário; reduzir drasticamente o Linux e anexar seu próprio código; começar por algum projeto de unikernel no GitHub; ou aparar outro sistema operacional, como o FreeBSD
    Gosto da ideia de uma máquina x64 em uma VM conectada a uma placa de rede funcionando como um recurso de computação de uso geral, recebendo tarefas por dados enviados pela rede. Por ser mais trabalhoso do que um daemon em espaço de usuário, ainda não parece ter tanto valor, mas tenho curiosidade de saber por onde seria bom começar a hackear em nível de sistema operacional quando eu tiver tempo algum dia

    • A RedHat vem analisando Linux-as-unikernel desde 2018: https://research.redhat.com/blog/article/unikernel-linux-ukl...
      O Unikernel Linux (UKL) começou como uma tentativa de aproveitar a configurabilidade do Linux e tem como objetivo um kernel que cubra desde sistemas operacionais de uso geral até unikernels especializados em aplicações e hardware. io_uring e eBPF também são mencionados como áreas relacionadas: io_uring distribui o custo das chamadas de sistema, e eBPF é outra forma, ainda que limitada, de executar código no espaço do kernel
      Código: https://github.com/unikernelLinux/ukl
      O UKL é um pequeno patch para Linux e glibc que permite compilar muitos programas como unikernels sem modificações. O programa é linkado com o kernel Linux e com o vmlinuz final, executa no espaço do kernel, pode inicializar em bare metal ou em uma VM e pode usar quase todos os recursos e drivers do Linux
    • Assumindo a família Linux, basta primeiro criar uma aplicação compilada estaticamente, colocá-la como o único arquivo no initramfs, nomeá-la simplesmente como /init, empacotá-la com o kernel e inicializar
      Assim, o app se torna o PID 1 e, na prática, o único processo; exceto por algumas threads do kernel, você pode fazer o que quiser
    • Unikraft também vale conferir: https://unikraft.org
      Ele oferece suporte a várias linguagens e apps, x86/ARM64, QEMU/Firecracker, e também consegue executar como unikernel um ELF compilado no Linux: https://unikraft.org/guides/bincompat
      O Discord fica em https://unikraft.org/discord
    • Para OCaml, há o framework MirageOS: https://mirage.io/
      Se você quer aprender OCaml e também quer um unikernel, esse é um caminho possível
    • Existem essencialmente três formas de criar um unikernel: minimizar um sistema operacional de uso geral existente, contornar o sistema operacional ou criar um do zero
      Mais detalhes podem ser vistos na documentação do Unikraft: https://unikraft.org/docs/concepts/design-principles#approac...
  • É um bom projeto. Gosto do fato de WASM ter sido projetado desde o início com sandboxing e portabilidade em mente
    Gostaria que o WASM tivesse surgido nos anos 90 em vez do JavaScript, e acho que o WASM vai dominar o mundo. O que mais desejo é persistência. Hoje há muitos programas que já não conseguimos mais executar, e jogos antigos são o exemplo clássico. Uma especificação simples tem mais chance de sobreviver por muito tempo, então a adição de novos recursos me deixa um pouco apreensivo, mas o futuro dos binários parece interessante

    • Pode parecer difícil de acreditar, mas nos anos 90 os navegadores web eram vistos, em geral, como navegadores de documentos de hipertexto, não como substitutos de sistemas operacionais
      Havia um motivo para o JS ter sido inicialmente limitado a scripting básico, como manipuladores de clique e validação de formulários. O fato de ele ter crescido para outra coisa não se deve apenas a falhas de design do JS, mas também aos usos enfiados à força nele. Usar o navegador como mecanismo de entrega para esses apps está longe do que Tim Berners-Lee ou Marc Andreesen imaginaram
      Na época, o pessoal do “a rede é o computador” lançou thin clients X para apps mais ricos: https://en.wikipedia.org/wiki/Network_Computer
      Tenho sentimentos mistos em relação ao WASM. No momento, há um grande véu de hype e novidade sobre ele. Se tratarmos o navegador apenas como um viewport para a fantasia de linguagem que designers de UI e desenvolvedores preferirem no momento, muita coisa ruim acontece em áreas como acessibilidade e leitores de tela
      A tendência de tratar o WASM fora do navegador como uma VM universal também é um caminho que já percorremos 30 anos atrás. Era isso que a JVM tentava fazer, mas agora parece não ser “cool”
    • Ingenuamente, eu gostaria que a web se dividisse entre apps WASM em sandbox e conteúdo documental que nem precisasse de JS
      Não sei bem como deveria ser a zona intermediária, nem por que deveríamos querê-la. Mas, realisticamente, o WASM provavelmente vai engolir até o conteúdo documental, e aí bloqueadores de anúncios e modos de leitura podem estar acabados
    • Acho que, para isso funcionar, era necessário JavaScript ou algo parecido. Caso contrário, é provável que o ecossistema tivesse sido infectado por algo como Java
  • Gostei muito. Havia várias tecnologias linkadas que eu ainda não tinha visto, então favoritei tudo
    Em seguida, quero tentar configurar uma conexão WireGuard no hipervisor. O estabelecimento da conexão talvez possa passar por algo como o Tailscale
    Assim, o WebAssembly desta máquina conversaria diretamente com o WebAssembly daquela outra. Não seria um processo abrindo uma conexão TCP para um local arbitrário, mas uma estrutura que se comunica com base na configuração e nas permissões fornecidas

  • Meio tarde, mas será que alguém já pensou em executar o Zephyr como unikernel? https://docs.zephyrproject.org/latest/boards/x86/acrn/doc/in...

  • Quanto tempo falta até surgir hardware dedicado para WASM?

    • Estritamente falando, acho que nunca vai surgir. Em um sentido bem restrito, porque o WASM não é especificado o suficiente para isso
      Dito isso, alguém poderia criar algo do tipo “por fora é wasm, mas por baixo na verdade há RISC-V”
    • Com certeza alguém vai fazer. Já existiram máquinas Lisp e CPUs dedicadas à JVM
      Mas acho que esse tipo de hardware sempre vai continuar sendo de nicho. Porque, em geral, rodar WASM em hardware comum de prateleira será mais rápido. O próprio WASM foi projetado para rodar rapidamente em hardware existente, e as economias de escala dos processadores de uso geral são muito melhores
      A International Conference on Functional Programming também começou como uma conferência chamada Functional Programming and Computer Architecture, mas depois foram encontradas formas de compilar linguagens funcionais de avaliação preguiçosa, como Haskell, de maneira eficiente para hardware existente
      Com máquinas Lisp e Java foi parecido. Um dos motivos pelos quais quase não se vê mais esse tipo de coisa é que a tecnologia de compiladores alcançou esse ponto
  • Quais seriam os casos de uso de unikernels e WASM?

    • Sobre WASM, não vou comentar. Acho que eu entraria no modo “essa garotada de hoje em dia...”
      Vejo o valor dos unikernels em 1) desempenho: descartar o que não é necessário e puxar o que é necessário para o “ring 0” para extrair até alguns ciclos a mais; 2) simplificação: a possibilidade de reduzir a complexidade eliminando partes desnecessárias; 3) segurança: também a possibilidade de mudar a superfície de ataque reduzindo o que é desnecessário
      Ainda assim, não acho que seja uma abordagem adequada para escrever microsserviços ou webapps, como muita gente neste fórum faz. O uso está mais próximo de criar componentes de infraestrutura, como bancos de dados e balanceadores de carga
    • Micro VMs podem competir com contêineres Linux em algumas tarefas e têm a vantagem de não expor o kernel Linux a código menos confiável
      Por isso, alguns provedores de edge cloud convertem imagens Docker em micro VMs ao executá-las
      Porém, no edge, WASM dentro de uma micro VM pode ter dificuldade para competir com WASM em sandbox no próprio edge. Do ponto de vista do provedor, esta última opção provavelmente facilita adicionar integrações e recursos úteis de fronteira
    • Parece ser para ampliar os lugares aonde o WASM pode chegar. Depois de navegadores e contêineres Docker, agora isso inclui até sistemas operacionais leves que podem ser colocados em dispositivos embarcados
  • Como foi “previsto” há muito tempo em Birth & Death of Javascript, um dia surgiria um unikernel capaz de rodar um runtime seguro com coleta de lixo no espaço do kernel, e a ideia era que, com isso, seria possível remover do CPU o suporte a mapeamento de memória virtual para torná-lo mais rápido
    Em 2014, o autor imaginava JS e asm.js, mas hoje o WASM parece ser esse caminho. Estou animado, haha
    https://www.destroyallsoftware.com/talks/the-birth-and-death...

    • A lógica do vídeo era que, se o navegador já é de qualquer forma um único processo e tudo roda dentro desse processo, então essa separação não seria necessária
      Mas depois aprendemos que navegadores de processo único são um pesadelo de segurança, e os navegadores atuais já não são mais de processo único, para permitir um sandboxing adequado
      Ainda assim, é legal ver o quanto aquele vídeo chegou perto da resposta certa, e também é interessante ver de que forma ele errou
    • A evolução de JavaScript/WASM flui de um design para apps executados no navegador → escrita de apps desktop e de servidor → escrita de sistemas operacionais ou kernels
      É difícil apontar exatamente, mas há algo familiar nisso. A dica é que aquilo também começa com “J”
    • O JavaStation foi um computador de rede desenvolvido pela Sun Microsystems entre 1996 e 2000, pensado para executar apenas aplicações Java
      https://en.wikipedia.org/wiki/JavaStation
    • Memória virtual e paginação não servem apenas para proteção, segurança e isolamento de processos. Elas também fornecem um conjunto de abstrações para o uso eficiente da memória física e para o gerenciamento de memória
      O uso virtual de um processo pode exceder o RSS mesmo que não seja por causa de swapping, e o sistema operacional e o alocador trabalham juntos para lidar com isso de forma bastante inteligente nos casos comuns
      Por isso, é difícil dizer que remover isso traria automaticamente ganhos de desempenho. Especialmente se ainda houver uma camada de VM WASM, que é bem lenta, no caminho
      Para algumas aplicações, como bancos de dados, pode haver grandes benefícios em rodar como unikernel ou mais próximo do kernel, com acesso direto à MMU: https://github.com/tuhhosg/exmap & https://github.com/viktorleis/vmcache & https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/_my_direct_up...
      Mas tenho dúvidas quanto a aplicações comuns que assumem o padrão POSIX ou pressupõem que o ambiente de execução se pareça com um computador moderno genérico. No fim, parece que muita coisa que a camada VMM fazia acabaria sendo reescrita em código de usuário
    • Remover o suporte a mapeamento de memória virtual faz cada vez menos sentido quanto mais se pensa nisso
      Motores JS dependem do VMM, e o WASM também depende dele de várias maneiras. Quase todo programa não trivial que não seja embarcado assume sutilmente a existência do VMM. Em especial, algumas tecnologias de VM em torno de micro-VMs também usam VMM, e unikernels só fazem real sentido quando usados como VMs