Seit Debian 8, Ubuntu 15.04 und RHEL 7 gehört Systemd zum Quasi-Standard der Start- und Stopscripte unter Linux. Früher musste man in /etc/init.d/ eigene Scripts schreiben und ablegen. Und heute? Heute ist es einfacher. Ein bisschen.

Ich migriere diese Tage alle meine Dienste von einem dicken Dedicated Server von OVH auf zwei virtuelle KVM Maschinen bei Contabo. Dazu zählt auch ein Teamspeak 3 Server mit 6 Instanzen, der jeweils noch mit sehr alten und klobigen SysVinit Scritps – also /etc/init.d/ts3 und /etc/init.d/tsdns – gestartet wird.

Debian «Jessie» 8 hat schon vor zwei Jahren Systemd als Standard mitgebracht, machte aber auch SysVinit Scripts ausführbar und kann dadurch mit beiden Startscript-Varianten gleichzeitig umgehen. Aber schon bald steht Debian 9 ins Haus und es wird langsam aber sicher Zeit, alte SysVinit Scripte auf Systemd-Varianten umzubauen.

Beispiel 1 – Teamspeak 3: Wenn start.sh/stop.sh schon vorhanden sind

Im ersten Beispiel baue ich aus einem schon bestehenden Start- und Stopscript – das von Teamspeak 3 mitgliefert wird – ein fähiges Systemd-Startscript, so dass der TS3 hochfährt und mit systemctl Befehlen gesteuert werden kann. Los geht’s.

Folgende Datei anlegen:

touch /etc/systemd/system/ts3.service

Der Inhalt für unseres Beispiel Teamspeak 3 ist wie folgt:

[Unit]
Description=TeamSpeak 3 Server
After=network.service

