Cómo usar el comando docker buildx build para construir y gestionar imágenes

DockerDockerBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, adquirirás experiencia práctica utilizando el comando docker buildx build para construir y gestionar imágenes de Docker. Comenzarás construyendo una imagen simple con configuraciones predeterminadas, aprendiendo cómo definir instrucciones de imagen usando un Dockerfile.

Más allá de lo básico, explorarás funciones más avanzadas como el uso de argumentos de construcción (build arguments) y la selección de etapas específicas en una construcción multi-etapa (multi-stage build). También aprenderás a gestionar eficazmente la caché de construcción para optimizar los tiempos de compilación utilizando --cache-from y --cache-to. Además, el laboratorio te guiará en la construcción de imágenes multiplataforma y su envío a un registro (registry), y demostrará cómo exponer de forma segura secretos (secrets) y agentes SSH durante el proceso de construcción.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL docker(("Docker")) -.-> docker/ContainerOperationsGroup(["Container Operations"]) docker(("Docker")) -.-> docker/ImageOperationsGroup(["Image Operations"]) docker(("Docker")) -.-> docker/SystemManagementGroup(["System Management"]) docker(("Docker")) -.-> docker/DockerfileGroup(["Dockerfile"]) docker/ContainerOperationsGroup -.-> docker/run("Run a Container") docker/ImageOperationsGroup -.-> docker/push("Push Image to Repository") docker/ImageOperationsGroup -.-> docker/rmi("Remove Image") docker/ImageOperationsGroup -.-> docker/images("List Images") docker/SystemManagementGroup -.-> docker/login("Log into Docker Registry") docker/DockerfileGroup -.-> docker/build("Build Image from Dockerfile") subgraph Lab Skills docker/run -.-> lab-555045{{"Cómo usar el comando docker buildx build para construir y gestionar imágenes"}} docker/push -.-> lab-555045{{"Cómo usar el comando docker buildx build para construir y gestionar imágenes"}} docker/rmi -.-> lab-555045{{"Cómo usar el comando docker buildx build para construir y gestionar imágenes"}} docker/images -.-> lab-555045{{"Cómo usar el comando docker buildx build para construir y gestionar imágenes"}} docker/login -.-> lab-555045{{"Cómo usar el comando docker buildx build para construir y gestionar imágenes"}} docker/build -.-> lab-555045{{"Cómo usar el comando docker buildx build para construir y gestionar imágenes"}} end

Construir una imagen simple con configuraciones predeterminadas

En este paso, aprenderás cómo construir una imagen simple de Docker usando un Dockerfile. Un Dockerfile es un documento de texto que contiene todos los comandos que un usuario podría ejecutar en la línea de comandos para ensamblar una imagen. Docker puede construir imágenes automáticamente al leer las instrucciones de un Dockerfile.

Primero, navega al directorio ~/project, que es tu directorio de trabajo para este laboratorio.

cd ~/project

Ahora, creemos un Dockerfile simple. Este Dockerfile definirá una imagen basada en la imagen base ubuntu y simplemente imprimirá "¡Hola, Docker!" cuando se ejecute un contenedor a partir de esta imagen.

Usa el editor nano para crear un archivo llamado Dockerfile en el directorio ~/project.

nano Dockerfile

Agrega el siguiente contenido al Dockerfile:

FROM ubuntu:latest
CMD ["echo", "Hello, Docker!"]

Analicemos este Dockerfile simple:

  • FROM ubuntu:latest: Esta instrucción establece la imagen base para nuestra nueva imagen. Estamos usando la última versión de la imagen oficial de Ubuntu de Docker Hub.
  • CMD ["echo", "Hello, Docker!"]: Esta instrucción especifica el comando que se ejecutará cuando se inicie un contenedor a partir de esta imagen. En este caso, ejecutará el comando echo con el argumento "Hello, Docker!".

Guarda el archivo presionando Ctrl + S y sal del editor nano presionando Ctrl + X.

Ahora que tenemos nuestro Dockerfile, podemos construir la imagen usando el comando docker build. El . al final del comando le indica a Docker que busque el Dockerfile en el directorio actual (~/project). También etiquetaremos la imagen con un nombre, por ejemplo, my-hello-image.

docker build -t my-hello-image .

