Optimización de Imágenes Docker: Guía Completa para Imágenes Más Pequeñas y Rápidas
Las imágenes Docker optimizadas resultan en despliegues más rápidos, costos de almacenamiento reducidos, superficies de ataque más pequeñas y mejor rendimiento. Esta guía completa cubre técnicas probadas para crear imágenes Docker mínimas y eficientes para entornos de producción.
Tabla de Contenidos
- Introducción
- Por Qué Importa el Tamaño de la Imagen
- Construcciones Multi-Etapa
- Selección de Imagen Base
- Optimización de Capas
- Gestión de Dependencias
- Archivo .dockerignore
- Seguridad y Minimalismo
- Optimizaciones Específicas por Lenguaje
- Herramientas de Análisis de Imágenes
- Mejores Prácticas de Producción
- Conclusión
Introducción
El tamaño de la imagen Docker afecta directamente los tiempos de construcción, velocidad de despliegue, ancho de banda de red, costos de almacenamiento y postura de seguridad. Una imagen optimizada puede ser 10-100x más pequeña que una no optimizada, mejorando significativamente todo tu flujo de trabajo de contenedores.
Beneficios de la Optimización
- Despliegues Más Rápidos: Las imágenes más pequeñas se despliegan más rápido
- Costos Reducidos: Menor uso de almacenamiento y ancho de banda
- Mejor Seguridad: Superficie de ataque más pequeña
- Rendimiento Mejorado: Menos para cargar y escanear
- Escalado Eficiente: Escalado horizontal más rápido
Por Qué Importa el Tamaño de la Imagen
Análisis de Impacto
# Compare image sizes
docker images
REPOSITORY TAG SIZE
app-bloated latest 1.2GB
app-optimized latest 45MB # 26x smaller!
Implicaciones del Mundo Real
- Docker Hub: Imagen de 1GB = 10 minutos de descarga en conexión lenta
- Kubernetes: Las imágenes más pequeñas escalan más rápido entre nodos
- CI/CD: Construcciones y despliegues más rápidos
- Seguridad: Menos paquetes = menos vulnerabilidades
- Costos: Cargos de almacenamiento y egreso en la nube
Construcciones Multi-Etapa
Las construcciones multi-etapa son la técnica de optimización más efectiva, separando entornos de construcción y tiempo de ejecución.
Construcción Multi-Etapa Básica
# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
# Runtime stage - much smaller
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]
Resultado: ~800MB → ~15MB
Ejemplo Multi-Etapa Avanzado
# Stage 1: Dependencies
FROM node:18-alpine AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Stage 2: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 3: Production
FROM node:18-alpine
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
Construcción Multi-Etapa Python
# Build stage
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
# Runtime stage
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*
COPY . .
CMD ["python", "app.py"]
Selección de Imagen Base
Elegir la imagen base correcta afecta drásticamente el tamaño final.
Comparación de Tamaños
# Ubuntu-based: ~200MB
FROM ubuntu:22.04
# Debian slim: ~80MB
FROM python:3.11-slim
# Alpine: ~5MB
FROM python:3.11-alpine
# Distroless: ~2MB (language runtime only)
FROM gcr.io/distroless/python3
# Scratch: 0MB (static binaries only)
FROM scratch
Imágenes Basadas en Alpine
# Alpine is minimal but may have compatibility issues
FROM node:18-alpine
# Install build dependencies if needed
RUN apk add --no-cache python3 make g++
# Clean up after installation
RUN apk del python3 make g++
Variantes Slim
# Debian slim - good balance
FROM python:3.11-slim
# Install only what you need
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
Imágenes Distroless
# Build stage
FROM golang:1.21 AS build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o app
# Distroless runtime
FROM gcr.io/distroless/static-debian11
COPY --from=build /app/app /app
ENTRYPOINT ["/app"]
Imágenes Scratch (Binarios Estáticos)
# Build static binary
FROM golang:1.21 AS build
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o app
# Minimal runtime
FROM scratch
COPY --from=build /app/app /app
ENTRYPOINT ["/app"]
Optimización de Capas
Combinar Comandos RUN
# Bad - 3 layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# Good - 1 layer
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
Ordenar Capas por Frecuencia de Cambio
# Bad - invalidates cache on code change
FROM node:18-alpine
COPY . .
RUN npm install
# Good - dependencies cached separately
FROM node:18-alpine
COPY package*.json ./
RUN npm ci
COPY . .
Eliminar Archivos Innecesarios en la Misma Capa
# Bad - files remain in layer
RUN wget https://example.com/big-file.tar.gz
RUN tar xzf big-file.tar.gz
RUN rm big-file.tar.gz
# Good - removed in same layer
RUN wget https://example.com/big-file.tar.gz && \
tar xzf big-file.tar.gz && \
rm big-file.tar.gz
Limpiar Cachés de Gestores de Paquetes
# Alpine
RUN apk add --no-cache package-name
# Debian/Ubuntu
RUN apt-get update && \
apt-get install -y --no-install-recommends package-name && \
rm -rf /var/lib/apt/lists/*
# CentOS/Rocky
RUN dnf install -y package-name && \
dnf clean all
Gestión de Dependencias
Optimización de Python
# Bad - installs everything
FROM python:3.11
COPY requirements.txt .
RUN pip install -r requirements.txt
# Good - optimized
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
find /usr/local -type d -name '__pycache__' -exec rm -rf {} + && \
find /usr/local -type f -name '*.pyc' -delete
# Better - multi-stage
FROM python:3.11 AS builder
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.11-slim
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*
Optimización de Node.js
# Bad - includes dev dependencies
FROM node:18
COPY package*.json ./
RUN npm install
COPY . .
# Good - production only
FROM node:18-alpine
COPY package*.json ./
RUN npm ci --only=production && \
npm cache clean --force
COPY . .
# Better - multi-stage
FROM node:18-alpine AS build
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=build /app/dist ./dist
Optimización de Go
# Good - static binary
FROM golang:1.21-alpine AS build
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o app
FROM scratch
COPY --from=build /app/app /
ENTRYPOINT ["/app"]
Optimización de Java
# Multi-stage Maven build
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:17-jre-alpine
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
Archivo .dockerignore
Excluye archivos innecesarios del contexto de construcción.
.dockerignore Completo
# Version control
.git
.gitignore
.gitattributes
# CI/CD
.github
.gitlab-ci.yml
.travis.yml
Jenkinsfile
# IDEs
.vscode
.idea
*.swp
*.swo
*~
# Documentation
*.md
README*
LICENSE
docs/
# Build artifacts
node_modules
dist
build
target
*.log
# Testing
test
tests
**/*_test.go
*.test
coverage
# OS files
.DS_Store
Thumbs.db
# Environment
.env
.env.*
!.env.example
# Docker
Dockerfile*
docker-compose*
.dockerignore
Ejemplos Específicos
# Node.js
node_modules
npm-debug.log
.npm
# Python
__pycache__
*.py[cod]
.pytest_cache
.venv
# Go
vendor
*.exe
*.test
# Ruby
.bundle
vendor/bundle
Seguridad y Minimalismo
Eliminar Shells y Utilidades
# Distroless - no shell
FROM gcr.io/distroless/nodejs:18
# Or remove from Alpine
FROM alpine:latest
RUN rm -rf /bin/sh /bin/ash
Ejecutar como No Root
FROM node:18-alpine
# Create user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Use user
USER nodejs
Eliminar Binarios Setuid/Setgid
RUN find / -perm +6000 -type f -exec chmod a-s {} \; || true
Optimizaciones Específicas por Lenguaje
Imagen de Producción Node.js
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
FROM node:18-alpine
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
COPY --from=build --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=nodejs:nodejs /app/dist ./dist
USER nodejs
CMD ["node", "dist/index.js"]
Imagen de Producción Python
FROM python:3.11-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=build /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
Imagen de Producción Go
FROM golang:1.21-alpine AS build
WORKDIR /src
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o /app
FROM gcr.io/distroless/static-debian11
COPY --from=build /app /app
ENTRYPOINT ["/app"]
Herramientas de Análisis de Imágenes
Docker History
# View image layers
docker history my-image
# Show sizes
docker history --no-trunc my-image
# Human-readable
docker history --human my-image
Herramienta Dive
# Install dive
wget https://github.com/wagoodman/dive/releases/download/v0.11.0/dive_0.11.0_linux_amd64.deb
sudo dpkg -i dive_0.11.0_linux_amd64.deb
# Analyze image
dive my-image
# CI mode
dive my-image --ci
Docker Slim
# Install docker-slim
curl -L -o ds.tar.gz https://github.com/slimtoolkit/slim/releases/download/1.40.0/dist_linux.tar.gz
tar -xvf ds.tar.gz
# Slim image
./dist_linux/docker-slim build my-image
Escáner de Vulnerabilidades Trivy
# Install trivy
sudo apt-get install trivy
# Scan image
trivy image my-image
# Generate report
trivy image --format json -o report.json my-image
Mejores Prácticas de Producción
Fijación de Versiones
# Bad - unpredictable
FROM node:latest
# Good - specific version
FROM node:18.17.0-alpine3.18
Construcciones Multi-Arquitectura
# Build for multiple platforms
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t my-image:latest .
Minimizar Imagen Final
FROM scratch
COPY --from=builder /app /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/app"]
Argumentos de Construcción para Flexibilidad
ARG NODE_ENV=production
ARG BUILD_VERSION=latest
FROM node:18-alpine
ENV NODE_ENV=${NODE_ENV}
LABEL version=${BUILD_VERSION}
Verificaciones de Salud
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
Conclusión
La optimización de imágenes Docker es crucial para despliegues de producción. Implementar estas técnicas resulta en aplicaciones contenedorizadas más rápidas, seguras y rentables.
Conclusiones Clave
- Construcciones Multi-Etapa: Separa entornos de construcción y tiempo de ejecución
- Imágenes Base: Elige variantes Alpine, slim o distroless
- Optimización de Capas: Combina comandos, ordena por frecuencia de cambio
- Limpieza: Elimina cachés y archivos innecesarios en la misma capa
- .dockerignore: Excluye archivos innecesarios del contexto de construcción
- Seguridad: Las imágenes mínimas tienen superficies de ataque más pequeñas
Lista de Verificación de Optimización
- Usar construcciones multi-etapa
- Elegir imagen base mínima (Alpine/slim/distroless)
- Ordenar instrucciones de Dockerfile por frecuencia de cambio
- Combinar comandos RUN para reducir capas
- Limpiar cachés de gestores de paquetes
- Usar archivo .dockerignore
- Instalar solo dependencias de producción
- Eliminar dependencias de construcción después del uso
- Fijar versiones exactas
- Agregar verificaciones de salud
- Ejecutar como usuario no root
- Escanear en busca de vulnerabilidades
Referencia Rápida
# Optimized Multi-Stage Template
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
FROM node:18-alpine
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
COPY --from=build --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=build --chown=nodejs:nodejs /app/dist ./dist
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
Resultados
Resultados típicos de optimización:
- Antes: 1.2 GB
- Después: 45 MB
- Mejora: 96% de reducción
Domina estas técnicas de optimización para construir imágenes Docker listas para producción.


