🌅 Saturday Setup: Docker Development Workflow - Entornos Consistentes que Escalan

Los sábados optimizamos nuestros espacios de trabajo. Hoy enfoque en Docker como herramienta de desarrollo: cómo crear entornos consistentes, reproducibles y que eliminen el clásico "funciona en mi máquina" para siempre.

🚀 Por Qué Docker en Desarrollo (No Solo Producción)

El Problema Universal:

# La pesadilla del onboarding tradicional
git clone proyecto-backend
cd proyecto-backend
# 30 minutos instalando Node.js versión específica
# 20 minutos configurando PostgreSQL
# 15 minutos con Redis
# 45 minutos debuggeando "por qué no funciona en mi sistema"
# Total: 2+ horas para primer "Hello World"

La Solución Docker:

# Onboarding con Docker
git clone proyecto-backend
cd proyecto-backend
docker-compose up
# Total: 5 minutos para entorno completo funcionando

🛠️ Setup Base: Docker Compose para Desarrollo

📁 Estructura de Proyecto Optimizada:

my-project/
├── docker-compose.yml          # Servicios principales
├── docker-compose.dev.yml      # Overrides para desarrollo
├── docker-compose.prod.yml     # Overrides para producción
├── .env.example                 # Template de variables
├── dockerfiles/
│   ├── Dockerfile.api          # Backend service
│   ├── Dockerfile.frontend     # Frontend service
│   └── Dockerfile.worker       # Background jobs
└── scripts/
    ├── setup.sh                # Script de inicialización
    ├── reset-db.sh             # Reset completo de DB
    └── logs.sh                 # Ver logs de todos los servicios

🐳 Docker Compose Inteligente:

# docker-compose.yml - Base configuration
version: '3.8'

services:
  # Base de datos principal
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: ${DB_NAME:-myapp}
      POSTGRES_USER: ${DB_USER:-developer}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-dev_password}
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-developer}"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Redis para cache/sessions
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes

  # API Backend
  api:
    build:
      context: .
      dockerfile: dockerfiles/Dockerfile.api
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development
      DATABASE_URL: postgresql://${DB_USER:-developer}:${DB_PASSWORD:-dev_password}@postgres:5432/${DB_NAME:-myapp}
      REDIS_URL: redis://redis:6379
    volumes:
      - ./src:/app/src:ro
      - ./package.json:/app/package.json:ro
      - ./package-lock.json:/app/package-lock.json:ro
      - node_modules:/app/node_modules
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: package.json

volumes:
  postgres_data:
  redis_data:
  node_modules:

⚡ Development Overrides:

# docker-compose.dev.yml - Development-specific overrides
version: '3.8'

services:
  api:
    environment:
      DEBUG: "app:*"
      LOG_LEVEL: debug
    volumes:
      # Bind mount para hot reload
      - ./src:/app/src
      - ./tests:/app/tests
      # Named volume para node_modules (performance)
      - node_modules:/app/node_modules
    command: npm run dev  # Con nodemon/hot reload

  # Frontend development server
  frontend:
    build:
      context: .
      dockerfile: dockerfiles/Dockerfile.frontend
      target: development
    ports:
      - "3001:3001"
    environment:
      VITE_API_URL: http://localhost:3000
      VITE_HOT_RELOAD: "true"
    volumes:
      - ./frontend/src:/app/src
      - ./frontend/public:/app/public
      - frontend_node_modules:/app/node_modules
    develop:
      watch:
        - action: sync
          path: ./frontend/src
          target: /app/src

  # Database admin interface
  pgadmin:
    image: dpage/pgadmin4:latest
    environment:
      PGADMIN_DEFAULT_EMAIL: [email protected]
      PGADMIN_DEFAULT_PASSWORD: admin
    ports:
      - "5050:80"
    depends_on:
      - postgres

volumes:
  frontend_node_modules:

🔧 Dockerfiles Optimizados para Desarrollo

📦 Multi-stage Dockerfile para Node.js:

# dockerfiles/Dockerfile.api
FROM node:20-alpine AS base

# Instalar dependencias del sistema
RUN apk add --no-cache \
    dumb-init \
    git \
    curl

# Crear usuario no-root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

WORKDIR /app

# Copiar package files
COPY package*.json ./
COPY --chown=nextjs:nodejs . .

# Development stage
FROM base AS development
ENV NODE_ENV=development
USER nextjs

# Instalar todas las dependencias (incluyendo devDependencies)
RUN npm ci

EXPOSE 3000
CMD ["dumb-init", "npm", "run", "dev"]

# Production stage
FROM base AS production
ENV NODE_ENV=production
USER nextjs

# Solo instalar dependencias de producción
RUN npm ci --only=production && npm cache clean --force

# Build de la aplicación
RUN npm run build

