Hub Streamlit — hosting compartido de reportes
Decomisionado el 2026-08-26. Confirmado por Esteban que ya no se usa, pero la baja
real (borrar trivasa-bi-dev/exploracion/layout-gastos-ctunlinux/streamlit_venv/) había
pasado sin detener streamlit-hub.service ni quitar la ruta del túnel — el unit
quedó en crash-loop indefinido (Restart=on-failure, RestartSec=5s) desde que el venv
dejó de existir, acumulando 30,344 reinicios (status=203/EXEC, “Unable to locate
executable”) para cuando se detectó. Mismo síntoma externo que el incidente de
2026-08-09 (502 en explore.frento.com.mx) pero causa distinta: esa vez era un
shebang con ruta vieja (venv seguía existiendo), esta vez el venv completo ya no está en
disco. Cierre real aplicado el 2026-08-26 con acceso SSH directo:
systemctl stop+disable streamlit-hub.service, más la línea de ingress quitada de
/etc/cloudflared/config.yml (ver ctunlinux § túnel) — explore.frento.com.mx
pasó de 502 a 404 limpio, mismo estado final que Superset. El resto de esta página
describe el servicio como corría mientras estuvo activo.
explore.frento.com.mx expone, en un solo túnel cloudflared y un solo puerto
(localhost:8501), todos los reportes Streamlit de Trivasa como páginas de una misma
app — en vez de abrir un hostname/puerto nuevo por reporte. El servicio systemd
streamlit-hub.service corre hub_app.py (repo layout-gastos-ctunlinux), que
usa st.navigation/st.Page de Streamlit para despachar a cada reporte por ruta.
cloudflared (explore.frento.com.mx) -> localhost:8501 -> streamlit-hub.service -> hub_app.py
├── Inicio
├── Layout de Gastos (layout-gastos-ctunlinux/streamlit_app.py)
└── Consumo Interno (consumo_interno_trazabilidad/streamlit_app.py)
Por qué un hub y no un servicio por reporte
Decisión explícita: agregar un reporte nuevo no debía abrir un puerto/hostname nuevo.
Antes de este hito, el proceso en 8501 corría directo
consumo_interno_trazabilidad/streamlit_app.py; al publicar Layout de Gastos se
reemplazó el entrypoint por hub_app.py, que expone ambos reportes como páginas de la
misma app en el mismo puerto — el patrón a seguir para cualquier reporte Streamlit
nuevo de Trivasa.
Cómo agregar un reporte nuevo al hub
- Referenciar el
streamlit_app.pydel reporte nuevo desdehub_app.py(ruta absoluta, víast.Page). - Envolver cualquier
st.set_page_config(...)del reporte nuevo entry/except st.errors.StreamlitAPIException— solo puede llamarse una vez por sesión, y el hub ya lo llama antes de despachar a cualquier página. Es el único cambio de código que necesita un reporte existente para integrarse al hub. - El venv que usa el servicio (
layout-gastos-ctunlinux/streamlit_venv) necesita las dependencias de todos los reportes servidos (streamlit, pandas, sqlalchemy, pymssql, openpyxl, …) — en producción no hay un venv por reporte. - Validar con
streamlit.testing.v1.AppTestcada reporte standalone, luego el servidor real en un puerto alterno (ej. 8511) concurla cada ruta, antes de tocar el servicio real en 8501.
Rollback
El unit file anterior a la migración al hub se conserva como backup (no se borró):
/etc/systemd/system/streamlit-consumo-interno.service.bak-pre-hub.
sudo systemctl stop streamlit-hub.service
sudo systemctl disable streamlit-hub.service
sudo cp /etc/systemd/system/streamlit-consumo-interno.service.bak-pre-hub \
/etc/systemd/system/streamlit-consumo-interno.service
sudo systemctl daemon-reload
sudo systemctl enable --now streamlit-consumo-interno.serviceIncidente: crash-loop de ~40 horas por ruta desactualizada (2026-08-09)
streamlit-hub.service estuvo en crash-loop (Restart=on-failure,
RestartSec=5) durante ~40 horas sin que nadie lo notara —
explore.frento.com.mx devolvía 502 todo ese tiempo. Se detectó recién en
una revisión de disco no relacionada, con el contador de reinicios de
systemd ya en 30,624.
Causa: en algún punto la carpeta del repo se renombró de layout-gastos a
layout-gastos-ctunlinux (nombre actual, correcto) sin actualizar dos cosas
que quedaron con la ruta vieja grabada:
- El propio
.service—WorkingDirectoryyExecStartapuntaban a.../exploracion/layout-gastos/streamlit_venv/bin/streamlit, un path que ya no existe (status=203/EXEC, “Unable to locate executable”). - El shebang de cada script en
streamlit_venv/bin/(streamlit,pip,pip3, etc.) —venvgraba la ruta absoluta al intérprete del venv como primera línea de cada script generado (#!/ruta/completa/al/venv/bin/python3) en el momento de crearlo. Renombrar la carpeta que contiene el venv no actualiza esos shebangs — corregir solo el.serviceno bastaba, seguía fallando con el mismo error porque el propiostreamlitbinario apuntaba a un intérprete inexistente.
Fix: corregido el path en /etc/systemd/system/streamlit-hub.service
(WorkingDirectory + ExecStart) y sed sobre todos los scripts de
streamlit_venv/bin/ para reescribir el shebang a la ruta actual —
alternativa más rápida que recrear el venv entero, funciona porque el
intérprete real (streamlit_venv/bin/python3) es un symlink a
/usr/bin/python3, no una instalación propia con dependencias distintas.
Cero pérdida de datos — la ruta vieja siempre había sido un nombre stale, no
una carpeta faltante.
Lección para cualquier futura reorganización de carpetas de este repo:
mover o renombrar un directorio que contiene un venv de Python rompe todos
sus scripts en bin/ en silencio (siguen ahí, con permisos de ejecución,
pero apuntan a un intérprete que ya no existe) — revisar
grep -rl "<ruta-vieja>" venv/bin/ después de cualquier rename, no solo los
.service/.env que referencian la carpeta desde afuera.
Incidente sin causa raíz confirmada (2026-07-26)
Durante la validación de la migración, streamlit-consumo-interno.service se detuvo
solo (log limpio: “Stopping…” → “Deactivated successfully”, exit code 0 — no fue un
crash) sin que apareciera ningún systemctl stop en el journal en ese rango. Se
restauró de inmediato y quedó sano. No se identificó causa concluyente — si vuelve a
pasar, vale la pena revisar si hay un límite de recursos o watchdog externo no
documentado en ninguno de los repos.
Véase también
- Layout de Gastos — uno de los dos reportes servidos por este hub.
- Trazabilidad Consumo Interno → Compras — el otro reporte servido por este hub.