[Service]
User=ts3
Group=ts3
Type=forking
WorkingDirectory=/home/ts3/teamspeak3-server_linux_amd64/
ExecStart=/home/ts3/teamspeak3-server_linux_amd64/ts3server_startscript.sh start
ExecStop=/home/ts3/teamspeak3-server_linux_amd64/ts3server_startscript.sh stop
PIDFile=/home/ts3/teamspeak3-server_linux_amd64/ts3server.pid
RestartSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Interessant zu wissen: Unter diesem Pfad /etc/systemd/system/* werden üblicherweise selbstangelegte Systemd-Files abgespeichert. Systemweite Systemd-Scripts liegen hingegen unter /lib/systemd/system/*.

Was bewirkt dieses ts3.service File?

Zu Beginn erzwingen wir nach der Beschreibung des Services mit After=network.service den Start des TS3 erst dann, wenn die Internetverbindung hochgefahren wurde.

Unter [Service] habe ich das WorkingDirectory angegeben und user:group jeweils ts3. Natürlich muss der Pfad und der Username angeglichen werden, falls der eigene TS3 anderswo liegt oder mit einem anderslautenden Account gestartet wird.
Type=forking wählt man, wenn ExecStart und ExecStop auf vorhandene fähige Start-/Stopscripte zeigen. Falls kein Stopscript vorhanden ist, kann Type=simple die einfachste Lösung sein, mehr dazu weiter unten im zweiten Beispiel.

Desweiteren sollte mit Type=forking eine PIDFile angegeben werden. Die letzten zwei Restart-Parameter sind fast selbsterklärend, falls der Service einmal crashed würde wird Systemd alle 15 Sekunden versuchen den TS3 wieder zu starten. Anstatt Restart=always gibts noch weitere Optionen um nur in gewollten Fällen einen Restart auszulösen, mehr dazu kann in der hässlichen Dokumentation nachgelesen werden 🙂

Unter [Install] wird das angegeben, was wir noch als Runlevel von SysVinit kennen, i.d.R. bei Serversystemen WantedBy=multi-user.target.

ts3.service aktivieren!

Jetzt muss Systemd das neue Startscript ohne Reboot bekannt gemacht werden:

  1. systemctl daemon-reload
  2. systemctl enable ts3.service
  3. systemctl start ts3
  4. systemctl status ts3

Sofern das Script mit .service endet und am richtigen Ort liegt, erkennt systemctl daemon-reload die neue Datei und macht sich bereit damit zu arbeiten. Somit darf es ab sofort aktiviert werden: systemctl enable ts3. Der erste Start mittels systemctl start ts3 wird zeigen, ob die Mühe es Wert war 🙂 Ob der TS läuft und welche PID er bekommen hat, wird systemctl status ts3 anzeigen. Alternativ kann mit ps -ef | grep -i ts3 nachgeschaut werden, welche (alten) PIDs mit ts3 noch laufen.

Zu guter Letzt kann ein Reboot durchgeführt werden um zu testen, ob der automatische Startup auch wirklich funktioniert. Der Teamspeak sollte spätestens jetzt automatisch nach jedem Server-Neustart hochfahren.

Im nächsten Schritt starten wir TSDNS, und zwar mit Type=simple weil TSDNS kein Stopscript mitliefert. Diese Umgehungslösung ist nun wirklich deutlich einfacher als noch mit SysVinit Scripts.

Beispiel 2 – TSDNS: Falls das Stopscript fehlt

Warum soll man sich überhaupt noch ein Stopscript basteln, wenn Systemd den Stop einfach selbst vornehmen kann 😀 ? TS3 liefert lediglich ein binäres Startscript für TSDNS mit und das Stopscript fehlt einfach. An diesem kleinen Beispiel lässt sich illustrieren wie das Stop-Problem gelöst werden kann:

touch /etc/systemd/system/tsdns.service

Der Inhalt wie folgt:

[Unit]
Description=TSDNS for TS3
After=ts3.service

[Service]
Type=simple
User=ts3
Group=ts3
WorkingDirectory=/home/ts3/teamspeak3-server_linux_amd64/tsdns/
ExecStart=/home/ts3/teamspeak3-server_linux_amd64/tsdns/tsdnsserver
ExecStop=/bin/kill -9 $MAINPID
RestartSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Hier geben wir Type=simple mit und ExecStop=/bin/kill -9 $MAINPID und damit ist der Stop-Mechanismus gegeben. Systemd erstellt beim Start keine neue PID, forked also nicht, und deshalb funktioniert kill -9 $MAINPID danach problemlos.

Nicht vergessen: Auch dieses Service-File muss bekannt gemacht werden damit Systemd damit umgehen kann:

  1. systemctl daemon-reload
  2. systemctl enable tsdns.service
  3. systemctl start tsdns
  4. systemctl status tsdns

TSDNS besser mit SRV Records ersetzen

Zum vorherigen Absatz muss ich gestehen: Mein TSDNS binäres Startscript will verda*** nochmal immer als Root gestartet werden, nur mit „ts3“ geht das bei mir partout nicht. Wieso, habe ich noch nicht herausgefunden, denn wie gesagt, das ./tsdnsserver Script ist kompilliert und kein Bash-Script.

Besser als TSDNS zu benutzen ist es auf SRV Records zu setzen. Eine brauchbare Anleitung zum Syntax der DNS SRV Records gibts unter https://support.teamspeakusa.com/index.php?/Knowledgebase/Article/View/293/12/does-teamspeak-3-support-dns-srv-records, vorausgesetzt, man kennt sich mit DNS bzw. SRV Records ein kleines bisschen aus 🙂 Dann ist diese Lösung deutlich besser als TSDNS separat als Prozess auf dem Server starten zu müssen.

Der Teamspeak-Client prüft nämlich bei jedem Verbindungsaufbau ob valide SRV Records vorliegen und würde darauf anspringen. Damit wird TSDNS an sich überflüssig und man hat einen Dienst weniger den man verwalten muss.

Was ist TSDNS, wofür sind SRV Records gut!?

TSDNS oder extra für TS3 erstellte SRV Records erlauben es auf den TS3-Server zu Verbinden ohne einen Port angeben zu müssen, zum Beispiel ts3.meineURL.de anstatt 123.123.123.123:9987. Es ist also mehr eine kosmetische Angelegenheit, aber eine Schöne 😉

Und zu guter Letzt: Falls jemand konstruktive Ergänzungen hat zu den oben erwähnten Systemd-Beispielen gerne in die Kommentare damit – ich finde es nämlich ätzend die offizielle Dokumentation lesen zu müssen – sie ist mit sehr wenig Liebe und kaum realitätsnahen «Best Practice»-Beispielen erstellt worden. Schade!

  • Martin

    Kann es sein das sich das Problem mit tsdns auflöst wenn du das WorkingDirectory mit angeben würdest?

    MfG Martin

    • Schroeffu

      Leider nein, das File ./tsdnsserver lässt sich ganz unabhängig von Systemd auch so in der Bash nicht ohne Root starten. Habe es eben auch mit einem frischen Download von Teamspeak.com ausprobiert > Entpacken > cd tsdns > ./tsdnsserver > Fail.

      Ich werde die Tage auf SRV Records umstellen, diese Lösung erscheint mir sowieso viel besser.

    • Schroeffu

      Korrektur: Das WorkingDirectory muss angegeben werden, damit die tsdns_settings.ini im selben Ordner eingelesen wird. Fehlt WorkingDirectory, startet TSDNS ohne Konfiguration.

      Bezüglich dem Root-Problem habe ich bemerkt, mit Ubuntu 16.04 (Desktop) klappt es als nicht-root zu starten, mit Debian 8 (meine Server) nicht. Hmm.

  • John

    Dieses ganze ts3server_startscript.sh ist meines Erachtens beim Einsatz von systemd gar nicht mehr nötig. Hast du dir mal angesehen, was das macht? All das kann systemd auch ohne dieses Skript. 🙂

    Ich habe es ganz pragmatisch so gelöst:
    # /etc/systemd/system/teamspeak.service
    [Unit]
    Description=TeamSpeak 3 Server
    After=network.target mysql.service

    [Service]
    ExecStart=/opt/ts3server/ts3server inifile=ts3server.ini
    ExecStop=/bin/kill -s INT $MAINPID
    User=ts3server
    Group=ts3server
    Environment=“LD_LIBRARY_PATH=/opt/ts3server“
    WorkingDirectory=/opt/ts3server

    [Install]
    WantedBy=multi-user.target