How do I specify the platform of a multi-platform image?

Docker: Guía Definitiva de Builds Multiplataforma

05/01/2022

Valoración: 4.85 (2032 votos)

En el mundo del desarrollo de software, la frase "funciona en mi máquina" ha sido tanto un chiste como una pesadilla recurrente. Docker llegó como un superhéroe para resolver este problema, empaquetando aplicaciones y sus dependencias en contenedores portátiles. Sin embargo, pronto descubrimos una nueva capa de complejidad: la arquitectura del procesador. Con la creciente popularidad de dispositivos ARM como las Raspberry Pi, las instancias Graviton de AWS y los Mac con Apple Silicon, ya no podemos asumir que todo el mundo ejecuta código en procesadores x86-64 (amd64). Aquí es donde entran en juego las builds multiplataforma, una capacidad esencial de Docker que te permite construir una única imagen que se ejecuta de forma nativa en múltiples arquitecturas. Este artículo es tu guía completa para entender, configurar y dominar esta poderosa herramienta.

What does the train leaves on Platform 9 mean?
I would not say "the train leaves on platform 9" but it would be understandable. "leaves on" is not used normally in that case because "leaves" is used to mean "depart from" but there are cases in which it would be used. For example "the train leaves on track 9" Since the train travels on the track, it would be correct.
Índice de Contenido

¿Qué es Exactamente una Build Multiplataforma?

Una build multiplataforma es el proceso de usar una única invocación de compilación para generar variantes de una imagen de Docker destinadas a diferentes combinaciones de sistema operativo y arquitectura de CPU. El resultado es una sola "etiqueta" de imagen (por ejemplo, mi-app:latest) que en realidad es un puntero a múltiples imágenes específicas de la plataforma.

Esto es posible gracias a una característica llamada lista de manifiestos (manifest list). Una imagen de Docker tradicional, de plataforma única, tiene un manifiesto que describe sus capas y configuración. En cambio, una imagen multiplataforma tiene una lista de manifiestos, que actúa como un índice. Cada entrada en esta lista apunta al manifiesto de una variante de imagen específica, como linux/amd64, linux/arm64, o incluso windows/amd64.

Cuando ejecutas un comando como docker pull mi-app:latest, el cliente de Docker se comunica con el registro, recibe la lista de manifiestos y automáticamente selecciona y descarga la variante de imagen que coincide con la arquitectura de su propio sistema anfitrión. Es un proceso transparente y elegante que garantiza que siempre se ejecute el código más optimizado para el hardware subyacente, sin necesidad de emulación en tiempo de ejecución (en la mayoría de los casos).

El Misterio del Build Exitoso en una Plataforma Incorrecta

Muchos desarrolladores se encuentran con una situación confusa. Tienen un Dockerfile como este en su máquina amd64:

FROM --platform=linux/arm64 ubuntu:22.04

Y ejecutan el siguiente comando de compilación:

docker buildx build --platform linux/arm64 -t mi-app-arm .

Intuitivamente, esperarían que esto fallara si su máquina anfitriona no es arm64. Sorprendentemente, la compilación a menudo tiene éxito. ¿Por qué? La respuesta está en dos tecnologías clave: BuildKit y QEMU.

What is a platform party?
The platform party (= the group on the platform) applauded loudly. All southbound trains leave from platform one. Three men were feared dead last night after a helicopter veered off course into an oil platform. I slipped as I stepped onto the platform. He mounted the platform and began to speak to the assembled crowd.

docker buildx es la interfaz de línea de comandos para BuildKit, el motor de compilación de próxima generación de Docker. BuildKit es increíblemente inteligente y, cuando se le pide que construya para una arquitectura extranjera, busca una forma de hacerlo. Aquí es donde entra QEMU, un emulador de sistema completo. Docker Desktop y muchas configuraciones de Docker Engine en Linux vienen con soporte para QEMU preconfigurado. Cuando BuildKit detecta que necesita ejecutar un comando para arm64 en un host amd64, utiliza QEMU de forma transparente para emular el entorno arm64 y ejecutar el comando. Por lo tanto, tu build no falla; simplemente se vuelve mucho más lento porque cada paso se ejecuta bajo emulación.

El problema real que el usuario original experimentó es que la imagen base nvidia/cuda no tenía una variante arm64. Buildx, en su intento de cumplir la solicitud, probablemente recurrió a la única imagen disponible (amd64) y la ejecutó dentro de un entorno emulado de arm64. La advertencia al ejecutar el contenedor (WARNING: The requested image's platform (linux/arm64) does not match the detected host platform (linux/amd64)) es la confirmación de este comportamiento: creaste con éxito una imagen para linux/arm64, y al intentar ejecutarla en tu host linux/amd64, Docker te advierte que está recurriendo a la emulación nuevamente para ejecutarla.

Preparando tu Entorno para el Éxito Multiplataforma

Antes de poder empezar a construir imágenes para múltiples arquitecturas, debes asegurarte de que tu entorno de Docker esté correctamente configurado. El almacén de imágenes "clásico" de Docker Engine no soporta listas de manifiestos. Tienes dos opciones principales para habilitar esta capacidad:

  1. Cambiar al almacén de imágenes de containerd: Esta es la opción recomendada y más moderna. Asegura que tu Docker Engine pueda manejar, empujar y extraer imágenes multiplataforma de forma nativa. En Docker Desktop, esto es un simple ajuste en la configuración. Para Docker Engine standalone, requiere una modificación en el archivo de configuración del daemon.
  2. Crear un builder personalizado: Puedes usar docker buildx create para configurar un nuevo "builder" que utilice un driver con soporte multiplataforma, como docker-container. Esto te permite construir imágenes multiplataforma sin cambiar tu almacén de imágenes principal. La desventaja es que las imágenes construidas no se cargan automáticamente en tu almacén local (docker images). Debes empujarlas directamente a un registro usando la bandera --push en tu comando de build.

Tres Estrategias para Construir Imágenes Multiplataforma

Una vez que tu entorno está listo, puedes elegir entre tres estrategias principales para realizar tus builds. La elección dependerá de tus necesidades de rendimiento, complejidad y del lenguaje de programación que utilices.

1. Emulación con QEMU: La Vía Sencilla

Como ya hemos comentado, esta es la forma más fácil de empezar. No requiere cambios en tu Dockerfile. Simplemente pasas múltiples plataformas a tu comando de build, y BuildKit se encarga del resto a través de QEMU.

docker buildx build --platform linux/amd64,linux/arm64 -t mi-app:latest --push .

Ventajas: Extremadamente fácil de usar. Cero modificaciones en el Dockerfile.

What is a multi-platform build?
A multi-platform build refers to a single build invocation that targets multiple different operating system or CPU architecture combinations. When building images, this lets you create a single image that can run on multiple platforms, such as linux/amd64, linux/arm64, and windows/amd64. Why multi-platform builds?

Desventajas: El rendimiento puede ser significativamente más lento, especialmente para tareas intensivas en CPU como la compilación de código o la compresión de archivos. La emulación a veces puede tener errores sutiles o incompatibilidades.

2. Nodos Nativos: Rendimiento Superior

Esta estrategia implica configurar un builder de Buildx que consista en múltiples máquinas (nodos), cada una ejecutando una de las arquitecturas de destino de forma nativa. Por ejemplo, podrías tener un nodo en una máquina amd64 y otro en una Raspberry Pi (arm64). BuildKit es lo suficientemente inteligente como para distribuir las etapas de compilación al nodo apropiado.

Puedes agregar nodos a un builder existente usando el comando docker buildx create --append. Aunque esto requiere la sobrecarga de gestionar múltiples máquinas, ofrece el mejor rendimiento y la mayor fiabilidad.

Ventajas: Rendimiento nativo completo. La compilación es rápida y fiable.

Desventajas: Requiere la configuración y el mantenimiento de hardware físico o virtual para cada arquitectura de destino, lo que aumenta la complejidad y el costo.

Who is platform?
Platform was created in 2001 and aims to reach the ultimate vision of our clients. We work closely with all stakeholders and take pride in our communication, project management, and execution abilities. With our considerable experience in a variety of sectors, Platform will deliver.

3. Cross-Compilation: La Opción Avanzada y Eficiente

Para lenguajes de programación que lo soportan (como Go, Rust y C/C++ con la toolchain adecuada), la compilación cruzada o cross-compilation es la mejor opción. La idea es usar la velocidad nativa de tu máquina de compilación para compilar binarios para diferentes arquitecturas de destino.

Esto se logra utilizando un Dockerfile de múltiples etapas. BuildKit proporciona automáticamente argumentos de compilación especiales como TARGETPLATFORM (ej: linux/arm64), TARGETOS (ej: linux) y TARGETARCH (ej: arm64) que puedes usar en tu Dockerfile para instruir a tu compilador.

Veamos un ejemplo para una aplicación Go:

# syntax=docker/dockerfile:1 # Fijamos la etapa de build a la plataforma del builder para velocidad nativa FROM --platform=$BUILDPLATFORM golang:1.19-alpine AS builder # Hacemos los ARGs de destino disponibles en esta etapa ARG TARGETOS ARG TARGETARCH WORKDIR /app COPY . . # Usamos los ARGs para instruir al compilador de Go RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /app/server . # Etapa final, copia el binario compilado FROM alpine COPY --from=builder /app/server /server ENTRYPOINT [ "/server" ] 

Con este Dockerfile, la compilación se ejecuta a velocidad nativa en la máquina anfitriona, produciendo binarios para cada plataforma de destino, que luego se copian en una imagen final mínima.

Ventajas: El más rápido de los tres métodos. No requiere hardware adicional. Produce artefactos de compilación optimizados.

Desventajas: Requiere que tu lenguaje y herramientas soporten la compilación cruzada. El Dockerfile es más complejo.

What are the different types of platform?

Tabla Comparativa de Estrategias

CriterioEmulación con QEMUNodos NativosCross-Compilation
VelocidadLentaRápida (Nativa)Muy Rápida (Nativa)
Complejidad de ConfiguraciónBajaAltaMedia
Modificaciones al DockerfileNingunaNingunaRequeridas (multi-etapa, ARGs)
Casos de Uso IdealesDesarrollo rápido, scripts simples, pruebas iniciales.Proyectos complejos, CI/CD, cuando la emulación falla.Lenguajes compilados con soporte de cross-compilation.

Preguntas Frecuentes (FAQ)

¿Por qué mi build no falla si la imagen base no existe para mi plataforma de destino?

Esto se debe a la emulación con QEMU. docker buildx intentará emular la plataforma de destino en tu host. Ejecutará los pasos del Dockerfile dentro de este entorno emulado. Si la imagen base no tiene la variante de plataforma solicitada, puede intentar usar la variante de la plataforma del host y ejecutarla bajo emulación, lo que puede llevar a comportamientos inesperados pero no necesariamente a un fallo inmediato.

¿Cómo puedo forzar que el build falle si la plataforma no está disponible?

Aunque no hay una bandera simple como --no-emulation, una práctica robusta es usar la bandera --platform en la instrucción FROM de tu Dockerfile, como FROM --platform=$TARGETPLATFORM alpine. Esto le indica a Docker que debe fallar si no puede encontrar una variante de la imagen base que coincida exactamente con la plataforma de destino para esa etapa, haciendo tu build más predecible.

¿Cómo puedo saber para qué arquitecturas está disponible una imagen?

Puedes usar el comando docker buildx imagetools inspect <imagen:tag>. Esto te mostrará el manifiesto de la imagen, listando todas las plataformas soportadas si es una imagen multiplataforma.

¿Realmente necesito usar `buildx`?

Sí. Mientras que el comando docker build básico ha incorporado algunas características de BuildKit, el subcomando buildx es la interfaz completa que te da acceso a todas las capacidades avanzadas, incluyendo la gestión de builders, la construcción multiplataforma, y diferentes tipos de salida. Para todo lo discutido en este artículo, buildx es la herramienta a usar.

Si quieres conocer otros artículos parecidos a Docker: Guía Definitiva de Builds Multiplataforma puedes visitar la categoría Juegos.

Subir