74 lines
2.4 KiB
Bash
Executable File
74 lines
2.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# V124 FPM Saturation Guard v2 - detection + alerte multi-pool (NO auto-restart)
|
|
# Doctrine 24: monitor saturation pattern V9.67 recurrent 11:00 UTC
|
|
# Aggregates all FPM pools (php7.4/8.4/8.5) for global pressure view
|
|
|
|
HISTORY=/tmp/fpm-saturation-history.json
|
|
MAX_HISTORY=288 # 24h at 5min interval
|
|
ALERT_THRESHOLD_PCT=85 # Alert if saturation >= 85%
|
|
|
|
# Get total max_children across all active pools
|
|
TOTAL_MAX=0
|
|
for conf in /etc/php/*/fpm/pool.d/*.conf; do
|
|
[ -f "$conf" ] || continue
|
|
v=$(grep -E "^pm.max_children" "$conf" 2>/dev/null | awk "{print \$NF}")
|
|
[ -n "$v" ] && TOTAL_MAX=$(( TOTAL_MAX + v ))
|
|
done
|
|
[ $TOTAL_MAX -eq 0 ] && TOTAL_MAX=150 # Fallback
|
|
|
|
# Get total active FPM workers (exclude master processes)
|
|
TOTAL_ACTIVE=$(ps -ef | grep "php-fpm: pool" | grep -v grep | wc -l)
|
|
|
|
# Get main pool (php8.5/www) state for detail
|
|
MAIN_MAX=$(grep -E "^pm.max_children" /etc/php/8.5/fpm/pool.d/www.conf 2>/dev/null | awk "{print \$NF}")
|
|
MAIN_MAX=${MAIN_MAX:-150}
|
|
MAIN_ACTIVE=$(ps -ef | grep "php-fpm: pool www$" | grep -v grep | wc -l)
|
|
|
|
CONNECTIONS=$(ss -ant 2>/dev/null | grep -c ":443")
|
|
LOAD1=$(cut -d" " -f1 /proc/loadavg)
|
|
TS=$(date +%s)
|
|
TSISO=$(date -Iseconds)
|
|
|
|
# Calculate saturation (main pool focus since it handles the principal traffic)
|
|
if [ $MAIN_MAX -gt 0 ]; then
|
|
SAT_PCT=$(( MAIN_ACTIVE * 100 / MAIN_MAX ))
|
|
else
|
|
SAT_PCT=0
|
|
fi
|
|
|
|
# Determine status
|
|
STATUS="healthy"
|
|
if [ $SAT_PCT -ge $ALERT_THRESHOLD_PCT ]; then
|
|
STATUS="SATURATED"
|
|
elif [ $SAT_PCT -ge 70 ]; then
|
|
STATUS="warn"
|
|
fi
|
|
|
|
# Output compact single line
|
|
echo "sat_pct=$SAT_PCT main=$MAIN_ACTIVE/$MAIN_MAX total=$TOTAL_ACTIVE/$TOTAL_MAX load1=$LOAD1 conn=$CONNECTIONS status=$STATUS ts=$TSISO"
|
|
|
|
# Append to history (keep last 288 entries = 24h)
|
|
ENTRY="{\"ts\":$TS,\"iso\":\"$TSISO\",\"sat_pct\":$SAT_PCT,\"main_active\":$MAIN_ACTIVE,\"main_max\":$MAIN_MAX,\"total_active\":$TOTAL_ACTIVE,\"total_max\":$TOTAL_MAX,\"load1\":$LOAD1,\"conn\":$CONNECTIONS,\"status\":\"$STATUS\"}"
|
|
|
|
if [ -f "$HISTORY" ]; then
|
|
python3 -c "
|
|
import json
|
|
try:
|
|
h = json.load(open('$HISTORY'))
|
|
if not isinstance(h, list): h = []
|
|
except: h = []
|
|
h.append($ENTRY)
|
|
h = h[-${MAX_HISTORY}:]
|
|
json.dump(h, open('$HISTORY', 'w'))
|
|
" 2>/dev/null
|
|
else
|
|
echo "[$ENTRY]" > "$HISTORY"
|
|
fi
|
|
|
|
# If SATURATED, log to syslog for trace
|
|
if [ "$STATUS" = "SATURATED" ]; then
|
|
logger -t "fpm-saturation-guard" "SATURATED sat_pct=$SAT_PCT main=$MAIN_ACTIVE/$MAIN_MAX load=$LOAD1"
|
|
fi
|
|
|
|
exit 0
|