Docker para Desarrolladores: De Cero a Experto

Desde 2013, Docker redefinió el ciclo de vida del software al empaquetar aplicaciones con sus dependencias en contenedores, acelerando la entrega y reduciendo la fricción entre entornos. En esta guía ampliada pasamos de lo esencial a prácticas de nivel experto que verás en equipos de plataforma y DevOps maduros.

1) Fundamentos de Contenedores: qué son (realmente)

Un contenedor no es una VM. Comparte kernel con el host y aísla procesos mediante namespaces (pid, mnt, net, ipc, uts, user) y controla recursos con cgroups. El sistema de ficheros se apoya en OverlayFS (capas de solo lectura + capa superior de escritura por contenedor).

  • Arranque: milisegundos/segundos al no bootear un SO completo.
  • Portabilidad: la misma imagen corre en local, CI y prod.
  • Inmutabilidad: capas reconstruibles, rollbacks predecibles.

2) Imágenes y BuildKit: velocidad, caché y trazabilidad

Activa BuildKit para builds paralelos, mejores diagnósticos, secretos en tiempo de build y cache mounts.

export DOCKER_BUILDKIT=1
docker build -t miapp:dev .

2.1 Multi-stage builds (patrones)

# Node.js (producción mínima + cache de dependencias)
# Etapa de dependencias
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci

# Etapa de build
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Etapa final (runtime mínimo, sin toolchain)
FROM node:20-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
USER node
CMD ["node","dist/main.js"]
# Python (slim + caches + usuario no root)
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*
COPY pyproject.toml poetry.lock* ./
RUN --mount=type=cache,target=/root/.cache/pip pip install --upgrade pip && pip install poetry && poetry config virtualenvs.create false && poetry install --no-root --no-dev
COPY . .
RUN useradd -m app && chown -R app:app /app
USER app
CMD ["python","-m","app.main"]
# Go (binario estático + distroless)
FROM golang:1.22 as build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /out/app ./cmd/app

FROM gcr.io/distroless/static-debian12
USER 65532:65532
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]

2.2 Caché avanzada y reproducibilidad

  • Ordena COPY: copia primero manifiestos (package.json/go.mod) para maximizar caché.
  • --mount=type=cache: acelera npm ci, pip, go build.
  • Etiquetas OCI: añade metadatos útiles (VCS, commit, autor, licencias).
LABEL org.opencontainers.image.source="https://github.com/empresa/miapp" 
      org.opencontainers.image.revision="$COMMIT_SHA" 
      org.opencontainers.image.licenses="MIT"

3) Seguridad de imágenes y runtime

  • Usuario no root: define USER y permisos mínimos.
  • Capacidades: elimina privilegios innecesarios.
  • Seccomp/AppArmor: perfiles restrictivos por defecto.
  • Secreto en build: nunca ARG con credenciales; usa --secret.
# Ejemplo: drop de capabilities en Compose (runtime)
# docker-compose.yml
services:
  api:
    image: empresa/api:prod
    cap_drop: ["ALL"]
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp:size=64m

4) Docker Compose v2: orquestación local y de equipo

Compose describe entornos multi-servicio (app + DB + cache) con perfiles, healthchecks y dependencias robustas.

version: "3.9"
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    env_file: .env
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    healthcheck:
      test: ["CMD","curl","-f","http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 5
    deploy:
      resources:
        limits:
          cpus: "1.00"
          memory: 512M
        reservations:
          memory: 256M
    volumes:
      - type: bind
        source: ./src
        target: /app/src
        consistency: delegated
    profiles: ["dev"]

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL","pg_isready -U postgres"]
      interval: 10s
      retries: 10

  redis:
    image: redis:7-alpine

volumes:
  pgdata: {}

4.1 Perfiles y overrides

Activa servicios opcionales con --profile y emplea docker-compose.override.yml para dev.

5) Redes, DNS y rendimiento

  • Red bridge por defecto: cada proyecto Compose crea una red aislada con DNS interno.
  • Exposición mínima: usa ports solo cuando sea necesario; para comunicación interna, usa el nombre del servicio.
  • macOS/Windows: usa volúmenes o soluciones como mutagen para mejorar IO frente a bind mounts.

6) Patrones de Dockerfile por ecosistema

6.1 PHP-FPM + Nginx (Laravel, Symfony)

# PHP-FPM
FROM php:8.3-fpm-alpine AS php
RUN docker-php-ext-install pdo_mysql bcmath opcache
WORKDIR /var/www/html
COPY . .
RUN adduser -D app && chown -R app:app /var/www/html
USER app