Verás una salida que indica que Docker está construyendo la imagen capa por capa. Primero descargará la imagen ubuntu:latest si no está presente en tu sistema, y luego ejecutará la instrucción CMD.

Una vez completada la construcción, puedes verificar que la imagen se creó correctamente listando las imágenes disponibles con el comando docker images.

docker images

Deberías ver my-hello-image en la lista.

Finalmente, ejecutemos un contenedor a partir de nuestra nueva imagen para ver la salida de la instrucción CMD.

docker run my-hello-image

Deberías ver la salida "Hello, Docker!" impresa en tu terminal. Esto confirma que nuestra imagen se construyó correctamente y que la instrucción CMD funciona como se esperaba.

Usar argumentos de construcción y etapas específicas

En este paso, aprenderás a utilizar argumentos de construcción (ARG) y etapas específicas (target stages) en tu Dockerfile para crear builds más flexibles y eficientes. Los argumentos de construcción te permiten pasar variables al proceso de build, mientras que las etapas específicas te permiten definir múltiples etapas de construcción en un solo Dockerfile y construir solo una etapa en particular.

Primero, asegúrate de estar en el directorio ~/project.

cd ~/project

Modifiquemos nuestro Dockerfile existente para incluir un argumento de construcción y un build multi-etapa simple. Definiremos un argumento para un mensaje de saludo y usaremos una segunda etapa para copiar un archivo desde la primera etapa.

Abre el Dockerfile usando nano:

nano Dockerfile

Reemplaza el contenido existente con lo siguiente:

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo $GREETING > /app/greeting.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]

Entendamos los cambios:

  • ARG GREETING="Hello from build argument!": Esta instrucción define un argumento de construcción llamado GREETING con un valor por defecto. Puedes sobrescribir este valor durante el proceso de build.
  • FROM ubuntu:latest as builder: Inicia la primera etapa de construcción y la nombra builder. Esto es útil para builds multi-etapa.
  • RUN echo $GREETING > /app/greeting.txt: En la etapa builder, este comando crea un archivo greeting.txt en el directorio /app y escribe el valor del argumento GREETING en él.
  • FROM ubuntu:latest: Inicia la segunda etapa de construcción. Por defecto, esta será la imagen final.
  • COPY --from=builder /app/greeting.txt /greeting.txt: Copia el archivo greeting.txt desde la etapa builder (específicamente desde /app/greeting.txt) al directorio raíz (/) de la etapa actual. Así se transfieren artefactos entre etapas en un build multi-etapa.
  • CMD ["cat", "/greeting.txt"]: Establece el comando que se ejecutará cuando se inicie un contenedor desde la imagen final, mostrando el contenido del archivo /greeting.txt.

Guarda el Dockerfile y sal de nano.

Ahora, construyamos la imagen usando el comando docker build. Usaremos el flag --build-arg para pasar un valor personalizado al argumento GREETING. También etiquetaremos esta imagen como my-arg-image.

docker build --build-arg GREETING="Greetings from the command line!" -t my-arg-image .

Observa la salida. Deberías ver que el proceso de construcción utiliza el argumento proporcionado.

Una vez completado el build, lista las imágenes para confirmar que my-arg-image está presente.

docker images

Ahora, ejecuta un contenedor desde my-arg-image para ver la salida.

docker run my-arg-image

Deberías ver "Greetings from the command line!" impreso, confirmando que se usó el argumento de construcción.

A continuación, construyamos solo la etapa builder. Esto es útil para crear imágenes intermedias que contengan artefactos de construcción sin la aplicación final. Usamos el flag --target para especificar el nombre de la etapa.

docker build --target builder -t my-builder-stage .

Lista las imágenes nuevamente. Ahora deberías ver my-builder-stage además de my-arg-image.

docker images

Ejecutar un contenedor desde my-builder-stage no producirá la misma salida que my-arg-image porque la instrucción CMD de la etapa final no está incluida en la etapa builder. Probemos ejecutarlo para ver qué ocurre (probablemente solo iniciará y terminará rápidamente ya que no hay un comando por defecto).

docker run my-builder-stage

Esto demuestra cómo las etapas específicas te permiten controlar qué parte de tu Dockerfile se construye en una imagen.

Gestionar la caché de construcción con --cache-from y --cache-to

