todos-contra-el-fuego-web/RUNBOOK.md
vjrj 197a59f641 fase 3: stack docker-compose local del sistema modernizado
Compose con los 5 servicios: mongo:7 (replica set rs0 + mongo-init one-shot),
redis (AOF), web Meteor 3.1 (Dockerfile multi-stage: builder debian con
meteor-tool 3.1 -> server-deps alpine -> runtime node:22-alpine), notifications
(dry-run) y node-red 4. Healthchecks (127.0.0.1, no localhost: busybox wget
prefiere IPv6 y Meteor escucha IPv4), restart unless-stopped, logging rotado
3x10MB, secretos montados desde ./secrets (gitignored, solo .example versionado).
.npmrc legacy-peer-deps para el ERESOLVE de las libs react viejas.

Validado: los 5 servicios healthy y smoke REST byte-identico (12/12) contra la
web dockerizada en :3200. RUNBOOK.md documenta arranque, smoke, backups y
rollback. No toca infra de Comunes.
2026-07-15 01:01:07 +02:00

117 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RUNBOOK — stack local dockerizado (fase 3, validación)
Stack Docker Compose del sistema modernizado de "Todos contra el fuego":
web Meteor 3.1 (Node 22), MongoDB 7 (replica set de un nodo), Redis (AOF),
`tcef-notifications` y node-red. **Es el entorno de validación local previo a
producción** — el despliegue real irá al ansible de Comunes (fase 3 del plan);
este compose no toca el proxy ni la infra compartida.
## Requisitos
- Docker + Docker Compose v2.
- El repo hermano `../tcef-notifications` (el compose construye su imagen desde ahí).
## Primer arranque
```bash
# 1. Secretos (gitignorados; ver secrets/README.md)
cp settings-development.json secrets/meteor-settings.json
cp secrets/notifications.env.example secrets/notifications.env # MODE=dry-run
# 2. Construir y levantar (el primer build de la web tarda: descarga meteor-tool
# y compila el bundle; ~10-20 min en frío)
docker compose build
docker compose up -d
# 3. Comprobar salud
docker compose ps # todo "healthy" / mongo-init "exited (0)"
```
Servicios y puertos en el host:
| Servicio | Contenedor | URL / puerto |
|---|---|---|
| web Meteor | `tcef-stack-web` | http://localhost:3200 |
| node-red | `tcef-stack-node-red` | http://localhost:1880 |
| MongoDB 7 | `tcef-stack-mongo` | solo red interna (usar `docker exec`) |
| Redis | `tcef-stack-redis` | solo red interna |
| notificaciones | `tcef-stack-notifications` | sin puerto (worker) |
`mongo-init` es un one-shot: inicia el replica set `rs0` y fija
`db.migrations` a la versión 18 (los `up()` históricos ya son async, pero se
pinta v18 porque los datos reales de producción llegan pre-migrados).
> Nota healthcheck: los checks usan `127.0.0.1`, no `localhost`. El `wget` de
> busybox (alpine) resuelve `localhost` a `::1` (IPv6) primero, pero los
> servidores Meteor/node-red escuchan solo en IPv4 → un `localhost` daría
> unhealthy aunque la app responda perfectamente por el puerto mapeado.
## Smoke test REST (red de seguridad del contrato Flutter)
Debe ser **byte-idéntico** a los snapshots committeados:
```bash
MONGO_CONTAINER=tcef-stack-mongo MONGO_SHELL=mongosh MONGO_PORT=27017 \
BASE_URL=http://localhost:3200 ./smoke/smoke.sh
```
## Operación diaria
```bash
docker compose logs -f web # logs de un servicio (rotados: 3×10MB)
docker compose ps # estado + healthchecks
docker compose restart notifications # reiniciar un servicio
docker compose down # parar todo (los volúmenes persisten)
docker compose down -v # ⚠️ borra también mongo/redis/node-red
```
## Desplegar una nueva versión
```bash
# web (tras cambios en el repo)
docker compose build web && docker compose up -d web
# notificaciones (tras cambios en ../tcef-notifications)
docker compose build notifications && docker compose up -d notifications
```
Verificar tras cada despliegue de la web: `docker compose ps` healthy + smoke
REST byte-idéntico (arriba).
## Backups y restauración (volúmenes locales)
```bash
# Mongo: dump / restore de la db fuegos
docker exec tcef-stack-mongo mongodump --db fuegos --archive > fuegos.dump
docker exec -i tcef-stack-mongo mongorestore --archive --drop < fuegos.dump
# node-red: tar del volumen de datos
docker run --rm -v tcef-stack_nodered-data:/data -v "$PWD:/backup" alpine \
tar czf /backup/nodered-data.tgz -C /data .
```
## Rollback
- **web**: `git checkout <commit-anterior>` + `docker compose build web && docker compose up -d web`.
(En producción se conservará la imagen anterior etiquetada; aquí basta git.)
- **notificaciones**: igual, desde `../tcef-notifications`. `KILL_SWITCH=1` en
`secrets/notifications.env` + `docker compose up -d notifications` detiene
todo envío al instante sin parar el servicio.
- **datos**: restaurar el último dump (arriba).
## Salvaguardas de notificaciones
`secrets/notifications.env` arranca en `MODE=dry-run` (no envía nada). La
progresión dry-run → shadow → canary → full está documentada en
`../tcef-notifications/docs/rollout.md` y **nunca** se salta pasos. En este
stack local no hay credenciales reales salvo que las pongas tú.
## Deuda conocida de este compose
- `tcef-notifications` no expone endpoint HTTP de salud → healthcheck por
proceso (`pgrep`). Añadir `/healthz` al servicio cuando toque.
- node-red corre la imagen oficial vacía (los flows de los bots viven en
shiva; su migración de datos es parte del cutover real de fase 3).
- El driver mongodb 3.7 de `tcef-notifications` (elegido por el Mongo 3.2 de
producción) conecta con Mongo 7 en pruebas locales, pero al hacer el cutover
real a Mongo 7 debe subirse a driver 6.x (y habilitar change streams).