Tools: Watchdog Apache con Bash, Cron e Systemd: monitoraggio automatico di memoria, PID e socket close-wait - 2025 Update
Obiettivo dello script
Script completo
Installazione
Configurazione del cron
Perché usare flock
Analisi delle metriche monitorate
1. RAM disponibile
2. Saturazione PID Apache
3. Socket close-wait
Logging e monitoraggio
Quando usare questo approccio
Limiti della soluzione In alcuni scenari ad alto carico o in presenza di problemi lato backend, Apache può degradare progressivamente fino a diventare lento, saturare le risorse o smettere di rispondere correttamente. Per mitigare questo tipo di problemi è possibile implementare un watchdog leggero in Bash che: Questo approccio è particolarmente utile in ambienti: In base alle soglie configurate: Salvare lo script in: Rendere il file eseguibile: Aggiungere in /etc/crontab: Questo esegue il controllo ogni 2 minuti. flock evita esecuzioni concorrenti dello script. In condizioni di degrado del sistema: solo una istanza dello script può essere eseguita alla volta. che corrisponde alla colonna available. Se la memoria disponibile scende sotto: viene eseguito un restart completo di Apache. Questo è utile perché: Lo script legge direttamente i contatori cgroup di systemd: Questo approccio è molto più affidabile rispetto al parsing di ps. Se il servizio supera: viene eseguito un reload. Lo stato close-wait indica: Molte connessioni in close-wait possono indicare: Questo approccio è preferibile rispetto a netstat perché: viene eseguito un reload. Quindi gli eventi sono consultabili tramite: Per seguire i log in tempo reale: Questo watchdog è particolarmente utile quando: Questo approccio non sostituisce: Il watchdog serve principalmente come: Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse
#!/bin/bash # --- PARAMETRI DI SOGLIA ---
SOGLIA_RAM_FREE_MB=100
SOGLIA_PIDS_PERCENT=60
SOGLIA_close-wait=300 # --- RACCOLTA DATI ---
# Usiamo percorsi assoluti per cron e variabili pulite
RAM_DISPONIBILE_MB=$(/usr/bin/free -m | grep Mem | awk '{print $7}') # Conteggio in tempo reale delle connessioni orfane in close-wait
close-wait_COUNT=$(/usr/bin/ss -H -tanp state close-wait | /usr/bin/wc -l) # Gestione PIDs con controllo esistenza file (evita errori se Apache è spento)
PIDS_FILE="/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.current"
MAX_FILE="/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.max" if [ -f "$PIDS_FILE" ]; then CURRENT_APACHE_PIDS=$(cat "$PIDS_FILE") MAX_APACHE_PIDS=$(cat "$MAX_FILE") # Se MAX è la stringa "max", o è 0, o è vuoto -> la percentuale è 0 (limite infinito) if [ "$MAX_APACHE_PIDS" = "max" ] || [ -z "$MAX_APACHE_PIDS" ] || [ "$MAX_APACHE_PIDS" -eq 0 ] 2>/dev/null; then PERCENTUAL_PIDS=0 else PERCENTUAL_PIDS=$(( 100 * CURRENT_APACHE_PIDS / MAX_APACHE_PIDS )) fi
else PERCENTUAL_PIDS=0
fi # --- LOGICA DI CONTROLLO ---
AZIONE_NECESSARIA=false
TIPO_AZIONE="reload" # Default if [ "$RAM_DISPONIBILE_MB" -lt "$SOGLIA_RAM_FREE_MB" ]; then MOTIVO="RAM bassa (${RAM_DISPONIBILE_MB}MB)" AZIONE_NECESSARIA=true TIPO_AZIONE="-weight: 500;">restart" # Se la RAM è finita, il reload è troppo lento
elif [ "$PERCENTUAL_PIDS" -gt "$SOGLIA_PIDS_PERCENT" ]; then MOTIVO="PIDs al ${PERCENTUAL_PIDS}% (${CURRENT_APACHE_PIDS}/${MAX_APACHE_PIDS})" AZIONE_NECESSARIA=true TIPO_AZIONE="reload"
elif [ "$close-wait_COUNT" -gt "$SOGLIA_close-wait" ]; then MOTIVO="Rilevato leak di socket (${close-wait_COUNT} connessioni in close-wait)" AZIONE_NECESSARIA=true TIPO_AZIONE="reload" # Il reload è sufficiente a liberare i socket orfani
fi # Check sopravvivenza: se Apache non risponde, forza RESTART a prescindere dalle soglie
if ! /usr/bin/-weight: 500;">curl -s --max-time 5 http://127.0.0.1/ > /dev/null; then MOTIVO="Apache non risponde (Health Check fallito)" AZIONE_NECESSARIA=true TIPO_AZIONE="-weight: 500;">restart"
fi # --- AZIONE ---
if $AZIONE_NECESSARIA; then echo "$(date): Allarme: ${MOTIVO}. Eseguo ${TIPO_AZIONE}." /usr/bin/logger -t apache_health_watchdog "Allarme: ${MOTIVO}. Eseguo ${TIPO_AZIONE}." if [ "$TIPO_AZIONE" == "reload" ]; then /usr/bin/-weight: 500;">systemctl reload apache2 # Se il reload fallisce (es. kernel già in fork rejected), prova il -weight: 500;">restart if [ $? -ne 0 ]; then /usr/bin/logger -t apache_health_watchdog "Reload fallito, forzo -weight: 500;">restart." /usr/bin/-weight: 500;">systemctl -weight: 500;">restart apache2 fi else /usr/bin/-weight: 500;">systemctl -weight: 500;">restart apache2 fi
fi
#!/bin/bash # --- PARAMETRI DI SOGLIA ---
SOGLIA_RAM_FREE_MB=100
SOGLIA_PIDS_PERCENT=60
SOGLIA_close-wait=300 # --- RACCOLTA DATI ---
# Usiamo percorsi assoluti per cron e variabili pulite
RAM_DISPONIBILE_MB=$(/usr/bin/free -m | grep Mem | awk '{print $7}') # Conteggio in tempo reale delle connessioni orfane in close-wait
close-wait_COUNT=$(/usr/bin/ss -H -tanp state close-wait | /usr/bin/wc -l) # Gestione PIDs con controllo esistenza file (evita errori se Apache è spento)
PIDS_FILE="/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.current"
MAX_FILE="/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.max" if [ -f "$PIDS_FILE" ]; then CURRENT_APACHE_PIDS=$(cat "$PIDS_FILE") MAX_APACHE_PIDS=$(cat "$MAX_FILE") # Se MAX è la stringa "max", o è 0, o è vuoto -> la percentuale è 0 (limite infinito) if [ "$MAX_APACHE_PIDS" = "max" ] || [ -z "$MAX_APACHE_PIDS" ] || [ "$MAX_APACHE_PIDS" -eq 0 ] 2>/dev/null; then PERCENTUAL_PIDS=0 else PERCENTUAL_PIDS=$(( 100 * CURRENT_APACHE_PIDS / MAX_APACHE_PIDS )) fi
else PERCENTUAL_PIDS=0
fi # --- LOGICA DI CONTROLLO ---
AZIONE_NECESSARIA=false
TIPO_AZIONE="reload" # Default if [ "$RAM_DISPONIBILE_MB" -lt "$SOGLIA_RAM_FREE_MB" ]; then MOTIVO="RAM bassa (${RAM_DISPONIBILE_MB}MB)" AZIONE_NECESSARIA=true TIPO_AZIONE="-weight: 500;">restart" # Se la RAM è finita, il reload è troppo lento
elif [ "$PERCENTUAL_PIDS" -gt "$SOGLIA_PIDS_PERCENT" ]; then MOTIVO="PIDs al ${PERCENTUAL_PIDS}% (${CURRENT_APACHE_PIDS}/${MAX_APACHE_PIDS})" AZIONE_NECESSARIA=true TIPO_AZIONE="reload"
elif [ "$close-wait_COUNT" -gt "$SOGLIA_close-wait" ]; then MOTIVO="Rilevato leak di socket (${close-wait_COUNT} connessioni in close-wait)" AZIONE_NECESSARIA=true TIPO_AZIONE="reload" # Il reload è sufficiente a liberare i socket orfani
fi # Check sopravvivenza: se Apache non risponde, forza RESTART a prescindere dalle soglie
if ! /usr/bin/-weight: 500;">curl -s --max-time 5 http://127.0.0.1/ > /dev/null; then MOTIVO="Apache non risponde (Health Check fallito)" AZIONE_NECESSARIA=true TIPO_AZIONE="-weight: 500;">restart"
fi # --- AZIONE ---
if $AZIONE_NECESSARIA; then echo "$(date): Allarme: ${MOTIVO}. Eseguo ${TIPO_AZIONE}." /usr/bin/logger -t apache_health_watchdog "Allarme: ${MOTIVO}. Eseguo ${TIPO_AZIONE}." if [ "$TIPO_AZIONE" == "reload" ]; then /usr/bin/-weight: 500;">systemctl reload apache2 # Se il reload fallisce (es. kernel già in fork rejected), prova il -weight: 500;">restart if [ $? -ne 0 ]; then /usr/bin/logger -t apache_health_watchdog "Reload fallito, forzo -weight: 500;">restart." /usr/bin/-weight: 500;">systemctl -weight: 500;">restart apache2 fi else /usr/bin/-weight: 500;">systemctl -weight: 500;">restart apache2 fi
fi
#!/bin/bash # --- PARAMETRI DI SOGLIA ---
SOGLIA_RAM_FREE_MB=100
SOGLIA_PIDS_PERCENT=60
SOGLIA_close-wait=300 # --- RACCOLTA DATI ---
# Usiamo percorsi assoluti per cron e variabili pulite
RAM_DISPONIBILE_MB=$(/usr/bin/free -m | grep Mem | awk '{print $7}') # Conteggio in tempo reale delle connessioni orfane in close-wait
close-wait_COUNT=$(/usr/bin/ss -H -tanp state close-wait | /usr/bin/wc -l) # Gestione PIDs con controllo esistenza file (evita errori se Apache è spento)
PIDS_FILE="/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.current"
MAX_FILE="/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.max" if [ -f "$PIDS_FILE" ]; then CURRENT_APACHE_PIDS=$(cat "$PIDS_FILE") MAX_APACHE_PIDS=$(cat "$MAX_FILE") # Se MAX è la stringa "max", o è 0, o è vuoto -> la percentuale è 0 (limite infinito) if [ "$MAX_APACHE_PIDS" = "max" ] || [ -z "$MAX_APACHE_PIDS" ] || [ "$MAX_APACHE_PIDS" -eq 0 ] 2>/dev/null; then PERCENTUAL_PIDS=0 else PERCENTUAL_PIDS=$(( 100 * CURRENT_APACHE_PIDS / MAX_APACHE_PIDS )) fi
else PERCENTUAL_PIDS=0
fi # --- LOGICA DI CONTROLLO ---
AZIONE_NECESSARIA=false
TIPO_AZIONE="reload" # Default if [ "$RAM_DISPONIBILE_MB" -lt "$SOGLIA_RAM_FREE_MB" ]; then MOTIVO="RAM bassa (${RAM_DISPONIBILE_MB}MB)" AZIONE_NECESSARIA=true TIPO_AZIONE="-weight: 500;">restart" # Se la RAM è finita, il reload è troppo lento
elif [ "$PERCENTUAL_PIDS" -gt "$SOGLIA_PIDS_PERCENT" ]; then MOTIVO="PIDs al ${PERCENTUAL_PIDS}% (${CURRENT_APACHE_PIDS}/${MAX_APACHE_PIDS})" AZIONE_NECESSARIA=true TIPO_AZIONE="reload"
elif [ "$close-wait_COUNT" -gt "$SOGLIA_close-wait" ]; then MOTIVO="Rilevato leak di socket (${close-wait_COUNT} connessioni in close-wait)" AZIONE_NECESSARIA=true TIPO_AZIONE="reload" # Il reload è sufficiente a liberare i socket orfani
fi # Check sopravvivenza: se Apache non risponde, forza RESTART a prescindere dalle soglie
if ! /usr/bin/-weight: 500;">curl -s --max-time 5 http://127.0.0.1/ > /dev/null; then MOTIVO="Apache non risponde (Health Check fallito)" AZIONE_NECESSARIA=true TIPO_AZIONE="-weight: 500;">restart"
fi # --- AZIONE ---
if $AZIONE_NECESSARIA; then echo "$(date): Allarme: ${MOTIVO}. Eseguo ${TIPO_AZIONE}." /usr/bin/logger -t apache_health_watchdog "Allarme: ${MOTIVO}. Eseguo ${TIPO_AZIONE}." if [ "$TIPO_AZIONE" == "reload" ]; then /usr/bin/-weight: 500;">systemctl reload apache2 # Se il reload fallisce (es. kernel già in fork rejected), prova il -weight: 500;">restart if [ $? -ne 0 ]; then /usr/bin/logger -t apache_health_watchdog "Reload fallito, forzo -weight: 500;">restart." /usr/bin/-weight: 500;">systemctl -weight: 500;">restart apache2 fi else /usr/bin/-weight: 500;">systemctl -weight: 500;">restart apache2 fi
fi
/usr/local/sbin/check_system_health.sh
/usr/local/sbin/check_system_health.sh
/usr/local/sbin/check_system_health.sh
chmod +x /usr/local/sbin/check_system_health.sh
chmod +x /usr/local/sbin/check_system_health.sh
chmod +x /usr/local/sbin/check_system_health.sh
/etc/crontab
*/2 * * * * root flock -n /tmp/apache_watchdog.lock /usr/local/sbin/check_system_health.sh >> /var/log/apache_health_watchdog.log 2>&1
*/2 * * * * root flock -n /tmp/apache_watchdog.lock /usr/local/sbin/check_system_health.sh >> /var/log/apache_health_watchdog.log 2>&1
*/2 * * * * root flock -n /tmp/apache_watchdog.lock /usr/local/sbin/check_system_health.sh >> /var/log/apache_health_watchdog.log 2>&1
flock -n /tmp/apache_watchdog.lock
flock -n /tmp/apache_watchdog.lock
flock -n /tmp/apache_watchdog.lock
free -m
awk '{print $7}'
awk '{print $7}'
awk '{print $7}'
SOGLIA_RAM_FREE_MB=100
SOGLIA_RAM_FREE_MB=100
SOGLIA_RAM_FREE_MB=100
/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.current
/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.current
/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.current
/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.max
/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.max
/sys/fs/cgroup/system.slice/apache2.-weight: 500;">service/pids.max
SOGLIA_PIDS_PERCENT=60
SOGLIA_PIDS_PERCENT=60
SOGLIA_PIDS_PERCENT=60
ss -tanp state close-wait
ss -tanp state close-wait
ss -tanp state close-wait
SOGLIA_close-wait=300
SOGLIA_close-wait=300
SOGLIA_close-wait=300
logger -t apache_health_watchdog
logger -t apache_health_watchdog
logger -t apache_health_watchdog
journalctl -t apache_health_watchdog
journalctl -t apache_health_watchdog
journalctl -t apache_health_watchdog
May 18 10:42:01 server apache_health_watchdog: Allarme: Rilevato leak di socket (182 connessioni in close-wait). Eseguo reload.
May 18 10:42:01 server apache_health_watchdog: Allarme: Rilevato leak di socket (182 connessioni in close-wait). Eseguo reload.
May 18 10:42:01 server apache_health_watchdog: Allarme: Rilevato leak di socket (182 connessioni in close-wait). Eseguo reload.
journalctl -f -t apache_health_watchdog
journalctl -f -t apache_health_watchdog
journalctl -f -t apache_health_watchdog - monitora periodicamente lo stato del sistema;
- verifica alcune metriche critiche;
- esegue automaticamente un reload o un -weight: 500;">restart di Apache;
- registra gli eventi tramite syslog e journalctl. - con reverse proxy Apache;
- con backend applicativi instabili;
- con leak di connessioni;
- dove è necessario un meccanismo di self-healing semplice e trasparente. - RAM disponibile;
- numero di PID usati dal servizio Apache;
- connessioni TCP in stato close-wait verso un backend specifico;
- health check HTTP locale; - esegue un reload di Apache;
- oppure un -weight: 500;">restart completo;
- scrive i dettagli nel journal di systemd. - un controllo potrebbe impiegare più tempo del previsto;
- il cron successivo potrebbe partire mentre il precedente è ancora attivo;
- si rischiano reload o -weight: 500;">restart multipli contemporanei. - il reload può richiedere fork aggiuntivi;
- in condizioni di memoria critica il reload potrebbe peggiorare la situazione;
- un -weight: 500;">restart libera immediatamente worker e memoria frammentata. - rigenerare worker;
- liberare processi stuck;
- evitare il raggiungimento del limite massimo PID. - il peer remoto ha chiuso la connessione;
- il processo locale non ha ancora rilasciato il socket. - leak applicativi;
- backend instabili;
- worker Apache bloccati;
- timeout mal configurati. - ss è più moderno;
- usa meno CPU;
- legge direttamente dal kernel tramite netlink;
- è più affidabile sotto alto carico;
- evita dipendenze da net-tools. - Apache funge da reverse proxy;
- i backend possono bloccarsi o degradare;
- non è disponibile un orchestratore avanzato;
- si vuole una soluzione semplice e immediata;
- si desidera un meccanismo di self-healing leggero. - un monitoraggio completo;
- metriche Prometheus/Grafana;
- tracing applicativo;
- analisi root cause;
- tuning corretto di Apache e backend. - mitigazione automatica;
- protezione operativa;
- recovery rapido.