En este paso, aprenderás a gestionar la caché de construcción de Docker usando los flags --cache-from y --cache-to. La caché de construcción puede acelerar significativamente builds posteriores al reutilizar capas de builds anteriores. --cache-from te permite especificar una imagen como fuente de caché, y --cache-to te permite exportar la caché de construcción a una ubicación específica (como un registro o directorio local).

Primero, asegúrate de estar en el directorio ~/project.

cd ~/project

Modifiquemos ligeramente nuestro Dockerfile para simular un cambio que normalmente invalidaría la caché. Añadiremos una instrucción RUN simple.

Abre el Dockerfile con nano:

nano Dockerfile

Añade la siguiente línea después de la instrucción ARG en la etapa builder:

RUN echo "Adding a new layer"

El Dockerfile actualizado debería verse así:

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding a new layer"
RUN echo $GREETING > /app/greeting.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]

Guarda el Dockerfile y sal de nano.

Ahora, construyamos la imagen nuevamente sin usar opciones específicas de caché. Docker usará automáticamente la caché local si está disponible.

docker build -t my-cached-image .

Verás que algunas capas se construyen desde cero porque añadimos una nueva instrucción, invalidando la caché para instrucciones posteriores.

Ahora, simulemos un escenario donde quieras usar una imagen previamente construida como fuente de caché, quizás desde un registro u otro build. Para demostración, usaremos my-cached-image como fuente de caché para un nuevo build.

Primero, hagamos otro pequeño cambio al Dockerfile para simular otra modificación.

Abre el Dockerfile:

nano Dockerfile

Cambia el mensaje en la instrucción RUN nueva:

RUN echo "Adding another new layer"

El Dockerfile actualizado debería verse así:

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
CMD ["cat", "/greeting.txt"]

Guarda y sal de nano.

Ahora, construye la imagen nuevamente, pero esta vez usa el flag --cache-from para especificar my-cached-image como fuente de caché.

docker build --cache-from my-cached-image -t my-cached-image-from .

Observarás que Docker intenta usar capas de my-cached-image. La capa correspondiente a la primera instrucción RUN probablemente se reconstruirá porque la instrucción cambió, pero capas posteriores podrían tomarse de la caché si coinciden.

El flag --cache-to se usa para exportar la caché de construcción. Esto es particularmente útil en pipelines CI/CD para compartir caché entre builds. Para usar --cache-to, normalmente necesitas un controlador de construcción que soporte exportación de caché, como el controlador docker-container.

Primero, instalemos Docker Compose, que se usa frecuentemente con buildx.

sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Ahora, creemos una instancia de constructor buildx.

docker buildx create --use

Ahora, construyamos la imagen y exportemos la caché a un directorio local. Usaremos el exportador de caché local.

docker buildx build --cache-to type=local,dest=./build-cache -t my-exported-cache-image . --load
  • docker buildx build: Usa la herramienta buildx para construir
  • --cache-to type=local,dest=./build-cache: Exporta la caché a un directorio local llamado build-cache
  • -t my-exported-cache-image: Etiqueta la imagen resultante
  • .: Especifica el contexto de construcción (directorio actual)
  • --load: Carga la imagen construida en la caché local de Docker

Verás una salida indicando la construcción y exportación de caché. Se creará un directorio build-cache en tu directorio ~/project.

Ahora, simulemos un entorno de construcción limpio e intentemos construir usando la caché exportada. Primero, eliminemos las imágenes construidas.

docker rmi my-cached-image my-cached-image-from my-exported-cache-image

Ahora, construye la imagen nuevamente, esta vez usando la caché exportada como fuente.

docker buildx build --cache-from type=local,src=./build-cache -t my-imported-cache-image . --load
  • --cache-from type=local,src=./build-cache: Importa la caché del directorio local build-cache

Observarás que Docker usa las capas de la caché exportada, acelerando significativamente el proceso de construcción comparado con construir desde cero.

Construir imágenes multiplataforma y subirlas a un registro

En este paso, aprenderás a construir imágenes Docker para múltiples arquitecturas (como linux/amd64 y linux/arm64) y subirlas a un registro de contenedores. Construir imágenes multiplataforma es esencial para asegurar que tus aplicaciones puedan ejecutarse en diferentes tipos de hardware. Usaremos Docker Buildx, que inicializaste en el paso anterior.

Primero, asegúrate de estar en el directorio ~/project.

cd ~/project

