Die Frage lautet fast immer gleich: Nachts brauchte die Website zwanzig Sekunden für eine Antwort, und am Morgen war alles von selbst wieder in Ordnung. Zu sehen ist nichts mehr — top beantwortet die aktuelle Sekunde, gefragt wird aber nach drei Uhr morgens. Im Folgenden geht es darum, was die Lastzahlen tatsächlich bedeuten, welche davon paarweise gelesen werden müssen und warum eine einzelne Messung öfter in die Irre führt, als sie hilft.

Load Average: drei Zahlen und ein verbreiteter Irrtum

uptime
cat /proc/loadavg
nproc

Die drei Zahlen sind Mittelwerte über eine, fünf und fünfzehn Minuten. Zu vergleichen sind sie nicht mit null, sondern mit der Anzahl der Kerne, die nproc ausgibt. Ein Wert von 8 auf einer Maschine mit acht Kernen ist volle, aber gesunde Last: Es liegt genau so viel Arbeit an, wie der Server erledigen kann. Ein Wert von 2 auf einem VPS mit einem Kern ist eine Warteschlange, die doppelt so lang ist wie die Kapazität — jede Anfrage wartet.

Das Verhältnis der drei Zahlen zueinander gibt die Richtung an. Liegt der Ein-Minuten-Wert deutlich über dem Fünfzehn-Minuten-Wert, steigt die Last gerade jetzt. Umgekehrt ist der Höhepunkt vorbei, und Sie sehen nur noch seinen Ausläufer.

Nun zu dem Irrtum, an dem diese Zahlen am häufigsten scheitern. Load Average ist unter Linux keine „CPU-Auslastung“. Anders als andere Unix-Systeme zählt Linux dort nicht nur Prozesse, die laufen oder laufbereit sind, sondern auch solche im Zustand D — im unterbrechungsfreien Schlaf. Das bedeutet: Warten auf die Festplatte oder auf ein Netzwerkdateisystem. Daher der Server mit einem Load von 12 bei nahezu untätiger CPU: Es wird nicht gearbeitet, es wird angestanden.

CPU oder Festplatte

Diese beiden Fälle zu trennen ist der erste sinnvolle Schritt:

vmstat 1 5
iostat -x 1 3

In der Ausgabe von vmstat zählen drei Spalten. r ist die Zahl der Prozesse, die auf die CPU warten, b die Zahl derer, die auf Ein-/Ausgabe blockiert sind, und wa der Zeitanteil, den die CPU untätig wartend auf die Festplatte verbracht hat. Ein dauerhaftes wa über 10–15 % bei bescheidenem us und sy heißt: Der Server hängt an der Festplatte. Mehr Kerne bringen nichts, die vorhandenen sind ohnehin frei.

Welche Prozesse warten, lässt sich namentlich auflisten:

ps -eo state,pid,comm,wchan:30 | awk '$1 ~ /^D/'

Ein Beispiel aus der eigenen Praxis. Suricata schreibt mit der Standardkonfiguration rund dreißig Ereignistypen und richtet dafür keine Rotation ein — auf einem produktiven Server ergab das 15 GB Logs in zwei Tagen. Die Last zeigte sich dabei nicht als ausgelastete CPU, sondern genau als I/O wait: Das System schrieb ununterbrochen, alles andere stand dahinter in der Schlange. Die Abhilfe ist kein größerer Tarif, sondern eine kürzere Liste von Ereignistypen und eine eingerichtete Rotation.

Der umgekehrte Fall, rein prozessorgebunden und deutlich weniger offensichtlich. Eine Webseite rief apt unter dem Benutzer auf, unter dem PHP-FPM läuft. Root hält einen Binärcache unter /var/cache/apt/pkgcache.bin — auf einem Host mit einem Dutzend Repositories sind das 70 MB — und blendet ihn kostenlos in den Speicher ein. Ein gewöhnlicher Benutzer darf in dieses Verzeichnis nicht schreiben und baut den Cache bei jedem Aufruf neu auf. Gemessen auf ein und derselben Maschine: 0,01 Sekunden CPU-Zeit als root gegenüber 4,2 Sekunden als unprivilegierter Benutzer. Derselbe Befehl, ein Faktor von vierhundert — multipliziert mit jedem Seitenaufruf.

