Construcción Multi-Etapa (Multi-Stage Builds) y BuildKit
Al desarrollar aplicaciones en lenguajes como Go, Rust, Java o frameworks frontend (React, Angular, Vue), nos encontramos con un dilema clásico:
- Para compilar el código necesitamos herramientas pesadas: SDKs, compiladores, paquetes de desarrollo y dependencias de testeo (ocupando entre y ).
- Para ejecutar la aplicación en producción solo necesitamos el binario compilado o los ficheros estáticos HTML/JS.
Las Construcciones Multi-Etapa (Multi-Stage Builds) resuelven este problema permitiendo declarar múltiples directivas FROM en un mismo Dockerfile y copiar exclusivamente los artefactos finales compilados a una imagen limpia y ultra-ligera.
1. Anatomía de una Multi-Stage Build #
1┌──────────────────────────────────────────────┐ 2│ ETAPA 1: BUILDER (Imagen Pesada ~1.2 GB) │ 3│ FROM node:20 AS builder │ 4│ - Instala dependencias completas de dev │ 5│ - Compila con Vite / TypeScript │ 6│ - Genera carpeta /app/dist │ 7└──────────────────────┬───────────────────────┘ 8 │ COPY --from=builder /app/dist /usr/share/nginx/html 9 ▼ 10┌──────────────────────────────────────────────┐ 11│ ETAPA 2: PRODUCCIÓN (Imagen Mínima ~25 MB) │ 12│ FROM nginx:alpine │ 13│ - Cero herramientas de compilación │ 14│ - Cero código fuente ni node_modules │ 15│ - Máxima seguridad y arranque instantáneo │ 16└──────────────────────────────────────────────┘
Ejemplo Práctico: Aplicación Frontend (React/Vue/Next estático) #
1# ─── Etapa 1: Compilación ─── 2FROM node:20-alpine AS builder 3WORKDIR /app 4 5COPY package*.json ./ 6RUN npm ci 7 8COPY . . 9RUN npm run build 10 11# ─── Etapa 2: Servidor de Producción ─── 12FROM nginx:alpine-slim 13WORKDIR /usr/share/nginx/html 14 15# Limpiar archivos por defecto de Nginx 16RUN rm -rf ./* 17 18# Copiar ÚNICAMENTE el resultado compilado desde el builder 19COPY /app/dist . 20 21# Configuración personalizada de Nginx 22COPY nginx.conf /etc/nginx/conf.d/default.conf 23 24EXPOSE 80 25CMD ["nginx", "-g", "daemon off;"]
2. Imágenes Base Ultraligeras: Alpine, Distroless y Scratch #
| Tipo de Imagen Base | Tamaño Típico | Shell y Herramientas | Caso de Uso Ideal |
|---|---|---|---|
| Alpine Linux | Sí (/bin/sh, gestor apk) | Entornos Node, Python o Ruby que requieren shell para scripting ligero. | |
| Google Distroless | No (sin shell, sin apt) | Runtimes de Java, Python o Node en entornos corporativos de alta seguridad. | |
| Scratch | No (completamente vacía) | Binarios estáticos compilados en Go o Rust (ej. CGO_ENABLED=0 go build). |
3. BuildKit y docker buildx #
BuildKit es el motor de compilación de última generación integrado en Docker que optimiza drásticamente las builds:
- Paralelización de Etapas: Si dos etapas no dependen entre sí, BuildKit las compila de forma concurrente.
- Caché de Paquetes (
--mount=type=cache): Conserva la caché denpm,pipomavenentre builds sin inflar la imagen final. - Compilación Multi-Arquitectura:
1# Habilitar BuildKit 2export DOCKER_BUILDKIT=1 3 4# Compilar para arquitecturas x86_64 y ARM64 (Apple Silicon / Raspberry Pi / Graviton) 5docker buildx build --platform linux/amd64,linux/arm64 -t miusuario/mi-app:v1.0 --push .
Resumen del tema
Conceptos clave #
- Multi-Stage Builds: técnica con múltiples
FROMque aísla las herramientas pesadas de compilación de la imagen final de producción. COPY --from=builder: transfiere únicamente binarios o carpetas compiladas entre etapas.- Distroless y Scratch: eliminan shells y utilidades del sistema, impidiendo que atacantes ejecuten comandos en caso de intrusión.
- BuildKit: motor moderno que acelera compilaciones con ejecución paralela y caché inteligente.
Qué debes recordar #
Utiliza siempre Multi-Stage Builds para mantener tus imágenes de producción por debajo de 50 MB, reduciendo los tiempos de despliegue y eliminando vulnerabilidades.