Saltar al contenido principal

🐳 ¿Qué es Docker?

📖 Definición

Docker es una plataforma que ejecuta aplicaciones en entornos aislados llamados contenedores. Los contenedores empaquetan una aplicación con todas sus dependencias, asegurando que se ejecute de manera idéntica en cualquier lugar. Resuelve el problema de "funciona en mi máquina..." y hace que el desarrollo, las pruebas y el despliegue sean simples y consistentes.

🎯 Entendiendo a través de Analogías

Contenedores de Envío

Piensa en Docker como contenedores de envío en logística:

Envío Tradicional (VM)
├─ Embalaje diferente para cada artículo
├─ Tamaños/pesos variados
├─ Métodos de transporte diferentes
└─ Ineficiente, complejo

Envío por Contenedor (Docker)
├─ Contenedores estandarizados
├─ Puede contener cualquier mercancía
├─ Compatible con barco, camión, tren
└─ Eficiente, simple

Contenedor Docker = Paquete de software estandarizado

Apartamentos vs Casas

Máquina Virtual (VM) = Casa Independiente
├─ Infraestructura completa para cada una
├─ Terreno, edificio, servicios todo separado
├─ Costoso
└─ Inicio lento

Contenedor Docker = Apartamento
├─ Infraestructura compartida (terreno, edificio)
├─ Cada unidad independiente
├─ Eficiente
└─ Mudanza rápida

⚙️ Cómo Funciona

1. Arquitectura de Docker

┌─────────────────────────────────────┐
│ Cliente Docker (CLI) │
│ docker run, docker build, etc. │
└───────────────┬─────────────────────┘
│ Llamadas API
┌───────────────▼─────────────────────┐
│ Demonio Docker │
│ Gestión de Contenedores/Imágenes│
└───────────────┬─────────────────────┘

┌───────────────▼─────────────────────┐
│ Contenedores (Apps en Ejecución) │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │App 1│ │App 2│ │App 3│ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘

┌───────────────▼─────────────────────┐
│ SO Host (Kernel Linux) │
└─────────────────────────────────────┘

2. Imagen vs Contenedor

Imagen = Plano, Clase
├─ Solo lectura
├─ Estructura en capas
├─ Reutilizable
└─ Ejemplo: ubuntu, node, nginx

Contenedor = Instancia en Ejecución, Objeto
├─ Creado desde imagen
├─ Ejecutable
├─ Entorno aislado
└─ Múltiples instancias posibles

Relación:
Imagen → Contenedor 1
→ Contenedor 2
→ Contenedor 3

3. Dockerfile → Imagen → Contenedor

1. Escribir Dockerfile (Receta)
├─ FROM node:18
├─ COPY package.json .
├─ RUN npm install
└─ CMD ["npm", "start"]

2. Construir Imagen (Cocinar)
docker build -t mi-app .

3. Ejecutar Contenedor (Servir)
docker run -p 3000:3000 mi-app

💡 Ejemplos Prácticos

Escribiendo un Dockerfile

# Dockerfile de Aplicación Node.js

# Imagen base
FROM node:18-alpine

# Establecer directorio de trabajo
WORKDIR /app

# Copiar package.json
COPY package*.json ./

# Instalar dependencias
RUN npm ci --only=production

# Copiar código fuente
COPY . .

# Exponer puerto
EXPOSE 3000

# Variables de entorno
ENV NODE_ENV=production

# Comando para ejecutar al iniciar el contenedor
CMD ["node", "server.js"]

Construyendo y Ejecutando Imágenes

# 1. Construir imagen
docker build -t mi-app-node:1.0 .
# -t: etiqueta (nombre:versión)
# .: usar Dockerfile en directorio actual

# 2. Verificar imágenes
docker images
# REPOSITORY TAG IMAGE ID CREATED
# mi-app-node 1.0 abc123 hace 2 minutos

# 3. Ejecutar contenedor
docker run -d \
--name mi-app \
-p 3000:3000 \
-e NODE_ENV=production \
mi-app-node:1.0

# -d: ejecutar en segundo plano
# --name: nombre del contenedor
# -p: mapeo de puertos (host:contenedor)
# -e: variable de entorno

# 4. Verificar contenedores en ejecución
docker ps

# 5. Ver registros
docker logs mi-app

# 6. Acceder al shell del contenedor
docker exec -it mi-app sh

# 7. Detener contenedor
docker stop mi-app

# 8. Eliminar contenedor
docker rm mi-app

# 9. Eliminar imagen
docker rmi mi-app-node:1.0

