Monitoreo con Scripts Bash Personalizados
Introducción
Si bien las soluciones de monitoreo empresariales como Prometheus, Nagios y Zabbix ofrecen características completas, los scripts Bash personalizados proporcionan una alternativa ligera, flexible y rentable para monitorear servidores Linux. Los scripts Bash pueden adaptarse a las necesidades específicas de tu infraestructura, desplegarse instantáneamente sin dependencias e integrarse sin problemas con herramientas y flujos de trabajo existentes.
Los scripts de monitoreo personalizados destacan en escenarios donde necesitas un despliegue rápido, métricas específicas no cubiertas por herramientas estándar o monitoreo ligero para entornos con recursos limitados. Son particularmente valiosos para monitorear aplicaciones personalizadas, aplicar lógica de negocio específica y crear soluciones de monitoreo que se integren directamente con tus sistemas de notificación.
Esta guía completa te enseña cómo construir scripts de monitoreo robustos, desde verificaciones simples de recursos hasta sistemas avanzados de monitoreo multi-servidor. Aprenderás a monitorear CPU, memoria, disco, red, servicios y métricas de aplicaciones personalizadas, implementar mecanismos de alerta, crear dashboards y seguir mejores prácticas para scripts de monitoreo mantenibles y listos para producción.
Requisitos Previos
Antes de construir scripts de monitoreo personalizados, asegúrate de tener:
- Un servidor Linux (Ubuntu 20.04/22.04, Debian 10/11, CentOS 7/8, Rocky Linux 8/9 o similar)
- Acceso root o sudo para monitoreo a nivel de sistema
- Conocimiento básico a intermedio de scripting en Bash
- Familiaridad con comandos del sistema Linux
- Editor de texto (nano, vim o vi)
Conocimiento Recomendado:
- Comprensión de gestión de procesos
- Conocimiento básico de awk, grep y sed
- Familiaridad con cron para programación
- Comprensión de códigos de salida y manejo de errores
Herramientas Opcionales:
- Agente de transferencia de correo (para alertas por email)
- curl o wget (para notificaciones webhook)
- bc (para cálculos de punto flotante)
- jq (para procesamiento JSON)
Estructura Básica de Scripts de Monitoreo
Componentes Esenciales
Un script de monitoreo bien estructurado debe incluir:
#!/bin/bash
#
# Nombre del Script: monitor-system.sh
# Descripción: Script básico de monitoreo del sistema
# Autor: Tu Nombre
# Fecha: 2024-01-11
# Versión: 1.0
# Salir en caso de error
set -e
# Configuración
LOG_FILE="/var/log/monitoring/system-monitor.log"
ALERT_EMAIL="[email protected]"
HOSTNAME=$(hostname)
# Umbrales
CPU_THRESHOLD=80
MEMORY_THRESHOLD=85
DISK_THRESHOLD=90
# Funciones
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
send_alert() {
local subject="$1"
local message="$2"
echo "$message" | mail -s "$subject" "$ALERT_EMAIL"
log_message "Alerta enviada: $subject"
}
# Lógica principal de monitoreo
main() {
log_message "Iniciando monitoreo del sistema"
# Las verificaciones de monitoreo van aquí
log_message "Monitoreo completado"
}
# Ejecutar función principal
main "$@"
Manejo de Errores y Registro
#!/bin/bash
# Manejo de errores mejorado
set -euo pipefail # Salir en error, variables indefinidas, fallos de pipe
# Trampa de errores
trap 'error_handler $? $LINENO' ERR
error_handler() {
local exit_code=$1
local line_number=$2
log_message "ERROR: Script falló en la línea $line_number con código de salida $exit_code"
send_alert "Script de Monitoreo Falló" \
"El script falló en $HOSTNAME en la línea $line_number"
exit "$exit_code"
}
# Registro con niveles
log_level() {
local level=$1
shift
local message="$*"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a "$LOG_FILE"
}
log_info() { log_level "INFO" "$@"; }
log_warn() { log_level "WARN" "$@"; }
log_error() { log_level "ERROR" "$@"; }
Scripts de Monitoreo de CPU
Verificación Básica de Uso de CPU
#!/bin/bash
# cpu-monitor.sh - Monitorear uso de CPU
CPU_THRESHOLD=80
LOG_FILE="/var/log/monitoring/cpu.log"
# Obtener uso de CPU (porcentaje de tiempo inactivo restado de 100)
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
CPU_USAGE_INT=${CPU_USAGE%.*} # Convertir a entero
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log_message "Uso actual de CPU: ${CPU_USAGE}%"
if [ "$CPU_USAGE_INT" -gt "$CPU_THRESHOLD" ]; then
log_message "ADVERTENCIA: Uso de CPU por encima del umbral (${CPU_USAGE}% > ${CPU_THRESHOLD}%)"
# Obtener principales consumidores de CPU
TOP_PROCESSES=$(ps aux --sort=-%cpu | head -n 6)
# Enviar alerta
{
echo "Uso alto de CPU detectado en $(hostname)"
echo "Uso actual: ${CPU_USAGE}%"
echo "Umbral: ${CPU_THRESHOLD}%"
echo ""
echo "Principales consumidores de CPU:"
echo "$TOP_PROCESSES"
} | mail -s "Alerta de CPU: $(hostname)" [email protected]
fi
Monitoreo Avanzado de CPU con Análisis por Núcleo
#!/bin/bash
# cpu-detailed-monitor.sh - Monitoreo detallado de CPU
monitor_cpu_cores() {
local threshold=$1
# Obtener uso de CPU por núcleo
mpstat -P ALL 1 1 | tail -n +4 | while read line; do
if [[ $line =~ ^[0-9] ]]; then
core=$(echo "$line" | awk '{print $2}')
idle=$(echo "$line" | awk '{print $NF}')
usage=$(echo "100 - $idle" | bc)
usage_int=${usage%.*}
echo "Núcleo $core: ${usage}%"
if (( $(echo "$usage_int > $threshold" | bc -l) )); then
echo "ADVERTENCIA: Uso del núcleo $core: ${usage}%"
fi
fi
done
}
# Obtener promedios de carga
get_load_average() {
local load_1min load_5min load_15min
read load_1min load_5min load_15min < /proc/loadavg
# Obtener número de núcleos de CPU
local cpu_cores=$(nproc)
# Calcular carga por núcleo
local load_per_core=$(echo "scale=2; $load_1min / $cpu_cores" | bc)
echo "Promedio de Carga: $load_1min (1m), $load_5min (5m), $load_15min (15m)"
echo "Núcleos de CPU: $cpu_cores"
echo "Carga por Núcleo: $load_per_core"
# Alertar si la carga por núcleo excede 1.0
if (( $(echo "$load_per_core > 1.0" | bc -l) )); then
echo "ADVERTENCIA: Alta carga por núcleo: $load_per_core"
return 1
fi
}
# Ejecución principal
echo "=== Reporte de Monitoreo de CPU ==="
echo "Marca de tiempo: $(date)"
echo ""
echo "--- Promedio de Carga ---"
get_load_average
echo ""
echo "--- Uso por Núcleo ---"
monitor_cpu_cores 80
Monitoreo de Tiempo de Espera de CPU
#!/bin/bash
# cpu-iowait-monitor.sh - Monitorear tiempo de espera de E/S
IOWAIT_THRESHOLD=20
# Obtener porcentaje de espera de E/S
get_iowait() {
iostat -c 1 2 | tail -1 | awk '{print $4}'
}
IOWAIT=$(get_iowait)
IOWAIT_INT=${IOWAIT%.*}
echo "Espera de E/S actual: ${IOWAIT}%"
if [ "$IOWAIT_INT" -gt "$IOWAIT_THRESHOLD" ]; then
echo "ADVERTENCIA: Alta espera de E/S detectada"
# Identificar procesos intensivos en E/S
echo ""
echo "Principales procesos de E/S:"
sudo iotop -b -n 1 -o | head -n 20
# Verificar estadísticas de disco
echo ""
echo "Estadísticas de E/S de disco:"
iostat -x 1 2 | tail -n +4
fi
Scripts de Monitoreo de Memoria
Monitor Completo de Memoria
#!/bin/bash
# memory-monitor.sh - Monitoreo completo de memoria
MEMORY_THRESHOLD=85
SWAP_THRESHOLD=50
LOG_FILE="/var/log/monitoring/memory.log"
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Obtener estadísticas de memoria
get_memory_stats() {
local total used free shared buffers cached available
read -r _ total used free shared buffers cached <<< \
"$(free -m | grep Mem:)"
available=$(free -m | grep Mem: | awk '{print $7}')
local mem_percent=$(echo "scale=2; ($used / $total) * 100" | bc)
local mem_percent_int=${mem_percent%.*}
echo "Total: ${total}MB"
echo "Usado: ${used}MB"
echo "Libre: ${free}MB"
echo "Disponible: ${available}MB"
echo "Uso: ${mem_percent}%"
if [ "$mem_percent_int" -gt "$MEMORY_THRESHOLD" ]; then
log_message "ADVERTENCIA: Uso de memoria ${mem_percent}% excede umbral ${MEMORY_THRESHOLD}%"
return 1
fi
return 0
}
# Obtener estadísticas de swap
get_swap_stats() {
local total used free
read -r _ total used free <<< "$(free -m | grep Swap:)"
if [ "$total" -eq 0 ]; then
echo "Swap: No configurado"
return 0
fi
local swap_percent=$(echo "scale=2; ($used / $total) * 100" | bc)
local swap_percent_int=${swap_percent%.*}
echo "Total de Swap: ${total}MB"
echo "Swap Usado: ${used}MB"
echo "Uso de Swap: ${swap_percent}%"
if [ "$swap_percent_int" -gt "$SWAP_THRESHOLD" ]; then
log_message "ADVERTENCIA: Uso de swap ${swap_percent}% excede umbral ${SWAP_THRESHOLD}%"
return 1
fi
return 0
}
# Obtener principales consumidores de memoria
get_top_memory_processes() {
echo "Top 10 Procesos que Consumen Memoria:"
ps aux --sort=-%mem | head -n 11 | awk '{printf "%-10s %-8s %-8s %s\n", $1, $2, $4"%", $11}'
}
# Verificar fugas de memoria
check_memory_growth() {
local current_usage previous_usage
# Obtener uso de memoria actual
current_usage=$(free -m | grep Mem: | awk '{print $3}')
# Leer uso anterior si existe
if [ -f /tmp/memory_usage.tmp ]; then
previous_usage=$(cat /tmp/memory_usage.tmp)
local growth=$((current_usage - previous_usage))
echo "Crecimiento de memoria desde última verificación: ${growth}MB"
if [ "$growth" -gt 500 ]; then
log_message "ADVERTENCIA: Crecimiento rápido de memoria detectado: ${growth}MB"
fi
fi
# Guardar uso actual
echo "$current_usage" > /tmp/memory_usage.tmp
}
# Ejecución principal
main() {
log_message "Iniciando monitoreo de memoria"
echo "=== Reporte de Monitoreo de Memoria ==="
echo "Marca de tiempo: $(date)"
echo ""
echo "--- Memoria Física ---"
if ! get_memory_stats; then
get_top_memory_processes
echo "" | mail -s "Alerta de Memoria: $(hostname)" [email protected]
fi
echo ""
echo "--- Memoria Swap ---"
get_swap_stats
echo ""
check_memory_growth
echo ""
get_top_memory_processes
}
main
Detección de OOM Killer
#!/bin/bash
# oom-monitor.sh - Detectar eventos del Out Of Memory killer
LOG_FILE="/var/log/monitoring/oom.log"
# Verificar eventos del OOM killer
check_oom_events() {
local oom_events
# Buscar eventos OOM en logs del kernel de la última hora
oom_events=$(journalctl -k --since "1 hour ago" | grep -i "Out of memory\|oom-killer\|Killed process")
if [ -n "$oom_events" ]; then
echo "Eventos del OOM Killer Detectados:"
echo "$oom_events"
# Registrar el evento
echo "[$(date)] Eventos OOM detectados" >> "$LOG_FILE"
echo "$oom_events" >> "$LOG_FILE"
# Enviar alerta
{
echo "Eventos Out Of Memory detectados en $(hostname)"
echo "Hora: $(date)"
echo ""
echo "Eventos:"
echo "$oom_events"
echo ""
echo "Estado Actual de Memoria:"
free -h
echo ""
echo "Principales Consumidores de Memoria:"
ps aux --sort=-%mem | head -n 11
} | mail -s "Alerta OOM: $(hostname)" [email protected]
return 1
fi
return 0
}
check_oom_events
Scripts de Monitoreo de Disco
Monitor de Espacio en Disco
#!/bin/bash
# disk-monitor.sh - Monitorear uso de espacio en disco
DISK_THRESHOLD=85
LOG_FILE="/var/log/monitoring/disk.log"
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Verificar uso de disco para todos los sistemas de archivos montados
check_disk_usage() {
local alert_triggered=0
df -h | tail -n +2 | while read filesystem size used avail percent mountpoint; do
# Eliminar signo % del porcentaje
usage=${percent%\%}
echo "Verificando $mountpoint: $percent usado"
if [ "$usage" -gt "$DISK_THRESHOLD" ]; then
log_message "ADVERTENCIA: $mountpoint está ${percent} lleno"
alert_triggered=1
# Encontrar directorios más grandes
echo "Directorios más grandes en $mountpoint:"
du -h "$mountpoint" 2>/dev/null | sort -rh | head -n 10
# Enviar alerta
{
echo "Alerta de espacio en disco en $(hostname)"
echo "Punto de montaje: $mountpoint"
echo "Uso: $percent"
echo "Umbral: ${DISK_THRESHOLD}%"
echo ""
echo "Directorios más grandes:"
du -h "$mountpoint" 2>/dev/null | sort -rh | head -n 10
} | mail -s "Alerta de Espacio en Disco: $mountpoint" [email protected]
fi
done
return $alert_triggered
}
# Verificar uso de inodos
check_inode_usage() {
local inode_threshold=85
df -i | tail -n +2 | while read filesystem inodes iused ifree ipercent mountpoint; do
# Eliminar signo %
usage=${ipercent%\%}
if [ "$usage" -gt "$inode_threshold" ]; then
log_message "ADVERTENCIA: Uso de inodos en $mountpoint es ${ipercent}"
{
echo "Alerta de uso de inodos en $(hostname)"
echo "Punto de montaje: $mountpoint"
echo "Uso de inodos: $ipercent"
echo ""
echo "Directorios con más archivos:"
find "$mountpoint" -xdev -type d -exec sh -c \
'echo $(find "{}" -maxdepth 1 | wc -l) "{}"' \; 2>/dev/null | \
sort -rn | head -n 10
} | mail -s "Alerta de Inodos: $mountpoint" [email protected]
fi
done
}
# Verificar directorios de crecimiento rápido
check_directory_growth() {
local watch_dirs=("/var/log" "/tmp" "/var/tmp" "/home")
for dir in "${watch_dirs[@]}"; do
if [ ! -d "$dir" ]; then
continue
fi
current_size=$(du -sm "$dir" 2>/dev/null | awk '{print $1}')
state_file="/tmp/dirsize_$(echo $dir | tr '/' '_').tmp"
if [ -f "$state_file" ]; then
previous_size=$(cat "$state_file")
growth=$((current_size - previous_size))
if [ "$growth" -gt 1000 ]; then # Alertar si creció >1GB
log_message "ADVERTENCIA: $dir creció ${growth}MB"
{
echo "Crecimiento rápido de directorio en $(hostname)"
echo "Directorio: $dir"
echo "Crecimiento: ${growth}MB"
echo ""
echo "Subdirectorios más grandes:"
du -h "$dir" 2>/dev/null | sort -rh | head -n 10
} | mail -s "Alerta de Crecimiento de Directorio: $dir" [email protected]
fi
fi
echo "$current_size" > "$state_file"
done
}
# Ejecución principal
main() {
log_message "Iniciando monitoreo de disco"
echo "=== Monitoreo de Espacio en Disco ==="
check_disk_usage
echo ""
echo "=== Uso de Inodos ==="
check_inode_usage
echo ""
echo "=== Verificación de Crecimiento de Directorio ==="
check_directory_growth
}
main
Monitor de Rendimiento de E/S de Disco
#!/bin/bash
# disk-io-monitor.sh - Monitorear rendimiento de E/S de disco
IOPS_THRESHOLD=1000
LATENCY_THRESHOLD=100 # milisegundos
# Obtener estadísticas de E/S de disco
get_disk_io_stats() {
iostat -x 1 2 | tail -n +4 | tail -n +7 | while read line; do
device=$(echo "$line" | awk '{print $1}')
tps=$(echo "$line" | awk '{print $2}')
read_kb=$(echo "$line" | awk '{print $3}')
write_kb=$(echo "$line" | awk '{print $4}')
await=$(echo "$line" | awk '{print $10}')
echo "Dispositivo: $device"
echo " Transacciones/seg: $tps"
echo " Lectura: ${read_kb} kB/s"
echo " Escritura: ${write_kb} kB/s"
echo " Await: ${await} ms"
# Verificar umbrales
if (( $(echo "$await > $LATENCY_THRESHOLD" | bc -l) )); then
echo " ADVERTENCIA: Alta latencia detectada"
# Obtener procesos haciendo E/S
sudo iotop -b -n 1 -o | head -n 20
fi
done
}
get_disk_io_stats
Scripts de Monitoreo de Red
Monitor de Tráfico de Red
#!/bin/bash
# network-monitor.sh - Monitorear tráfico de red y conexiones
BANDWIDTH_THRESHOLD=80 # Porcentaje de capacidad de la interfaz
LOG_FILE="/var/log/monitoring/network.log"
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Monitorear tráfico de interfaz de red
monitor_interface_traffic() {
local interface=$1
# Obtener estadísticas de interfaz
rx_bytes_before=$(cat /sys/class/net/"$interface"/statistics/rx_bytes)
tx_bytes_before=$(cat /sys/class/net/"$interface"/statistics/tx_bytes)
sleep 1
rx_bytes_after=$(cat /sys/class/net/"$interface"/statistics/rx_bytes)
tx_bytes_after=$(cat /sys/class/net/"$interface"/statistics/tx_bytes)
# Calcular bytes por segundo
rx_bps=$((rx_bytes_after - rx_bytes_before))
tx_bps=$((tx_bytes_after - tx_bytes_before))
# Convertir a Mbps
rx_mbps=$(echo "scale=2; $rx_bps * 8 / 1000000" | bc)
tx_mbps=$(echo "scale=2; $tx_bps * 8 / 1000000" | bc)
echo "Interfaz: $interface"
echo " RX: ${rx_mbps} Mbps"
echo " TX: ${tx_mbps} Mbps"
}
# Verificar cantidad de conexiones
check_connection_count() {
local established=$(ss -tan | grep ESTAB | wc -l)
local time_wait=$(ss -tan | grep TIME-WAIT | wc -l)
local total=$(ss -tan | tail -n +2 | wc -l)
echo "Estadísticas de Conexión:"
echo " Total: $total"
echo " Establecidas: $established"
echo " Time-Wait: $time_wait"
if [ "$established" -gt 1000 ]; then
log_message "ADVERTENCIA: Alto número de conexiones establecidas: $established"
# Mostrar principales fuentes de conexión
echo " Principales fuentes de conexión:"
ss -tan | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -n 10
fi
}
# Verificar errores de red
check_network_errors() {
local interface=$1
local rx_errors=$(cat /sys/class/net/"$interface"/statistics/rx_errors)
local tx_errors=$(cat /sys/class/net/"$interface"/statistics/tx_errors)
local rx_dropped=$(cat /sys/class/net/"$interface"/statistics/rx_dropped)
local tx_dropped=$(cat /sys/class/net/"$interface"/statistics/tx_dropped)
echo "Errores de interfaz $interface:"
echo " Errores RX: $rx_errors"
echo " Errores TX: $tx_errors"
echo " RX descartados: $rx_dropped"
echo " TX descartados: $tx_dropped"
if [ "$rx_errors" -gt 100 ] || [ "$tx_errors" -gt 100 ]; then
log_message "ADVERTENCIA: Errores de red detectados en $interface"
fi
}
# Monitorear disponibilidad de puertos
check_port_listening() {
local ports=("80" "443" "22" "3306")
echo "Disponibilidad de Puertos:"
for port in "${ports[@]}"; do
if ss -tuln | grep -q ":$port "; then
echo " Puerto $port: ESCUCHANDO"
else
echo " Puerto $port: NO ESCUCHANDO"
log_message "ADVERTENCIA: Puerto $port no está escuchando"
fi
done
}
# Ejecución principal
main() {
local interface=${1:-eth0}
echo "=== Reporte de Monitoreo de Red ==="
echo "Marca de tiempo: $(date)"
echo ""
echo "--- Tráfico de Interfaz ---"
monitor_interface_traffic "$interface"
echo ""
echo "--- Estadísticas de Conexión ---"
check_connection_count
echo ""
echo "--- Errores de Red ---"
check_network_errors "$interface"
echo ""
echo "--- Estado de Puertos ---"
check_port_listening
}
main "$@"
Monitor de Conexiones con Geo-IP
#!/bin/bash
# connection-geoip-monitor.sh - Monitorear conexiones con información geográfica
# Verificar patrones de conexión sospechosos
monitor_connections() {
echo "=== Análisis de Conexiones Activas ==="
# Obtener IPs únicas con cantidad de conexiones
ss -tan | grep ESTAB | awk '{print $5}' | cut -d: -f1 | \
grep -v "^127\|^::1\|^$" | sort | uniq -c | sort -rn | \
while read count ip; do
echo "IP: $ip (Conexiones: $count)"
# Alertar sobre alto número de conexiones desde una IP única
if [ "$count" -gt 50 ]; then
echo " ADVERTENCIA: Alto número de conexiones desde $ip"
# Obtener información GeoIP si geoiplookup está disponible
if command -v geoiplookup &> /dev/null; then
geo=$(geoiplookup "$ip")
echo " Ubicación: $geo"
fi
fi
done
}
monitor_connections
Scripts de Monitoreo de Servicios
Monitor Completo de Servicios
#!/bin/bash
# service-monitor.sh - Monitorear servicios críticos
SERVICES=("nginx" "mysql" "redis" "ssh")
LOG_FILE="/var/log/monitoring/services.log"
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Verificar si el servicio está ejecutándose
check_service() {
local service=$1
if systemctl is-active --quiet "$service"; then
echo "Servicio $service: EJECUTÁNDOSE"
return 0
else
echo "Servicio $service: NO EJECUTÁNDOSE"
log_message "ERROR: Servicio $service no está ejecutándose"
# Intentar iniciar servicio
log_message "Intentando iniciar $service"
if systemctl start "$service"; then
log_message "Servicio $service iniciado exitosamente"
# Enviar notificación
echo "El servicio $service estaba caído y ha sido reiniciado en $(hostname)" | \
mail -s "Servicio Recuperado: $service" [email protected]
else
log_message "ERROR: Falló al iniciar $service"
# Obtener estado del servicio
status=$(systemctl status "$service" --no-pager)
# Enviar alerta
{
echo "CRÍTICO: El servicio $service falló al iniciar en $(hostname)"
echo "Hora: $(date)"
echo ""
echo "Estado del Servicio:"
echo "$status"
echo ""
echo "Logs Recientes:"
journalctl -u "$service" -n 50 --no-pager
} | mail -s "CRÍTICO: Servicio $service Falló" [email protected]
fi
return 1
fi
}
# Verificar tiempo de respuesta del servicio
check_service_response() {
local service=$1
local url=$2
local max_time=5
if [ -z "$url" ]; then
return 0
fi
response_time=$(curl -o /dev/null -s -w '%{time_total}' --max-time "$max_time" "$url" 2>/dev/null || echo "timeout")
if [ "$response_time" = "timeout" ]; then
echo " Respuesta: TIMEOUT"
log_message "ADVERTENCIA: Timeout de respuesta de $service"
return 1
else
echo " Tiempo de respuesta: ${response_time}s"
# Alertar si el tiempo de respuesta es lento
if (( $(echo "$response_time > 2" | bc -l) )); then
log_message "ADVERTENCIA: Respuesta lenta de $service: ${response_time}s"
fi
fi
return 0
}
# Verificar puertos de servicio
check_service_port() {
local service=$1
local port=$2
if [ -z "$port" ]; then
return 0
fi
if ss -tuln | grep -q ":$port "; then
echo " Puerto $port: ESCUCHANDO"
return 0
else
echo " Puerto $port: NO ESCUCHANDO"
log_message "ERROR: Puerto $port de $service no escuchando"
return 1
fi
}
# Bucle principal de monitoreo
main() {
log_message "Iniciando monitoreo de servicios"
echo "=== Reporte de Monitoreo de Servicios ==="
echo "Marca de tiempo: $(date)"
echo ""
local failed_services=0
for service in "${SERVICES[@]}"; do
if ! check_service "$service"; then
((failed_services++))
fi
# Verificaciones específicas por servicio
case "$service" in
nginx|apache2)
check_service_port "$service" "80"
check_service_response "$service" "http://localhost"
;;
mysql|mariadb)
check_service_port "$service" "3306"
;;
redis)
check_service_port "$service" "6379"
# Probar conexión de redis
if command -v redis-cli &> /dev/null; then
if redis-cli ping &>/dev/null; then
echo " Redis: RESPONDIENDO"
else
echo " Redis: NO RESPONDIENDO"
log_message "ERROR: Redis no respondiendo"
fi
fi
;;
ssh)
check_service_port "$service" "22"
;;
esac
echo ""
done
if [ "$failed_services" -gt 0 ]; then
log_message "ADVERTENCIA: $failed_services servicio(s) fallaron verificaciones"
exit 1
fi
log_message "Todos los servicios operacionales"
exit 0
}
main
Monitor de Procesos
#!/bin/bash
# process-monitor.sh - Monitorear procesos específicos
# Monitorear proceso por nombre
monitor_process() {
local process_name=$1
local min_instances=${2:-1}
local max_instances=${3:-10}
# Contar instancias en ejecución
local count=$(pgrep -c "$process_name" || echo "0")
echo "Proceso $process_name: $count instancia(s)"
if [ "$count" -lt "$min_instances" ]; then
echo " ADVERTENCIA: Muy pocas instancias (mínimo: $min_instances)"
log_message "ERROR: Proceso $process_name tiene solo $count instancias (mínimo: $min_instances)"
return 1
elif [ "$count" -gt "$max_instances" ]; then
echo " ADVERTENCIA: Demasiadas instancias (máximo: $max_instances)"
log_message "ADVERTENCIA: Proceso $process_name tiene $count instancias (máximo: $max_instances)"
return 1
fi
# Verificar uso de recursos del proceso
total_cpu=0
total_mem=0
while IFS= read -r pid; do
cpu=$(ps -p "$pid" -o %cpu= | tr -d ' ')
mem=$(ps -p "$pid" -o %mem= | tr -d ' ')
total_cpu=$(echo "$total_cpu + $cpu" | bc)
total_mem=$(echo "$total_mem + $mem" | bc)
done < <(pgrep "$process_name")
echo " CPU Total: ${total_cpu}%"
echo " Memoria Total: ${total_mem}%"
return 0
}
# Monitorear proceso por archivo PID
monitor_pid_file() {
local pid_file=$1
local process_name=$2
if [ ! -f "$pid_file" ]; then
echo "Proceso $process_name: Archivo PID no encontrado"
log_message "ERROR: Archivo PID $pid_file no encontrado para $process_name"
return 1
fi
pid=$(cat "$pid_file")
if kill -0 "$pid" 2>/dev/null; then
echo "Proceso $process_name (PID: $pid): EJECUTÁNDOSE"
return 0
else
echo "Proceso $process_name (PID: $pid): NO EJECUTÁNDOSE"
log_message "ERROR: Proceso $process_name con PID $pid no ejecutándose"
return 1
fi
}
# Ejemplo de uso
monitor_process "nginx" 1 10
monitor_process "php-fpm" 5 50
monitor_pid_file "/var/run/nginx.pid" "nginx"
Dashboard de Monitoreo Completo
Script de Monitoreo Todo-en-Uno
#!/bin/bash
# comprehensive-monitor.sh - Dashboard completo de monitoreo del sistema
# Configuración
CONFIG_FILE="/etc/monitoring/config.conf"
LOG_DIR="/var/log/monitoring"
ALERT_EMAIL="[email protected]"
HOSTNAME=$(hostname)
# Asegurar que existe el directorio de logs
mkdir -p "$LOG_DIR"
# Cargar configuración si existe
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
else
# Umbrales predeterminados
CPU_THRESHOLD=80
MEMORY_THRESHOLD=85
DISK_THRESHOLD=90
LOAD_THRESHOLD=2.0
fi
# Códigos de color para salida de terminal
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # Sin Color
# Funciones de registro
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_DIR/monitoring.log"
}
# Seguimiento de estado
declare -a ALERTS=()
declare -a WARNINGS=()
declare -a INFO=()
add_alert() {
ALERTS+=("$1")
log_message "ALERTA: $1"
}
add_warning() {
WARNINGS+=("$1")
log_message "ADVERTENCIA: $1"
}
add_info() {
INFO+=("$1")
}
# Verificaciones del sistema
check_cpu() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
local cpu_int=${cpu_usage%.*}
add_info "Uso de CPU: ${cpu_usage}%"
if [ "$cpu_int" -gt "$CPU_THRESHOLD" ]; then
add_alert "Uso alto de CPU: ${cpu_usage}%"
return 1
fi
return 0
}
check_memory() {
local total=$(free -m | grep Mem: | awk '{print $2}')
local used=$(free -m | grep Mem: | awk '{print $3}')
local percent=$(echo "scale=2; ($used / $total) * 100" | bc)
local percent_int=${percent%.*}
add_info "Uso de Memoria: ${percent}% (${used}MB / ${total}MB)"
if [ "$percent_int" -gt "$MEMORY_THRESHOLD" ]; then
add_alert "Uso alto de memoria: ${percent}%"
return 1
fi
return 0
}
check_disk() {
local failed=0
while IFS= read -r line; do
local mountpoint=$(echo "$line" | awk '{print $6}')
local percent=$(echo "$line" | awk '{print $5}' | tr -d '%')
add_info "Disco $mountpoint: ${percent}% usado"
if [ "$percent" -gt "$DISK_THRESHOLD" ]; then
add_alert "Uso alto de disco en $mountpoint: ${percent}%"
failed=1
fi
done < <(df -h | tail -n +2 | grep -v "tmpfs")
return $failed
}
check_load() {
local load_1min=$(cat /proc/loadavg | awk '{print $1}')
local cpu_cores=$(nproc)
local load_per_core=$(echo "scale=2; $load_1min / $cpu_cores" | bc)
add_info "Promedio de Carga: $load_1min (núcleos: $cpu_cores, por-núcleo: $load_per_core)"
if (( $(echo "$load_per_core > $LOAD_THRESHOLD" | bc -l) )); then
add_warning "Promedio de carga alto: $load_per_core por núcleo"
return 1
fi
return 0
}
check_services() {
local services=("nginx" "mysql" "redis" "ssh")
local failed=0
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null; then
add_info "Servicio $service: ejecutándose"
else
if systemctl list-unit-files | grep -q "^${service}.service"; then
add_alert "Servicio $service: no ejecutándose"
failed=1
fi
fi
done
return $failed
}
check_network() {
local interface=${1:-eth0}
if [ ! -d "/sys/class/net/$interface" ]; then
add_warning "Interfaz de red $interface no encontrada"
return 1
fi
local rx_errors=$(cat /sys/class/net/"$interface"/statistics/rx_errors)
local tx_errors=$(cat /sys/class/net/"$interface"/statistics/tx_errors)
add_info "Red $interface - Errores RX: $rx_errors, Errores TX: $tx_errors"
if [ "$rx_errors" -gt 1000 ] || [ "$tx_errors" -gt 1000 ]; then
add_warning "Alto número de errores de red en $interface"
return 1
fi
return 0
}
# Generar reporte
generate_report() {
local report=""
report+="========================================\n"
report+="Reporte de Monitoreo del Sistema\n"
report+="========================================\n"
report+="Hostname: $HOSTNAME\n"
report+="Marca de tiempo: $(date)\n"
report+="========================================\n\n"
# Alertas
if [ ${#ALERTS[@]} -gt 0 ]; then
report+="ALERTAS (${#ALERTS[@]}):\n"
for alert in "${ALERTS[@]}"; do
report+=" [!] $alert\n"
done
report+="\n"
fi
# Advertencias
if [ ${#WARNINGS[@]} -gt 0 ]; then
report+="ADVERTENCIAS (${#WARNINGS[@]}):\n"
for warning in "${WARNINGS[@]}"; do
report+=" [*] $warning\n"
done
report+="\n"
fi
# Info
if [ ${#INFO[@]} -gt 0 ]; then
report+="ESTADO DEL SISTEMA:\n"
for info in "${INFO[@]}"; do
report+=" $info\n"
done
report+="\n"
fi
report+="========================================\n"
echo -e "$report"
}
# Enviar alertas por email
send_alerts() {
if [ ${#ALERTS[@]} -gt 0 ]; then
local subject="ALERTA: $HOSTNAME - ${#ALERTS[@]} alerta(s)"
generate_report | mail -s "$subject" "$ALERT_EMAIL"
log_message "Email de alerta enviado a $ALERT_EMAIL"
fi
}
# Mostrar en terminal con colores
display_colored_report() {
echo "========================================"
echo "Reporte de Monitoreo del Sistema"
echo "========================================"
echo "Hostname: $HOSTNAME"
echo "Marca de tiempo: $(date)"
echo "========================================"
echo ""
if [ ${#ALERTS[@]} -gt 0 ]; then
echo -e "${RED}ALERTAS (${#ALERTS[@]}):${NC}"
for alert in "${ALERTS[@]}"; do
echo -e " ${RED}[!] $alert${NC}"
done
echo ""
fi
if [ ${#WARNINGS[@]} -gt 0 ]; then
echo -e "${YELLOW}ADVERTENCIAS (${#WARNINGS[@]}):${NC}"
for warning in "${WARNINGS[@]}"; do
echo -e " ${YELLOW}[*] $warning${NC}"
done
echo ""
fi
if [ ${#INFO[@]} -gt 0 ]; then
echo -e "${GREEN}ESTADO DEL SISTEMA:${NC}"
for info in "${INFO[@]}"; do
echo " $info"
done
echo ""
fi
echo "========================================"
}
# Ejecución principal
main() {
log_message "Iniciando monitoreo completo del sistema"
# Ejecutar todas las verificaciones
check_cpu
check_memory
check_disk
check_load
check_services
check_network
# Mostrar reporte
if [ -t 1 ]; then
# Salida de terminal - usar colores
display_colored_report
else
# No-terminal (cron, etc.) - texto plano
generate_report
fi
# Enviar alertas si hay
send_alerts
# Guardar reporte en archivo
generate_report > "$LOG_DIR/latest-report.txt"
# Retornar código de salida apropiado
if [ ${#ALERTS[@]} -gt 0 ]; then
log_message "Monitoreo completado con ${#ALERTS[@]} alertas"
exit 2
elif [ ${#WARNINGS[@]} -gt 0 ]; then
log_message "Monitoreo completado con ${#WARNINGS[@]} advertencias"
exit 1
else
log_message "Monitoreo completado exitosamente"
exit 0
fi
}
# Ejecutar
main "$@"
Conclusión
Los scripts de monitoreo Bash personalizados proporcionan un enfoque flexible, ligero y potente para el monitoreo del sistema. Ofrecen despliegue inmediato, personalización completa e integración sin problemas con la infraestructura existente sin la sobrecarga de frameworks de monitoreo complejos.
Ventajas clave del monitoreo Bash personalizado:
- Simplicidad - Fácil de entender, modificar y mantener
- Portabilidad - Funciona en cualquier distribución Linux sin dependencias
- Personalización - Adaptado exactamente a tus necesidades específicas de monitoreo
- Integración - Se integra fácilmente con sistemas de alertas y registro existentes
- Eficiencia de recursos - Sobrecarga mínima comparado con monitoreo basado en agentes
- Despliegue rápido - Sin instalación o configuración de software complejo
Mejores prácticas para scripts de monitoreo en producción:
- Implementar manejo de errores y registro completo
- Usar códigos de salida significativos para integración con sistemas de monitoreo
- Agregar archivos de configuración para gestión fácil de umbrales
- Incluir comentarios detallados y documentación
- Probar scripts exhaustivamente antes del despliegue en producción
- Implementar limitación de tasa para alertas para prevenir fatiga de notificaciones
- Usar bloqueos para prevenir ejecuciones concurrentes
- Monitorear los monitores - asegurar que los scripts de monitoreo se estén ejecutando
Si bien los scripts personalizados destacan para casos de uso específicos y monitoreo ligero, considera complementarlos con soluciones de monitoreo empresarial como Prometheus, Grafana o Nagios para entornos complejos que requieren datos históricos, visualización avanzada y capacidades de monitoreo distribuido.
Los scripts de monitoreo presentados en esta guía proporcionan una base sólida que puedes extender y adaptar a tus requisitos específicos, creando una solución de monitoreo robusta que crece con las necesidades de tu infraestructura.