EXPOSE 3000
CMD ["dumb-init", "npm", "start"]

🎨 Frontend con Hot Reload:

# dockerfiles/Dockerfile.frontend
FROM node:20-alpine AS development

WORKDIR /app

# Instalar dependencias
COPY package*.json ./
RUN npm ci

# Copiar código fuente
COPY . .

# Configurar Vite para Docker
ENV VITE_HOST=0.0.0.0
ENV VITE_PORT=3001

EXPOSE 3001

CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]

🎯 Scripts de Automatización

🚀 Setup Automático:

#!/bin/bash
# scripts/setup.sh

set -e

echo "🚀 Inicializando entorno de desarrollo..."

# Crear archivo .env si no existe
if [ ! -f .env ]; then
    echo "📝 Creando archivo .env..."
    cp .env.example .env
    
    # Generar valores random para desarrollo
    DB_PASSWORD=$(openssl rand -base64 32)
    JWT_SECRET=$(openssl rand -base64 64)
    
    sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$DB_PASSWORD/" .env
    sed -i "s/JWT_SECRET=.*/JWT_SECRET=$JWT_SECRET/" .env
fi

# Build de imágenes
echo "🏗️  Building Docker images..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml build

# Iniciar servicios
echo "🐳 Iniciando servicios..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# Esperar a que la DB esté lista
echo "⏳ Esperando a que PostgreSQL esté listo..."
while ! docker-compose exec postgres pg_isready -U developer > /dev/null 2>&1; do
    sleep 1
done

# Ejecutar migraciones
echo "🗄️  Ejecutando migraciones..."
docker-compose exec api npm run db:migrate

# Seed data para desarrollo
echo "🌱 Insertando datos de prueba..."
docker-compose exec api npm run db:seed

echo "✅ Entorno listo!"
echo "🌐 API: http://localhost:3000"
echo "🎨 Frontend: http://localhost:3001"
echo "📊 PgAdmin: http://localhost:5050"

🔄 Reset y Limpieza:

#!/bin/bash
# scripts/reset-db.sh

echo "🗑️  Reseteando base de datos..."

# Parar servicios
docker-compose down

# Eliminar volumes de datos
docker volume rm $(docker volume ls -q | grep postgres_data) 2>/dev/null || true
docker volume rm $(docker volume ls -q | grep redis_data) 2>/dev/null || true

# Reiniciar servicios
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

echo "✅ Base de datos reseteada!"

📊 Monitoring de Logs:

#!/bin/bash
# scripts/logs.sh

if [ -z "$1" ]; then
    echo "📋 Logs de todos los servicios:"
    docker-compose logs -f --tail=50
else
    echo "📋 Logs del servicio: $1"
    docker-compose logs -f --tail=50 "$1"
fi

🎨 Configuraciones Avanzadas

🔍 Debugging con VS Code:

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug API in Docker",
      "type": "node",
      "request": "attach",
      "port": 9229,
      "address": "localhost",
      "localRoot": "${workspaceFolder}/src",
      "remoteRoot": "/app/src",
      "restart": true,
      "sourceMaps": true
    }
  ]
}
# docker-compose.debug.yml
version: '3.8'

services:
  api:
    command: npm run debug  # node --inspect=0.0.0.0:9229 src/index.js
    ports:
      - "9229:9229"  # Debug port

⚡ Performance Optimizations:

# docker-compose.override.yml - Para desarrollo local
version: '3.8'

services:
  # Usar bind mounts para hot reload pero named volumes para node_modules
  api:
    volumes:
      - ./src:/app/src:cached           # Source code
      - ./tests:/app/tests:cached       # Tests
      - api_node_modules:/app/node_modules  # Named volume para performance
    tmpfs:
      - /tmp                           # Usar tmpfs para archivos temporales

  postgres:
    # Configuración para desarrollo (no para producción)
    command: >
      postgres
      -c shared_buffers=256MB
      -c max_connections=200
      -c effective_cache_size=1GB
      -c fsync=off                     # ⚠️ Solo para desarrollo
      -c synchronous_commit=off        # ⚠️ Solo para desarrollo

volumes:
  api_node_modules:
    driver: local

🌐 Networking y Service Discovery

🔗 Comunicación Entre Servicios:

// src/config/database.js
// Los servicios se comunican usando nombres de servicio como hostnames
const config = {
  development: {
    host: 'postgres',        // Nombre del servicio en docker-compose
    port: 5432,
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
  },
  redis: {
    host: 'redis',           // Nombre del servicio
    port: 6379,
  }
};

🏥 Health Checks Inteligentes:

# En docker-compose.yml
services:
  api:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  frontend:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001"]
      interval: 30s
      timeout: 10s
      retries: 3
    depends_on:
      api:
        condition: service_healthy

📊 Monitoreo y Observabilidad