Docker Compose (Gestionando Múltiples Contenedores)

# docker-compose.yml

version: '3.8'

services:
# Aplicación web
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=database
depends_on:
- database
- redis
volumes:
- ./logs:/app/logs

# Base de datos
database:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=miapp
volumes:
- db-data:/var/lib/mysql
ports:
- "3306:3306"

# Caché
redis:
image: redis:7-alpine
ports:
- "6379:6379"

# Nginx (Proxy Inverso)
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web

volumes:
db-data:
# Comandos de Docker Compose

# Iniciar todos los servicios
docker-compose up -d

# Ver registros
docker-compose logs -f web

# Reiniciar servicio específico
docker-compose restart web

# Escalar servicio
docker-compose up -d --scale web=3

# Detener y eliminar todos los servicios
docker-compose down

# Eliminar con volúmenes
docker-compose down -v

Ejemplo del Mundo Real: React + Node.js + MongoDB

# frontend/Dockerfile
FROM node:18-alpine AS build

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Servir archivos estáticos con Nginx
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# backend/Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 5000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'

services:
frontend:
build:
context: ./frontend
ports:
- "80:80"
depends_on:
- backend

backend:
build:
context: ./backend
ports:
- "5000:5000"
environment:
- MONGO_URL=mongodb://mongodb:27017/miapp
depends_on:
- mongodb

mongodb:
image: mongo:6
volumes:
- mongo-data:/data/db
ports:
- "27017:27017"

volumes:
mongo-data:

Gestionando Volúmenes de Datos

# Crear volumen
docker volume create mis-datos

# Listar volúmenes
docker volume ls

# Montar volumen en contenedor
docker run -v mis-datos:/app/data mi-app

# Montar directorio del host (útil para desarrollo)
docker run -v $(pwd):/app mi-app

# Montaje de solo lectura
docker run -v $(pwd):/app:ro mi-app

# Eliminar volumen
docker volume rm mis-datos

# Eliminar todos los volúmenes no utilizados
docker volume prune

Archivo .dockerignore

# .dockerignore

# Git
.git
.gitignore

# Node
node_modules
npm-debug.log

# Entorno
.env
.env.local

# Salida de compilación
dist
build

# IDE
.vscode
.idea

# Pruebas
coverage
*.test.js

# Documentación
README.md
docs/

🤔 Preguntas Frecuentes

P1. ¿Docker vs Máquina Virtual?

R:

Máquina Virtual (VM)
┌─────────────────┐ ┌─────────────────┐
│ App A │ │ App B │
│ Libs │ │ Libs │
│ SO Invitado │ │ SO Invitado │
│ (Linux) │ │ (Ubuntu) │
└─────────────────┘ └─────────────────┘
┌─────────────────────────────────────┐
│ Hipervisor (VMware, etc.) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ SO Host │
└─────────────────────────────────────┘

Contenedor Docker
┌───────┐ ┌───────┐ ┌───────┐
│ App A │ │ App B │ │ App C │
│ Libs │ │ Libs │ │ Libs │
└───────┘ └───────┘ └───────┘
┌─────────────────────────────┐
│ Motor Docker │
└─────────────────────────────┘
┌─────────────────────────────┐
│ SO Host (Kernel Linux) │
└─────────────────────────────┘

Comparación:
VM Docker
Tamaño GB MB
Inicio Minutos Segundos
Aislamiento Completo Nivel de proceso
Rendimiento Alto overhead Casi nativo
Portabilidad Baja Alta

P2. ¿Qué son las Capas de Imagen Docker?

R:

FROM node:18          # Capa 1: Imagen base
WORKDIR /app # Capa 2: Directorio de trabajo
COPY package.json . # Capa 3: package.json
RUN npm install # Capa 4: Instalar dependencias
COPY . . # Capa 5: Código fuente
CMD ["node", "app.js"]# Capa 6: Comando de inicio

# Caché de capas
# - Las capas sin cambios se reutilizan
# - Compilaciones más rápidas

# ✅ Bueno: Elementos que cambian frecuentemente al final
COPY package.json .
RUN npm install # Cacheado (si package.json no cambia)
COPY . . # Solo cambios de código

# ❌ Malo: Elementos que cambian frecuentemente primero
COPY . . # Cambios de código cada vez
RUN npm install # ¡npm install se ejecuta de nuevo!

P3. ¿Qué es la Orquestación de Contenedores?

R:

