systemd ist lange überfällig…

(2011-11-12)

systemd ist lange überfällig, denn die Schmerzen beim Überführen eines quick'n'dirty shell scripts in einen ordentlich wartbaren Dienst sind mit sysvinit einfach viel zu groß.

Vor ziemlich langer Zeit habe ich den RaumZeitMPD geschrieben, einen IRC-Bot, welcher bei !stream anzeigt, welches Lied gerade gespielt wird und bei !stream <url> die angegebene URL abspielt. Später wurde er erweitert um bei !ping die Rundumleuchte für 5 Sekunden aufleuchten zu lassen, damit einer der Anwesenden seine Aufmerksamkeit auf das IRC richtet.

Dieser Bot lief bislang in einer screen-Session auf der Blackbox. Dieses Setup ist natürlich nicht ideal, denn sofern die Blackbox neugestartet wird, muss jemand mit Zugriff auf die Blackbox diese Session neustarten. Das ist unzuverlässig und erfordert, dass permanent mindestens eine Person das Wissen haben muss, wie man den RaumZeitMPD startet. Eine bessere Lösung ist es, ihn bei Systemstart zu starten und als Daemon laufen zu lassen.

Das kriegt man natürlich mit sysvinit hin. Ein Initscript, welches anhand des minimale Debian-Beispiels angefertigt wurde, ist allerdings 140 Zeilen lang. Zum Vergleich: Der (Perl-)Code für RaumZeitMPD ist 173 Zeilen, inklusive 30 Zeilen Dokumentation! Mein Projekt hat sich also gerade um fast 100% aufgeblasen, nur damit ich es bei Systemstart starten lassen kann.

Doch das ist nicht das eigentliche Problem, daran hat man sich ja gewöhnt. Die richtigen Schmerzen spürt man, wenn man sich um das daemonizing kümmert. Hierzu gibt es zwei Möglichkeiten:

  1. Man benutzt --background und --make-pidfile beim Aufruf von start-stop-daemon(8) im Initscript. Der Nachteil: start-stop-daemon setzt /dev/null als stdin, stdout und stderr. Plötzlich braucht man also eigenes Logging, und damit verbunden wieder Parameter, also auch Parameterverarbeitung, eine Hilfefunktion, Dokumentation.
  2. Man implementiert das daemonizing selbst und benutzt Ausgabeumlenkung im Initscript. Hierzu gibt es zwar das Modul Proc::Daemon, das stellt aber eine zusätzliche Dependency dar, da es nicht mit perl mitgeliefert wird. In der Vergangenheit habe ich die Erfahrung gemacht, dass sehr viele Nutzer (auch erfahrene Nutzer) vor CPAN zurückschrecken. Das Modulsystem von Perl ist leider zu unbekannt. Daraus resultiert, dass ich daemonizing selbst implementieren müsste, was zwar möglich ist (und auch nicht mehr als ca. 50 Zeilen Code sind), aber das ist Code, der nicht in mein Script (!) gehört. Das soll kurz und prägnant bleiben, ohne Code, der nicht dem eigentlichen Zweck dient.

Nun kann man natürlich einen Kompromiss wählen und start-stop-daemon zum daemonizing benutzen, aber via Sys::Syslog nach syslog loggen. Das ist gut, denn damit bekommen wir die aktuelle Uhrzeit, den Programmnamen, die PID und flexible Umlenkungsmöglichkeiten (remote logging) beim Logging geschenkt. Allerdings sind viele Scripts so gestaltet, dass sie relativ viel loggen. Das syslog ist (meiner Ansicht nach) wichtigen Meldungen vorbehalten. Ich würde also deutlich weniger loggen als in meiner ursprünglichen Version in der screen-Session, was schlecht ist – logs helfen enorm beim Verstehen/Analysieren von Problemen. Die Lösung dafür ist also, rsyslogd so zu konfigurieren, dass er die Ausgabe für den RaumZeitMPD in eine eigene Datei schreibt:

$ cat /etc/rsyslog.d/raumzeitmpd.conf
# Log all lines from RaumZeitMPD to /var/log/raumzeitmpd.log (without syncing)
# and discard them.
:programname, isequal, "RaumZeitMPD"  -/var/log/raumzeitmpd.log
:programname, isequal, "RaumZeitMPD" ~

Doch damit ist aus dem Script auf einmal ein vollwertiger Daemon geworden, den ein Gelegenheitshacker nicht mehr interaktiv "mal eben" erweitern kann – nach dem aufwändigen Clonen des Codes und Installieren der Abhängigkeiten müsste er nun auch noch seinen syslog-daemon umkonfigurieren, neustarten und permanent tail -f /var/log/raumzeitmpd.log in einem anderen Fenster laufen lassen.

Wie hilft hier nun systemd? Es nimmt mir die ganze Arbeit ab. Ich muss mich nicht darum kümmern, beim starten zu daemonizen (oder im Vordergrund zu bleiben, wenn man interaktiv debuggen will). Ich kann in meinem Script einfach nach stdout und stderr loggen, im service-file raumzeitmpd.service langt der Eintrag StandardOutput=syslog damit bei Systemstart nach syslog geloggt wird – interaktive Aufrufe schreiben dennoch nach stdout. Ich brauche keine Optionen einführen. Mein Script bleibt hübsch und konzentriert sich auf das Wesentliche.

Sobald systemd etwas verbreiteter ist, können wir uns endlich von daemonizing, logging und anderem obsoleten Unsinn trennen und ohne viel Aufwand einfache Scripts in sinnvoller Weise deployen.