Der Schluss aus beiden Fällen ist derselbe: Last muss gemessen und nicht geraten werden. Jeden verdächtigen Befehl einzeln zu stoppen dauert eine halbe Stunde und weist meist woandershin, als der Verdacht anfangs zeigte.

Speicher: free zeigt nicht das, wonach es aussieht

free -h

Die Spalte used sagt für sich genommen sehr wenig, und die Spalte free führt geradezu in die Irre: Linux gibt ungenutzten Speicher an den Seitencache und holt ihn auf Anforderung sofort zurück. Zu lesen ist die Spalte available — wie viel belegt werden kann, ohne in den Swap zu gehen. Ein großer buff/cache ist kein Problem, sondern das Zeichen eines Systems, das arbeitet wie vorgesehen.

Beim Swap zählt nicht der aktuelle Wert, sondern die Form. Hinauf und zurück auf null heißt: Es gab eine kurze Spitze. Einmal hinauf und dort geblieben heißt: Die Spitze ist bereits vorbei, Seiten wurden verdrängt, und niemand holt sie zurück — der Server wirkt ruhig, obwohl ihm irgendwann der Speicher ausging. Ob gerade ausgelagert wird, zeigen die Spalten si und so in vmstat; von null verschiedene Werte dort sind die Form der Verlangsamung, die Benutzer am deutlichsten spüren.

Ist ein Prozess während des nächtlichen Einbruchs einfach verschwunden, steht die Erklärung meist hier:

journalctl -k --since yesterday | grep -i "out of memory"
dmesg -T | grep -i "killed process"

Eine Zeile wie Out of memory: Killed process 1234 (mysqld) klärt die Sache besser als jedes Diagramm: Die Datenbank ist nicht „von selbst abgestürzt“, der Kernel hat sie beendet, weil der Speicher zu Ende war.

Wer es verursacht

ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10

Eine Einschränkung: %CPU in ps ist der Mittelwert über die gesamte Lebensdauer des Prozesses, ein kurzer Ausschlag geht in dieser Liste also unter. Dafür braucht es top oder pidstat 1 5, die über ein Intervall messen.

Wann Last zur Sicherheitsfrage wird

Konstante 100 % CPU in der Nacht, dazu ein Prozess mit nichtssagendem Namen und Arbeitsverzeichnis in /tmp oder /dev/shm, ist das klassische Bild eines Miners und nicht das einer gewachsenen Website. Ein Ausschlag im eingehenden Verkehr zusammen mit mehr Schreibzugriffen auf die Logs ist ein laufender Passwortangriff. Ein plötzlich springendes Logvolumen ist meist ein Sturm von Fehlalarmen eines Filters.

Eine eigene Kategorie sind die Schutzwerkzeuge selbst. Auf einem unserer Server lag der Load bei etwa drei, ganz ohne Besucher, und an der Spitze von ps nach aufgelaufener CPU-Zeit standen weder die Website noch die Datenbank, sondern CrowdSec, fail2ban, Falco und Suricata. Das ist kein Ausfall und kein Grund, sie abzuschalten, aber den Preis des Schutzes sollte man als Zahl kennen: Auf einem kleinen VPS ist er spürbar.

Wozu das Beobachten

Alles Bisherige beantwortet die Frage „Was passiert gerade?“. Die Frage vom Morgen — was um drei Uhr nachts los war — decken diese Befehle nicht ab: Für die vergangene Nacht gibt es keine Daten, wenn sie niemand aufgezeichnet hat. Und für einen einzigen VPS Prometheus und Grafana aufzubauen, ist fragwürdig, weil der beobachtende Stack schwerer ausfällt als der beobachtete Server.

Eine Zeile in einer Datenbank alle fünf Minuten und eine Seite, die daraus die letzten 24 Stunden zeichnet, genügen: Load Average, CPU-Auslastung und I/O wait getrennt, Speicher und Swap, Lesen und Schreiben der Festplatte, Belegung der Partition, Inodes, Dateideskriptoren, Verbindungen. Auf einer gemeinsamen Zeitachse lassen sich die Paare mit bloßem Auge lesen: hohes wa bei ruhiger CPU, Swap, der nie auf null zurückkam, Deskriptoren nahe am Limit — der kommende Fehler too many open files wird Stunden vorher sichtbar. Wie das zusammengesetzt aussieht, zeigt die Demo-Seite unten.