Servidor Único
┌─────────────────────┐
│ ┌───┐ ┌───┐ ┌───┐ │
│ │C1 │ │C2 │ │C3 │ │
│ └───┘ └───┘ └───┘ │
└─────────────────────┘

Orquestación (Kubernetes)
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Server1 │ │ Server2 │ │ Server3 │
│ ┌───┐ │ │ ┌───┐ │ │ ┌───┐ │
│ │C1 │ │ │ │C2 │ │ │ │C3 │ │
│ └───┘ │ │ └───┘ │ │ └───┘ │
└─────────┘ └─────────┘ └─────────┘

Características:
- Despliegue automatizado
- Escalado (escalar arriba/abajo automáticamente)
- Equilibrio de carga
- Auto-recuperación (reiniciar contenedores muertos)
- Actualizaciones progresivas

Herramientas:
- Kubernetes (más popular)
- Docker Swarm
- Amazon ECS

P4. ¿Usar Docker en Desarrollo?

R:

# Escenario: Diferentes entornos por miembro del equipo

# Miembro A: macOS, Node 16
# Miembro B: Windows, Node 18
# Miembro C: Linux, Node 14

# Problema: "Funciona en mi máquina..."

# Solución: Unificar entorno con Docker

# docker-compose.yml
version: '3.8'
services:
app:
image: node:18
volumes:
- .:/app
working_dir: /app
command: npm run dev
ports:
- "3000:3000"

# Todos los miembros del equipo usan el mismo entorno
docker-compose up

# Beneficios:
# 1. No se necesita instalación de Node.js
# 2. Versión unificada
# 3. Dependencias aisladas
# 4. Configuración de entorno compartida

P5. ¿Seguridad en Docker?

R:

# ✅ Mejores Prácticas de Seguridad

# 1. Imagen base con privilegios mínimos
FROM node:18-alpine # ✅ Pequeña y segura
FROM node:18 # ❌ Herramientas innecesarias

# 2. Ejecutar como usuario no root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs # ✅ Minimizar privilegios

# 3. Excluir información sensible
# Agregar a .dockerignore
.env
*.key
secrets/

# 4. Compilación multi-etapa (eliminar herramientas innecesarias)
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN npm run build

FROM node:18-alpine # Imagen más pequeña
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/main.js"]

# 5. Escanear imágenes
docker scan mi-imagen

# 6. Sistema de archivos raíz de solo lectura
docker run --read-only mi-app

# 7. Límites de recursos
docker run --memory="512m" --cpus="1" mi-app

🎓 Próximos Pasos

Después de entender Docker, explora:

  1. ¿Qué es Git? (Próximamente) - Control de versiones y despliegue
  2. ¿Qué es Node.js? (Próximamente) - Desplegar aplicaciones Node con Docker
  3. ¿Qué es TDD? - Ejecutar pruebas en Docker

Pruébalo Tú Mismo

# 1. Verificar instalación de Docker
docker --version

# 2. Hola Mundo
docker run hello-world

# 3. Ejecutar Nginx
docker run -d -p 8080:80 nginx
# Visita http://localhost:8080

# 4. Verificar contenedores
docker ps

# 5. Detener y eliminar
docker stop <container-id>
docker rm <container-id>

# 6. Contenerizar tu aplicación
# - Escribir Dockerfile
# - docker build
# - docker run

Comandos Útiles

# Gestión de imágenes
docker pull nginx # Descargar imagen
docker images # Listar imágenes
docker rmi nginx # Eliminar imagen

# Gestión de contenedores
docker run # Ejecutar contenedor
docker ps # Contenedores en ejecución
docker ps -a # Todos los contenedores
docker stop <id> # Detener contenedor
docker start <id> # Iniciar contenedor
docker restart <id> # Reiniciar contenedor
docker rm <id> # Eliminar contenedor

# Registros y depuración
docker logs <id> # Ver registros
docker logs -f <id> # Seguir registros
docker exec -it <id> sh # Acceder al shell del contenedor
docker inspect <id> # Información detallada

# Limpieza
docker system prune # Eliminar recursos no utilizados
docker system prune -a # Eliminar todos los recursos no utilizados
docker volume prune # Eliminar volúmenes no utilizados

🎬 Conclusión

Docker es una herramienta esencial para el desarrollo moderno:

  • Contenedores: Entornos de ejecución aislados
  • Imágenes: Paquetes de aplicaciones
  • Portabilidad: Se ejecuta de manera idéntica en cualquier lugar
  • Eficiencia: Ligero y rápido

¡Resuelve los problemas de "funciona en mi máquina" con Docker! 🐳✨