feat(tmux-host): Dockerfile con WORKDIR fijo + healthcheck cada 30 min
Reemplaza el docker run inline (alpine sh -c "apk add ...") por una imagen construida desde scripts/tmux-host/Dockerfile, con WORKDIR en la raiz del lab para que docker exec y lab-session.sh ya arranquen posicionados ahi sin cd manual. Agrega scripts/tmux-host/healthcheck.sh (via entrypoint.sh, background del propio contenedor): cada 30 min chequea conectividad real (curl a gitea.cruzcloud.net) y la existencia de la sesion tmux "lab". La red solo se loguea+notifica por Telegram (nunca se intenta corregir desde el contenedor); la sesion tmux si se recrea sola si falta. Log persistente en /DATA/AppData/k3d-lab-v3/tmux-host/logs/health.log. Reusa el bot/chat_id de Telegram ya configurado en scripts/lab-agent/. Validado en un contenedor descartable (tmux-host-test, intervalo de 60s): confirmado WORKDIR correcto via docker exec, creacion inicial de la sesion y recreacion automatica tras un kill-session manual. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012xyPak6HWFmBCqXrvFsejL
This commit is contained in:
1 parent
40cb058fd4
commit
78b97bdd59
4 files changed
+132
-1
No files matched your search
+6
-1
@@ -10,6 +10,11 @@ set -Eeuo pipefail
|
|||||||
|
|
||||||
CONTAINER="tmux-host"
|
CONTAINER="tmux-host"
|
||||||
SESSION="lab"
|
SESSION="lab"
|
||||||
|
# Repo raiz del lab (CLAUDE.md, apps-registry/, platform-infra/, scripts/).
|
||||||
|
# Mismo path que el WORKDIR de scripts/tmux-host/Dockerfile -- pasado
|
||||||
|
# explicito acá (-c) para no depender de que docker exec herede el WORKDIR
|
||||||
|
# de la imagen.
|
||||||
|
WORKDIR="/DATA/AppData/k3d-lab-v3/lab-cruzcloud"
|
||||||
|
|
||||||
if ! docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
if ! docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||||
echo "El contenedor '$CONTAINER' no esta corriendo. Verifica con 'docker ps -a'." >&2
|
echo "El contenedor '$CONTAINER' no esta corriendo. Verifica con 'docker ps -a'." >&2
|
||||||
@@ -17,4 +22,4 @@ if ! docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
docker exec -it "$CONTAINER" tmux attach -t "$SESSION" || \
|
docker exec -it "$CONTAINER" tmux attach -t "$SESSION" || \
|
||||||
docker exec -it "$CONTAINER" tmux new -s "$SESSION"
|
docker exec -it "$CONTAINER" tmux new -s "$SESSION" -c "$WORKDIR"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Imagen de tmux-host: sesion tmux persistente para Claude Code sobre el
|
||||||
|
# lab GitOps de cruzcloud. Reemplaza el `docker run ... alpine sh -c "apk
|
||||||
|
# add ... && npm install ..."` inline documentado antes en
|
||||||
|
# docs/arquitectura/sesion-tmux-persistente.md — mismo contenido, pero
|
||||||
|
# construido una vez como imagen en vez de reinstalar todo en cada arranque
|
||||||
|
# del contenedor.
|
||||||
|
FROM alpine:3.24
|
||||||
|
|
||||||
|
RUN 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 '*'
|
||||||
|
|
||||||
|
# Repo raiz del lab (CLAUDE.md, apps-registry/, platform-infra/, scripts/).
|
||||||
|
# docker exec hereda este WORKDIR como cwd por defecto, y ademas
|
||||||
|
# lab-session.sh y healthcheck.sh lo pasan explicito via `tmux ... -c`
|
||||||
|
# para no depender de ese comportamiento implicito.
|
||||||
|
WORKDIR /DATA/AppData/k3d-lab-v3/lab-cruzcloud
|
||||||
|
|
||||||
|
COPY healthcheck.sh /usr/local/bin/healthcheck.sh
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/healthcheck.sh /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Proceso principal (PID 1) del contenedor tmux-host: lanza el chequeo de
|
||||||
|
# salud en background y mantiene el contenedor vivo, igual que el
|
||||||
|
# `tail -f /dev/null` del `docker run` inline original.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
/usr/local/bin/healthcheck.sh &
|
||||||
|
|
||||||
|
exec tail -f /dev/null
|
||||||
Executable
+94
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Chequeo de salud de tmux-host, corre en background dentro del propio
|
||||||
|
# contenedor (ver entrypoint.sh). Cada HEALTHCHECK_INTERVAL_SEC (30 min
|
||||||
|
# por defecto):
|
||||||
|
#
|
||||||
|
# a) Conectividad de red real (curl a gitea.cruzcloud.net). Si falla,
|
||||||
|
# SOLO se registra en el log y se notifica por Telegram (si el canal
|
||||||
|
# del lab-agent ya esta configurado) -- nunca se intenta "arreglar"
|
||||||
|
# la red del host desde aca, eso puede ser un problema de
|
||||||
|
# hardware/BIOS fuera del alcance del contenedor.
|
||||||
|
# b) Existencia de la sesion tmux "lab". Si no existe (crash, o el
|
||||||
|
# contenedor se acaba de recrear), SI se recrea automaticamente --
|
||||||
|
# es solo recuperar el propio entorno de trabajo, no tocar
|
||||||
|
# infraestructura externa.
|
||||||
|
#
|
||||||
|
# Todo evento (chequeo ok, falla, accion tomada) queda en LOG_FILE, que
|
||||||
|
# vive bajo /DATA (persistente, sobrevive a recrear el contenedor) y no
|
||||||
|
# solo en el stdout del contenedor.
|
||||||
|
#
|
||||||
|
# set -u sin 'e': un check individual que falle no debe matar el loop
|
||||||
|
# completo -- el siguiente ciclo tiene que seguir corriendo igual.
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
WORKDIR="/DATA/AppData/k3d-lab-v3/lab-cruzcloud"
|
||||||
|
SESSION="lab"
|
||||||
|
LOG_DIR="/DATA/AppData/k3d-lab-v3/tmux-host/logs"
|
||||||
|
LOG_FILE="${LOG_DIR}/health.log"
|
||||||
|
# Reusa el mismo canal de Telegram que ya configuro scripts/lab-agent/ --
|
||||||
|
# una sola fuente de verdad para el bot token / chat id, en vez de
|
||||||
|
# duplicar credenciales entre los dos agentes del lab.
|
||||||
|
LAB_AGENT_ENV="${WORKDIR}/scripts/lab-agent/lab-agent.env"
|
||||||
|
NETWORK_CHECK_URL="https://gitea.cruzcloud.net/"
|
||||||
|
INTERVAL_SEC="${HEALTHCHECK_INTERVAL_SEC:-1800}"
|
||||||
|
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" >> "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_telegram() {
|
||||||
|
local title="$1" message="$2"
|
||||||
|
if [[ ! -f "$LAB_AGENT_ENV" ]]; then
|
||||||
|
log "NOTIFY omitida: no existe ${LAB_AGENT_ENV}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local token_file chat_id token resp
|
||||||
|
token_file="$(grep -E '^TELEGRAM_BOT_TOKEN_FILE=' "$LAB_AGENT_ENV" | cut -d= -f2-)"
|
||||||
|
chat_id="$(grep -E '^TELEGRAM_CHAT_ID=' "$LAB_AGENT_ENV" | cut -d= -f2-)"
|
||||||
|
if [[ -z "$token_file" || ! -r "$token_file" || -z "$chat_id" ]]; then
|
||||||
|
log "NOTIFY omitida: canal de Telegram del lab-agent no esta configurado todavia"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
token="$(<"$token_file")"
|
||||||
|
resp="$(curl -s -m 10 -X POST "https://api.telegram.org/bot${token}/sendMessage" \
|
||||||
|
--data-urlencode "chat_id=${chat_id}" \
|
||||||
|
--data-urlencode "text=🤖 tmux-host healthcheck
|
||||||
|
${title}
|
||||||
|
──────────────
|
||||||
|
${message}" 2>&1)"
|
||||||
|
token=""
|
||||||
|
[[ "$resp" == *'"ok":true'* ]] || log "WARN: fallo al notificar por Telegram: ${resp}"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_network() {
|
||||||
|
local code
|
||||||
|
code="$(curl -sL -o /dev/null -w '%{http_code}' --max-time 10 "$NETWORK_CHECK_URL" 2>/dev/null || echo "000")"
|
||||||
|
if [[ "$code" == "000" ]]; then
|
||||||
|
log "RED: sin conectividad real (curl ${NETWORK_CHECK_URL} sin respuesta). No se intenta corregir desde el contenedor."
|
||||||
|
notify_telegram "Sin conectividad" "curl ${NETWORK_CHECK_URL} no respondio desde tmux-host (posible falla de red del host/NAS, ej. hardware/BIOS). El contenedor NO intenta corregir esto, solo lo registra."
|
||||||
|
else
|
||||||
|
log "RED: ok (curl ${NETWORK_CHECK_URL} -> HTTP ${code})"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_tmux_session() {
|
||||||
|
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
||||||
|
log "TMUX: sesion '${SESSION}' ok"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
log "TMUX: sesion '${SESSION}' no existe -- recreando en ${WORKDIR}"
|
||||||
|
if tmux new-session -d -s "$SESSION" -c "$WORKDIR" 2>>"$LOG_FILE"; then
|
||||||
|
log "TMUX: sesion '${SESSION}' recreada exitosamente"
|
||||||
|
else
|
||||||
|
log "TMUX: ERROR al recrear la sesion '${SESSION}'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log "=== healthcheck inicio (intervalo=${INTERVAL_SEC}s) ==="
|
||||||
|
while true; do
|
||||||
|
check_network
|
||||||
|
check_tmux_session
|
||||||
|
sleep "$INTERVAL_SEC"
|
||||||
|
done
|
||||||
Reference in new issue
Block a user