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

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.