WebAssembly en contenedores: Docker, Wasm y WASI

0
150

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

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 Maven 3.9.6
  • Go 1.21.5
  • Rustup 1.27.0 y Rustc 1.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.

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 😱):

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad