it-swarm.com.de

Wie richte ich einen Daemon mit Python-Daemon ein?

Ich bin neu bei Dämonen, also entschuldige mich, wenn dies eine Neueinsteigerfrage ist.

In einigen anderen Antworten (zum Beispiel diese Frage ) wurde vorgeschlagen, dass das Paket python-daemon der richtige Weg ist, weil es den Standard PEP 3143 vollständig implementiert. 

Leider ist Python-Daemon ein bisschen Licht in der Dokumentation (oder eher ich bin ein bisschen Licht in Bezug auf Wissen/Erfahrung ...;)), und ich denke, ich vermisse wahrscheinlich etwas wirklich grundlegendes. Folgendes mache ich:

Ich habe folgendes:

import daemon

logfile = open('daemon.log', 'w')

context = daemon.DaemonContext(stdout = logfile, stderr = logfile)

context.open()

with context:
    do_something_1()
    do_something_2()

Question: Wie kann ich einen Daemon mit Python-Daemon einrichten, wie kann ich ihn starten und stoppen?


Side Notes:

Ich vermute im Grunde eine wilde Vermutung, wie/ob die .open()-Methode hier verwendet werden sollte - Dokumente waren in diesem Punkt nicht wirklich klar. Gleiches scheint zu geschehen, ob ich es einbeziehe oder nicht.

Was mache ich jetzt? Wenn ich versuche, diese Datei auszuführen, zB:

python startConsumerDaemons.py

es scheint do_something_1() zu laufen, aber nicht die zweite. Und es scheint das Programm angefügt dem Terminalfenster zu belassen. IE, stdout wird nicht umgeleitet, und wenn ich das Terminalfenster schließe, wird der Prozess beendet. Ich bin mir ziemlich sicher, dass ich hier etwas falsch mache ... was soll ich anders machen?

Und wenn ich den Dämon zum Laufen gebracht habe, wie kann ich ihn stoppen/neu starten (zum Beispiel, wenn ich Änderungen am zugrunde liegenden Code vornehme)?

28
CQP

Hier ist was ich habe, das funktioniert für mich. Es hat auch ein Sysv-Init-Skript. Repo ist bei GitHub , und ich habe auch einen kurzen Blogbeitrag mit Links zu anderen möglichen Lösungen, die ich gefunden habe.

Es kann nur ein Daemon-Prozess ausgeführt werden, der wie die meisten anderen Linux-Daemons von der PID-Sperrdatei verwaltet wird. Um es zu stoppen, mache

kill `cat /var/run/eg_daemon.pid`

Um zu sehen, ob es läuft:

ps -elf | grep `cat /var/run/eg_daemon.pid`

Mit dem pidfile-Untermodul wird die PID-Datei automatisch verwaltet. Wenn der Dämon gestoppt ist, wird die PID-Datei gelöscht. In dem verlinkten GitHub-Repo finden Sie das Init-Skript.

Hier ist der Python-Daemon-Code:

#!/usr/bin/env python3.5
import sys
import os
import time
import argparse
import logging
import daemon
from daemon import pidfile

debug_p = False

def do_something(logf):
    ### This does the "work" of the daemon

    logger = logging.getLogger('eg_daemon')
    logger.setLevel(logging.INFO)

    fh = logging.FileHandler(logf)
    fh.setLevel(logging.INFO)

    formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(formatstr)

    fh.setFormatter(formatter)

    logger.addHandler(fh)

    while True:
        logger.debug("this is a DEBUG message")
        logger.info("this is an INFO message")
        logger.error("this is an ERROR message")
        time.sleep(5)


def start_daemon(pidf, logf):
    ### This launches the daemon in its context

    ### XXX pidfile is a context
    with daemon.DaemonContext(
        working_directory='/var/lib/eg_daemon',
        umask=0o002,
        pidfile=pidfile.TimeoutPIDLockFile(pidf),
        ) as context:
        do_something(logf)


if __== "__main__":
    parser = argparse.ArgumentParser(description="Example daemon in Python")
    parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid')
    parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log')

    args = parser.parse_args()

    start_daemon(pidf=args.pid_file, logf=args.log_file)