Usaremos nuestro Dockerfile existente para este paso. Construyamos una imagen para las plataformas linux/amd64 y linux/arm64. Etiquetaremos la imagen con un nombre y versión, por ejemplo, tu-usuario-dockerhub/my-multi-platform-image:latest. Reemplaza tu-usuario-dockerhub con tu nombre de usuario real en Docker Hub. Si no tienes una cuenta en Docker Hub, puedes crear una gratis.

Para construir para múltiples plataformas, usamos el flag --platform con docker buildx build. También necesitamos usar el flag --push para subir el manifiesto resultante y las imágenes a un registro.

docker buildx build --platform linux/amd64,linux/arm64 -t tu-usuario-dockerhub/my-multi-platform-image:latest . --push

Nota: Este comando requerirá que inicies sesión en Docker Hub si no lo has hecho. Puedes iniciar sesión usando el comando docker login en una sesión de terminal separada si es necesario.

docker login

Ingresa tu nombre de usuario y contraseña de Docker Hub cuando se solicite.

Después de iniciar sesión, ejecuta nuevamente el comando docker buildx build.

El proceso de construcción tomará más tiempo que un build de plataforma única, ya que construye la imagen para cada arquitectura especificada. Una vez completado, Buildx creará una lista de manifiestos que referencia las imágenes para cada plataforma y subirá todo a tu repositorio de Docker Hub especificado.

Puedes verificar que la imagen multiplataforma se subió visitando tu repositorio de Docker Hub en un navegador web. Deberías ver my-multi-platform-image con la etiqueta latest, y en la pestaña "Tags", verás que soporta múltiples arquitecturas.

Alternativamente, puedes usar el comando docker buildx imagetools inspect para inspeccionar la lista de manifiestos que acabas de subir. Reemplaza tu-usuario-dockerhub con tu nombre de usuario.

docker buildx imagetools inspect tu-usuario-dockerhub/my-multi-platform-image:latest

La salida mostrará la lista de manifiestos y las diferentes imágenes (con sus respectivas arquitecturas) a las que apunta.

Esto demuestra cómo construir y subir imágenes que pueden ejecutarse en diferentes arquitecturas de CPU, haciendo tus imágenes Docker más versátiles.

Exponer secretos y el agente SSH durante el build

En este paso, aprenderás cómo exponer de forma segura secretos y tu agente SSH al proceso de construcción de Docker usando Buildx. Esto es crucial para escenarios donde tu build necesita acceder a repositorios privados, instalar paquetes privados o interactuar con servicios externos que requieren autenticación, sin incrustar información sensible directamente en tu Dockerfile.

Primero, asegúrate de estar en el directorio ~/project.

cd ~/project

Modificaremos nuestro Dockerfile para demostrar cómo usar un secreto durante el build. Para este ejemplo, crearemos un archivo secreto ficticio y leeremos su contenido durante la construcción.

Crea un archivo llamado mysecret.txt en el directorio ~/project con contenido secreto.

echo "This is a secret message!" > ~/project/mysecret.txt

Ahora, abre el Dockerfile con nano:

nano Dockerfile

Añade una nueva instrucción RUN en la etapa builder que use el flag --mount=type=secret para acceder al secreto.

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
COPY --from=builder /app/secret_content.txt /secret_content.txt
CMD ["cat", "/greeting.txt", "/secret_content.txt"]

Entendamos la nueva instrucción:

  • RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt: Esta instrucción monta un secreto en el contenedor de build.
    • --mount=type=secret: Especifica que estamos montando un secreto.
    • id=mysecret: Es el ID del secreto que proporcionaremos durante el build.
    • cat /run/secrets/mysecret: Dentro del contenedor de build, el secreto está disponible en /run/secrets/mysecret. Usamos cat para leer su contenido.
    • > /app/secret_content.txt: Redirigimos el contenido secreto a un archivo llamado secret_content.txt en el directorio /app dentro de la etapa builder.

También añadimos una instrucción COPY en la etapa final para copiar el archivo secret_content.txt desde la etapa builder.

Guarda el Dockerfile y sal de nano.

Ahora, construye la imagen usando docker buildx build y proporciona el secreto usando el flag --secret.