📈 Stack de Observabilidad Local:

# docker-compose.monitoring.yml
version: '3.8'

services:
  # Métricas con Prometheus
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus

  # Visualización con Grafana
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3003:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: admin
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana:/etc/grafana/provisioning

  # Logs centralizados
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"

  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

volumes:
  prometheus_data:
  grafana_data:

🎯 Testing en Containers:

# docker-compose.test.yml
version: '3.8'

services:
  test-db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp_test
      POSTGRES_USER: test_user
      POSTGRES_PASSWORD: test_password
    tmpfs:
      - /var/lib/postgresql/data  # Base de datos en memoria para tests

  api-test:
    build:
      context: .
      dockerfile: dockerfiles/Dockerfile.api
      target: development
    environment:
      NODE_ENV: test
      DATABASE_URL: postgresql://test_user:test_password@test-db:5432/myapp_test
    depends_on:
      - test-db
    command: npm test
    volumes:
      - ./src:/app/src:ro
      - ./tests:/app/tests:ro
# Ejecutar tests
docker-compose -f docker-compose.test.yml run --rm api-test

🛡️ Security Best Practices

🔒 Secrets Management:

# docker-compose.yml
services:
  api:
    secrets:
      - db_password
      - jwt_secret
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
      JWT_SECRET_FILE: /run/secrets/jwt_secret

secrets:
  db_password:
    file: ./secrets/db_password.txt
  jwt_secret:
    file: ./secrets/jwt_secret.txt

👤 Non-root Users:

# En Dockerfile
FROM node:20-alpine

# Crear usuario no-root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Cambiar ownership de archivos
COPY --chown=nextjs:nodejs . .

# Cambiar a usuario no-root
USER nextjs

💡 Tips de Productividad

⚡ Aliases Útiles:

# Agregar a ~/.bashrc o ~/.zshrc

# Docker Compose shortcuts
alias dc="docker-compose"
alias dcup="docker-compose -f docker-compose.yml -f docker-compose.dev.yml up"
alias dcdown="docker-compose down"
alias dcbuild="docker-compose build"
alias dclogs="docker-compose logs -f"

# Ejecutar comandos en containers
alias dcexec="docker-compose exec"
alias dcrun="docker-compose run --rm"

# Limpieza
alias docker-clean="docker system prune -a --volumes"
alias docker-reset="docker-compose down -v && docker-compose up -d"

🎯 Makefile para Comandos Comunes:

# Makefile
.PHONY: setup up down build logs shell test clean

setup:
	@./scripts/setup.sh

up:
	docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

down:
	docker-compose down

build:
	docker-compose build

logs:
	docker-compose logs -f

shell:
	docker-compose exec api sh

test:
	docker-compose -f docker-compose.test.yml run --rm api-test

clean:
	docker-compose down -v
	docker system prune -a --volumes

reset-db:
	@./scripts/reset-db.sh

📈 Métricas de un Setup Exitoso

⏱️ Tiempos Objetivo:

# Benchmark de productividad
Onboarding completo:     < 10 minutos
Restart después de cambios: < 30 segundos
Build completo:          < 5 minutos
Test suite completa:     < 2 minutos

🎯 Indicadores de Éxito:

  • Zero "funciona en mi máquina" - Todos los environments son idénticos

  • Onboarding de 1 comando - ./setup.sh y listo

  • Hot reload perfecto - Cambios de código reflejados instantáneamente

  • Debugging seamless - Attach del debugger a containers

  • Testing isolated - Tests no interfieren con desarrollo

🚀 Comandos de Uso Diario

# Workflow típico de desarrollo

# Iniciar día de trabajo
make up

# Ver logs mientras desarrollo
make logs

# Ejecutar tests específicos
docker-compose exec api npm test -- --grep "user authentication"

# Acceder a shell del container
make shell

# Reset completo si algo se rompe
make reset-db

# Finalizar día de trabajo
make down

💬 Optimización Continua

Docker para desarrollo transform how teams collaborate y elimina friction en el workflow diario. Un setup bien configurado se convierte en invisible - just works.

Beneficios inmediatos:

  • Consistency entre todos los developers del team

  • Speed en onboarding y context switching

  • Reliability eliminando "environment issues"

  • Scalability fácil agregar nuevos servicios

¿Usan Docker para desarrollo o solo para producción? ¿Qué challenges han encontrado con performance en sistemas locales? ¿Cómo manejan el balance entre isolation y speed?

¿Qué herramientas complementarias usan con Docker? ¿K8s local, Docker Desktop, OrbStack? Compartamos setups y optimizaciones para mejorar la developer experience.

#SaturdaySetup #Docker #DevEnvironment #Containerization #DeveloperExperience #Docker-Compose #Productivity

🤖 Claude Assistant

How can I help you today?
Press / to open Claude