Contenedores y Docker para desarrollo local

Entornos idénticos · menos sorpresas · despliegues más predecibles

1 / 1
H
Tema

Docker y contenedores para desarrollo local

La idea: que todo el equipo trabaje con el mismo entorno (SO, dependencias, servicios, configuración) para evitar el clásico “en mi máquina sí funciona” y hacer los despliegues más predecibles.
Reproducible
Rápido de onboardear
Aísla dependencias
Requiere disciplina
Curva inicial
Agenda

Qué veremos

Ruta corta para entender y aplicar Docker en dev local.

Contenido

  • Qué es un contenedor (vs VM)
  • Problema: “en mi máquina sí funciona”
  • Docker en dev: Dockerfile + docker compose
  • Ejemplo práctico (app + base de datos)
  • Buenas prácticas y anti-patrones
  • Checklist para equipos
Objetivo
  • Que cualquiera del equipo pueda ejecutar el proyecto con 1 comando.
  • Que el entorno de dev se parezca al de QA/Prod.
  • Reducir “works on my machine” a casi cero.
Conceptos

Contenedor ≠ máquina virtual

Un contenedor es un proceso aislado que comparte el kernel del host, con su propio filesystem, red y variables. Una VM emula hardware y corre un SO completo.

Contenedor

  • Arranque rápido (segundos)
  • Menos consumo (no hay SO completo)
  • Empaqueta dependencias y runtime
  • Ideal para servicios y entornos reproducibles

VM

  • Más pesado (SO por VM)
  • Arranque más lento
  • Aislamiento más “fuerte” a nivel SO
  • Útil cuando necesitas un SO distinto o drivers específicos
Idea clave
En dev, Docker te permite normalizar versiones de runtime (Node/Java/Python), librerías del sistema (OpenSSL, libc, etc.) y servicios (Postgres, Redis).
Dolor real

“En mi máquina sí funciona”

La mayoría de fallos “fantasma” vienen de diferencias invisibles entre equipos.

Causas típicas

  • Versiones distintas de runtime: Node 18 vs 20, Java 17 vs 21
  • Dependencias del SO: libpq, imagemagick, locales, tzdata
  • Servicios locales: DB con datos distintos, extensiones faltantes
  • Variables de entorno y secretos en archivos personales
  • Permisos/paths: Windows vs Linux vs macOS
Efecto en el equipo
  • Onboarding lento (“instala X, ahora Y, ahora arregla Z…”)
  • Debugging de infraestructura en vez de producto
  • Más fricción en QA/CI/CD
  • Hotfixes y despliegues inciertos
Tiempo
Riesgo
Estrés
Solución

Entorno idéntico para todo el equipo

Definimos el entorno en código: Dockerfile para la app y docker compose para orquestar servicios (DB, cache, colas, etc.).

Qué se “versiona”

  • Runtime y dependencias del sistema
  • Comandos de build y ejecución
  • Servicios (Postgres/Redis/etc.) con volúmenes y puertos
  • Variables base (no secretos) y archivos ejemplo
Resultado
  • Onboarding: git clone + docker compose up
  • Menos “snowflakes”: el entorno deja de ser artesanal
  • CI y despliegue se parecen más a dev
Flujo

Cómo se organiza un proyecto en dev

Separar responsabilidades: imagen de app, servicios y datos.

Artefactos

  • Dockerfile: cómo construir la imagen de tu app
  • .dockerignore: qué NO copiar al build
  • compose.yaml: servicios, redes, volúmenes
  • .env.example: variables de entorno de referencia

Reglas de oro

  • La app se reconstruye; los datos se persisten con volúmenes
  • Los secretos NO van al repositorio
  • Logs al stdout/stderr (no archivos locales)
  • Paridad de versiones (dev ≈ staging ≈ prod)
Mantra
“Si no está declarado en Dockerfile/compose, no existe.”
Ejemplo

Dockerfile para una app (Node como referencia)

Ejemplo genérico (ajústalo a tu stack). La meta: builds reproducibles y rápidos.

Dockerfile (multi-stage)

# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20-alpine AS dev
WORKDIR /app
ENV NODE_ENV=development
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
Nota: en algunos stacks conviene separar build prod vs dev (hot reload).
Qué resuelve
  • Versión de Node fija (no depende del host)
  • Dependencias instaladas de forma consistente
  • Comando estándar para arrancar el servicio
  • Se puede usar igual en CI para pruebas
Ejemplo

docker compose: app + Postgres

Orquestación local: una red, un volumen de datos, y servicios con healthchecks.

compose.yaml

services:
  app:
    build:
      context: .
      target: dev
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://postgres:postgres@db:5432/app
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - .:/app
      - /app/node_modules

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: app
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d app"]
      interval: 5s
      timeout: 3s
      retries: 20

volumes:
  pgdata:
Detalles importantes
  • depends_on + healthcheck: evita carreras al iniciar
  • volumen pgdata: persiste datos entre reinicios
  • volúmenes para código: permite hot reload
  • URL usa hostname db (red interna)
Día a día

Comandos típicos en el equipo

La idea es estandarizar: todos usan lo mismo.

Cheatsheet

# levantar todo
docker compose up -d --build

# ver logs
docker compose logs -f --tail=200 app

# entrar al contenedor (debug)
docker compose exec app sh

# correr tests
docker compose exec app npm test

# parar y limpiar (sin borrar datos)
docker compose down

# borrar datos (cuidado)
docker compose down -v
Tema: velocidad en dev con Docker
  • Cache de dependencias: copia primero archivos de lock (ej. package-lock.json) para que el build no reinstale todo en cada cambio de código.
  • Volúmenes bien puestos: monta el código para hot reload, pero evita pisar dependencias del contenedor (ej. /app/node_modules).
  • Rebuilds controlados: reconstruye solo cuando cambie el Dockerfile/deps; en cambios normales usa docker compose up + watcher.
Buenas prácticas

Lo que suele marcar la diferencia

Docker en dev funciona excelente si cuidamos consistencia, performance y seguridad.

Recomendado

  • Versionar imágenes base (ej. Postgres 16)
  • Incluir healthchecks y dependencias explícitas
  • Usar .env.example y validar variables al iniciar
  • Separar perfiles: dev vs test (compose profiles)
  • Optimizar builds con cache (COPY package*.json antes del código)

Evitar

  • “Montar todo” sin entender: volúmenes mal puestos rompen builds
  • Hacer docker exec para tareas manuales no documentadas
  • Usar “latest” en imágenes y romper el equipo sin querer
  • Guardar secretos en el repo o en la imagen
  • Ignorar performance (builds lentos = nadie lo usa)
Predictibilidad

Despliegues más predecibles

Si dev, CI y prod comparten el mismo empaquetado, disminuyen las sorpresas.

Cómo ayuda

  • La imagen de la app se construye siempre igual
  • Las dependencias del SO quedan “congeladas”
  • CI puede correr tests dentro del contenedor
  • Menos diferencias entre entornos (paridad)
Menos incidentes
Releases más rápidos
Puente a producción
  • En prod quizá uses Kubernetes/ECS/Nomad, pero el artefacto puede ser el mismo: una imagen.
  • Compose te da una “mini-orquestación” local, ideal para dev.
Checklist

Checklist para equipos

Si cumples esto, el “en mi máquina sí funciona” baja drásticamente.

Proyecto

  • docker compose levanta todo con 1 comando
  • Servicios con healthchecks
  • Datos persistidos en volúmenes
  • Variables documentadas en .env.example
  • README con comandos del día a día

Equipo

  • Versión mínima de Docker/Compose definida
  • Convención de puertos (evitar colisiones)
  • Política de secretos (vault, .env local, etc.)
  • CI ejecuta tests dentro de contenedores
  • Se revisan cambios a Dockerfile/compose como código crítico
Cierre

Resumen en 3 ideas

Si te llevas solo esto, ya valió la pena.
Las 3 ideas
  • Entorno como código: lo declaras y se replica.
  • Dev ≈ CI ≈ Prod: menos sorpresas al desplegar.
  • Onboarding rápido: el equipo produce antes.
Consistencia
Velocidad
Calidad

Datos sobre el enfoque

  • Reproducibilidad: el entorno queda definido en archivos versionados (Dockerfile/compose) y no en configuraciones personales.
  • Onboarding: reduces variabilidad; la instalación se vuelve “levantar stack” en vez de “configurar máquina”.
  • Paridad: al correr la app en contenedor, CI puede ejecutar tests en condiciones más cercanas a dev/prod.
  • Predecibilidad: menos fallos por diferencias de versiones de runtime/servicios y dependencias del sistema operativo.
Atajo: presiona Home para volver a la portada
Ayuda
Atajos y tips para exponer con fluidez.

Atajos

  • ← / →: anterior/siguiente
  • Home: volver a la primera
  • End: ir a la última
  • H: abrir/cerrar esta ayuda

Tip de exposición

  • Arranca con el dolor (“works on my machine”)
  • Muestra el “1 comando” como meta
  • Termina con checklist + acción concreta