feat: Implementación completa Fase 4 - Sistema de monitoreo con API REST y WebSocket
✨ Nuevas funcionalidades: - API REST unificada en puerto 8080 (eliminado CORS) - WebSocket para logs en tiempo real desde journalctl - Integración completa con systemd para gestión de servicios - Escaneo automático de procesos Node.js y Python - Rate limiting (1 operación/segundo por app) - Interface web moderna con Tailwind CSS (tema oscuro) - Documentación API estilo Swagger completamente en español 🎨 Interface Web (todas las páginas en español): - Dashboard con estadísticas en tiempo real - Visor de escaneo de procesos con filtros - Formulario de registro de aplicaciones con variables de entorno - Visor de logs en tiempo real con WebSocket y sidebar - Página de selección de apps detectadas - Documentación completa de API REST 🏗️ Arquitectura: - Módulo models: ServiceConfig, ManagedApp, AppStatus - Módulo systemd: wrapper de systemctl, generador de .service, parser - Módulo orchestrator: AppManager, LifecycleManager con validaciones - Módulo api: handlers REST, WebSocket manager, DTOs - Servidor unificado en puerto 8080 (Web + API + WS) 🔧 Mejoras técnicas: - Eliminación de CORS mediante servidor unificado - Separación clara frontend/backend con carga dinámica - Thread-safe con Arc<DashMap> para estado compartido - Reconciliación de estados: sysinfo vs systemd - Validaciones de paths, usuarios y configuraciones - Manejo robusto de errores con thiserror 📝 Documentación: - README.md actualizado con arquitectura completa - EJEMPLOS.md con casos de uso detallados - ESTADO_PROYECTO.md con progreso de Fase 4 - API docs interactiva en /api-docs - Script de despliegue mejorado con health checks 🚀 Producción: - Deployment script con validaciones - Health checks y rollback capability - Configuración de sudoers para systemctl - Hardening de seguridad en servicios systemd
This commit is contained in:
504
EJEMPLOS.md
Normal file
504
EJEMPLOS.md
Normal file
@@ -0,0 +1,504 @@
|
||||
# Ejemplos Prácticos de Uso - SIAX Monitor
|
||||
|
||||
## Tabla de Contenidos
|
||||
|
||||
1. [Instalación](#instalación)
|
||||
2. [Gestión de Aplicaciones Node.js](#nodejs)
|
||||
3. [Gestión de Aplicaciones Python](#python)
|
||||
4. [Monitoreo y Logs](#monitoreo)
|
||||
5. [Casos de Uso Reales](#casos-de-uso)
|
||||
6. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Instalación
|
||||
|
||||
### Instalación Completa
|
||||
|
||||
```bash
|
||||
# Clonar el proyecto
|
||||
git clone <repo-url>
|
||||
cd siax_monitor
|
||||
|
||||
# Ejecutar script de deployment
|
||||
sudo ./desplegar_agent.sh
|
||||
|
||||
# Verificar instalación
|
||||
sudo systemctl status siax-agent
|
||||
```
|
||||
|
||||
### Verificar que todo funciona
|
||||
|
||||
```bash
|
||||
# Verificar servicio
|
||||
curl http://localhost:8081/api/apps
|
||||
|
||||
# Debe responder:
|
||||
# {"success":true,"data":{"apps":[],"total":0}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## <a name="nodejs"></a>Gestión de Aplicaciones Node.js
|
||||
|
||||
### Ejemplo 1: API Express Básica
|
||||
|
||||
```bash
|
||||
# Registrar aplicación
|
||||
curl -X POST http://localhost:8081/api/apps \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"app_name": "api-express",
|
||||
"script_path": "/home/nodejs/api-express/server.js",
|
||||
"working_directory": "/home/nodejs/api-express",
|
||||
"user": "nodejs",
|
||||
"environment": {
|
||||
"PORT": "3000",
|
||||
"NODE_ENV": "production"
|
||||
},
|
||||
"restart_policy": "always",
|
||||
"app_type": "nodejs",
|
||||
"description": "API REST con Express"
|
||||
}'
|
||||
|
||||
# Iniciar aplicación
|
||||
curl -X POST http://localhost:8081/api/apps/api-express/start
|
||||
|
||||
# Ver estado
|
||||
curl http://localhost:8081/api/apps/api-express/status | jq
|
||||
```
|
||||
|
||||
### Ejemplo 2: Aplicación Next.js
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8081/api/apps \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"app_name": "frontend-nextjs",
|
||||
"script_path": "/var/www/frontend/.next/standalone/server.js",
|
||||
"working_directory": "/var/www/frontend",
|
||||
"user": "www-data",
|
||||
"environment": {
|
||||
"PORT": "3000",
|
||||
"NODE_ENV": "production",
|
||||
"HOSTNAME": "0.0.0.0"
|
||||
},
|
||||
"restart_policy": "always",
|
||||
"app_type": "nodejs"
|
||||
}'
|
||||
|
||||
curl -X POST http://localhost:8081/api/apps/frontend-nextjs/start
|
||||
```
|
||||
|
||||
### Ejemplo 3: Worker de Background (Bull Queue)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8081/api/apps \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"app_name": "queue-worker",
|
||||
"script_path": "/opt/workers/queue-worker/index.js",
|
||||
"working_directory": "/opt/workers/queue-worker",
|
||||
"user": "workers",
|
||||
"environment": {
|
||||
"REDIS_URL": "redis://localhost:6379",
|
||||
"CONCURRENCY": "5"
|
||||
},
|
||||
"restart_policy": "on-failure",
|
||||
"app_type": "nodejs",
|
||||
"description": "Worker de procesamiento de colas"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## <a name="python"></a>Gestión de Aplicaciones Python
|
||||
|
||||
### Ejemplo 1: API FastAPI
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8081/api/apps \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"app_name": "ml-api",
|
||||
"script_path": "/home/python/ml-api/main.py",
|
||||
"working_directory": "/home/python/ml-api",
|
||||
"user": "python",
|
||||
"environment": {
|
||||
"PORT": "8000",
|
||||
"WORKERS": "4",
|
||||
"PYTHONUNBUFFERED": "1"
|
||||
},
|
||||
"restart_policy": "always",
|
||||
"app_type": "python",
|
||||
"description": "API de Machine Learning"
|
||||
}'
|
||||
|
||||
curl -X POST http://localhost:8081/api/apps/ml-api/start
|
||||
```
|
||||
|
||||
**Nota:** Asegúrate de que tu `main.py` tiene un servidor ASGI:
|
||||
|
||||
```python
|
||||
# main.py
|
||||
from fastapi import FastAPI
|
||||
import uvicorn
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
port = int(os.getenv("PORT", 8000))
|
||||
workers = int(os.getenv("WORKERS", 4))
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=port, workers=workers)
|
||||
```
|
||||
|
||||
### Ejemplo 2: Script de Data Processing
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8081/api/apps \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"app_name": "data-processor",
|
||||
"script_path": "/opt/scripts/data-processor/processor.py",
|
||||
"working_directory": "/opt/scripts/data-processor",
|
||||
"user": "dataops",
|
||||
"environment": {
|
||||
"DB_HOST": "localhost",
|
||||
"DB_PORT": "5432",
|
||||
"LOG_LEVEL": "INFO"
|
||||
},
|
||||
"restart_policy": "on-failure",
|
||||
"app_type": "python"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## <a name="monitoreo"></a>Monitoreo y Logs
|
||||
|
||||
### Ver logs en tiempo real con WebSocket
|
||||
|
||||
#### Opción 1: JavaScript (Browser)
|
||||
|
||||
```javascript
|
||||
// Conectar a logs
|
||||
const ws = new WebSocket('ws://localhost:8081/ws/logs/api-express');
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('Conectado a logs de api-express');
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
console.log('LOG:', event.data);
|
||||
// Mostrar en UI
|
||||
document.getElementById('logs').innerHTML += event.data + '<br>';
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('Error:', error);
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('Desconectado');
|
||||
};
|
||||
```
|
||||
|
||||
#### Opción 2: wscat (CLI)
|
||||
|
||||
```bash
|
||||
# Instalar wscat
|
||||
npm install -g wscat
|
||||
|
||||
# Conectar a logs
|
||||
wscat -c ws://localhost:8081/ws/logs/api-express
|
||||
```
|
||||
|
||||
#### Opción 3: Python
|
||||
|
||||
```python
|
||||
import websocket
|
||||
import json
|
||||
|
||||
def on_message(ws, message):
|
||||
try:
|
||||
data = json.loads(message)
|
||||
print(f"[{data.get('timestamp', 'N/A')}] {data.get('MESSAGE', message)}")
|
||||
except:
|
||||
print(message)
|
||||
|
||||
def on_error(ws, error):
|
||||
print(f"Error: {error}")
|
||||
|
||||
def on_close(ws, close_status_code, close_msg):
|
||||
print("Conexión cerrada")
|
||||
|
||||
def on_open(ws):
|
||||
print("Conectado a logs")
|
||||
|
||||
ws = websocket.WebSocketApp(
|
||||
"ws://localhost:8081/ws/logs/api-express",
|
||||
on_open=on_open,
|
||||
on_message=on_message,
|
||||
on_error=on_error,
|
||||
on_close=on_close
|
||||
)
|
||||
|
||||
ws.run_forever()
|
||||
```
|
||||
|
||||
### Verificar estado de múltiples apps
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# check-all-apps.sh
|
||||
|
||||
echo "Estado de todas las aplicaciones:"
|
||||
echo "=================================="
|
||||
|
||||
# Obtener lista de apps
|
||||
apps=$(curl -s http://localhost:8081/api/apps | jq -r '.data.apps[]')
|
||||
|
||||
for app in $apps; do
|
||||
status=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.status')
|
||||
pid=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.pid')
|
||||
cpu=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.cpu_usage')
|
||||
mem=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.memory_usage')
|
||||
|
||||
printf "%-20s [%s] PID: %-6s CPU: %-6s RAM: %s\n" "$app" "$status" "$pid" "$cpu" "$mem"
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## <a name="casos-de-uso"></a>Casos de Uso Reales
|
||||
|
||||
### Caso 1: Microservicios
|
||||
|
||||
```bash
|
||||
# Registrar todos los microservicios
|
||||
|
||||
# Auth Service
|
||||
curl -X POST http://localhost:8081/api/apps -H "Content-Type: application/json" -d '{
|
||||
"app_name": "auth-service",
|
||||
"script_path": "/services/auth/index.js",
|
||||
"working_directory": "/services/auth",
|
||||
"user": "services",
|
||||
"environment": {"PORT": "3001", "NODE_ENV": "production"},
|
||||
"restart_policy": "always",
|
||||
"app_type": "nodejs"
|
||||
}'
|
||||
|
||||
# Users Service
|
||||
curl -X POST http://localhost:8081/api/apps -H "Content-Type: application/json" -d '{
|
||||
"app_name": "users-service",
|
||||
"script_path": "/services/users/index.js",
|
||||
"working_directory": "/services/users",
|
||||
"user": "services",
|
||||
"environment": {"PORT": "3002", "NODE_ENV": "production"},
|
||||
"restart_policy": "always",
|
||||
"app_type": "nodejs"
|
||||
}'
|
||||
|
||||
# Orders Service
|
||||
curl -X POST http://localhost:8081/api/apps -H "Content-Type: application/json" -d '{
|
||||
"app_name": "orders-service",
|
||||
"script_path": "/services/orders/index.js",
|
||||
"working_directory": "/services/orders",
|
||||
"user": "services",
|
||||
"environment": {"PORT": "3003", "NODE_ENV": "production"},
|
||||
"restart_policy": "always",
|
||||
"app_type": "nodejs"
|
||||
}'
|
||||
|
||||
# Iniciar todos
|
||||
for service in auth-service users-service orders-service; do
|
||||
curl -X POST http://localhost:8081/api/apps/$service/start
|
||||
echo "Iniciado: $service"
|
||||
done
|
||||
```
|
||||
|
||||
### Caso 2: Deployment con Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy-app.sh
|
||||
|
||||
APP_NAME=$1
|
||||
APP_PATH=$2
|
||||
PORT=$3
|
||||
|
||||
if [ -z "$APP_NAME" ] || [ -z "$APP_PATH" ] || [ -z "$PORT" ]; then
|
||||
echo "Uso: ./deploy-app.sh <nombre> <path> <puerto>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Desplegando $APP_NAME..."
|
||||
|
||||
# Registrar app
|
||||
curl -X POST http://localhost:8081/api/apps \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"app_name\": \"$APP_NAME\",
|
||||
\"script_path\": \"$APP_PATH/index.js\",
|
||||
\"working_directory\": \"$APP_PATH\",
|
||||
\"user\": \"nodejs\",
|
||||
\"environment\": {\"PORT\": \"$PORT\", \"NODE_ENV\": \"production\"},
|
||||
\"restart_policy\": \"always\",
|
||||
\"app_type\": \"nodejs\"
|
||||
}"
|
||||
|
||||
echo ""
|
||||
echo "Iniciando $APP_NAME..."
|
||||
curl -X POST http://localhost:8081/api/apps/$APP_NAME/start
|
||||
|
||||
echo ""
|
||||
echo "Estado de $APP_NAME:"
|
||||
curl http://localhost:8081/api/apps/$APP_NAME/status | jq
|
||||
```
|
||||
|
||||
**Uso:**
|
||||
```bash
|
||||
./deploy-app.sh mi-nueva-app /opt/apps/mi-nueva-app 3004
|
||||
```
|
||||
|
||||
### Caso 3: Rolling Restart
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# rolling-restart.sh - Reinicia servicios uno por uno
|
||||
|
||||
APPS=("auth-service" "users-service" "orders-service")
|
||||
|
||||
for app in "${APPS[@]}"; do
|
||||
echo "Reiniciando $app..."
|
||||
curl -X POST http://localhost:8081/api/apps/$app/restart
|
||||
|
||||
# Esperar que esté activo
|
||||
sleep 5
|
||||
|
||||
status=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.status')
|
||||
if [ "$status" = "running" ]; then
|
||||
echo "✅ $app reiniciado correctamente"
|
||||
else
|
||||
echo "❌ ERROR: $app no está corriendo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delay entre reinici os
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo "✅ Rolling restart completado"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## <a name="troubleshooting"></a>Troubleshooting
|
||||
|
||||
### Problema: App no inicia
|
||||
|
||||
```bash
|
||||
# 1. Ver estado en systemd
|
||||
sudo systemctl status mi-app.service
|
||||
|
||||
# 2. Ver logs de systemd
|
||||
sudo journalctl -u mi-app.service -n 100
|
||||
|
||||
# 3. Ver logs en tiempo real
|
||||
wscat -c ws://localhost:8081/ws/logs/mi-app
|
||||
|
||||
# 4. Verificar permisos del archivo
|
||||
ls -la /path/to/script.js
|
||||
|
||||
# 5. Probar ejecución manual
|
||||
sudo -u nodejs node /path/to/script.js
|
||||
```
|
||||
|
||||
### Problema: Rate limit excedido
|
||||
|
||||
```bash
|
||||
# Error: "Rate limit excedido para: mi-app"
|
||||
# Solución: Esperar 1 segundo entre operaciones
|
||||
|
||||
curl -X POST http://localhost:8081/api/apps/mi-app/restart
|
||||
sleep 2 # Esperar antes de la siguiente operación
|
||||
curl -X POST http://localhost:8081/api/apps/otra-app/restart
|
||||
```
|
||||
|
||||
### Problema: WebSocket no conecta
|
||||
|
||||
```bash
|
||||
# Verificar que el puerto está abierto
|
||||
sudo netstat -tlnp | grep 8081
|
||||
|
||||
# Verificar que hay menos de 5 conexiones activas
|
||||
# (límite por app)
|
||||
|
||||
# Probar con curl primero
|
||||
curl http://localhost:8081/api/apps
|
||||
```
|
||||
|
||||
### Problema: Permisos sudo
|
||||
|
||||
```bash
|
||||
# Verificar configuración
|
||||
sudo cat /etc/sudoers.d/siax-agent
|
||||
|
||||
# Probar manualmente
|
||||
sudo -u siax-agent sudo systemctl status siax-agent
|
||||
|
||||
# Si falla, re-ejecutar deployment
|
||||
sudo ./desplegar_agent.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scripts Útiles
|
||||
|
||||
### Monitor Dashboard (Bash)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# dashboard.sh - Dashboard simple en terminal
|
||||
|
||||
watch -n 2 '
|
||||
echo "=== SIAX Monitor Dashboard ==="
|
||||
echo ""
|
||||
curl -s http://localhost:8081/api/apps | jq -r ".data.apps[]" | while read app; do
|
||||
status=$(curl -s http://localhost:8081/api/apps/$app/status)
|
||||
echo "$status" | jq -r "\"[\(.data.status)] \(.data.name) - PID: \(.data.pid) CPU: \(.data.cpu_usage) RAM: \(.data.memory_usage)\""
|
||||
done
|
||||
'
|
||||
```
|
||||
|
||||
### Auto-Restart on Crash
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# auto-restart.sh - Reinicia apps crasheadas automáticamente
|
||||
|
||||
while true; do
|
||||
apps=$(curl -s http://localhost:8081/api/apps | jq -r '.data.apps[]')
|
||||
|
||||
for app in $apps; do
|
||||
status=$(curl -s http://localhost:8081/api/apps/$app/status | jq -r '.data.status')
|
||||
|
||||
if [ "$status" = "crashed" ] || [ "$status" = "failed" ]; then
|
||||
echo "[$(date)] Detectado $app en estado $status, reiniciando..."
|
||||
curl -X POST http://localhost:8081/api/apps/$app/restart
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
|
||||
sleep 30
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
¡Estos ejemplos cubren los casos de uso más comunes! Para más información, consulta el `README.md`.
|
||||
Reference in New Issue
Block a user