Como algumas tecnologias inovadoras, pessoas diferentes têm pontos de vista diferentes sobre onde a WebAssembly se encaixa no cenário da tecnologia.
O WebAssembly (também chamado WASM) é certamente o assunto de muito hype agora. Mas o que é? É o assassino de JavaScript? É uma nova linguagem de programação para a web? É (como gostamos de dizer) a próxima onda de computação em nuvem? Ouvimos isso chamado muitas coisas: um EBPF melhor, a alternativa ao RISC V, um concorrente do Java (ou Flash), um impulsionador de desempenho para navegadores, um substituto para o Docker.
– Como pensar em WebAssembly
Neste post, ficarei longe desses debates e focarei apenas em como usar o WebAssembly no Kubernetes.
Minha abordagem e o caso de uso
Diferentemente das linguagens de programação regulares, você não escreve o WebAssembly diretamente: você escreve código que gera o WebAssembly. No momento, vá e a ferrugem são os principais idiomas da fonte. Eu sei que Kotlin e Python estão trabalhando para esse objetivo. Pode haver outros idiomas de que não estou ciente.
Eu decidi a Rust para este post por causa da minha familiaridade com o idioma. Em particular, vou manter o mesmo código em três arquiteturas diferentes:
- Código de ferrugem para nativo regular como linha de base
- Rusto-to-webassembly usando um tempo de execução incorporado a Wasmedge
- Rusto-to-Webassembly usando um tempo de execução externo
Não se preocupe; Vou explicar a diferença entre as duas últimas abordagens mais tarde.
O caso de uso deve ser mais avançado do que o Hello World para destacar as capacidades do WebAssembly. Eu implementei um servidor HTTP imitando um único terminal do excelente utilitário de teste de API HTTPBIN. O código em si não é essencial, pois a postagem não é sobre ferrugem, mas, caso você esteja interessado, você pode encontrá -lo no Github. Eu adiciono um campo à resposta para retornar explicitamente a abordagem subjacente, respectivamente native
Assim, embed
ou runtime
.
Linha de base: ferrugem regular à nativa
Para a compilação nativa regular, estou usando um arquivo do Docker de vários estágios:
FROM rust:1.84-slim AS build #1
RUN <
- Comece a partir da última imagem de ferrugem
- Heredocs para a vitória
- Instale a cadeia de ferramentas necessária para cruzar o compile
- Compilar estaticamente
- Eu poderia usar
FROM scratch
mas depois de ler isso, prefiro usar distritos - Copie o executável da fase de compilação anterior
O final wasm-kubernetes:native
A imagem pesa 8,71m, com sua imagem base distroless/static
levando 6,03m deles.
Adaptando -se ao WebAssembly
A principal idéia por trás da WebAssembly é que ela é segura porque não pode acessar o sistema host. No entanto, devemos abrir um soquete para ouvir solicitações de entrada para executar um servidor HTTP. WebAssembly não pode fazer isso. Precisamos de um tempo de execução que forneça esse recurso e outros recursos dependentes do sistema. É o objetivo da interface do sistema WebAssembly.
A interface do sistema WebAssembly (WASI) é um grupo de especificações da API de padrões-track para software compilado com o padrão W3C WebAssembly (WASM). O WASI foi projetado para fornecer uma interface padrão segura para aplicativos que podem ser compilados em WASM de qualquer idioma e que possa ser executado em qualquer lugar – de navegadores a nuvens a dispositivos incorporados.
– Introdução a Wasi
A especificação v0.2 define as seguintes interfaces do sistema:
- Relógios
- Aleatório
- FileSystem
- Soquetes
- CLI
- Http
Alguns tempos de execução já implementam a especificação:
- Wasmtime, desenvolvido pela ByteCode Alliance
- Wasmer
- Wazero, baseado em Go
- Wasmedge, projetado para aplicativos de nuvem, computação de borda e IA
- Girar para cargas de trabalho sem servidor
Eu tive que escolher sem ser especialista em nenhum deles. Finalmente decidi por Wasmedge por causa de seu foco na nuvem.
Devemos interceptar o código que chama as APIs do sistema e redirecioná -las para o tempo de execução. Em vez de interceptação de tempo de execução, o ecossistema de ferrugem fornece um mecanismo de patch: substituímos o código que chama APIs do sistema pelo código que chama APIs WASI. Devemos saber quais dependência chama qual API do sistema e esperamos que exista um patch para nossa versão de dependência.
[patch.crates-io]
tokio = { git = " branch = "v1.36.x" } #1-2
socket2 = { git = " branch = "v0.5.x" } #1
[dependencies]
tokio = { version = "1.36", features = ["rt", "macros", "net", "time", "io-util"] } #2
axum = "0.8"
serde = { version = "1.0.217", features = ["derive"] }
- Patch o
tokio
esocket2
caixas com chamadas relacionadas a Wasi - O mais recente
tokio
A caixa é 1,43, mas o patch mais recente (e único) é v1.36. Não podemos usar a versão mais recente porque não há patch.
Devemos alterar o DockerFile para compilar o código WebAssembly em vez de nativo:
FROM --platform=$BUILDPLATFORM rust:1.84-slim AS build
RUN <<3>
- Instale o alvo WASM
- Compilar com WASM
- Devemos ativar o
wasmedge
bandeira, bem como otokio_unstable
um, para compilar com êxito com o WebAssembly
Nesta fase, temos duas opções para o segundo estágio:
-
Use o tempo de execução do Wasmedge como uma imagem base:
FROM --platform=$BUILDPLATFORM wasmedge/slim-runtime:0.13.5 COPY --from=build /wasm/target/wasm32-wasip1/release/httpbin.wasm /httpbin.wasm CMD ["wasmedge", "--dir", ".:/", "/httpbin.wasm"]
Do ponto de vista do uso, é bem semelhante à abordagem nativa.
-
Copie o arquivo WebAssembly e faça dele uma responsabilidade de tempo de execução:
FROM scratch COPY --from=build /wasm/target/wasm32-wasip1/release/httpbin.wasm /httpbin.wasm ENTRYPOINT ["/httpbin.wasm"]
É onde as coisas ficam interessantes.
O native
a abordagem é um pouco melhor que a embed
um, mas o runtime
é o mais magro, pois contém apenas um único arquivo WebAssembly.
Executando a imagem WASM no Docker
Nem todos os tempos de execução do Docker são iguais e, para executar cargas de trabalho do WASM, precisamos nos aprofundar um pouco no nome do Docker. Enquanto o Docker, a empresa, criou o Docker como o produto, a realidade atual é que os contêineres evoluíram além do Docker e agora respondem às especificações.
O Iniciativa de contêiner aberto é uma estrutura de governança aberta com o objetivo expresso de criar padrões abertos do setor em torno dos formatos de contêineres e tempos de execução.
Fundada em junho de 2015 por Docker e outros líderes da indústria de contêineres, a OCI atualmente contém três especificações: a especificação de tempo de execução (especificação de tempo de execução), a especificação da imagem (especificação da imagem) e a especificação de distribuição (especificação de distribuição). A especificação de tempo de execução descreve como executar um “pacote de sistemas de arquivos” que não é embalado no disco. Em um nível de alto nível, uma implementação da OCI baixaria uma imagem OCI e descompacte essa imagem em um pacote de sistema de arquivos de tempo de execução da OCI. Nesse ponto, o pacote de tempo de execução da OCI seria executado por um tempo de execução da OCI.
– Iniciativa de contêiner aberto
A partir de então, usarei a terminologia adequada para imagens e contêineres da OCI. Nem todos os tempos de execução da OCI são iguais, e longe de todos eles podem executar cargas de trabalho do WASM: Orbstack, meu tempo de execução da OCI atual, não pode, mas o Docker Desktop pode, como um experimental recurso. De acordo com a documentação, devemos:
- Usar
containerd
Para puxar e armazenar imagens - Ativar Wasm
Por fim, podemos executar a imagem OCI acima que contém o arquivo WASM selecionando um tempo de execução do WASM, Wasmedge, no meu caso. Vamos fazê-lo:
docker run --rm -p3000:3000 --runtime=io.containerd.wasmedge.v1 ghcr.io/ajavageek/wasm-kubernetes:runtime
io.containerd.wasmedge.v1
é a versão atual do tempo de execução do Wasmedge. Você deve ser autenticado com o GitHub se quiser experimentá -lo.
curl localhost:3000/get\?foo=bar | jq
O resultado é o mesmo da versão nativa:
{
"flavor": "runtime",
"args": {
"foo": "bar"
},
"headers": {
"accept": "*/*",
"host": "localhost:3000",
"user-agent": "curl/8.7.1"
},
"url": "/get?foo=bar"
}
O Wasi On Docker Desktop permite que você gire um servidor HTTP que se comporte como uma imagem nativa comum! Melhor ainda, o tamanho da imagem é tão pequeno quanto o arquivo WebAssembly que ele contém:
Executando a imagem WASM em Kubernetes
Agora vem a parte divertida: seu (s) provedor (s) de nuvem favorito não está usando o Docker Desktop. Apesar disso, ainda podemos executar cargas de trabalho no WebAssembly em Kubernetes. Para isso, precisamos entender um pouco sobre os níveis não muito baixos do que acontece quando você executa um contêiner, independentemente de ser de um tempo de execução da OCI ou Kubernetes.
Este último executa um processo; No nosso caso, é containerd
. Ainda, containerd
é apenas um orquestrador de outros processos de contêiner. Ele detecta o “sabor” do contêiner e chama o executável relevante. Por exemplo, para contêineres “regulares”, ele chama runc
via a calço. O bom é que podemos instalar outros calços dedicados a outros tipos de contêineres, como o WASM. A ilustração a seguir, retirada do site de Wasmedge, resume o fluxo:
Apesar de alguns dos principais provedores de nuvem que oferecem integração do WASM, nenhum deles fornece um nível tão baixo. Continuarei no meu laptop, mas o Docker Desktop também não oferece uma integração direta: é hora de ser criativo. Por exemplo, o Minikube é uma distribuição de Kubernetes completa que cria uma máquina virtual Linux intermediária dentro de um ambiente do Docker. Podemos ssh na VM e configurá -la para o conteúdo do nosso coração. Vamos começar instalando minikube
.
brew install minikube
Agora, começamos minikube
com o containerd
driver e especifique um perfil para ativar VMs configuradas de maneira diferente. Nós chamamos de maneira sem imaginação este perfil wasm
.
minikube start --driver=docker --container-runtime=containerd -p=wasm
Dependendo de você já instalou minikube
E se já baixou suas imagens, o início pode levar alguns segundos a dezenas de minutos. Ser paciente. A saída deve ser algo semelhante a:
😄 [wasm] minikube v1.35.0 on Darwin 15.1.1 (arm64)
✨ Using the docker driver based on user configuration
📌 Using Docker Desktop driver with root privileges
👍 Starting "wasm" primary control-plane node in "wasm" cluster
🚜 Pulling base image v0.0.46 ...
❗ minikube was unable to download gcr.io/k8s-minikube/kicbase:v0.0.46, but successfully downloaded docker.io/kicbase/stable:v0.0.46@sha256:fd2d445ddcc33ebc5c6b68a17e6219ea207ce63c005095ea1525296da2d1a279 as a fallback image
🔥 Creating docker container (CPUs=2, Memory=12200MB) ...
📦 Preparing Kubernetes v1.32.0 on containerd 1.7.24 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔗 Configuring CNI (Container Networking Interface) ...
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use "wasm" cluster and "default" namespace by default
Neste ponto, nosso objetivo é instalar na VM subjacente:
- Wasmedge de executar cargas de trabalho de Wasm
- Um calço para a ponte entre
containerd
ewasmedge
minikube ssh -p wasm
Podemos instalar o Wasmedge, mas não encontrei nenhum lugar para baixar o Shim. Na próxima etapa, construiremos os dois. Primeiro precisamos instalar a ferrugem:
curl --proto '=https' --tlsv1.2 -sSf | sh
O script provavelmente reclama que não pode executar o binário baixado:
Cannot execute /tmp/tmp.NXPz8utAQx/rustup-init (likely because of mounting /tmp as noexec).
Please copy the file to a location where you can execute binaries and run ./rustup-init.
Siga as instruções:
cp /tmp/tmp.NXPz8utAQx/rustup-init .
./rustup-init
Prosseguir com a instalação padrão pressionando o ENTER
botão. Quando terminar, obtenha seu shell atual.
. "$HOME/.cargo/env"
O sistema está pronto para construir Wasmedge e o calço.
sudo apt-get update
sudo apt-get install -y git
git clone
cd runwasi
./scripts/setup-linux.sh
make build-wasmedge
INSTALL="sudo install" LN="sudo ln -sf" make install-wasmedge
A última etapa requer configurar o containerd
Processar com o calço. Insira o seguinte trecho no [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
seção do /etc/containerd/config.toml
arquivo:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wasmedgev1]
runtime_type = "io.containerd.wasmedge.v1"
Reiniciar containerd
Para carregar a nova configuração.
sudo systemctl restart containerd
Nosso sistema está finalmente pronto para aceitar cargas de trabalho WebAssembly. Os usuários podem implantar uma Wasmedge pod
Com o seguinte manifesto:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: wasmedge #1
handler: wasmedgev1 #2
---
apiVersion: v1
kind: Pod
metadata:
name: runtime
labels:
arch: runtime
spec:
containers:
- name: runtime
image: ghcr.io/ajavageek/wasm-kubernetes:runtime
runtimeClassName: wasmedge #3
- Cargas de trabalho de Wasmedge devem usar este nome
- Manipulador para usar. Deve ser o último segmento da seção adicionado no arquivo Toml, ou sejaAssim,
containerd.runtimes.wasmedgev2
- Aponte para o nome da classe de tempo de execução, definimos logo acima
Eu usei um único Pod
em vez de um pleno de pleno direito Deployment
Para manter as coisas simples.
Observe os muitos níveis de indireção:
- O
pod
refere -se aowasmedge
Nome da classe de tempo de execução - O
wasmedge
A classe de tempo de execução aponta para owasmedgev1
manipulador - O
wasmedgev1
manipulador no arquivo Toml especifica oio.containerd.wasmedge.v1
Tipo de tempo de execução
Etapas finais
Para comparar as abordagens e testar nosso trabalho, podemos usar o minikube
ingress
addon e vcluster. O primeiro oferece um único ponto de acesso para todas as três cargas de trabalho, native
Assim, embed
e runtime
enquanto o Vcluster isola as cargas de trabalho umas das outras em seu cluster virtual.
Vamos começar instalando o addon:
minikube -p wasm addons enable ingress
Ele implanta um controlador de entrada nginx no ingress-nginx
namespace:
💡 ingress is an addon maintained by Kubernetes. For any concerns contact minikube on GitHub.
You can view the list of minikube maintainers at:
💡 After the addon is enabled, please run "minikube tunnel" and your ingress resources would be available at "127.0.0.1"
▪ Using image registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4
▪ Using image registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4
▪ Using image registry.k8s.io/ingress-nginx/controller:v1.11.3
🔎 Verifying ingress addon...
🌟 The 'ingress' addon is enabled
Devemos criar um cluster virtual dedicado para implantar o Pod
mais tarde.
helm upgrade --install runtime vcluster/vcluster --namespace runtime --create-namespace --values vcluster.yaml
Vamos definir o Ingress
o Service
e seus relacionados Pod
em cada cluster virtual. Precisamos vcluster para sincronizar o Ingress
com o controlador de entrada. Aqui está a configuração para conseguir isso:
sync:
toHost:
ingresses:
enabled: true
A saída deve ser semelhante a:
Release "runtime" does not exist. Installing it now.
NAME: runtime
LAST DEPLOYED: Thu Jan 30 11:53:14 2025
NAMESPACE: runtime
STATUS: deployed
REVISION: 1
TEST SUITE: None
Podemos alterar o manifesto acima com o Service
e Ingress
para expor o Pod
:
apiVersion: v1
kind: Service
metadata:
name: runtime
spec:
type: ClusterIP #1
ports:
- port: 3000 #1
selector:
arch: runtime
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: runtime
annotations:
nginx.ingress.kubernetes.io/use-regex: "true" #2
nginx.ingress.kubernetes.io/rewrite-target: /$2 #2
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /runtime(/|$)(.*) #4
pathType: ImplementationSpecific #4
backend:
service:
name: runtime
port:
number: 3000
- Exponha o
Pod
dentro do cluster - Anotações específicas para o nginx para lidar com a expressão regular de caminho e reescrever
- Caminho regex
Nginx encaminhará todas as solicitações começando com /runtime
para o runtime
serviço, removendo o prefixo. Para aplicar o manifesto, primeiro nos conectamos ao cluster virtual criado anteriormente:
vcluster connect runtime
11:53:21 info Waiting for vcluster to come up...
11:53:39 done vCluster is up and running
11:53:39 info Starting background proxy container...
11:53:39 done Switched active kube context to vcluster_embed_embed_vcluster_runtime_runtime_wasm
- Use `vcluster disconnect` to return to your previous kube context
- Use `kubectl get namespaces` to access the vcluster
Agora, aplique o manifesto:
kubectl apply -f runtime.yaml
Fazemos o mesmo com o embed
e o native
vagens, exceto o runtimeClassName
como são imagens “regulares”.
O diagrama de implantação final é o seguinte:
O toque final é o túnel para expor os serviços:
minikube -p wasm tunnel
✅ Tunnel successfully started
📌 NOTE: Please do not close this terminal as this process must stay alive for the tunnel to be accessible ...
❗ The service/ingress runtime-x-default-x-runtime requires privileged ports to be exposed: [80 443]
🔑 sudo permission will be asked for it.
🏃 Starting tunnel for service runtime-x-default-x-runtime.
Password:
Vamos solicitar o contêiner leve que usa o tempo de execução do Wasmedge:
curl localhost/runtime/get\?foo=bar | jq
Temos a saída esperada:
{
"flavor": "runtime",
"args": {
"foo": "bar"
},
"headers": {
"user-agent": "curl/8.7.1",
"x-forwarded-host": "localhost",
"x-request-id": "dcbdfde4715fbfc163c7c9098cbdf077",
"x-scheme": "http",
"x-forwarded-for": "10.244.0.1",
"x-forwarded-scheme": "http",
"accept": "*/*",
"x-real-ip": "10.244.0.1",
"x-forwarded-proto": "http",
"host": "localhost",
"x-forwarded-port": "80"
},
"url": "/get?foo=bar"
}
Devemos obter resultados semelhantes com as outras abordagens, com diferentes flavor
valores.
Conclusão
Neste post, mostrei como usar o WebAssembly em Kubernetes com o tempo de execução do Wasmedge. Eu criei três sabores para fins de comparação: native
Assim, embed
e runtime
. Os dois primeiros são imagens “regulares” do Docker, enquanto o último contém apenas um único arquivo WASM, o que o torna muito leve e seguro. No entanto, precisamos de um tempo de execução dedicado para executá -lo.
Os serviços regulares de Kubernetes gerenciados não permitem configurar um calço adicional, como o Shim Wasmedge. Mesmo no meu laptop, eu tinha que ser criativo para fazer isso acontecer. Eu tive que usar o Minikube e fazer muito esforço para configurar sua máquina virtual intermediária para executar cargas de trabalho do WASM em Kubernetes. No entanto, consegui executar todas as três imagens dentro de seu cluster virtual, expostas fora do cluster por um controlador de entrada nginx.
Agora, cabe a você decidir se o esforço extra vale a redução de 10x do tamanho da imagem e a segurança aprimorada. Espero que o futuro melhore o suporte para que os profissionais superassem os contras.
O código -fonte completo para esta postagem pode ser encontrado no GitHub.
Vá mais longe: