Mit Systemd gestartetes Pythonskript arbeitet nicht

  • Hallo Zusammen,

    anlässlich der Diskussionen über systemd wollte ich passender weise meine erste service.unit erstellen. Gestartet werden soll ein Pythonskript.

    Dies ist ein Wetterbot, dieses Skript soll 24/7 aktiv sein.

    Das Skript wird schon ca. 1 Jahr über rc.local erfolgreich gestartet:

    /etc/rc.local: /usr/bin/python3 /home/pi/Wetterbot/wetterbot.py &


    Jetzt wollte ich wie folgt auf systemd umstellen:

    /etc/systemd/system/wetterbot.service:

    Code
    [Unit]
    Description=ServiceUnit zum starten des Wetterbots
    [Service]
    Type=simple
    ExecStart=/usr/bin/python3 /home/pi/Wetterbot/wetterbot.py
    Restart=always
    [Install]
    WantedBy=multi-user.target

    Anschließend noch aktiviert mit systemctl enable wetterbot.service


    Eine Überprüfung ergibt folgendes sudo systemctl status wetterbot.service:

    Code
    ● wetterbot.service - ServiceUnit zum starten des Wetterbots
    Loaded: loaded (/etc/systemd/system/wetterbot.service; enabled; vendor preset: enabled)
    Active: active (running) since Tue 2018-04-17 19:16:00 CEST; 8min ago
    Main PID: 20853 (python3)
    CGroup: /system.slice/wetterbot.service
    └─20853 /usr/bin/python3 /home/pi/Wetterbot/wetterbot.py
    Apr 17 19:16:00 wetterstation systemd[1]: Started ServiceUnit zum starten des Wetterbots.

    ps uax | grep wetterbot.py

    Code
    root 20853 1.0 2.6 30180 24900 ? Ss 19:15 0:08 /usr/bin/python3 /home/pi/Wetterbot/wetterbot.py
    pi 21427 0.0 0.0 4776 532 pts/1 S+

    Jedoch reagiert der Telegram Bot widererwartend nicht. Der vollständigkeithalber hier noch das Pythonskript, jedoch da auch über rc.local mit absoluten Pfaden gearbeitet wird, kann ich mir nicht vorstellen dass das Skript der Schuldige ist. Für diejenige die es doch ansehen, der sei gewarnt, dieses Skript wartet noch auf seine Überarbeitung um meine neue Kenntnisse in Sachen Python einfließen zu lassen.


    Quellcode aufgrund der Zeichenbeschränkung als Anhang


    Also warum funktioniert es über rc.local aber nicht via systemd?


    EDIT: Das Pfade via os.path.join zusammengesetzt werden sollten und absolute Pfade immer und überall ist mittlerweile bekannt :^^: - nur um da gleich bzgl des Skriptes zuvorzukommen - aber in rc.local funktioniert es ja auch warum nicht in systemd:conf:

  • Hallo,


    für den Telegramm-Bot brauchst du doch das Netzwerk, oder? Dann fehlt da noch dir `After=` Direktive, die das festlegt.


    Wenn du `bot = api.bot(token)` _ohne_ Netzwerk ausführst, welche Wert hat dann `bot`?


    Gruß, noisefloor

  • Da die api auch selbst geschrieben ist, traue ich mich ohne es zu testen behaupten, dass es das selbe Verhalten hat. Die init von der Botklasse besteht nur aus folgenden Zeilen:

    Python
    class bot:
    def __init__(self, token):
    self.token = token

    Dennoch danke schon einmal für den Tipp mit After= das hätte ich übersehen.

    Der Pi wurde bei dem Test jedoch nicht neu gestartet. Somit müsste das Netzwerk auch zur Verfügung gestanden sein beim starten

  • Hallo,


    ansonsten hilft, dass Logging von journald zu aktivieren (ist es unter Raspbian AFAIK nicht) und da auch zu schauen. Alles, was du mit `print()` im Skript aus gibst sollte darin landen.


    Gruß, noisefloor

  • Hofei


    Ich habe grundsätzlich ein geteiltes Vorgehen. Zuerst erstelle ich die Service-Unit mit einem Dummy-Programm, danach das eigentliche Programm, welches im root-Terminal mit dem gleichen Eintrag gestartet wird, wie er in der Service-Unit seht. Wenn beides einzeln funktioniert, funktioniert es auch zusammen. Und "sudo" ist bei allen Tests absolut tabu!


    Als Dummy-Programm verwende ich meistens ein Bash-Script, aber ähnliches sollte auch mit Python klappen... ich hoffe mal, dass auch Python direkt ins Journal schreibt.

    Bash
    #!/bin/bash
    echo "Programm gestartet: User=$USER Param.: 1=$1 2=$2 3=$3" | systemd-cat -t "hofei:$(basename $0)" -p info
    exit 0

    Wenn jetzt also auch das Journal aktiviert ist, könntest Du nachsehen, ob das Programm mit Parameter ordentlich aufgerufen wurde. Also, wichtig ist, das Journal zu aktivieren... und wenn man sich mal dran gewöhnt hat, kann man schließlich sogar die alte Leiche "rsyslog" purgen... das Journal ist viel "mächtiger".


    Code
    journalctl -b | grep hofei


    Nachtrag:

    Hier liegt noch ein Problem mit der Codedarstellung vor... der Shebang wird nicht angezeigt, obwohl er im Codeblock drin steht..., wie kann das sein?

  • ansonsten hilft, dass Logging von journald zu aktivieren (ist es unter Raspbian AFAIK nicht)

    Bei mir war es schon aktiv. :conf::conf:


    journalctl -u wetterbot ergibt folgende Meldung:

    Code
    Apr 17 19:08:58 wetterstation systemd[1]: [/etc/systemd/system/wetterbot.service:6] Executable path is no
    Apr 17 19:08:58 wetterstation systemd[1]: wetterbot.service: Service lacks both ExecStart= and ExecStop=


    Vielen Dank für dein Dummyskript, werd ich in der Pause integrieren und testen. Du erstellst also erst die service.unit die anschließend erhalten bleibt. Verläuft der Test erfolgreich wird das Dummyskript einfach mit dem tatsächlichen Skript ersetzt...?


    Nachtrag:

    Hier liegt noch ein Problem mit der Codedarstellung vor... der Shebang wird nicht angezeigt, obwohl er im Codeblock drin steht..., wie kann das sein?

    Der Bug ist bekannt, alles mit # wird aktuell im Codeblock nicht dargestellt

  • Die Reihenfolge ist eigentlich egal, ich entwickel und teste nur beides getrennt.


    Und was die Service-Unit angeht, verwende ich dazu eben ein völlig unverfängliches Script, was definitiv fehlerfrei funktioniert.... was ich dann auch mit Log-Einträgen kontrollieren kann. Wie sich die Unit in den Start einreiht, ob das kollisionsfrei ist, ob die sachlichen und zeitlichen Abhängigkeiten passen, kontrolliere ich mit systemd-analyze. Wenn ich sicher bin, dass es so ist, wie ich will, hänge ich das richtige Programm ein. Wenn es dann Probleme gibt, weiss ich, dass es nicht an systemd oder der Unit liegt. Mitterweile habe ich verschiedene dieser Testscripte, die ich in Abhängigkeit des Unit-Types wähle, bis hin zu einem kurzen Testscript, welches sich forken kann... für den Unit-Type "forking".


    Es ist definitiv anspruchsvoller, als früher unter sysvinit, aber auch definitiv weitaus effektiver, mit genialen Möglichkeiten zu absolut 'feinen' Einstellungen.

  • Ich bin wirklich gespannt auf die Lösung des Problems... Ist das alles was in dem Log steht? Woran erkennt man ob es ein Warning oder ein Error ist? Oder sind das alles Errors?


    Hofei wurde bei der ersten Meldung was abgeschnitten?

    Der Unterschied zwischen Genie und Wahnsinn definiert sich im Erfolg.

  • Hallo,


    die 2. Zeile der Fehlermeldung passt nicht zur gezeigten Unit... `ExecStart=` hast du ja drin, auch richtig geschrieben. Und bei der ersten Zeile fehtl scheinbar wirklich was... Bzw. wenn in der zweiten Zeile eine Fehler ist und `ExecStart=` wird nicht ausgeführt, dann fehlt da natürlich was...


    Was ich gestern vergessen habe: `systemctl enable...`startet die Unit _nicht_. Dazu musst du noch mit `systemctl start UNIT` starten. Bzw. nach einem Reboot sollte sie auch laufen.


    Gruß, noisefloor

  • Das etwas fehlt kann durchaus sein, hab dies heute in der Arbeit via mein Smartphone ausgeführt und anschließend abgetippt.


    Kann es leider jetzt nicht mehr reproduzieren da ich in der zwischenzeit den Pi neu gestartet habe, jetzt heißt es bei journalctl -u wetterbot

    Logs begin at Wed 2018-04-18 09:21:34 CEST ....


    Erstaunlicherweise funkioniert jetzt auch mein Bot, seit ich Neu gestartet habe.

    systemctl start wetterbot.service wurde aber definitiv von mir ausgeführt nachdem ich systemctl enable wetterbot.service ausführte.

    Auch habe ich ja eben diesen Prozess in der Prozessliste via ps uax finden können. Nur eben ohne Reaktion des Bots.


    Auch funktioniert der Restart wenn ich den Prozess mit kill abschieße.


    Die after Methode werde ich dennoch definitiv einbauen.


    Ein Dankeschön an alle die bei dem Problem geholfen haben!:danke_ATDE:


    ThomasL dein Dummyskript hab ich auch schon im Pi :danke_ATDE:


    Ist das alles was in dem Log steht? Woran erkennt man ob es ein Warning oder ein Error ist? Oder sind das alles Errors?

    Mittlerweile ist das Log größer, aber eine Unterscheidung welche Meldungsart es ist ist für mich auch noch nicht ersichtlich.

  • Das etwas fehlt kann durchaus sein, hab d

    Mittlerweile ist das Log größer, aber eine Unterscheidung welche Meldungsart es ist ist für mich auch noch nicht ersichtlich.

    Doch, das geht... und die Grösse des Journals habe ich begrenzt.... das regelt also journald für mich. Ich verwende

    err

    warning und

    info (siehe oben)


    zur Unterscheidung meiner Meldungstypen

  • Ok, gerade nochmals mit der Dummydatei getestet.


    Was ich bzgl der Unterscheidung meinte ist, dass bei der Ausführung von journalctl -b | grep hofei man in der Ausgabe keine Unterscheidung von den Einträgen (info, warning, err) hat in Form von farblicher Kennzeichnung oder dass es in der Ausgabe in Textform mit dabei steht.


    Dass ich es mir jedoch filtern lassen kann durch den Zusatz -p "kategorie" ist mir bekannt >> journalctl -b -p err| grep hofei


    Eine farbliche Unterscheidung wäre natürlich mein Favorit, gefolgt von einer Ausgabe in Textform

  • Eine farbliche Unterscheidung wäre natürlich mein Favorit, gefolgt von einer Ausgabe in Textform

    Wie soll das gehen? "grep" kennt doch gar nicht die Farbcodes für journald. Soll heissen, Du hast die Journald-Ausgabe nach grep gepiped, siehst also nicht mehr die Ausgabe von Journald, sondern die von grep.


    Damit kommt die Anzeige auch farblich korrekt:

    Code
    journalctl -b -p err
    journalctl -b -p warning

    Aber es gibt ne bessere Alternative... öffne einfach ein weiteres Fenster und starte dort

    Code
    journalctl -f

    Damit siehst Du live alle Meldungen, die Dein Job in dem anderen Fenster produziert... und hier dann auch farblich markiert.


    BTW: Noch'n Tip... das Statement "systemctl enable" solltest Du tunlichst nicht vorher verwenden, bis Du 100% bestätigt hast, dass Service-Unit und Job zweifelsfrei fehlerfrei funktionieren. Sowas sofort in der Startphase zu machen erschwert es Dir nur, Probleme zu beheben. Der Job muss jederzeit OnTheFly mit

    Code
    systemctl start jobname
    systemctl restart jobname
    systemctl stop jobname

    kontrolliert werden können. Und im zweiten Terminalfenster siehst Du immer passend dazu alle Meldungen. Wenn ich im ersten Terminal den Daemon direkt starte:

    Code
    $ ./daemon start
    thomas@thomaspc:/testbin
    $ ./daemon stop
    thomas@thomaspc:/testbin

    sehe ich im zweiten Terminal bei vorherigem journalctl -f das hier:

    Code
    Apr 18 21:39:47 thomaspc thlu:daemon[6573]: starting
    Apr 18 21:39:47 thomaspc thlu:daemon[6577]: started
    Apr 18 21:39:47 thomaspc thlu:daemon[6580]: running 1: warte 10 Sekunden
    Apr 18 21:39:57 thomaspc thlu:daemon[6586]: running 2: warte 10 Sekunden
    Apr 18 21:40:07 thomaspc thlu:daemon[6592]: running 3: warte 10 Sekunden
    Apr 18 21:40:09 thomaspc thlu:daemon[6599]: stopping
    Apr 18 21:40:09 thomaspc thlu:daemon[6606]: stopped


    Mach ich das gleiche über die Service-Unit (bei mir Type forking) sieht das so aus.... im Terminal 1:

    Code
    # systemctl start daemon
    root@thomaspc:/testbin
    # systemctl stop daemon
    root@thomaspc:/testbin

    was dann im Terminal 2 zur folgenden Ausgabe führt:



    Also... wie ich schon sagte... das beste Vorgehen ist, es "direkt" zu testen... und erst wenns wirklich fehlerfrei läuft folgt ganz am Ende der

    Code
    systemctl enable servicename

    und ggf. ein Reboot für die Abschlußkontrolle.


    Das hier ist mein "Daemon"-Test-Script, welches bei der Entwicklung der Unit die Aufgabe des späteren Programms übernimmt:||


    Und das ist die Mini-Service-Unit, mit der man mit dem Unit-Type Forking rumbasteln kann....

    Code
    [Unit]
    Description=Test Daemon
    [Service]
    Type=forking
    ExecStart=/testbin/daemon start
    ExecStop=/testbin/daemon stop
    [Install]
    WantedBy=multi-user.target


    Also.. alles zusammen demonstriert nur, wie ich sowas angehe.... vielleicht findest Du davon etwas hilfreich....

    Edited 3 times, last by ThomasL ().