Python-Skript als Linux-Dienst / Daemon

70

Hallo,

Ich versuche, ein Python-Skript als Dienst (Daemon) unter (Ubuntu) Linux ausführen zu lassen.

Im Web gibt es verschiedene Lösungen wie:

http://pypi.python.org/pypi/python-daemon/

Ein gut erzogener Unix-Daemon-Prozess ist schwierig, aber die erforderlichen Schritte sind für jedes Daemon-Programm ähnlich. Eine DaemonContext-Instanz enthält das Verhalten und die konfigurierte Prozessumgebung für das Programm. Verwenden Sie die Instanz als Kontextmanager, um einen Dämonstatus einzugeben.

http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/

Da ich mein Python-Skript jedoch speziell in Ubuntu Linux integrieren möchte, ist meine Lösung eine Kombination mit einem init.d-Skript

#!/bin/bash

WORK_DIR="/var/lib/foo"
DAEMON="/usr/bin/python"
ARGS="/opt/foo/linux_service.py"
PIDFILE="/var/run/foo.pid"
USER="foo"

case "$1" in
  start)
    echo "Starting server"
    mkdir -p "$WORK_DIR"
    /sbin/start-stop-daemon --start --pidfile $PIDFILE \
        --user $USER --group $USER \
        -b --make-pidfile \
        --chuid $USER \
        --exec $DAEMON $ARGS
    ;;
  stop)
    echo "Stopping server"
    /sbin/start-stop-daemon --stop --pidfile $PIDFILE --verbose
    ;;
  *)
    echo "Usage: /etc/init.d/$USER {start|stop}"
    exit 1
    ;;
esac

exit 0

und in Python:

import signal
import time
import multiprocessing

stop_event = multiprocessing.Event()

def stop(signum, frame):
    stop_event.set()

signal.signal(signal.SIGTERM, stop)

if __name__ == '__main__':
    while not stop_event.is_set():
        time.sleep(3)

Meine Frage ist jetzt, ob dieser Ansatz richtig ist. Muss ich zusätzliche Signale verarbeiten? Wird es ein "gut erzogener Unix-Daemon-Prozess" sein?

Tauran
quelle

Antworten:

89

Angenommen, Ihr Daemon hat eine Möglichkeit, kontinuierlich zu laufen (eine Ereignisschleife, verdreht, was auch immer), können Sie versuchen, sie zu verwenden upstart.

Hier ist ein Beispiel für eine Upstart-Konfiguration für einen hypothetischen Python-Dienst:

description "My service"
author  "Some Dude <[email protected]>"

start on runlevel [234]
stop on runlevel [0156]

chdir /some/dir
exec /some/dir/script.py
respawn

Wenn Sie dies als script.conf speichern, führen /etc/initSie einfach eine einmalige Aktion durch

$ sudo initctl reload-configuration
$ sudo start script

Sie können es mit stoppen stop script. Die obige Upstart-Konfiguration besagt, dass dieser Dienst beim Neustart gestartet und auch neu gestartet werden soll, wenn er stirbt.

Was die Signalverarbeitung betrifft, sollte Ihr Prozess natürlich darauf reagieren SIGTERM. Standardmäßig sollte dies behandelt werden, es sei denn, Sie haben speziell Ihren eigenen Signalhandler installiert.

rlotun
quelle
2
Sie haben Recht, Emporkömmling ist heutzutage der Standard! Da das obige Skript SIGTERM behandelt, sollte es mit Ihrer Konfigurationsdatei in
Ordnung sein
11
Eine zusätzliche Optimierung, die ich gerade vorgenommen habe. Wenn Ihr Python-Skript unter einer virtuellen Umgebung ausgeführt wird, müssen Sie nur den Start ändern, um die ausführbare Python-Datei aus der Umgebung zu verwenden: exec /home/user/.env/environ/bin/python /some/dir/script.py
Anthony Briggs
Tolle Infos. Wo ist die Dokumentation für die Dateien / etc / init?
Scott Willeke
Ab der Upstart-Version 1.4 können Sie "setid" und "setgid" verwenden. Das Argument ist der Benutzer- / Gruppenname.
Tiktak
4
Upstart scheint nicht mehr der Standard zu sein. Wikipedia listet viele "Linux-Distributionen auf, die [...] weggezogen sind oder sie [upstart] nicht mehr als Standard-Init-System verwenden". Sie verwenden stattdessen systemd. Mehr zu Upstream vs Systemd von unix.stackexchange
Hibuki
11

Rlotons Antwort ist gut. Hier ist eine leichte Verfeinerung, nur weil ich eine Menge Zeit mit dem Debuggen verbracht habe. Und ich muss eine neue Antwort geben, damit ich richtig formatieren kann.

Ein paar andere Punkte, die ich für immer zum Debuggen gebraucht habe:

  1. Wenn dies fehlschlägt, überprüfen Sie zuerst /var/log/upstart/.log
  2. Wenn Ihr Skript einen Daemon mit Python-Daemon implementiert , verwenden Sie NICHT die Zeilengruppe "Expect Daemon". Keine "Erwartung" zu haben, funktioniert. Ich weiß nicht warum. (Wenn jemand weiß warum - bitte posten!)
  3. Überprüfen Sie außerdem weiterhin das "initctl status script", um sicherzustellen, dass Sie aktiv sind (Start / Ausführung). (und laden Sie neu, wenn Sie Ihre conf-Datei aktualisieren)

Hier ist meine Version:

description "My service"
author  "Some Dude <[email protected]>"

env PYTHON_HOME=/<pathtovirtualenv>
env PATH=$PYTHON_HOME:$PATH

start on runlevel [2345]
stop on runlevel [016]

chdir <directory>

# NO expect stanza if your script uses python-daemon
exec $PYTHON_HOME/bin/python script.py

# Only turn on respawn after you've debugged getting it to start and stop properly
respawn
Ross R.
quelle
Wenn ich diese Konfiguration in / etc / init kopiere, gebe ich service myservicename ein, es wird nicht gefunden. Was ist damit.
David
1
Hast du initctl reload-configurationgefolgt von service myservice start?
Ross R
Entschuldigung, es funktioniert jetzt. Ich habe Service-Servicename eingegeben und dachte, es würde mir meine Optionen anscheinend nicht geben ... Sie müssen Servicename starten, stoppen oder neu starten. Trotzdem für die Antwort
David
2
Vielen Dank. Dieser Tipp über die Strophe "Keine Erwartung" ist golden. Es ist bedauerlich, dass Upstart zwischen den Neustarts einen gewissen Status beibehält und manchmal ohne Grund mit der richtigen Datei hängen bleibt. Macht das Debuggen sehr schwierig.
Yhager