docs(tmux-host): documentar Dockerfile + healthcheck cada 30 min
Build and Push Docs Portal / Escaneo de Secretos (Gitleaks) (push) Successful in 23s
Build and Push Docs Portal / Construir y publicar Docs Portal (push) Successful in 8m57s

Actualiza sesion-tmux-persistente.md: la imagen de tmux-host ahora se
construye desde scripts/tmux-host/Dockerfile (WORKDIR fijo en la raiz
del lab) en vez del docker run inline anterior, y se agrega la seccion
del chequeo de salud en background (red + sesion tmux, cada 30 min,
notificacion via el mismo canal de Telegram del lab-agent). Actualiza
tambien la tabla "Que sobrevive a que" para reflejar la auto-recuperacion
de la sesion tmux.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012xyPak6HWFmBCqXrvFsejL
This commit is contained in:
devopsandClaude Sonnet 5 committed 2026-09-06 17:18:10 +00:00
1 parent 997a00c284
commit 8835d8ca0c
1 file changed
+56 -3
@@ -37,6 +37,9 @@ flowchart LR
SSH["Cliente SSH<br/>(laptop / tablet / lo que sea)"] -->|docker exec| C["Contenedor tmux-host<br/>--network host, --restart unless-stopped"] SSH["Cliente SSH<br/>(laptop / tablet / lo que sea)"] -->|docker exec| C["Contenedor tmux-host<br/>--network host, --restart unless-stopped"]
C --> T["tmux server<br/>sesión 'lab'"] C --> T["tmux server<br/>sesión 'lab'"]
T --> CC["proceso claude<br/>(Claude Code)"] T --> CC["proceso claude<br/>(Claude Code)"]
C --> HC["healthcheck.sh (background)<br/>cada 30 min"]
HC -.->|curl| NET["gitea.cruzcloud.net"]
HC -.->|recrea si falta| T
C -.->|bind mount| DATA["/DATA (host)<br/>repos y configs del lab"] C -.->|bind mount| DATA["/DATA (host)<br/>repos y configs del lab"]
C -.->|bind mount| SOCK["/var/run/docker.sock (host)<br/>control de Docker/k3d"] C -.->|bind mount| SOCK["/var/run/docker.sock (host)<br/>control de Docker/k3d"]
C -.->|bind mount| HOME["/DATA/AppData/k3d-lab-v3/claude-code-home (host)<br/>-> /root/.claude"] C -.->|bind mount| HOME["/DATA/AppData/k3d-lab-v3/claude-code-home (host)<br/>-> /root/.claude"]
@@ -49,7 +52,17 @@ reinicia.
## Contenedor: `tmux-host` ## Contenedor: `tmux-host`
La imagen se construye desde `scripts/tmux-host/Dockerfile` (antes era
un `docker run ... alpine sh -c "apk add ..."` inline — mismo contenido,
pero ahora como imagen versionada en vez de reinstalar todo en cada
arranque). El Dockerfile fija `WORKDIR /DATA/AppData/k3d-lab-v3/lab-cruzcloud`
(la raíz del lab, con `CLAUDE.md`, `apps-registry/`, `platform-infra/` y
`scripts/`) y agrega `healthcheck.sh` (ver más abajo) como proceso en
background del contenedor.
```bash ```bash
docker build -t tmux-host:local scripts/tmux-host
docker run -d \ docker run -d \
--name tmux-host \ --name tmux-host \
--restart unless-stopped \ --restart unless-stopped \
@@ -59,9 +72,15 @@ docker run -d \
-v /DATA/AppData/k3d-lab-v3/claude-code-home:/root/.claude \ -v /DATA/AppData/k3d-lab-v3/claude-code-home:/root/.claude \
-v /DATA/AppData/k3d-lab-v3/claude-code-home.json:/root/.claude.json \ -v /DATA/AppData/k3d-lab-v3/claude-code-home.json:/root/.claude.json \
--network host \ --network host \
alpine sh -c "apk add --no-cache tmux docker-cli bash curl git kubectl nodejs npm && npm install -g @anthropic-ai/claude-code && git config --global --add safe.directory '*' && tail -f /dev/null" tmux-host:local
``` ```
`WORKDIR` hace que `docker exec` (y por lo tanto `lab-session.sh`)
arranquen ya posicionados en la raíz del lab sin tener que navegar a
mano — `lab-session.sh` además pasa la ruta explícita vía `tmux new -c`
por las dudas, para no depender de que `docker exec` herede el `WORKDIR`
de la imagen.
| Volumen | Destino | Por qué | | Volumen | Destino | Por qué |
|---|---|---| |---|---|---|
| `/DATA` | `/DATA` | Acceso a los repos (`apps-registry`, `platform-infra`, `scripts`) y configs del lab, mismos paths que en el host | | `/DATA` | `/DATA` | Acceso a los repos (`apps-registry`, `platform-infra`, `scripts`) y configs del lab, mismos paths que en el host |
@@ -195,13 +214,47 @@ Si la sesión `lab` existe, se adjunta a ella (mismo shell, mismo
ejemplo justo después de que el contenedor se recreó desde cero— la ejemplo justo después de que el contenedor se recreó desde cero— la
crea. No hay que recordar cuál de los dos casos aplica. crea. No hay que recordar cuál de los dos casos aplica.
## Chequeo de salud automático (cada 30 min)
`scripts/tmux-host/healthcheck.sh` corre en background dentro del
propio contenedor (lanzado por `entrypoint.sh`, el `ENTRYPOINT` de la
imagen) y cada `HEALTHCHECK_INTERVAL_SEC` (1800s por defecto) hace dos
chequeos independientes, con una respuesta distinta según el tipo de
falla:
- **Conectividad de red real** (`curl` a `https://gitea.cruzcloud.net/`):
si falla, **solo se registra** en el log y se notifica por Telegram —
el contenedor nunca intenta "arreglar" la red del host. Un problema de
red puede ser de hardware/BIOS (deuda técnica ya conocida, ver
`incidente-medusa-crashloop-2026-08.md`), fuera del alcance real de un
contenedor.
- **Existencia de la sesión tmux `lab`** (`tmux has-session`): si no
existe, **sí se recrea automáticamente** (`tmux new-session -d -s lab
-c <WORKDIR>`) — es solo recuperar el propio entorno de trabajo del
contenedor, no tocar infraestructura externa, así que es seguro
hacerlo sin intervención humana.
Todo evento (chequeo ok, falla de red, sesión recreada) queda en
`/DATA/AppData/k3d-lab-v3/tmux-host/logs/health.log` — persistente en
`/DATA`, no solo en el stdout del contenedor (que se pierde con
`docker logs` al recrearlo). Las notificaciones de Telegram reusan el
mismo bot/`chat_id` ya configurado para `scripts/lab-agent/`
(`lab-agent.env`), en vez de duplicar credenciales entre los dos
agentes del lab.
Para probar el intervalo sin esperar 30 minutos, se puede levantar el
contenedor con `-e HEALTHCHECK_INTERVAL_SEC=60` (solo para pruebas en un
contenedor descartable — nunca cambiar esto en el `tmux-host` real sin
volver a 1800 después).
## Qué sobrevive a qué ## Qué sobrevive a qué
| Evento | ¿Sobrevive la sesión tmux (`lab`)? | ¿Sobrevive el login de Claude Code? | | Evento | ¿Sobrevive la sesión tmux (`lab`)? | ¿Sobrevive el login de Claude Code? |
|---|---|---| |---|---|---|
| Corte de SSH / cerrar la terminal / cambiar de dispositivo | Sí — es exactamente el caso que este setup resuelve | Sí | | Corte de SSH / cerrar la terminal / cambiar de dispositivo | Sí — es exactamente el caso que este setup resuelve | Sí |
| `docker restart tmux-host` (reinicio del NAS) | **No** — el proceso `tmux server` muere con el contenedor; `lab-session.sh` crea una sesión nueva automáticamente | Sí, vía el volumen montado en `/root/.claude` | | `docker restart tmux-host` (reinicio del NAS) | **No al toque, pero se auto-recupera solo** — el proceso `tmux server` muere con el contenedor; `healthcheck.sh` la recrea en el próximo ciclo (máx. 30 min) sin intervención, o `lab-session.sh` la crea al instante si alguien se reconecta antes | Sí, vía el volumen montado en `/root/.claude` |
| `docker rm` + recrear el contenedor desde cero | No | Sí, vía el mismo volumen — no hace falta volver a autenticar ni reconfirmar el diálogo de confianza del folder (ver el mount de `claude-code-home.json`) | | `docker rm` + recrear el contenedor desde cero | No, pero igual se auto-recupera sola (mismo mecanismo de arriba) | Sí, vía el mismo volumen — no hace falta volver a autenticar ni reconfirmar el diálogo de confianza del folder (ver el mount de `claude-code-home.json`) |
| Falla de red real del host/NAS | N/A | N/A — `healthcheck.sh` la detecta y notifica, pero no la corrige (fuera de su alcance) |
## Validación en vivo ## Validación en vivo