Service Monitoring unter FreeBSD
Server mit FreeBSD zu betreiben. gefällt mir. Was allerdings ein schlechtes Gefühl hinterlässt, ist die Attitüde von FreeBSD, alles oder nichts zu liefern. In diesem Sinne gibt es beim leichtesten Husten einen Kernelpanik (der standardmäßig die Kiste hängen lässt). Für Dienste gilt ähnliches: Sie werden beim Booten gestartet und dann müssen sie allein überleben.
Leben am Limit
Keine Software ist perfekt. Wenn sie mal abstürzt, gibt es zwei Möglichkeiten damit umzugehen.
Die erste Variante besteht in einem stillen Restart des Dienstes oder der Maschine. Mit etwas Redundanz im Aufbau heilt sich ein Fehler von allein.
Die andere Variante soll den Fehler schnellstmöglich beheben. Dazu muss ein Fehler weh tun, nein, es muss richtig schmerzen. Und das ist der FreeBSD Weg.
Entdeckt der Kernel einen Fehler, so wirft er gern und schnell einen panic. Die Standardbehandlung besteht darin, Debuginfos auf die Konsole zu schreiben und das System anzuhalten. Der zuständige Admin kann dann an der Maschine selbst sehen, was vorgefallen ist und manuell neu starten.
In ähnlicher Weise wird der Userspace befüllt. Während des Boot-Prozesses werden die Dienste gemäß der rc.conf gestartet. On es klappt oder nicht, steht dabei nicht zur Debatte. Und wenn ein Dienst später mal abstürzt, dann muss der Admin auf die Maschine gehen und den Dienst manuell neu starten.
Das ist für die Produktion klar indiskutabel.
Selbstheilung
Der Umgang mit den Kernelpanics ist noch am einfachsten. Man übergibt das Handling dem Kernel Debugger und scripted dessen Verhalten.
[root@server ~]# service ddb rcvar # ddb : DDB kernel debugger # ddb_enable="YES" # (default: "")
Die zugehörigen Scripte finden sich in /etc/ddb.conf:
# kdb.enter.panic panic(9) was called. script kdb.enter.panic=textdump set; capture on; run lockinfo; show pcpu; bt; ps; alltrace; capture off; call doadump; reset
Im Detail tut das folgendes:
-
Umstellen auf Textdumps, statt eines kompletten Speicherabzugs.
Das macht Dumps deutlich schneller und kleiner, so dass sie in die Swappartition passen. - Dann wird der Mitschnitt der Konsolenausgabe (für den Textdump) aktiviert und verschiedene Debuginfos eingesammelt.
- Zum Schluss wird der Mitschnitt geschlossen und in die Swappartition geschrieben.
- Abschließend rebootet das System von allein.
Mit der folgenden Einstellung in der rc.conf werden die Dumps beim Booten (vor der Aktivierung des Swapbereiches) ausgelesen und nach /var/crash gespeichert.
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable dumpdev="AUTO"
Nach dem Reboot kann der Admin die Diagnoseinformation auslesen:
[root@server ~]# cat /var/crash/info.69 Dump header from device /dev/aacd0s1b Architecture: amd64 Architecture Version: 1 Dump Length: 105472B (0 MB) Blocksize: 512 Dumptime: Wed Jan 25 15:25:04 2017 Hostname: server... Magic: FreeBSD Text Dump Version String: FreeBSD ... Panic String: Dump Parity: 1769329166 Bounds: 69 Dump Status: good [root@server ~]# tar xOf /var/crash/textdump.tar.69 db:0:kdb.enter.default> show pcpu cpuid = 5 dynamic pcpu = 0xffffff807f3df680 curthread = 0xffffff000ccc9000: pid 12 "swi1: netisr 5" curpcb = 0xffffff835f80cd10 fpcurthread = none idlethread = 0xffffff0002b458c0: tid 100005 "idle: cpu5" curpmap = 0xffffffff80cb05d0 tssp = 0xffffffff80d21188 commontssp = 0xffffffff80d21188 ...
Soweit so einfach.
Lost in userland
Schlimmer ist die Situation mit den verschiedenen Diensten. Es gibt keinen zentralen Weg, die benötigten Dienste automatisch neu zu starten, wenn diese durch irgendeinen Fehler abstürzen.
Einige Dienste bringen deswegen eigene WatchDog-Services mit. Andere hoffen einfach darauf, dass alles gut geht.
Es gibt einige Pakete, die sich mit dem Thema befassen, aber die sind entweder aus einer anderen Welt (wie monit), bauen auf doppelte Datenhaltung (wie fsc) oder sind komplett ungepflegt.
Ich möchte nun eigentlich die Bordmittel benutzen, um die benötigten Services regelmäßig zu prüfen und bei Bedarf neu zu starten.
Zentrale Komponente der Diensteverwaltung ist bei FreeBSD rc.conf. Man benutzt es über Scripte, vor allem den Wrapper service.
service -e listet alle aktivierten Dienste auf, allerdings etwas zu viel. So interessiere ich mich nur für die Dienste, die ein pidfile mitbringen, d.h. langfristig laufen. Die anderen Dienste sind oft nur einmalig auszuführende Einstellungen. Und dann gibt es noch Dienste, auch ohne pidfile durchlaufen. Also habe ich ein schnelles Shellscript geschrieben:
[root@server ~]# cat /usr/local/sbin/restart_services.sh #! /bin/sh # services which do not have a pidfile in the config always="bird bird6" # services which should not be checked never="" service -e | while read a; do doit=0 for name in $always; do if expr "$a" : ".*/$name" >/dev/null; then doit=1 break fi done for name in $never; do if expr "$a" : ".*/$name" >/dev/null; then doit=-1 break fi done if [ "$doit" -eq 0 ]; then if egrep -q 'pidfile' $a; then doit=1 fi fi if [ "$doit" -gt 0 ]; then if ! $a onestatus >/dev/null; then echo "Starting $a" $a start fi fi done
Dieses Script prüft für jeden aktivierten (gemäß rc.conf) Dienst, ob dieser ein pidfile hat oder explizit benannt (always) wurde. Dienste, die ausgeschlossen wurden (never), werden ignoriert. Für die dann noch übrig bleibenden Dienste wird der Status abgefragt. Falls der Dienst nicht läuft, wird er gestartet.
Das Script kommt in die crontab von root:
2-58/5 * * * * /usr/local/sbin/restart_services.sh
Und ich kann wieder ruhig schlafen.