Optimizar imágenes Docker
Qué abarca este artículo sobre optimización de imágenes Docker
- Por qué las imágenes pequeñas son importantes.
- Cómo funcionan las capas y el almacenamiento en caché.
- Compilaciones multietapa y .dockerignore.
- Selección de imagen base.
- Mejores prácticas de seguridad y rendimiento.
Introducción: Optimizar imágenes Docker
Las imágenes Docker grandes ralentizan compilaciones, despliegues y descargas. Aumentan la superficie de ataque porque hay más software instalado y ocupan memoria innecesariamente. Con optimización dirigida, es posible reducir imágenes de varios gigabytes a cientos de megabytes, sin perder funcionalidad.
Este artículo muestra cómo hacer que tus imágenes Docker sean pequeñas, rápidas y seguras.
Términos clave
- Layer: Capa en una imagen Docker.
- Caching: Reutilización de capas sin cambios.
- Multi-Stage Build: Múltiples etapas de compilación en un Dockerfile.
- .dockerignore: Archivo que excluye archivos innecesarios de la compilación.
- Base Image: Imagen base sobre la que se construye.
- Distroless: Imagen sin shell ni herramientas innecesarias.
- Squashing: Fusión de múltiples capas en una sola.
¿Por qué optimizar imágenes?
- Compilaciones y despliegues más rápidos.
- Menor uso de almacenamiento.
- Superficie de ataque reducida.
- Menos ancho de banda al distribuir.
- Mantenimiento e inspección más sencillos.
Seleccionar la imagen base
Grande vs. pequeña
| Base | Tamaño | Ventaja | Desventaja |
|---|---|---|---|
| ubuntu | ~78 MB | Muchas herramientas | Relativamente grande |
| debian:slim | ~30 MB | Equilibrada | Menos herramientas |
| alpine | ~5 MB | Muy pequeña | Compatibilidad con musl |
| distroless | menos de 5 MB | Mínima | Sin shell, debugging difícil |
Recomendación
- Para Python:
python:3.11-slim. - Para Node.js:
node:20-alpineoslim. - Para Go:
golang:alpineoscratchpara binarios compilados. - Para entornos críticos:
distroless.
Compilaciones multietapa
# Builder
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
Solo los archivos necesarios en tiempo de ejecución van en la imagen final.
Optimizar capas y almacenamiento en caché
Cada instrucción en el Dockerfile crea una capa. Los comandos que cambian frecuentemente deben ir al final:
# Dependencias primero, rara vez cambian
COPY package*.json ./
RUN npm ci
# Código fuente al final, cambia con frecuencia
COPY . .
De esta forma se aprovecha el caché de capas de manera efectiva.
.dockerignore
Evita que archivos innecesarios entren en el contexto de compilación:
node_modules
.git
*.log
.env
Dockerfile
.dockerignore
README.md
tests/
Agrupar comandos
En lugar de:
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
mejor así:
RUN apt-get update && apt-get install -y curl && apt-get clean && rm -rf /var/lib/apt/lists/*
Esto genera una sola capa y elimina cachés.
Eliminar herramientas innecesarias
- Herramientas de compilación como compiladores solo en etapas builder.
apt-get cleany eliminar listas.npm cien lugar denpm install.- Sin documentación ni ejemplos en la imagen.
- Sin directorios de pruebas en la imagen de producción.
Usuario no root
FROM node:20-slim
RUN useradd -m appuser
USER appuser
Esto reduce el riesgo en caso de escapes de contenedor.
Etiquetas y metadatos
LABEL maintainer="team@example.com"
LABEL org.opencontainers.image.source="https://github.com/user/repo"
LABEL org.opencontainers.image.version="1.0.0"
Agregar verificaciones de salud
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:3000/health || exit 1
Analizar la imagen
Herramientas para análisis:
docker history mein-image:latest
dive mein-image:latest
dive muestra qué capas ocupan más espacio.
Ejemplo: Dockerfile optimizado
# Builder
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# Runtime
FROM python:3.11-slim
WORKDIR /app
RUN useradd -m appuser
COPY --from=builder /root/.local /home/appuser/.local
COPY --chown=appuser:appuser . .
USER appuser
ENV PATH=/home/appuser/.local/bin:$PATH
HEALTHCHECK CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "main.py"]
Seguridad
- Usar regularmente imágenes base actualizadas.
- Escanear imágenes en busca de vulnerabilidades.
- No incluir secretos en la imagen.
- Instalar solo paquetes necesarios.
- Considerar un sistema de archivos de solo lectura.
- Evitar
latest.
Errores comunes
- COPY . . muy temprano: Invalida el caché con cada cambio de código.
- Sin .dockerignore: El contexto de compilación se vuelve muy grande.
- Herramientas de compilación en la imagen final: Más grande e insegura.
- Ejecutar como root: Mayor riesgo.
- Demasiadas capas: Cada RUN crea una.
- Imágenes base muy grandes: Innecesarias si slim es suficiente.
- Sin apt clean: La capa se vuelve innecesariamente grande.
Enlaces y recursos adicionales
- BotServ.de Comandos Docker
- BotServ.de Docker CI/CD
- BotServ.de Seguridad Docker
- BotServ.de Docker Volumes
Preguntas frecuentes: Optimizar imágenes Docker
¿Cuán pequeña debe ser una imagen? Tan pequeña como sea posible sin perder funcionalidad. A menudo menos de 100 MB para aplicaciones simples.
¿Alpine siempre es mejor? No siempre. musl puede causar problemas con algunas librerías.
¿Necesito compilaciones multietapa? En lenguajes compilados y Node.js, generalmente sí.
¿Qué es distroless? Una imagen extremadamente mínima sin shell, solo con archivos de tiempo de ejecución.
¿Cómo verifico el tamaño de la imagen?
Con docker images, docker history o dive.
Fuentes y lecturas adicionales
- Docker Best Practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
- Distroless Images: https://github.com/GoogleContainerTools/distroless
- dive: https://github.com/wagoodman/dive
- Docker BuildKit: https://docs.docker.com/build/buildkit/
Resumen: Optimizar imágenes Docker
Las imágenes Docker optimizadas son más pequeñas, rápidas y seguras. Las medidas importantes incluyen elegir una imagen base ligera, compilaciones multietapa, almacenamiento en caché de capas, un buen .dockerignore y eliminar herramientas innecesarias. Un usuario no root, verificaciones de salud y etiquetas completan la imagen. Quien escanea regularmente imágenes y las analiza con dive mantiene el control sobre el tamaño y la seguridad.


