Der OOM-Killer kann auf stark ausgelasteten Maschinen für böse Überraschungen sorgen: Prozesse werden plötzlich und unerwartet beendet. Dieses Verhalten lässt sich aber mit Kernel-Bord-Mitteln sehr genau beeinflussen. Administratoren auf Linuxmaschinen mit hoher RAM-Nutzung erleben oft eine Begegnung der unheimlichen Art: den Linux OOM-Killer (OOM = Out Of Memory). Der Administrator findet in diesem Szenario eine „abgestürzte“ PostgreSQL®-Instanz vor, im Serverlog finden sich dann einer oder meist mehrere Einträge der Form
Out of Memory: Killed process PID (Prozessname)
Doch was genau steckt dahinter?
Virtueller Speicher und Overcommit
Virtueller Speicher in Linuxsystemen wird auf vielfältige Weise adressiert: RAM, mmap(), Swap oder Shared Memory, um ein paar Beispiele zu nennen. Es ist möglich, durch das sogenannte Overcommit-Verhalten bei Allokieren von Speicher mehr Ressourcen anzufordern, als tatsächlich im System aktuell vorhanden ist. In solchen Situationen spricht man von einer OOM-Situation, das System hat alle Ressourcen aufgebraucht und ist nicht mehr in der Lage, mehr virtuellen Speicher zu adressieren. Hier wird der OOM-Killer aktiv, der Prozesse nach festgelegten Kriterien auswählt und diese terminiert, um dem System ein wenig Luft zu verschaffen. Dieses Verhalten ist insbesondere für Datenbanksysteme zu berücksichtigen, die nicht auf dedizierter Hardware laufen. Der OOM-Killer bevorzugt in solchen Umgebungen häufig PostgreSQL®, da als Kandidaten zum Terminieren solche Prozesse ausgewählt werden, die mit aggressiver Speichernutzung auffallen. Da der OOM-Killer den gesamten Adressraum aller Kinder inklusive Shared Memory in Summe sieht, erkennt man recht schnell, dass PostgreSQL® auf jeden Fall weit oben in der Liste der Kandidaten auftauchen wird. Wie stark der zur Verfügung stehende Speicher genutzt wird, findet man am schnellsten über das /proc-Filesystem heraus:
$ grep Commit /proc/meminfo CommitLimit: 376176 kB Committed_AS: 265476 kB
In diesem Beispiel sind aktuell als Obergrenze 376176 kB(CommitLimit) an Speichernutzung möglich, zugewiesen wurden 265476 kB (Committed_AS). Nähert sich CommitLimit sehr stark an Committed_AS an oder übersteigt diesen sogar, dann ist der Einsatz des OOM-Killers wahrscheinlich. Der Linux-Kernel stellt einige Schnittstellen zur Verfügung, die das Verhalten des OOM-Killers gegenüber PostgreSQL® beeinflusst.
Overcommit abschalten
Die radikalste Methode ist, Overcommit generell im Kernel abzuschalten. Allerdings kommt dies nur für dedizierte Datenbanksysteme in Frage, auf denen PostgreSQL® exklusiv läuft. Das Overcommit-Verhalten lässt sich in modernen 2.6ern Kernel in drei Kategorien mit dem Parameter
vm.overcommit_memory = 0
konfigurieren. Die einzelnen Kategorien hierbei sind:
- 0: Vorsichtiges Overcommitverhalten. Während gemäßigte Allokierungen erlaubt sind, werden extrem große Allokierungen, die zu übermäßigem Overcommit führen, abgelehnt. In diesem Modus kann root auch mehr Speicher allokieren als ein unprivilegierter Benutzer. Dieser Modus ist auch die Standardeinstellung des Kernels.
- 1: Overcommit unterliegt keinen Einschränkungen
- 2: Schaltet Overcommitverhalten ab. Generell bedeutet dies, dass der maximale allokierbare tatsächliche Adressraum nicht größer werden kann, als swap + ein konfigurierbarer Anteil an Prozent des physkalischen RAM.
Der Anteil des physikalischen RAM bei Modus 2 wird über den zusätzlichen Parameter
vm.overcommit_ratio = 50
kontrolliert. Während vm.overcommit_memory=1 für Spezialanwendungen interessant sein könnte, wird es im Praxiseinsatz eher zum Einsatz für die Parameterwerte 0 oder 2 kommen. Wird Overcommit über vm.overcommit_memory=2 abgeschaltet, so wird ein Prozess (in Abhängigkeit von vm_overcommit_ratio) sofort eine „Out Of Memory“-Bedingung beim Allokieren von Speicher erhalten. Abhängig von der Distribution sollte man die Einstellungen permanent in die Datei /etc/sysctl.conf speichern, so dass diese auch nach einem Neustart des Systems aktiv sind:
$ echo "vm.overcommit_memory=2 >> /etc/sysctl.conf $ echo "vm.overcommit_ratio=60 >> /etc/sysctl.conf $ sysctl -p /etc/sysctl.conf
Die Änderungen wirken sich sofort auf den virtuellen Speicher aus, man kann dies erneut durch Abrufen von /proc/meminfo überprüfen:
$ grep Commit /proc/meminfo CommitLimit: 401440 kB Committed_AS: 266456 kB
Die Maschine verfügt über 249848 kB Swap und 252656 kB physikalischen RAM. Nach der Formel Swap + vm.overcommit_ratio * RAM ergibt dies ein CommitLimit von 401440 kB.
OOM-Killer auf Prozessebene konfigurieren
Ist PostgreSQL® nicht auf einem dedizierten Server installiert und wird mit einer speicherhungrigen Middleware (bspw. JBoss- oder Tomcat-Installation) auf demselben System betrieben, so ist es wünschenswert, Overcommit-Verhalten zwar zu erlauben, im Falle einer „Out Of Memory“-Situation aber PostgreSQL® vom OOM-Killer auszunehmen. Seit Kernel 2.6.11 bietet Linux daher ein Interface an, um den OOM-Score eines Prozesses zu tunen, so dass dieser vom OOM-Killer weniger oder stärker berücksichtigt wird. Dies erlaubt ein sehr feinfühliges Einstellen des Systems auf die Speicherbedürfnisse einzelner Prozesse. Die Konfiguration wird über eine Datei im /proc-Filesystem des Kernel vorgenommen, beispielsweise hier für den PostgreSQL®-Hauptprozess unter Debian (0 ist die Standardeinstellung für Prozesse):
$ cat /proc/$(cat /var/run/postgresql/8.4-main.pid)/oom_adj 0
Die erlaubten Werte sind von -17 bis +15, negative Werte verringern die Affinität des Prozesses gegenüber den OOM-Killer, positive Werte erhöhen diese. -17 schaltet den OOM-Killer für den jeweiligen Prozess komplett ab. Die Einstellung wird vom Parent an etwaige Kindprozesse weitervererbt. Da PostgreSQL® sich für eine Datenbankverbindung forked, reicht es, diese Einstellung dem PostgreSQL®-Hauptprozess mitzugeben:
$ echo -17 >> /proc/$(cat /var/run/postgresql/8.4-main.pid)/oom_adj
$ psql -q postgres
test=# SELECT pg_backend_pid();
pg_backend_pid
----------------
3429
(1 Zeile)
test=#
[1]+ Stopped psql -q test
$ cat /proc/3429/oom_adj
-17Der Nachteil dieser Methode ist, dass dies nun für alle Kindprozesse des PostgreSQL®-Hauptprozesses gilt, was eventuell vom DBA nicht mehr gewünscht ist. Beispielsweise möchte man zwar gerne die PostgreSQl-Systemprozesse wie Background Writer oder Autovacuum vor dem OOM-Killer schützen, nicht jedoch normale Datenbankverbindungen. Das Setzen von /proc/PID/oom_adj erfordert jedoch einen privilegierten Benutzer, so dass man am Besten die Einstellung direkt im Startskript der PostgreSQL®-Datenbank vornimmt.
Erweiterungen in PostgreSQL® 9.0
PostgreSQL® 9.0 wird hinsichtlich der Zusammenarbeit mit dem /proc-Interface ebenfalls einige Neuerungen mitbringen. Zum einen wurde das im Quelltext mitgelieferte Linux-Startskript dahingehend erweitert, zum anderen bietet das Backend nun auch Unterstützung, falls man die /proc-Einstellungen eben nicht an normale Datenbankverbindungen weitervererben möchte. Hierzu kann der PostgreSQL®-Server mit dem Makro LINUX_OOM_ADJ=0 kompiliert werden, beispielsweise:
$ ./configure CC="ccache gcc" CFLAGS="-DLINUX_OOM_ADJ=0"
Diese Methode schützt dann die PostgreSQL®-Systemprozesse effektiv, erlaubt aber dem OOM-Killer etwaige Amoklaufende Backends trotzdem zu terminieren.
Alternativen
Eine alternative Lösung gibt es auch in Form eines Kernelpatches. Dies ergänzt das /proc-Filesystem um eine Liste an Prozessnamen, die explizit vom OOM-Killer nicht berücksichtigt werden dürfen. Da dies jedoch eine inoffizielle Erweiterung des Kernels ist, muss man seinen eigenen Kernel damit pflegen, auch ist diese Erweiterung bei weitem nicht so flexibel wie das Interface über oom_adj. Des weiteren sind Prozessnamen relativ ungeeignet, um spezifische Prozesse eindeutig zu identifizieren (z.B. Java- oder Perlbasierte Prozesse).
Zusammenfassung
Der Linuxkernel bietet mittlerweile umfassende Möglichkeiten, die Speichernutzung von Prozessen an das Memory Management des Kernels anzupassen. Die flexibelste Lösung stellt das /proc-Filesystem mit dem oom_adj-Interface dar. PostgreSQL® 9.0 ergänzt dies durch weitere Maßnahmen. Dedizierte Datenbanksysteme können vom Administrator dahingehend angepasst werden, gar kein Overcommit des virtuellen Speichers zuzulassen, hier muss jedoch sorgfältig abgewogen werden, welche Anforderungen die PostgreSQL®-Instanz an die VM des Kernels stellt. Alle Blog-Artikel zum Thema PostgreSQL® werden auch als Kategorie PostgreSQL® samt eigenem Feed angeboten – und falls ihr nach Support und Services für PostgreSQL® sucht, seit ihr bei uns ebenfalls richtig.
Der Werkzeugkasten eines System-Administrators sollte immer mit effektiven Werkzeugen gefüllt sein. Heute stellen wir das Paket sysstat vor.
Das Paket sysstat ist eine Sammlung von Kommandozeilen-Programmen, die dem Systemadministrator einen schnellen Überblick über die Leistungsfähigkeit des Systems verschaffen. In ihrer Arbeitsweise sind sie ein Frontend zu den Daten des Linux-Kernels, und können dementsprechend nur Daten ausgeben, die der Kernel selbst kennt – sie sammeln keine Daten darüber hinaus!
iostat
iostat liefert vor allen Dingen Status-Informationen über den Datendurchsatz der Festplatten, angeschlossener NFS-Laufwerke und aller CPUs. Ist ein System im Leistungsverhalten auffällig, kann iostat verwendet werden, um z.B. IO-Waits zu identifizieren.
Linux 2.6.31-19-generic (mymachine) 04.03.2010 _x86_64_ (2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
11,82 0,29 3,44 1,25 0,00 83,20
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 9,39 161,19 168,44 4264806 4456696Die zur Verfügung stehenden Optionen sind dabei umfangreich, die Wichtigsten dienen vor allen Dingen der Feststellung für spezialisierte Ausgaben:
- -d
- Anzeige nur der Festplatten-Daten.
- -c
- Anzeige der reinen CPU-Daten.
- -p
- Anzeige der IO-Daten je Partition.
- -n
- Anzeige der NFS-IO-Daten.
- -x
- Erweiterte Anzeige der Festplatten-Daten.
- -t $NUM1
- Gibt an, nach wie vielen Sekunden die Anzeige aktualisiert werden soll.
mpstat
Das Werkzeug mpstat dient der genaueren Analyse der Prozessor-Auslastung – ohne weitere Option wird eine Standard-Übersicht gezeigt, die an die Ausgabe von iostat erinnert.
Linux 2.6.31-19-generic (mymachine) 04.03.2010 _x86_64_ (2 CPU) 17:01:52 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle 17:01:52 all 11,96 0,29 3,26 1,23 0,10 0,11 0,00 0,00 83,06
Im Gegensatz zu iostat wird hier aber direkt schon die Arbeit mit aufgelistet, die der Prozessor an Soft- und Hardware-Interrupts verrichtet. Mit der Option -A wird die Ausgabe erweitert: die Statistiken werden pro Prozessor angezeigt, außerdem werden die Interrupts pro Sekunde pro Prozessor angezeigt.Eine Zahl $NUM hinter dem Befehl lässt diesen als Prozess laufen, und aktualisiert die Anzeige alle $NUM Sekunden.
pidstat
pidstat hilft bei der Analyse einzelner Prozesse – während der Aufruf ohne Optionen noch eine Liste aller Prozesse anzeigt, hilft die Option -C bei der Spezifizierung der zu untersuchenden Tasks:
Linux 2.6.31-19-generic (mymachine) 04.03.2010 _x86_64_ (2 CPU) 17:02:32 PID %usr %system %guest %CPU CPU Command 17:02:32 1 0,00 0,00 0,00 0,00 1 init 17:02:32 2888 0,00 0,00 0,00 0,00 0 start_kdeinit 17:02:32 2889 0,00 0,00 0,00 0,00 0 kdeinit4
Mit der zusätzlichen Option -d werden I/O-Statistiken zu den Prozessen angezeigt. Mit -p kann die PID definiert werden statt des Prozess-Namens, -r verschafft einen Überblick über die Speicherauslastung. Eine Zahl $NUM hinter dem Befehl lässt diesen als Prozess laufen, und aktualisiert die Anzeige alle $NUM Sekunden.
sar
Die bisher vorgestellten Informationen geben jeweils nur einen Schnappschuss der Systemleistung wieder, echtes Status-Logging findet hier aber nicht statt. Dafür ist das Programm sar mit seinen Helfer-Programmen verantwortlich: es nimmt via Cron-Job alle 10 Minuten verschiedene Leistungsdaten des Systems auf, und sichert diese. Ein einfacher Aufruf gibt eine erste Idee des Performance-Verhaltens:
Linux 2.6.31-19-generic (mymachine) 04.03.2010 _x86_64_ (2 CPU) 09:30:30 LINUX RESTART 09:35:02 CPU %user %nice %system %iowait %steal %idle 09:45:01 all 17,38 1,02 5,10 3,87 0,00 72,63 09:55:01 all 11,90 0,27 2,86 0,75 0,00 84,23 10:05:01 all 10,20 3,52 3,46 2,55 0,00 80,27 10:15:02 all 12,96 0,32 3,18 0,65 0,00 82,89 10:25:01 all 7,94 0,18 3,17 2,42 0,00 86,30 10:35:01 all 12,41 0,89 4,55 0,56 0,00 81,60 10:45:02 all 8,97 0,09 3,51 0,89 0,00 86,55
Der Abruf aller Informationen mit sar -A sprengt aber leicht jede Bildschirmgröße. Die unterschiedlichen Einzel-Optionen sind zu zahlreich, um sie hier detailliert aufzulisten, einen Überblick gibt die Man-Page.
Dieser Artikel wurde ursprünglich geschrieben von Roland Wolters.