Skip to content
BotServBotServ
DockerImageOptimizaciónMulti-StageLayer

Optimizar Docker Images

Reduce el tamaño y velocidad de Docker Images. Optimización de capas, Multi-Stage, .dockerignore y mejores prácticas.

S

schutzgeist

4 min read
Optimizar Docker Images

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

BaseTamañoVentajaDesventaja
ubuntu~78 MBMuchas herramientasRelativamente grande
debian:slim~30 MBEquilibradaMenos herramientas
alpine~5 MBMuy pequeñaCompatibilidad con musl
distrolessmenos de 5 MBMínimaSin shell, debugging difícil

Recomendación

  • Para Python: python:3.11-slim.
  • Para Node.js: node:20-alpine o slim.
  • Para Go: golang:alpine o scratch para 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 clean y eliminar listas.
  • npm ci en lugar de npm 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

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

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.

Volver al blog
Share:

Entradas relacionadas