docker buildx build --secret id=mysecret,src=~/project/mysecret.txt -t my-secret-image . --load
  • --secret id=mysecret,src=~/project/mysecret.txt: Este flag proporciona el secreto al build.
    • id=mysecret: Coincide con el ID especificado en el Dockerfile.
    • src=~/project/mysecret.txt: Especifica la ruta al archivo secreto en tu máquina local.

El proceso de build ahora tendrá acceso al contenido de mysecret.txt durante la ejecución de la instrucción RUN --mount=type=secret.... El contenido secreto no se almacena en las capas finales de la imagen.

Una vez completado el build, ejecuta un contenedor desde la imagen.

docker run my-secret-image

Deberías ver tanto el mensaje de saludo como el contenido de tu archivo secreto impresos en la consola.

Ahora, demostremos cómo exponer tu agente SSH al build. Esto es útil para clonar repositorios Git privados durante el proceso de construcción.

Primero, asegúrate de que tu agente SSH esté ejecutándose y tenga tu clave cargada. Puedes verificarlo con ssh-add -l. Si tu agente no está ejecutándose o tu clave no está añadida, necesitarás iniciar el agente y añadir tu clave.

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa ## Reemplaza con la ruta de tu clave SSH si es diferente

Ahora, modifica el Dockerfile para usar el agente SSH. Añadiremos una instrucción RUN que intente clonar un repositorio privado (inexistente) usando SSH.

Abre el Dockerfile:

nano Dockerfile

Añade una nueva instrucción RUN en la etapa builder que use el flag --mount=type=ssh.

## Stage 1: Builder stage
FROM ubuntu:latest as builder
ARG GREETING="Hello from build argument!"
RUN echo "Adding another new layer"
RUN echo $GREETING > /app/greeting.txt
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /app/secret_content.txt
RUN --mount=type=ssh git clone [email protected]:your-username/your-private-repo.git || echo "Skipping git clone as this is a demo"

## Stage 2: Final stage
FROM ubuntu:latest
COPY --from=builder /app/greeting.txt /greeting.txt
COPY --from=builder /app/secret_content.txt /secret_content.txt
CMD ["cat", "/greeting.txt", "/secret_content.txt"]

Nota: Reemplaza your-username/your-private-repo.git con un marcador de posición para una URL de repositorio privado. La parte || echo "Skipping git clone as this is a demo" se añade para que el build no falle si el repositorio no existe o no tienes acceso.

Guarda el Dockerfile y sal de nano.

Ahora, construye la imagen usando docker buildx build y proporciona acceso a tu agente SSH usando el flag --ssh.

docker buildx build --ssh default -t my-ssh-image . --load
  • --ssh default: Este flag expone tu agente SSH predeterminado al build.

Durante el build, la instrucción RUN --mount=type=ssh... podrá usar tu agente SSH para autenticarse con el servidor Git.

Una vez completado el build, puedes ejecutar la imagen, aunque la salida será la misma que antes ya que la operación de clonación Git probablemente se saltó.

docker run my-ssh-image

Esto demuestra cómo usar de forma segura secretos y tu agente SSH durante el proceso de construcción de Docker con Buildx, evitando que información sensible se incruste en tus imágenes.

Resumen

En este laboratorio, aprendiste los fundamentos para construir imágenes Docker usando un Dockerfile. Comenzaste creando un Dockerfile simple que define una imagen basada en Ubuntu y ejecuta un comando básico. Luego usaste el comando docker build para construir esta imagen, entendiendo cómo Docker procesa las instrucciones en el Dockerfile capa por capa y cómo etiquetar la imagen resultante.

Sobre estos fundamentos, exploraste características más avanzadas del comando docker buildx build. Esto incluyó utilizar argumentos de build (build arguments) para pasar valores dinámicos al proceso de construcción y aprovechar etapas objetivo (target stages) dentro de un build multi-etapa para optimizar el tamaño de la imagen y el tiempo de construcción. También aprendiste a gestionar efectivamente la caché de build usando --cache-from y --cache-to para acelerar builds posteriores.

Además, adquiriste experiencia construyendo imágenes multiplataforma, permitiendo que tus imágenes se ejecuten en diferentes arquitecturas, y subiendo estas imágenes multiplataforma a un registro de contenedores. Finalmente, descubriste cómo exponer secretos de forma segura y utilizar un agente SSH durante el proceso de build, mejorando la seguridad y flexibilidad de tus construcciones de imágenes.