¿Por qué Docker?

Docker revolucionó la forma en que desarrollamos y desplegamos aplicaciones. Con contenedores, puedes:

Conceptos Fundamentales

Imágenes vs Contenedores

1
2
3
4
+----------------+     docker run     +----------------+
|    IMAGEN      | ----------------> |   CONTENEDOR   |
| (Blueprint)    |                   | (Instancia)    |
+----------------+                   +----------------+

Arquitectura Docker

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────┐
│           Docker Client             │
│         (docker CLI)                │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│          Docker Daemon              │
│         (dockerd)                   │
├─────────────────────────────────────┤
│  Images  │  Containers  │  Networks │
└─────────────────────────────────────┘

Comandos Esenciales

Gestión de Imágenes

1
2
3
4
5
6
7
8
9
10
11
# Descargar imagen
docker pull nginx:latest

# Listar imágenes
docker images

# Eliminar imagen
docker rmi nginx:latest

# Construir imagen desde Dockerfile
docker build -t mi-app:v1 .

Gestión de Contenedores

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Ejecutar contenedor
docker run -d -p 8080:80 --name web nginx

# Listar contenedores
docker ps        # En ejecución
docker ps -a     # Todos

# Detener/Iniciar
docker stop web
docker start web

# Ver logs
docker logs -f web

# Ejecutar comando dentro del contenedor
docker exec -it web bash

# Eliminar contenedor
docker rm web

Dockerfile

El Dockerfile define cómo construir tu imagen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Imagen base
FROM node:18-alpine

# Metadata
LABEL maintainer="[email protected]"

# Directorio de trabajo
WORKDIR /app

# Copiar archivos de dependencias primero (cache optimization)
COPY package*.json ./

# Instalar dependencias
RUN npm ci --only=production

# Copiar código fuente
COPY . .

# Variable de entorno
ENV NODE_ENV=production

# Exponer puerto
EXPOSE 3000

# Usuario no-root (seguridad)
USER node

# Comando por defecto
CMD ["node", "server.js"]

Multi-stage Builds

Reduce el tamaño de tu imagen final:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Stage 1: Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

Docker Compose

Para aplicaciones multi-contenedor, usa docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
version: '3.8'

services:
  # Aplicación web
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db
      - redis
    restart: unless-stopped
    networks:
      - app-network

  # Base de datos
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    networks:
      - app-network

  # Cache
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    networks:
      - app-network

  # Reverse proxy
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app
    networks:
      - app-network

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge

Comandos Docker Compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Iniciar servicios
docker-compose up -d

# Ver logs
docker-compose logs -f

# Escalar servicios
docker-compose up -d --scale app=3

# Detener
docker-compose down

# Eliminar todo (incluyendo volúmenes)
docker-compose down -v

Networking

Docker ofrece varios drivers de red:

1
2
3
4
5
6
7
8
# Crear red personalizada
docker network create --driver bridge mi-red

# Conectar contenedor a red
docker network connect mi-red mi-contenedor

# Inspeccionar red
docker network inspect mi-red

Tipos de Red

Driver Uso
bridge Red aislada (default)
host Comparte red del host
none Sin networking
overlay Multi-host (Swarm)

Volúmenes

Persiste datos fuera del contenedor:

1
2
3
4
5
6
7
8
9
10
11
# Crear volumen
docker volume create mi-data

# Montar volumen
docker run -v mi-data:/app/data nginx

# Bind mount (directorio local)
docker run -v $(pwd)/config:/app/config nginx

# Inspeccionar
docker volume inspect mi-data

Buenas Prácticas

1. Usa imágenes base pequeñas

1
2
3
4
5
6
7
# ❌ Evitar
FROM ubuntu:latest

# ✅ Preferir
FROM alpine:3.18
FROM node:18-alpine
FROM python:3.11-slim

2. Minimiza capas

1
2
3
4
5
6
7
8
9
10
# ❌ Múltiples RUN
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean

# ✅ Un solo RUN
RUN apt-get update && \
    apt-get install -y curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

3. No ejecutes como root

1
2
3
# Crear usuario
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

4. Usa .dockerignore

1
2
3
4
5
6
7
# .dockerignore
node_modules
.git
.env
*.md
Dockerfile
docker-compose.yml

5. Health checks

1
2
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

Seguridad

Prácticas de seguridad críticas:
1
2
3
4
5
6
7
8
9
10
11
# Escanear vulnerabilidades
docker scan mi-imagen:latest

# Ejecutar con capacidades limitadas
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

# Filesystem de solo lectura
docker run --read-only nginx

# Limitar recursos
docker run --memory=512m --cpus=1 nginx

Ejemplo Práctico: Stack MERN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# docker-compose.yml para MERN Stack
version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    environment:
      - REACT_APP_API_URL=http://localhost:5000
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    environment:
      - MONGODB_URI=mongodb://mongo:27017/myapp
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - mongo

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

volumes:
  mongo_data:

Debugging

1
2
3
4
5
6
7
8
9
10
11
12
# Ver procesos en contenedor
docker top mi-contenedor

# Estadísticas en tiempo real
docker stats

# Inspeccionar contenedor
docker inspect mi-contenedor

# Copiar archivos desde/hacia contenedor
docker cp mi-contenedor:/app/logs ./logs
docker cp ./config.json mi-contenedor:/app/

Conclusión

Docker es una herramienta fundamental en el desarrollo moderno. Con estos conocimientos puedes:

1
2
$ docker run -it --rm alpine echo "¡Docker es genial! 🐳"
¡Docker es genial! 🐳