Con la llegada de WebAssembly (de ahora en adelante Wasm) en 2017, se abría un nuevo mundo de posibilidades. Con Wasm se busca ejecutar código binario escrito en casi cualquier lenguaje de programación y sin importar el navegador, de forma segura (en un entorno aislado) y de manera nativa (en ocasiones con mejor rendimiento).
Más tarde apareció WebAssembly System Interface (de ahora en adelante WASI), que permite que los binarios de Wasm puedan interactuar con el sistema operativo de forma más sencilla sin importar dónde se ejecuten.
Vamos a ver cómo compilar diversos lenguajes a Wasm/WASI y usarlo con Docker.
Índice
- 1. Entorno y configuración
- 2. Java
- 3. Go
- 4. Rust
- 5. Python
- 6. Creación y arranque de contenedor Docker Wasm
- 7. Conclusiones
1. Entorno y configuración
El entorno en el que se han probado las herramientas explicadas en este tutorial es el siguiente:
- Hardware: Portátil MacBook Pro 16′ 2021 (
Apple M1 Max
, 64 GB LPDDR5-6400, 2TB SSD). - Sistema Operativo: macOS Sonoma
14.3.1
- Docker Desktop
4.28.0
- Java
17.0.9
y Maven3.9.6
- Go
1.21.5
- Rustup
1.27.0
y Rustc1.77.0
Para añadir el soporte de Wasm en Docker Desktop hay que habilitar lo siguiente:
- En Ajustes, en la pestaña General, marcar «Use containerd for pulling and storing images».
- En Ajustes, en la pestaña Features in development, marcar «Enable Wasm».
Después pulsamos en «Apply & Restart» y Docker Desktop configurará todo lo necesario para ejecutar el entorno de ejecución de Wasm.
2. Java
El proceso para compilar aplicaciones Java a Wasm puede realizarse de distintas maneras. En este caso se realiza con Maven a través del siguiente pom.xml
:
<project> ... <properties> <java.version>17</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <teavm.version>0.2.8</teavm.version> </properties> <dependencies> <dependency> <groupId>com.fermyon</groupId> <artifactId>teavm-classlib</artifactId> <version>${teavm.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>com.fermyon</groupId> <artifactId>teavm-maven-plugin</artifactId> <version>${teavm.version}</version> <executions> <execution> <goals> <goal>compile</goal> </goals> <configuration> <targetDirectory>${project.build.directory}/generated/wasm</targetDirectory> <targetType>WEBASSEMBLY</targetType> <mainClass>Main</mainClass> ... </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Como se puede observar, actualmente el plugin teavm-maven-plugin
soporta hasta Java 17
. Al ejecutar mvn clean package
, se generará el binario Wasm en el directorio target/generated/wasm
3. Go
En el caso de Go, el propio entorno soporta compilar a Wasm y WASI por lo que solo es necesario ejecutar el siguiente comando en un proyecto Go: GOOS=wasip1 GOARCH=wasm go build -o app.wasm
4. Rust
El caso de Rust es muy parecido al de Go, ya que de nuevo el propio entorno soporta Wasm y WASI. Por tanto, solo hay que ejecutar 2 acciones:
- Activar el soporte de forma global con
rustup target add wasm32-wasi
. - Compilar con el comando
cargo build --target wasm32-wasi
.
El resultado será el binario Wasm en el directorio target/wasm32-wasi/debug/rust.wasm
.
5. Python
Estado [Marzo 2024]
Aunque la comunidad detrás de Wasm/WASI para Python es activa, no hay una manera clara de compilar el código Python a Wasm. En muchos casos las soluciones funcionan parcialmente o generan errores que impiden su uso.
- Portar el proyecto de Python a WASI.
- Compilar el código Python a WASI (no funciona actualmente con la versión
2.2rc1
). - Framework Spin (solo compila a Wasm pero no a WASI).
6. Creación y arranque de contenedor Docker Wasm
El proceso para contenerizar aplicaciones Wasm siempre es el mismo: construir el binario Wasm e introducirlo en un contenedor. Ya que el entorno de Docker dispone de soporte para Wasm, no es necesario usar imágenes base complejas y con el siguiente Dockerfile
sería suficiente:
FROM scratch COPY app.wasm /app.wasm ENTRYPOINT ["/app.wasm"]
Después solo será necesario construir el contenedor con docker build --platform wasi/wasm -t wasm-app:latest .
y arrancarlo con docker run --rm --platform=wasi/wasm --runtime=io.containerd.wasmedge.v1 wasm-app:latest
.
7. Conclusiones
En el siguiente repositorio de GitHub se puede encontrar todo el código del tutorial. Para ejecutarlo solo hay que tener instalado el entorno de manera correcta y ejecutar el comando en un terminal “run-all.sh”.
Aunque esta tecnología es muy prometedora y no es nueva en el mundo del desarrollo, aún no está totalmente acogida por todos los lenguajes de programación. Hay lenguajes como C y Go que la soportan de manera nativa y pueden usarse en producción, mientras que con otros solo se puede «probar».
Por otro lado, Docker ha adoptado esta tecnología bastante rápido y la forma de usarla es sencilla. Esto puede hacer que cambie el paradigma de contenerización de las aplicaciones, ya que al compilar a Wasm todas las aplicaciones, se ejecutan de la misma forma (algo ideal para un entorno de contenedores como Kubernetes). Ejemplo de ello es lo que ocupan las imágenes «Hello Word» de cada tecnología (? la imagen de java solo ocupa 242kb ?):