Files
Lizandro GuarnizoandClaude Sonnet 4.6 66088ce691 feat(agent): auto-renice top CPU process after 2 consecutive high-CPU reports
- Agent tracks consecutive reports above cpu_threshold (default 90%)
- After renice_consecutive reports (default 2 = 60s sustained), runs
  renice -n 19 on the top CPU process
- Reports the action back in the heartbeat payload
- Backend sends Telegram alert when renice is applied
- All params configurable in agent.yml and install.sh flags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 11:17:07 -05:00

225 lines
8.3 KiB
Bash

#!/usr/bin/env bash
# install.sh — Instalador inteligente del agente usite-agent
# Se adapta automáticamente al entorno: VPS root, contenedor, usuario sin root, etc.
#
# Uso:
# curl -sL https://admin.u-site.app/agent/install.sh | bash -s -- --token=TU_TOKEN --url=https://admin.u-site.app
# bash install.sh --token=TU_TOKEN --url=https://admin.u-site.app [--interval=30] [--dir=/opt/usite-agent]
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
TOKEN=""
API_URL=""
INTERVAL=30
INSTALL_DIR=""
CPU_THRESHOLD=90
RENICE_ENABLED=true
RENICE_CONSECUTIVE=2
for arg in "$@"; do
case $arg in
--token=*) TOKEN="${arg#*=}" ;;
--url=*) API_URL="${arg#*=}" ;;
--interval=*) INTERVAL="${arg#*=}" ;;
--dir=*) INSTALL_DIR="${arg#*=}" ;;
--cpu-threshold=*) CPU_THRESHOLD="${arg#*=}" ;;
--renice=*) RENICE_ENABLED="${arg#*=}" ;;
--renice-consecutive=*) RENICE_CONSECUTIVE="${arg#*=}" ;;
esac
done
[[ -z "$TOKEN" ]] && error "Falta --token=TU_TOKEN"
[[ -z "$API_URL" ]] && error "Falta --url=https://admin.u-site.app"
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
case $ARCH in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
armv7l) ARCH="arm" ;;
*) error "Arquitectura no soportada: $ARCH" ;;
esac
BINARY_NAME="usite-agent-${OS}-${ARCH}"
DOWNLOAD_URL="${API_URL}/agent/download/${BINARY_NAME}"
IS_ROOT=0
[[ "$(id -u)" == "0" ]] && IS_ROOT=1
# ── Detectar entorno ──────────────────────────────────────────────────────────
ENV_TYPE="vps"
IS_CONTAINER=0
if [[ -f "/run/.containerenv" ]] || [[ -f "/.dockerenv" ]]; then
IS_CONTAINER=1; ENV_TYPE="contenedor"
fi
if [[ -n "${DOCKER_HOST:-}" ]] || [[ -n "${KUBERNETES_SERVICE_HOST:-}" ]]; then
IS_CONTAINER=1; ENV_TYPE="contenedor"
fi
if [[ "$IS_ROOT" == 0 ]]; then
ENV_TYPE="usuario"
fi
# ── Directorio ─────────────────────────────────────────────────────────────────
if [[ -z "$INSTALL_DIR" ]]; then
if [[ "$IS_ROOT" == 1 ]]; then
INSTALL_DIR="/opt/usite-agent"
else
INSTALL_DIR="${HOME}/.usite-agent"
fi
fi
info "Entorno: ${ENV_TYPE}${IS_CONTAINER:+(contenedor)}"
info "Sistema: ${OS}/${ARCH}"
info "Directorio: ${INSTALL_DIR}"
info "Intervalo: ${INTERVAL}s"
# ── Instalar binario ──────────────────────────────────────────────────────────
mkdir -p "$INSTALL_DIR" || error "No se pudo crear directorio ${INSTALL_DIR} (¿permisos?)"
# Verificar espacio disponible (necesita al menos 20 MB)
AVAIL_KB=$(df -k "$INSTALL_DIR" 2>/dev/null | awk 'NR==2 {print $4}')
if [[ -n "$AVAIL_KB" && "$AVAIL_KB" -lt 20480 ]]; then
error "Espacio insuficiente en ${INSTALL_DIR}: solo ${AVAIL_KB} KB disponibles (se necesitan ≥20 MB)"
fi
info "Descargando agente desde ${DOWNLOAD_URL}..."
DEST="${INSTALL_DIR}/usite-agent"
TMP_DEST="/tmp/usite-agent-download-$$"
if command -v curl &>/dev/null; then
curl -fsSL "$DOWNLOAD_URL" -o "$TMP_DEST" 2>/tmp/curl_err || {
CURL_ERR=$(cat /tmp/curl_err 2>/dev/null)
rm -f "$TMP_DEST"
error "No se pudo descargar: ${CURL_ERR}"
}
elif command -v wget &>/dev/null; then
wget -q "$DOWNLOAD_URL" -O "$TMP_DEST" || { rm -f "$TMP_DEST"; error "No se pudo descargar con wget"; }
else
error "Se requiere curl o wget"
fi
if [[ ! -s "$TMP_DEST" ]]; then
rm -f "$TMP_DEST"
error "El archivo descargado está vacío"
fi
mv "$TMP_DEST" "$DEST" || { cp "$TMP_DEST" "$DEST" && rm -f "$TMP_DEST"; } || error "No se pudo mover el binario a ${DEST}"
chmod +x "$DEST"
ok "Binario descargado ($(du -sh "$DEST" | cut -f1))"
# ── Configuración ─────────────────────────────────────────────────────────────
cat > "${INSTALL_DIR}/agent.yml" <<EOF
api_url: ${API_URL}
token: ${TOKEN}
interval: ${INTERVAL}
debug: false
cpu_threshold: ${CPU_THRESHOLD}
renice_enabled: ${RENICE_ENABLED}
renice_consecutive: ${RENICE_CONSECUTIVE}
EOF
ok "Configuración creada en ${INSTALL_DIR}/agent.yml"
# ── Auto-arranque inteligente ─────────────────────────────────────────────────
STARTED=0
# 1) systemd (VPS con root)
if [[ "$IS_ROOT" == 1 ]] && command -v systemctl &>/dev/null; then
cat > /etc/systemd/system/usite-agent.service <<EOF
[Unit]
Description=u-site Server Monitoring Agent
After=network.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=${INSTALL_DIR}/usite-agent --config=${INSTALL_DIR}/agent.yml
WorkingDirectory=${INSTALL_DIR}
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=usite-agent
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable usite-agent
if systemctl is-active --quiet usite-agent; then
systemctl restart usite-agent
ok "Servicio systemd 'usite-agent' reiniciado con nueva configuración"
else
systemctl start usite-agent
ok "Servicio systemd 'usite-agent' instalado y activo"
fi
STARTED=1
# 2) openrc (Alpine, algunos VPS)
elif [[ "$IS_ROOT" == 1 ]] && command -v rc-update &>/dev/null; then
cat > /etc/init.d/usite-agent <<EOF
#!/sbin/openrc-run
description="u-site Server Monitoring Agent"
command="${INSTALL_DIR}/usite-agent"
command_args="--config=${INSTALL_DIR}/agent.yml"
command_background=true
pidfile="/run/usite-agent.pid"
EOF
chmod +x /etc/init.d/usite-agent
rc-update add usite-agent default
rc-service usite-agent start
ok "Servicio openrc 'usite-agent' instalado y activo"
STARTED=1
# 3) Docker / contenedor — ejecución en foreground con restart automático
elif [[ "$IS_CONTAINER" == 1 ]]; then
warn "Entorno contenedor detectado. El agente se ejecutará en primer plano."
warn "Agrega al entrypoint de tu contenedor:"
warn " ${INSTALL_DIR}/usite-agent --config=${INSTALL_DIR}/agent.yml &"
STARTED=1
# 4) Usuario sin root — crontab para auto-arranque
else
CRON_JOB="@reboot ${INSTALL_DIR}/usite-agent --config=${INSTALL_DIR}/agent.yml >/dev/null 2>&1 &"
if crontab -l 2>/dev/null | grep -q "usite-agent"; then
warn "Crontab ya tiene una entrada para usite-agent, se omite"
else
(crontab -l 2>/dev/null || true; echo "$CRON_JOB") | crontab -
ok "Entrada @reboot agregada al crontab"
fi
nohup "${INSTALL_DIR}/usite-agent" --config="${INSTALL_DIR}/agent.yml" >/dev/null 2>&1 &
ok "Agente iniciado en background (PID $!)"
STARTED=1
fi
# ── Resumen ────────────────────────────────────────────────────────────────────
echo ""
if [[ "$STARTED" == 1 ]]; then
ok "usite-agent instalado correctamente en ${INSTALL_DIR}"
info "El agente reportará métricas a ${API_URL} cada ${INTERVAL}s"
info ""
info "Comandos útiles:"
if [[ "$IS_ROOT" == 1 ]] && command -v systemctl &>/dev/null; then
info " systemctl status usite-agent"
info " journalctl -u usite-agent -f"
fi
info " ${INSTALL_DIR}/usite-agent --config=${INSTALL_DIR}/agent.yml (ejecutar manual)"
info ""
info "Para desinstalar:"
if [[ "$IS_ROOT" == 1 ]] && command -v systemctl &>/dev/null; then
info " systemctl disable usite-agent && rm /etc/systemd/system/usite-agent.service && rm -rf ${INSTALL_DIR}"
elif [[ "$IS_ROOT" == 1 ]] && command -v rc-update &>/dev/null; then
info " rc-update del usite-agent && rm /etc/init.d/usite-agent && rm -rf ${INSTALL_DIR}"
else
info " crontab -l | grep -v usite-agent | crontab - && rm -rf ${INSTALL_DIR}"
fi
else
warn "No se pudo configurar auto-arranque. Ejecuta manualmente:"
warn " ${INSTALL_DIR}/usite-agent --config=${INSTALL_DIR}/agent.yml &"
fi