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.

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.