Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

Was fehlt?

Es liegt in der Natur der Sache: Ein Wiki ist niemals fertig. Wir geben uns große Mühe, mit der Entwicklung Schritt zu halten; lassen Supportanfragen direkt in neue Artikel einfließen … aber auch wir sind nicht perfekt. Wenn du hier nicht fündig wirst: Nicht schmollen - Bescheid sagen! Unter hallo@uberspace.de steht dir unser Team gerne bereit. Hand drauf!

system:daemontools

daemontools

Die daemontools sind eine Sammlung von kleinen Tools von Dan J. Bernstein, der auch schon für den von uns eingesetzten Mailserver verantwortlich zeichnet. Sie sind eine ideale Möglichkeit, um Programme, die dauerhaft laufen sollen, zu verwalten und vor allem auch zu überwachen - das gilt auch für den Einsatz von runwhen, was wir als Alternative zu Cron empfehlen; aber auch jeder andere Daemon kann prima auf diese Art gesteuert werden, beispielsweise wenn du MongoDB auf deinem Uberspace laufen lassen willst.

Für Ungeduldige

Mit dem Befehl uberspace-setup-svscan kannst du dein persönliches ~/service-Verzeichnis aktivieren, das den Betrieb eigener Daemons oder auch für den Einsatz von runwhen ermöglicht. So sieht's aus:

[annette@amnesia ~]$ test -d ~/service || uberspace-setup-svscan 
Creating the /etc/run-svscan-annette/run script
Symlinking /etc/run-svscan-annette to /service/svscan-annette to start the service
Waiting for the service to start ... 1 2 3 4 started!

Congratulations - your personal ~/service directory is now ready to use!

Wie man einen Dienst einrichtet und entfernt steht unten. Insbesondere beim Entfernen halte Dich bitte an diese Anleitung, sonst gibt es Probleme. Und noch ein Hinweis der Dir das Leben erleichtern kann: Lösche niemals das ~/service-Verzeichnis! Die daemontools mögen das überhaupt nicht und wenn Du es doch tust, musst Du Dich bei uns melden, damit wir es für Dich reparieren, Du kannst das nicht alleine.

Andere User (und wir) haben uns bei diversen Diensten auch schon damit beschäftigt wie run-Skripte aussehen müssen.

Worum geht's?

Webhosting hat heute nicht mehr viel mit dem Ablegen ein paar statischer Dateien zu tun, und auch der Einsatz von PHP-Scripts ist nicht das Ende der Fahnenstange. Komplexere Webapplikationen laufen inzwischen viel eher als eigene Dienste, seien es Catalyst-Applikationen mit Perl oder Rails-Applikationen mit ruby. Das hat entscheidende Vorteile: Eine komplexe Applikation muss nur einmal gestartet werden und kann dann laufend Anfragen beantworten und dabei Datenbankverbindungen und Logfiles offenhalten, es müssen nicht laufend wieder Codeschnipsel von Platte gelesen und neu interpretiert werden … mit dem Ergebnis, dass die Antwortzeiten solcher Applikationen oft sogar deutlich schneller sind als der Abruf statischer Dateien.

Die meisten Dokumentationen erläutern allerdings nur, wie man seine Applikation auf einem eigenen Server persistent laufen lassen sollte, wobei häufig root-Rechte vorausgesetzt werden und die Applikation in den Boot-Prozess des Servers integriert wird. Das halten wir bei Uberspace.de nicht mehr für zeitgemäß, denn solche Aufgaben lassen sich viel sauberer innerhalb des Userspaces realisieren - und dabei möchten wir dich gerne unterstützen.

Die Eigenschaften eines Daemons

Der ideale Daemon …

  1. sollte beim Booten automatisch starten.
  2. sollte zur Laufzeit problemlos angehalten oder neu gestartet werden können oder sonstige Signale erhalten können
  3. sollte überwacht werden und im Fall eines Crashs automatisch neu gestartet werden

Am dritten Punkt scheitern klassische Initscripts direkt - Service-Monitoring ist mit ihnen nicht zu leisten. Und der zweite Punkt ist oftmals auch sehr hakelig, weil er beim klassischen SysVinit darauf basiert, dass ein Daemon beim Starten seine Prozess-ID in eine definierte Datei schreibt und sich dann selbst in den Hintergrund verabschiedet, und wenn man den Dienst stoppen oder ihm irgendein Signal geben will, muss man erstmal die Prozess-ID aus dieser Datei auslesen. Wenn Software einmal crasht, bleibt die Datei mit der Prozess-ID bestehen, und nicht selten macht dann ein Neustart Probleme, wenn die Software aufgrund der bestehenden Datei meint, bereits zu laufen. Im schlimmsten Fall ist die Software abgebrochen, hat die Prozess-ID-Datei zurückgelassen, und ein anderer Prozess besitzt inzwischen die fragliche Prozess-ID und bekommt dann die eigentlich für einen ganz anderen Daemon gedachten Signale.

Ein Beispiel zu Beginn

Die daemontools lösen diese Probleme mit einem ganz simplen Prinzip: Der fragliche Daemon bleibt im Vordergrund, als Kind-Prozess unter der unmittelbaren Kontrolle eines Prozess-Supervisors. Dieser kann ihm jederzeit beliebige Signale senden, und kann sich auf Wunsch auch direkt um das Logging kümmern. Als kleinen Nebeneffekt lässt sich der Prozess in der Prozessliste auch noch sehr schön strukturiert betrachten. Hier ein praktisches Beispiel:

$ ps fuxwww
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
annette     27647  0.0  0.0  91052  1828 ?        S    23:13   0:00 sshd: annette@pts/2       
annette     27648  3.5  0.0  67668  3156 pts/2    Ss   23:13   0:00  \_ -bash
annette     27698  0.0  0.0  65568   932 pts/2    R+   23:13   0:00      \_ ps fux
annette     11843  0.0  0.0   3832   392 ?        S    Jan14   0:00 /command/svscan /home/annette/service
annette     11931  0.0  0.0   3660   336 ?        S    Jan14   0:00  \_ supervise uberspace
annette     27529  3.3  1.2 182388 81112 ?        S    23:13   0:01  |   \_ perl /home/annette/Uberspace/script/uberspace_fastcgi.pl --listen /var/www/virtual/annette/uberspace.sock --keeperr
annette     11932  0.0  0.0   3660   336 ?        S    Jan14   0:00  \_ supervise log
annette     11949  0.0  0.0   3804   408 ?        S    Jan14   0:00      \_ multilog t ./main

Der Prozess svscan ist gewissermaßen das Herzstück: Er überwacht ein Verzeichnis (hier entsprechend /home/annette/service) mit Diensten. Findet es einen Dienst vor, so wie hier den Dienst uberspace, startet es ihn unter einem supervise-Prozess. supervise führt den Dienst dann als Kind-Prozess - in diesem Fall eine in Perl entwickelte Catalyst-Applikation. Auf gleicher Hierarchie findet sich außerdem ein log-Prozess, der alle Ausgaben des Diensts mit multilog loggt.

Letztlich folgen die daemontools also der Unix-Devise, dass jedes Tool nur einen ganz bestimmten Job erledigen sollte. Damit erleichtert es gleichzeitig die Entwicklung von Daemons, denn jene brauchen keinen Code mehr, um Pid-Files zu schreiben und sich in den Hintergrund zu schieben, und sie können sich jeglichen Codes fürs Logging schenken, denn es reicht, einfach auf der Standardausgabe zu schreiben.

Einen Daemon einrichten

Du kannst von uns auf Wunsch problemlos dein eigenes ~/service-Verzeichnis bekommen, das genauso funktioniert wie das „große“ unter /service, unter dem die von uns betriebenen Dienste laufen. Das geht ganz einfach durch die Eingabe von uberspace-setup-svscan wie im Abschnitt Für Ungeduldige beschrieben. Dieser Vorgang ist eine einmalige Aktion - unter der für dich dann laufenden svscan-Instanz kannst du in Eigenregie beliebig viele Dienste einrichten. Und so geht's:

  1. Jeder Dienst besteht aus einem Verzeichnis, das ein Script namens run beinhaltet. Dieser Name ist fest vorgegeben. Im einfachsten Fall ist run ein Shell-Script, das etwas in der Art exec was-auch-immer beinhaltet, mehr nicht.
  2. Möchtest du Logging? Dann legst du in dem Dienstverzeichnis noch ein Unterverzeichnis log an und dort dann ebenfalls ein run-Script. Bei dieser Kombination erstellt supervise automatisch eine Pipe zwischen der Standardausgabe des Daemons und der Standardeingabe des Log-Prozesses, der dann typischerweise multilog einsetzt (dazu gleich mehr).
  3. Fertig? Sind die Scripts ausführbar? Dann legst du einen Symlink dieses Verzeichnisses nach ~/service an. Innerhalb weniger Sekunden wird dein Daemon gestartet.

In Code ausgedrückt könnte das so aussehen, wenn du einen Daemon namens ~/bin/my-daemon entwickelt hast, der jetzt unter ~/service laufen soll:

[annette@amnesia ~]$ mkdir ~/etc/run-my-daemon
[annette@amnesia ~]$ cat <<__EOF__ > ~/etc/run-my-daemon/run
#!/bin/sh
exec ~/bin/my-daemon 2>&1
__EOF__
[annette@amnesia ~]$ chmod +x ~/etc/run-my-daemon/run
[annette@amnesia ~]$ mkdir ~/etc/run-my-daemon/log
[annette@amnesia ~]$ cat <<__EOF__ > ~/etc/run-my-daemon/log/run
#!/bin/sh
exec multilog t ./main
__EOF__
[annette@amnesia ~]$ chmod +x ~/etc/run-my-daemon/log/run
[annette@amnesia ~]$ ln -s ~/etc/run-my-daemon ~/service/my-daemon

Du kannst das mit unserem Setup-Tool auch abkürzen, dem du einfach den Namen des Service (hier: my-daemon) sowie den Pfad zum auszuführenden Daemon (hier: ~/bin/my-daemon) mitgibst:

[annette@amnesia ~]$ uberspace-setup-service my-daemon ~/bin/my-daemon
Creating the ~/etc/run-my-daemon/run service run script
Creating the ~/etc/run-my-daemon/log/run logging run script
Symlinking ~/etc/run-my-daemon to ~/service/my-daemon to start the service
Waiting for the service to start ... 1 2 3 started!

Congratulations - the ~/service/my-daemon service is now ready to use!

Das war's! Nicht nur, dass dein Daemon jetzt läuft; er wird auch beim Booten automatisch gestartet, und sollte er einmal crashen (was ja nun mal auch den Besten von uns passieren kann), so wird er automatisch neu gestartet.

Wichtig für deinen Daemon ist eben: Er darf sich nicht in den Hintergrund schieben (sonst ist supervise der Meinung, er habe sich beendet, und startet eine neue Instanz). Wenn du einen Daemon einrichtest, schau bitte unbedingt nach Optionen, die dafür sorgen, dass er im Vordergrund bleibt. Insbesondere das Tool forever aus dem Umfeld von node.js kannst du keinesfalls unter den daemontools einsetzen: Ihm fehlt eine Option, im Vordergrund zu bleiben; es entzieht sich insofern selbst dem Prozessmonitoring, ohne dass man dies abstellen könnte.

Wenn der Daemon läuft

Sobald der Daemon einmal gestartet wurde, kannst du ihn mittels des Tools svc (service control) verwalten. Du gibst ihm mit einem Schalter das gewünschte Signal an und danach das Verzeichnis, in dem der Dienst läuft, den du steuern willst, wobei du auch gleich mehrere hintereinander angeben kannst. Die gängigsten Schalter sind:

-u up, also Dienst starten
-d down, also Dienst beenden
-h hup, ein HUP-Signal senden (Reload)

Ein vollständiger Aufruf sähe also so aus:

[annette@amnesia ~]$ svc -h ~/service/my-daemon

Du kannst Switche auch kombinieren, beispielsweise Stop und anschließend einen erneuten Start:

[annette@amnesia ~]$ svc -du ~/service/my-daemon

Bitte beachte, dass das Logging (wenn du dafür auch einen Dienst angelegt hat) unter einem separaten supervise-Prozess läuft. Änderst du also etwas am log/run-Script, musst du anschließend das Logging neu starten:

[annette@amnesia ~]$ svc -du ~/service/my-daemon/log

Es gibt noch einige weitere Switche, die du im Alltag aber eher selten brauchen wirst. Die Dokumentation zu svc verrät dir mehr dazu.

Logging

Im obigen Beispiel haben wir direkt multilog fürs Logging eingesetzt. Es nimmt auf der Standardeingabe zu loggende Daten entgegen, kann diese auf Wunsch noch mit einem Timestamp versehen und schreibt sie in eine Datei. Dabei nimmt es eine automatische Log-Rotation vor, die on the fly funktioniert: Erreichen Logs eine bestimmte Größe, werden sie automatisch rotiert; die Anzahl der Logs kannst du ebenso selbst bestimmen. Außerdem kannst du mit multilog auch einfaches Pattern Matching machen, um bestimmte Meldungen aus den Logs einfach direkt rauszuhalten, noch bevor sie geschrieben werden. Und schließlich bietet es auch noch die Möglichkeit, Logs vor der Rotation durch einen anderen Prozess zu schicken, der sie beispielsweise irgendwie statistisch auswertet oder komprimiert.

Im obigen Beispiel hatten wir den einfach Fall, bei dem fast nur Standardeinstellungen verwendet werden; lediglich Timestamps haben wir ergänzt; dafür sorgt das t:

multilog t ./main

