Ein Python-Script mit Systemd als Daemon (Systemd tut garnicht weh… :-) )

Einen Python-Server dauerhaft laufen zu lassen (z.B. auf einem Debian/Ubuntu Server oder einem Raspberry Pi) kann man mit Screen realisieren, oder -fast genauso einfach- mit Systemd…

Zuallererst brauchen wir ein Stück Python(3) Programmcode. In die Shebang-Zeile schreiben wir das ‚-u‘-Flag für Python3, damit die Scriptausgabe nicht gepuffert wird (wir wollen jede Ausgabe sofort im Log sehen):

#!/usr/bin/python3 -u
import socket

TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 20  # Normally 1024, but we want fast response

while 1:
   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   s.bind((TCP_IP, TCP_PORT))
   s.listen(1)
   
   conn, addr = s.accept()
   print('Connection address:', addr)
   while 1:
      data = conn.recv(BUFFER_SIZE)
      if not data: break
      print("received data:", data)
      conn.send(data)  # echo
   conn.close()

Wir speichern das Script unter ‚/etc/pyserver/pyserver.py‘ (Ordner vorher anlegen) und machen es mit ‚chmod +x /etc/pyserver/pyserver.py‘ ausführbar.

Das Script gibt alles aus was es empfangen hat, und schickt es unverändert zurück. Wird die Connection beendet so wartet es auf eine neue. Da das script nicht forkt/multithreaded ist kann dieser Server natürlich immer nur eine gleichzeitige Connection bedienen!

Nun benötigen wir einen User unter dem der Server läuft, dieser bekommt auch den Ordner:

useradd -r -s /bin/false pyserveruser
chown -R pyserveruser:pyserveruser /etc/pyserver

Jetzt brauchen wir die sog. Unit-Datei. Sie erklärt systemd was wir für ein Dienst sind:

[Unit]
Description=My Python Server
After=syslog.target

[Service]
Type=simple
User=pyserveruser
Group=pyserveruser
WorkingDirectory=/etc/pyserver
ExecStart=/etc/pyserver/pyserver.py
SyslogIdentifier=pyserver
StandardOutput=syslog
StandardError=syslog
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Diese Datei bitte als ‚/etc/systemd/system/pyserver.service‘ speichern.
(Falls man die Datei ändert muss man dies systemd mittels ’systemctl daemon-reload‘ mitteilen)

Und schon können wir unseren Server starten:

systemctl enable pyserver
systemctl start pyserver

Mittels ’systemctl status pyserver‘ sollten wir nun sehen dass er läuft:

Active: active (running)

Jetzt testen wir ihn mal, indem wir uns mittels netcat zu unserem localhost auf den Port 5005 verbinden:

nc 127.0.0.1 5005

Wenn er alles zurückschickt was man ihm schreibt funktioniert alles.
Mittels Strg+C beendet man Netcat.

Übrigens überlebt unser Dienst dank den Restart-Zeilen in seinem Unitfile auch einen ‚kill -9‘ auf seinen Prozess. Systemd startet ihn einfach 3 Sekunden später neu.

Steuern kann man den Server nun mit den normalen systemd Kommandos:

systemctl status pyserver
systemctl start pyserver
systemctl stop pyserver
systemctl restart pyserver

Seine Logausgaben sieht man so:

journalctl -u pyserver

Die Logausgaben landen übrigens in der Datei ‚/var/log/syslog‘.
Da das noch nicht ganz so optimal ist korrigieren wir das noch!

In der Datei ‚/etc/rsyslog.d/50-default.conf‘ teilen wir unserem Syslog-Deamon mit dass das Programm ‚pyserver‘ bitte nach ‚/var/log/pyserver.log‘ geschrieben werden soll:

:programname,isequal,"pyserver"         /var/log/pyserver.log
& ~

Neustart des Rsyslog-Daemons mittels

systemctl restart rsyslog

nicht vergessen.

So, und nun sorgen wir als letztes noch dafür dass Logrotate unsere Logs täglich zippt und maximal 5 Logs aufbewahrt, Datei ‚/etc/logrotate.d/pysever‘ erstellen, folgender Inhalt:

/var/log/pyserver.log { 
    su root syslog
    daily
    rotate 5
    compress
    delaycompress
    missingok
    postrotate
        systemctl restart rsyslog > /dev/null
    endscript    
}

Und nun testen wir das wegrotieren erstmal trocken:

logrotate -d /etc/logrotate.d/pysever

Und wenn es keine Fehler gab können wir ihn zwingen zu rotieren, obwohl der Tag noch nicht abgelaufen ist:

logrotate --force /etc/logrotate.d/pysever

Und so einfach haben wir unseren eigenen kleinen Daemon erschaffen! (an dieser Stelle bitte Bösewicht-Gelächter vorstellen!)

Die Anleitung sollte auch auf Debian gehen, da muss nur die Gruppe in der Logrotate-Datei von ’syslog‘ auf ‚adm‘ geändert werden.
Wie immer, bitte in den Kommentaren melden wenn ich irgendeinen schrecklichen Patzer gemacht hab und meine Anleitung den Server nach ein paar Stunden abbrennen lässt – danke 🙂

3 Antworten auf „Ein Python-Script mit Systemd als Daemon (Systemd tut garnicht weh… :-) )“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.