# Nginx
FROM nginx:alpine
COPY ./infra/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=php /var/www/html /var/www/html
# infra/nginx.conf (fragmento)
server {
  listen 80;
  root /var/www/html/public;
  location / {
    try_files $uri /index.php$is_args$args;
  }
  location ~ \.php$ {
    fastcgi_pass php:9000;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}

6.2 Node (SSR/Next/Nest) con imágenes mínimas

  • Evita npm install en producción; usa npm ci --omit=dev.
  • Congela versiones (lockfile), evita latest.
  • Usa USER node y read_only en runtime.

7) Supply Chain: SBOM, firmas y políticas

Genera SBOM (inventario de dependencias), firma imágenes y aplica políticas de admisión:

  • SBOM: genera y archiva junto al artefacto.
  • Firmas/Attestations: registra quién construyó qué y cómo.
  • Escaneo: integra análisis de vulnerabilidades en CI.

8) CI/CD: buildx, multi-arch y caché remota

# Ejemplo GitHub Actions (fragmento)
- name: Set up Buildx
  uses: docker/setup-buildx-action@v3

- name: Login registry
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GH_TOKEN }}

- name: Build & Push multi-arch
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: ghcr.io/empresa/miapp:{{github.sha}},ghcr.io/empresa/miapp:latest
    platforms: linux/amd64,linux/arm64
    cache-from: type=registry,ref=ghcr.io/empresa/miapp:buildcache
    cache-to: type=registry,ref=ghcr.io/empresa/miapp:buildcache,mode=max

9) Observabilidad y operación

  • Logs: rota json-file o usa drivers centralizados (ELK/Datadog/Fluentd).
  • Healthchecks: esenciales para restart policies y dependencias.
  • Métricas y trazas: expón prometheus endpoints o integra OpenTelemetry.

10) Seguridad en runtime

  • cap_drop: ["ALL"] y añade sólo lo que requiera tu app (p. ej., NET_BIND_SERVICE).
  • read_only: true + tmpfs para directorios temporales.
  • no-new-privileges, userns-remap y seccomp por defecto.
  • Separa redes por dominio (frontend/backoffice/infra) y restringe tráfico este-oeste.

11) Datos y volúmenes

  • Bind vs named: en desarrollo usa bind para iteración; en prod, volúmenes nombrados.
  • Backups: estrategias simples con contenedores efímeros.
# Backup de volumen (ejemplo Postgres)
docker run --rm --volumes-from contenedor_db -v $(pwd):/backup alpine 
  sh -c "tar czf /backup/pgdata-$(date +%F).tar.gz /var/lib/postgresql/data"

12) Rendimiento: tips prácticos

  • Ordena pasos del Dockerfile para maximizar caché.
  • Minimiza capas combinando RUN donde tenga sentido.
  • Alpine vs slim: alpine es ligera pero puede penalizar builds nativos; evalúa slim.
  • macOS: prefiere volúmenes a bind para IO intenso.

13) Troubleshooting avanzado

  • DNS/resolución: usa el nombre del servicio Compose; evita localhost entre contenedores.
  • Permisos: al usar USER, ajusta propiedad de ficheros (chown) y puertos <1024 requieren NET_BIND_SERVICE.
  • “Text file busy”: reconstruye capas que mantienen binarios abiertos; evita editar dentro del contenedor en caliente.
  • Capas obsoletas: limpia cachés si cambiaste FROM/ARG clave (docker builder prune con cuidado).
  • Inodos OverlayFS: vigila muchas capas/archivos pequeños; simplifica la imagen o usa stages.

14) Checklist de producción (resumen)

  1. Imagen mínima (slim/distroless), usuario no root, cap_drop, seccomp, read_only.
  2. Healthcheck, restart policy, límites de CPU/Mem y ulimits.
  3. SBOM + firma + escaneo en CI; políticas de admisión en el registry.
  4. Logs con rotación y métricas/trazas expuestas.
  5. Backups de volúmenes y procedimiento de restore probado.
  6. Buildx multi-arch con caché remota; etiquetas de versión inmutables.

Conclusión

El salto de “funciona en mi máquina” a “opera confiable en producción” exige disciplina: imágenes optimizadas y seguras, builds reproducibles, Compose bien diseñado, observabilidad y políticas de supply chain. Dominar estos pilares convierte Docker en un acelerador estratégico, no solo en una herramienta de empaquetado.