Das angegebene Verzeichnis ./main ist das, wohin multilog dann die Logs schreibt. Hierbei loggt es stets in eine Datei namens current, die bei Erreichen der vorgegebenen Größe dann in eine Datei umbenannt wird, die einen TAI64N-Zeitstempel als Name trägt (auch die Timestamps innerhalb des Logs, die mit t aktiviert werden, tragen dieses Format).

Die Logs werden standardmäßig beim Erreichen einer Größe von 99999 Bytes rotiert (zumindest ungefähr, weil multilog immer versucht, sauber an Zeilenumbrüchen zu rotieren), und es werden standardmäßig 10 Logfiles aufbewahrt. Auf diese Weise belegen die Logfiles von Haus aus maximal ca. 1 MB Speicherplatz in deinem Uberspace. Möchtest du größere Logs und/oder mehr davon aufbewahren, kannst du das mit den s- und n-Schaltern regeln (size bzw. number):

multilog t s999999 n20 ./main

Werden die Logs größer, möchtest du vielleicht zumindest die rotierten Logfiles komprimieren - gut, dass sich über die Preprozessor-Option ! unter anderem auch gzip einbinden lässt, was ohne weiteren Angaben die Standardeingabe liest, komprimiert und auf die Standardausgabe schreibt:

multilog t s999999 n20 '!/bin/gzip' ./main

Bitte beachte, dass du diese Angabe in einfache Anführungszeichen schreiben musst, weil ansonsten das Ausrufezeichen bereits von der Shell und nicht von multilog interpretiert wird.

Noch ein abschließendes Wort zu den Timestamps: Mit TAI64N hattest du vermutlich bisher wenig zu tun, und offen gesagt ist es auch in erster Linie für Maschinen lesbar. Die daemontools beinhalten aber auch das Tool tai64nlocal, das - als Pipe eingesetzt - die TAI64N-Timestamps in lokale Zeitangaben umwandeln. Du könntest dir deine Logs also mit konvertierten Zeitstempeln beispielsweise so anschauen:

[annette@amnesia ~]$ cat ~/service/my-daemon/log/main/* | tai64nlocal | less

Stefan Pfeiffer hat einen schönen Vorschlag, um sich das Lesen der Logs etwas einfacher zu machen - er hat seine ~/.bashrc um die Funktion readlog() erweitert, die den obigen Befehl abkürzt:

readlog()
{
        if [ -n "$1" ]; then
                cat ~/service/$1/log/main/* | tai64nlocal | less;
        else
                echo "Usage: readlog <daemonname>";
        fi;
}

Auf diese Weise lassen sich die Logs einzelner Dienste sehr einfach lesen, z.B. für ~/service/dropboxd mit readlog dropboxd. Danke für's Teilen!

Das Script kann natürlich beliebig angepasst werden,

echo -e "Usage: readlog <daemonname> \navailible daemons: \n\n`ls ~/service/`";

listet z.B. alle vorhandenen Dienste auf.

Einen Daemon entfernen

Wenn du den von dir eingerichteten Dienst nicht mehr brauchst, kannst du ihn natürlich auch wieder entfernen. Das ist ganz simpel: Du wechselst in das Verzeichnis, in dem der Dienst liegt, löschst dann den dafür Symlink und stoppst anschließend die noch laufenden Prozesse (also sowohl den Dienst selbst als auch das zugehörige multilog als auch die beiden überwachenden supervise-Instanzen). Beispiel:

[annette@amnesia ~]$ cd ~/service/my-daemon
[annette@amnesia my-daemon]$ rm ~/service/my-daemon
[annette@amnesia my-daemon]$ svc -dx . log
[annette@amnesia my-daemon]$ rm -rf ~/etc/run-my-daemon

Auf diese Weise wird der Dienst sauber entfernt. In der vorletzten Zeile findest du ein Beispiel für kombinierte svc-Switches: Der Dienst soll erst gestoppt werden (-d) und anschließend wird auch der überwachende supervise-Prozess gestoppt (-x). Im Alltag gibt es keinen Anlass, das zu tun, weshalb wir -x auch nicht in der obigen Tabelle aufgeführt haben; es ergibt ausschließlich beim Entfernen eines Daemons Sinn. Außerdem siehst du hier ein Beispiel, dass du svc auch mehrere Dienste übergeben kannst, im konkreten Fall erst . (also das aktuelle Verzeichnis, in das wir ja gerade gewechselt haben) und dann log (also das Unterverzeichnis log relativ zum aktuellen Verzeichnis).

Das Rad nicht neu erfinden

Die Frage wie das run-Skript für einen bestimmten Dienst aussehen muss, ist möglicherweise schon beantwortet.

system/daemontools.txt · Zuletzt geändert 2015/04/10 15:52 von uber