VPS überwachen – hoher CPU Steal?

Du hast einen VPS gebucht und möchtest gern wissen ob der node auf dem dein VPS läuft überbucht ist und im Durchschnitt einen hohen CPU Steal hat? Das kannst Du mit 2 Scripten überwachen.

Das erste Script – steal_monitor.sh – läuft regelmäßig, typischerweise alle fünf Minuten per Cronjob. Es liest dabei wiederholt die Werte aus /proc/stat und misst, wie viel CPU-Zeit deinem VPS vom Hypervisor entzogen wird („Steal Time“). Aus mehreren Einzelmessungen berechnet es einen Durchschnittswert, sodass zufällige Ausreißer geglättet werden. Dieser Durchschnittswert wird mit einem Zeitstempel in eine Logdatei geschrieben. Zusätzlich prüft das Script, ob der Durchschnitt oberhalb eines festgelegten Grenzwerts liegt (bei dir 5 %). Wenn das der Fall ist, verschickt es automatisch eine Warnmail an deine Adresse. Damit dient dieses Script in erster Linie der laufenden Überwachung und Alarmierung bei ungewöhnlich hoher Auslastung durch Nachbar-VPS.

Vorbereitung:

apt update && apt install -y bc gawk

nano /usr/local/bin/steal-monitor.sh

#!/usr/bin/env bash
# steal_monitor.sh
# Loggt den durchschnittlichen CPU Steal und warnt per E-Mail bei >5%

### Konfiguration ###
LOG=/var/log/steal_monitor.log      # Log-Datei
INTERVAL=5                          # Sekunden zwischen Samples
SAMPLES=12                           # Anzahl der Samples für Durchschnitt
ALERT_THRESHOLD=5.0                  # % Steal, ab dem Email versendet wird
ALERT_EMAIL=max@muster.de       # Empfänger

# Cron/Env sicher: absolute Pfade setzen
AWK=/usr/bin/awk
DATE=/bin/date
MAIL=/usr/bin/mail
BC=/usr/bin/bc

# Sicherstellen, dass Log existiert
mkdir -p $(dirname "$LOG")
touch "$LOG"

# Funktion: aktuelle CPU-Stats lesen (user,nice,system,idle,iowait,irq,softirq,steal)
read_stats() {
  $AWK '/^cpu /{for(i=2;i<=9;i++) printf "%s ",$i; exit}' /proc/stat
}

sum_array() {
  local -n arr=$1
  local s=0
  for x in "${arr[@]}"; do s=$((s + x)); done
  echo $s
}

# Startwerte
first_vals=($(read_stats))
prev=()
for v in "${first_vals[@]}"; do prev+=("$v"); done

count=0
sum_pct=0
while [ $count -lt $SAMPLES ]; do
  sleep $INTERVAL
  curr_vals=($(read_stats))

  prev_total=$(sum_array prev)
  curr_total=$(sum_array curr_vals)

  prev_steal=${prev[7]}
  curr_steal=${curr_vals[7]}

  delta_total=$((curr_total - prev_total))
  delta_steal=$((curr_steal - prev_steal))

  if [ $delta_total -le 0 ]; then
    pct=0
  else
    pct=$($AWK -v dsteal="$delta_steal" -v dtotal="$delta_total" 'BEGIN{if(dtotal==0){printf "0.0000"} else {printf "%.4f", (dsteal*100)/dtotal}}')
  fi

  sum_pct=$($AWK -v a="$sum_pct" -v b="$pct" 'BEGIN{printf "%.6f", a + b}')

  # prev <- curr für nächsten Durchgang
  prev=()
  for v in "${curr_vals[@]}"; do prev+=("$v"); done

  count=$((count + 1))
done

# Durchschnitt berechnen
avg_pct=$($AWK -v s="$sum_pct" -v n="$SAMPLES" 'BEGIN{ if(n==0) print "0.0000"; else printf "%.4f", s / n }')

# avg_pct mit Punkt für Logging und Vergleich
avg_pct_dot=$(echo "$avg_pct" | tr ',' '.')
ts=$($DATE -u +"%Y-%m-%dT%H:%M:%SZ")
echo "$ts average_steal_pct=$avg_pct_dot" >> "$LOG"

# Numerischer Vergleich mittels bc
comp=$(echo "$avg_pct_dot > $ALERT_THRESHOLD" | $BC -l)
if [ "$comp" -eq 1 ]; then
  echo "Warning: CPU steal average $avg_pct_dot% exceeded threshold $ALERT_THRESHOLD%" | $MAIL -s "[ALERT] CPU Steal High" $ALERT_EMAIL
fi

exit 0

# --- Logrotate Hinweise ---
# Datei /etc/logrotate.d/steal_monitor:
# /var/log/steal_monitor.log {
#     weekly
#     rotate 12
#     compress
#     missingok
#     notifempty
#     create 0640 root adm
# }

# --- Cron Beispiel (alle 5 Minuten) ---
# */5 * * * * root /usr/local/bin/steal_monitor.sh
# Prüfen: SAMPLES*INTERVAL < Cron Intervall, sonst überlappen sich Läufe.

das Script ausführbar machen und per Cron regelmäßig laufen lassen:

chmod +x /usr/local/bin/steal_monitor.sh
crontab -e
*/5 * * * * /usr/local/bin/steal_monitor.sh

Das zweite Script – steal_daily_summary.sh – läuft nur einmal am Tag, meist abends. Es greift auf die Logdatei des ersten Scripts zurück und wertet alle Messungen der letzten 24 Stunden aus. Dabei werden die Anzahl der Messungen, der minimale, der maximale sowie der durchschnittliche Steal-Wert berechnet. Zusätzlich ordnet es den Durchschnittswert mit einer kleinen Ampellogik ein: Grün, Gelb oder Rot, je nachdem ob der Wert unkritisch, auffällig oder kritisch ist. Diese Tageszusammenfassung wird dir ebenfalls per Mail zugeschickt. Damit bekommst du unabhängig von Warnmeldungen immer einen Überblick, wie sich dein Server über den ganzen Tag verhalten hat. Das zweite Script ist also weniger für Alarmierungen gedacht, sondern eher für die regelmäßige Berichterstattung und den langfristigen Vergleich zwischen Servern.

nano /usr/local/bin/steal_daily_summary.sh

#!/usr/bin/env bash
# steal_daily_summary.sh
# Tagesübersicht CPU Steal, robust mit gawk, verschickt Mail in UTF-8

LOG=/var/log/steal.log
AWK=/usr/bin/gawk
DATE=/bin/date
SENDMAIL=/usr/sbin/sendmail
ALERT_EMAIL=mail@alexnolte.de
BC=/usr/bin/bc

# Zeitraum: letzte 24h
since=$($DATE -u -d "24 hours ago" +%Y-%m-%dT%H:%M:%SZ)

# Werte extrahieren (per Regex aus jeder Zeile ziehen)
values=$($AWK -v since="$since" '
  $1 >= since {
    if (match($0, /average_steal_pct=([0-9.]+)/, m)) {
      print m[1];
    }
  }
' "$LOG")

count=$(echo "$values" | wc -l)

if [ "$count" -eq 0 ]; then
  (
    echo "To: $ALERT_EMAIL"
    echo "Subject: Steal Daily Summary"
    echo "Content-Type: text/plain; charset=UTF-8"
    echo ""
    echo "Keine Daten in den letzten 24h"
  ) | $SENDMAIL -t
  exit 0
fi

# min, max, avg berechnen
min=$(echo "$values" | $AWK 'NR==1{m=$1} {if($1<m) m=$1} END{printf "%.4f", m}')
max=$(echo "$values" | $AWK 'NR==1{m=$1} {if($1>m) m=$1} END{printf "%.4f", m}')
avg=$(echo "$values" | $AWK '{s+=$1} END{printf "%.4f", s/NR}')

# Ampel-Logik mit bc (falls später >5% Alarme kommen sollen)
avg_color=$($AWK -v val="$avg" -e 'BEGIN {
  if (val < 1.0) print "🟢 Grün (<1%)";
  else if (val < 5.0) print "🟡 Gelb (1-5%)";
  else print "🔴 Rot (>5%)";
}')

# Mail verschicken
(
  echo "To: $ALERT_EMAIL"
  echo "Subject: Steal Daily Summary"
  echo "Content-Type: text/plain; charset=UTF-8"
  echo ""
  echo "Steal Daily Summary (letzte 24h):"
  echo "Anzahl Messungen: $count"
  echo "Minimaler Steal: $min %"
  echo "Maximaler Steal: $max %"
  echo "Durchschnittlicher Steal: $avg % ($avg_color)"
) | $SENDMAIL -t

beim zweiten Script reicht eine Ausführung am Tag, in meine Fall Abends um 23:00 Uhr. Natürlich auch vorher ausführbar machen

chmod +x /usr/local/bin/steal_daily_summary.sh
crontab -e
0 23 * * * /usr/local/bin/steal_daily_summary.sh

Kurz gesagt:

  • Das erste Script sorgt für das Monitoring in Echtzeit und Alarmierung bei Problemen.
  • Das zweite Script liefert dir einen täglichen Report mit Min-, Max- und Durchschnittswerten, inklusive Ampelbewertung für die Einordnung.

Das Ergebnis sieht dann in Etwa wie folgt aus:

Steal Daily Summary (letzte 24h):
Anzahl Messungen: 287
Minimaler Steal: 0.0250 %
Maximaler Steal: 1.3394 %
Durchschnittlicher Steal: 0.2153 % (🟢 Grün (<1%))