Der Vollständigkeit halber ist hier das Init-Skript. Beachten Sie, dass "kill" eigentlich nur eine Methode zum Senden eines POSIX-Signals ist - eine Übersicht finden Sie in der Manpage zu Signal (7). Der Python-Daemon-Kontext fängt das Signal ein, beendet den Prozess und schließt die Dateideskriptoren sauber. Die PID-Datei wird automatisch gelöscht. Es ist also wirklich eine saubere Beendigung.

Sie können Ihren Code schreiben, um SIGUSR1 oder etwas Ähnliches abzufangen, um die Daemon-Konfiguration neu zu laden. Es ist nicht vorteilhaft, Python zu schreiben, um den Dämon zu stoppen.

#!/bin/bash
#
# eg_daemon      Startup script for eg_daemon
#
# chkconfig: - 87 12
# description: eg_daemon is a dummy Python-based daemon
# config: /etc/eg_daemon/eg_daemon.conf
# config: /etc/sysconfig/eg_daemon
# pidfile: /var/run/eg_daemon.pid
#
### BEGIN INIT INFO
# Provides: eg_daemon
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Short-Description: start and stop eg_daemon server
# Description: eg_daemon is a dummy Python-based daemon
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

if [ -f /etc/sysconfig/eg_daemon ]; then
        . /etc/sysconfig/eg_daemon
fi

eg_daemon=/var/lib/eg_daemon/eg_daemon.py
prog=eg_daemon
pidfile=${PIDFILE-/var/run/eg_daemon.pid}
logfile=${LOGFILE-/var/log/eg_daemon.log}
RETVAL=0

OPTIONS=""

start() {
        echo -n $"Starting $prog: "

        if [[ -f ${pidfile} ]] ; then
            pid=$( cat $pidfile  )
            isrunning=$( ps -elf | grep  $pid | grep $prog | grep -v grep )

            if [[ -n ${isrunning} ]] ; then
                echo $"$prog already running"
                return 0
            fi
        fi
        $eg_daemon -p $pidfile -l $logfile $OPTIONS
        RETVAL=$?
        [ $RETVAL = 0 ] && success || failure
        echo
        return $RETVAL
}

stop() {
    if [[ -f ${pidfile} ]] ; then
        pid=$( cat $pidfile )
        isrunning=$( ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print $4}' )

        if [[ ${isrunning} -eq ${pid} ]] ; then
            echo -n $"Stopping $prog: "
            kill $pid
        else
            echo -n $"Stopping $prog: "
            success
        fi
        RETVAL=$?
    fi
    echo
    return $RETVAL
}

reload() {
    echo -n $"Reloading $prog: "
    echo
}

# See how we were called.
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status -p $pidfile $eg_daemon
    RETVAL=$?
    ;;
  restart)
    stop
    start
    ;;
  force-reload|reload)
    reload
    ;;
  *)
    echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}"
    RETVAL=2
esac

exit $RETVAL
9
phzx_munki

Ein vollständiges Beispiel ist hier erhältlich .

Sie sollten die inneren Abläufe des Python-Daemons besser verstehen können.

Darüber hinaus enthält der bereitgestellte Code auch ein Beispiel für ein Init-Skript, um den Daemon einfach zu starten/stoppen. Sie können es jedoch einfach starten/stoppen, indem Sie die ursprüngliche Funktion erneut mit dem Argument stop aufrufen:

python original_func.py stop
5
gromain

Wie Sie in der 'with' Anweisungsdokumentation sehen können, führt diese Anweisung einige 'Magie' aus, die sich auf unseren Zweck bezieht. Speziell:

Die Ausführung der with-Anweisung mit einem "item" erfolgt als folgt:

  1. Der Kontextausdruck (der Ausdruck, der in with_item angegeben wird) wird ausgewertet, um einen Kontextmanager zu erhalten.

  2. Die __exit__() des Kontextmanagers wird zur späteren Verwendung geladen.

  3. die __enter__()-Methode des Kontextmanagers wird aufgerufen.

  4. Wenn in der with-Anweisung ein Ziel enthalten war, wird ihm der Rückgabewert von __enter__() zugewiesen.

  5. Die Suite wird ausgeführt.

  6. Die __exit__()-Methode des Kontextmanagers wird aufgerufen. Wenn eine Suite aufgrund einer Ausnahme beendet wurde, waren Typ, Wert und traceback werden als Argumente an __exit__() übergeben. Ansonsten drei Keine Argumente werden geliefert.

