Capítulo 34 — Docker para frontend¶
Introducción¶
Docker empaqueta tu aplicación y todas sus dependencias en un contenedor: una unidad ligera, portable y reproducible que funciona igual en tu portátil, en el servidor de tu compañero y en producción.
Para frontend, Docker resuelve el clásico "en mi máquina funciona". Con un Dockerfile describes el entorno exacto y cualquiera puede reproducirlo con un solo comando.
¿Qué es un contenedor?¶
Un contenedor es como una máquina virtual ultraligera:
- Comparte el kernel del sistema operativo (no emula hardware).
- Arranca en milisegundos.
- Ocupa MBs en lugar de GBs.
- Es inmutable: una vez construido, siempre se ejecuta igual.
Docker vs Máquina Virtual¶
| Aspecto | Docker | VM |
|---|---|---|
| Arranque | Segundos | Minutos |
| Tamaño | MBs | GBs |
| Sistema operativo | Comparte kernel | Completo |
| Aislamiento | Procesos | Hardware |
Instalación¶
- Linux (Arch):
sudo pacman -S docker docker-compose - macOS/Windows: Docker Desktop
Activa el servicio:
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Cierra sesión y vuelve a entrar
Conceptos clave¶
| Concepto | Qué es |
|---|---|
| Imagen | Plantilla inmutable (receta) |
| Contenedor | Instancia en ejecución de una imagen |
| Dockerfile | Receta para construir una imagen |
| Volume | Almacenamiento persistente |
| Compose | Orquestador local multi-contenedor |
Tu primer Dockerfile¶
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev", "--", "--host"]
Construir:
docker build -t mi-app .
Ejecutar:
docker run -p 5173:5173 mi-app
Visita http://localhost:5173.
Multi-stage build (el estándar en producción)¶
En producción no necesitas node_modules ni el código fuente. Solo el resultado del build. Multi-stage permite usar una imagen para compilar y otra (mucho más ligera) para servir.
# Etapa 1: Build
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Etapa 2: Servir con Nginx
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Resultado: de ~800 MB a ~25 MB. 🎯
Configuración de Nginx para SPA¶
nginx.conf:
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# SPA fallback: siempre sirve index.html si no encuentra archivo
location / {
try_files $uri $uri/ /index.html;
}
# Cache largo para assets con hash
location ~* \.(js|css|woff2|png|jpg|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Gzip
gzip on;
gzip_types text/css application/javascript image/svg+xml;
}
Docker Compose¶
Para varios servicios (frontend + backend + base de datos) usa docker-compose.yml:
services:
web:
build: .
ports:
- "8080:80"
depends_on:
- api
environment:
- VITE_API_URL=http://localhost:3000
api:
build: ./api
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/app
depends_on:
- db
db:
image: postgres:16-alpine
environment:
- POSTGRES_PASSWORD=pass
- POSTGRES_USER=user
- POSTGRES_DB=app
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Arrancar todo:
docker compose up -d
Parar:
docker compose down
.dockerignore¶
Igual que .gitignore, pero para Docker. Muy importante para que el build sea rápido:
node_modules
dist
.git
.env
.env.local
*.log
.DS_Store
coverage
Sin este archivo, Docker copiaría node_modules (miles de archivos) y el build tardaría minutos en lugar de segundos.
Volumes: persistencia¶
Los contenedores son efímeros: cuando se borran, pierden los datos. Los volumes persisten:
volumes:
- pgdata:/var/lib/postgresql/data
Buenas prácticas¶
- Usa imágenes Alpine: mucho más ligeras (~5 MB vs ~900 MB).
- Multi-stage builds para producción: solo lo necesario.
- Un proceso por contenedor: no mezcles frontend y backend.
.dockerignoresiempre.- Fija versiones:
node:20.11.0-alpine, nonode:latest. - No ejecutes como root: crea un usuario sin privilegios.
- Escanea vulnerabilidades:
docker scout cves mi-imagen. - Logs a stdout/stderr, nunca a archivos dentro del contenedor.
- Compose para desarrollo, Kubernetes para producción a gran escala.
Ejemplo: usuario no root¶
FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app . .
USER app
CMD ["node", "server.js"]
Ejercicios¶
- Crea un Dockerfile con
node:20-alpinepara un proyecto Vite y ejecútalo condocker run. - Convierte el Dockerfile a multi-stage usando Nginx.
- Añade un
.dockerignorey comprueba cuánto tarda el build antes y después. - Crea un
docker-compose.ymlcon frontend, backend y PostgreSQL. - Escanea tu imagen con
docker scout cvesy corrige las vulnerabilidades.
⬅️ Capítulo 33 — Edge Computing | ➡️ Capítulo 35 — GraphQL
🧠 Pon a prueba tus conocimientos¶
¿Qué es un contenedor Docker?