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