Was bedeutet das? Wenn Sie sich genauer mit dem fraglichen PEP beschäftigen, das auch als Python-Daemon-Dokumentation dient (und die tatsächlich erheblich verbessert werden könnte), werden Sie feststellen, dass es __enter__() und __exit__() implementiert:

Die Klasse implementiert auch das Kontextmanagerprotokoll über __enter__ und __exit__-Methoden.

__enter__()

Rufen Sie die open () -Methode der Instanz auf und geben Sie die Instanz zurück.

__exit__(exc_type, exc_value, exc_traceback)

Rufen Sie die close () - Methode der Instanz auf und geben Sie dann True zurück, wenn die Ausnahme behandelt wurde, oder False, falls dies nicht der Fall war.

Mit anderen Worten, open () wird nicht benötigt, das in der PEP angegebene Beispiel (obwohl es nicht richtig erklärt wurde) funktioniert so wie es ist. Während die with-Anweisung etwas bedeutet, behält sie keine Schleife bei und ruft, sobald das Ende ihres Gültigkeitsbereichs erreicht ist, exit () auf, was in python-daemon close () bedeutet. Daher müssen Sie dort eine Weile setzen oder die Endlosschleife, die Sie in Betracht ziehen. 

Wenn Ihr zweites Skript nicht funktioniert, kann ich Ihnen nicht sagen, ich bin überrascht, dass das erste bereits funktioniert. Wenn Ihr Daemon stoppt, liegt ein Problem mit Ihren Skripts vor. Sie können Ihre ConsumerDaemonLogFile überprüfen. (nebenbei haben Sie einen Tippfehler 'sderr' -> 'stderr')

Außerdem können Sie im PEP sehen, dass, wenn nicht angegeben, die Eigenschaft des Arbeitsverzeichnisses standardmäßig '/' ist. Dies kann die Ursache Ihres Problems sein, wenn Sie relative Pfade in Ihren Skripts verwenden.

Schließlich können Sie bei der letzten Frage leicht töten, dass Ihr Dämon seine PID findet:

ps ax | grep startConsumerDaemons.py

und sende ein SIGTERM:

kill <pid>

Die Antwort von gromain bietet eine praktischere Möglichkeit zum Starten und Beenden mit 'daemon.runner ()', aber die Einrichtung ist viel komplizierter. 

3
ArnauOrriols

Der daemon.DaemonContext-Konstruktor akzeptiert eine lockfile-Option. Verwenden Sie eine Sperrdatei-Bibliothek, die die PID des Prozesses aufzeichnet.

Die Bibliothek empfahl ursprünglich die Klasse lockfile.PIDLockFile, diese Bibliothek wird jedoch jetzt ohne guten Ersatz verworfen. Sie können jedoch ein anderes Objekt mit derselben Semantik implementieren.

Dann wird die PID des Prozesses ermittelt, indem einfach der Inhalt der genannten PID-Datei gelesen wird. Verwenden Sie diese PID, um Signale an Ihren laufenden Dämon zu senden.

0
bignose

Für das Modul "python-daemon" fehlt noch eine nützliche Dokumentation. Ich habe es persönlich aufgegeben, und jetzt verwende ich erfolgreich den Daemon-Code von Sander Marechal auf den in dieser Antwort verwiesen wird .

Ich habe es leicht modifiziert, um beim Aufruf von python testdaemon.py stop etwas tun zu können.

Hier ist der Code .


Verwendungsbeispiel:

import sys, daemon, time

class testdaemon(daemon.Daemon):
    def run(self):
        self.i = 0
        with open('test1.txt', 'w') as f:
            f.write(str(self.i))
        while True:
            self.i += 1
            time.sleep(1)

    def quit(self):
        with open('test2.txt', 'w') as f:
            f.write(str(self.i))

daemon = testdaemon()

if 'start' == sys.argv[1]: 
    daemon.start()
Elif 'stop' == sys.argv[1]: 
    daemon.stop()
Elif 'restart' == sys.argv[1]: 
    daemon.restart()
0
Basj

Unter Linux können Sie den Daemon stoppen, indem Sie Folgendes ausführen:

$ ps -x

und finden Sie die PID, die Ihrem Daemon entspricht, und beenden Sie dann einfach den Prozess.

0
Joseph Feeney