Ich habe ein einfaches Python-Skript, das als Daemon arbeitet. Ich versuche, ein systemd-Skript zu erstellen, um dieses Skript beim Start starten zu können.
Aktuelles Systemd-Skript:
[Unit]
Description=Text
After=syslog.target
[Service]
Type=forking
User=node
Group=node
WorkingDirectory=/home/node/Node/
PIDFile=/var/run/zebra.pid
ExecStart=/home/node/Node/node.py
[Install]
WantedBy=multi-user.target
node.py:
if __name__ == '__main__':
with daemon.DaemonContext():
check = Node()
check.run()
run
enthält while True
Schleife.
Ich versuche diesen Dienst mit auszuführen systemctl start zebra-node.service
. Leider hat der Dienst die Sequenz nie beendet - ich muss Strg + C drücken. Das Skript wird ausgeführt, aber der Status wird aktiviert und nach einer Weile wird er deaktiviert. Jetzt benutze ich Python-Daemon (aber bevor ich es ohne versuchte und die Symptome waren ähnlich).
Sollte ich einige zusätzliche Funktionen in mein Skript implementieren oder ist die systemd-Datei falsch?
python
python-daemon
systemd
pawelbial
quelle
quelle
daemon
und nicht klar, woher dasNode
kommt), so dass es nicht einfach / möglich ist, Ihre Situation zu reproduzieren.Antworten:
Der Grund dafür, dass die Startsequenz nicht abgeschlossen wird, ist, dass für Type
forking
erwartet wird, dass Ihr Startprozess verzweigt und beendet wird (siehe $ man systemd.service - Suche nach Gabelung).Verwenden Sie einfach nur den Hauptprozess, dämonisieren Sie nicht
Eine Möglichkeit ist, weniger zu tun. Mit systemd müssen häufig keine Dämonen erstellt werden, und Sie können den Code direkt ausführen, ohne ihn zu dämonisieren.
#!/usr/bin/python -u from somewhere import Node check = Node() check.run()
Dies ermöglicht die Verwendung eines einfacheren aufgerufenen
simple
Diensttyps, sodass Ihre Gerätedatei so aussehen würde.Beachten Sie, dass der
-u
In-Python-Shebang nicht erforderlich ist. Wenn Sie jedoch etwas auf stdout oder stderr ausdrucken,-u
wird sichergestellt, dass keine Ausgabepufferung vorhanden ist und gedruckte Zeilen sofort von systemd abgefangen und im Journal aufgezeichnet werden. Ohne sie würde es mit einiger Verzögerung erscheinen.Zu diesem Zweck habe ich die Zeilen
StandardOutput=syslog
und in die Einheitendatei eingefügtStandardError=syslog
. Wenn Sie sich nicht für die gedruckte Ausgabe in Ihrem Tagebuch interessieren, interessieren Sie sich nicht für diese Zeilen (sie müssen nicht vorhanden sein).systemd
macht die Dämonisierung überflüssigWährend der Titel Ihrer Frage explizit nach Daemonisierung fragt, ist der Kern der Frage, wie ich meinen Dienst zum Laufen bringen kann, und während die Verwendung des Hauptprozesses viel einfacher erscheint (Sie müssen sich überhaupt nicht um Daemons kümmern), ist dies der Fall könnte als Antwort auf Ihre Frage angesehen werden.
Ich denke, dass viele Leute Daemonisierung verwenden, nur weil "jeder es tut". Mit systemd sind die Gründe für die Dämonisierung oft überholt. Es kann einige Gründe geben, die Dämonisierung zu verwenden, aber dies wird jetzt selten der Fall sein.
EDIT:
python -p
richtig eingestelltpython -u
. danke kmftzgquelle
Es ist möglich zu dämonisieren, wie Schnouki und Amit es beschreiben. Bei systemd ist dies jedoch nicht erforderlich. Es gibt zwei bessere Möglichkeiten, den Dämon zu initialisieren: Socket-Aktivierung und explizite Benachrichtigung mit sd_notify ().
Die Socket-Aktivierung funktioniert für Daemons, die einen Netzwerkport oder einen UNIX-Socket oder ähnliches abhören möchten. Systemd würde den Socket öffnen, ihn abhören und dann den Dämon erzeugen, wenn eine Verbindung eingeht. Dies ist der bevorzugte Ansatz, da er dem Administrator die größte Flexibilität bietet. [1] und [2] geben eine schöne Einführung, [3] beschreibt die C-API, während [4] die Python-API beschreibt.
[1] http://0pointer.de/blog/projects/socket-activation.html
[2] http://0pointer.de/blog/projects/socket-activation2.html
[3] http: //www.freedesktop .org / software / systemd / man / sd_listen_fds.html
[4] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.listen_fds
Explizite Benachrichtigung bedeutet, dass der Dämon die Sockets selbst öffnet und / oder eine andere Initialisierung durchführt und init dann benachrichtigt, dass er bereit ist und Anforderungen bedienen kann. Dies kann mit dem "Forking-Protokoll" implementiert werden, aber eigentlich ist es besser, einfach eine Benachrichtigung mit sd_notify () an systemd zu senden. Der Python-Wrapper heißt systemd.daemon.notify und ist eine zu verwendende Zeile [5].
[5] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify
In diesem Fall hätte die Unit-Datei Type = notify und rufe systemd.daemon.notify ("READY = 1") auf, nachdem sie die Sockets eingerichtet hat. Es ist kein Forking oder Daemonization erforderlich.
quelle
systemd.daemon
via Pip bereitstellt ?Sie erstellen die PID-Datei nicht.
systemd erwartet, dass Ihr Programm seine PID schreibt
/var/run/zebra.pid
. Da Sie dies nicht tun, glaubt systemd wahrscheinlich, dass Ihr Programm fehlschlägt, und deaktiviert es daher.Um die PID-Datei hinzuzufügen, installieren Sie die Sperrdatei und ändern Sie Ihren Code wie folgt :
import daemon import daemon.pidlockfile pidfile = daemon.pidlockfile.PIDLockFile("/var/run/zebra.pid") with daemon.DaemonContext(pidfile=pidfile): check = Node() check.run()
(Kurzer Hinweis: Einige kürzlich durchgeführte Aktualisierungen haben
lockfile
die API geändert und sie mit python-daemon inkompatibel gemacht. Um dies zu beheben, bearbeiten Sie siedaemon/pidlockfile.py
, entfernen Sie sieLinkFileLock
aus den Importen und fügen Sie sie hinzufrom lockfile.linklockfile import LinkLockFile as LinkFileLock
.)Achten Sie auf eine andere Sache:
DaemonContext
Ändert das Arbeitsverzeichnis Ihres Programms in/
und macht dieWorkingDirectory
Ihrer Servicedatei unbrauchbar. Wenn SieDaemonContext
in ein anderes Verzeichnis chdir möchten , verwenden SieDaemonContext(pidfile=pidfile, working_directory="/path/to/dir")
.quelle
DaemonContext
Änderungen im Arbeitsverzeichnis des Programms gerade meine Dämonisierungsprobleme gelöst habenimport daemon.pidfile
und nichtimport daemon.pidlockfile
Außerdem müssen Sie höchstwahrscheinlich
daemon_context=True
beim Erstellen des festlegenDaemonContext()
.Dies liegt daran,
python-daemon
dass, wenn festgestellt wird, dass es unter einem Init-System ausgeführt wird, es sich nicht vom übergeordneten Prozess löst.systemd
erwartet, dass der Daemon-Prozess, mit dem ausgeführtType=forking
wird, dies tut. Daher brauchen Sie das sonstsystemd
wird weiter warten und schließlich den Prozess beenden.Wenn Sie neugierig sind, sehen Sie im
python-daemon
Daemon-Modul diesen Code:def is_detach_process_context_required(): """ Determine whether detaching process context is required. Return ``True`` if the process environment indicates the process is already detached: * Process was started by `init`; or * Process was started by `inetd`. """ result = True if is_process_started_by_init() or is_process_started_by_superserver(): result = False
Hoffentlich erklärt dies besser.
quelle
Ich bin auf diese Frage gestoßen, als ich versucht habe, einige python init.d-Dienste unter CentOS 7 in systemd zu konvertieren. Dies scheint für mich großartig zu funktionieren, indem ich diese Datei in Folgendes platziere
/etc/systemd/system/
:[Unit] Description=manages worker instances as a service After=multi-user.target [Service] Type=idle User=node ExecStart=/usr/bin/python /path/to/your/module.py Restart=always TimeoutStartSec=10 RestartSec=10 [Install] WantedBy=multi-user.target
Ich habe dann meine alte init.d-Servicedatei aus gelöscht
/etc/init.d
und ausgeführtsudo systemctl daemon-reload
, um systemd neu zu laden.Ich wollte, dass mein Dienst automatisch neu gestartet wird, daher die Neustartoptionen. Ich fand auch Verwendung
idle
fürType
als wettgemacht mehr Sinnsimple
.Weitere Details zu den Optionen, die ich hier verwendet habe .
Ich habe auch experimentiert, um den alten Dienst beizubehalten und systemd den Dienst neu starten zu lassen, aber ich bin auf einige Probleme gestoßen.
[Unit] # Added this to the above #SourcePath=/etc/init.d/old-service [Service] # Replace the ExecStart from above with these #ExecStart=/etc/init.d/old-service start #ExecStop=/etc/init.d/old-service stop
Die Probleme, die ich hatte, waren, dass das Dienstskript init.d anstelle des Dienstes systemd verwendet wurde, wenn beide den gleichen Namen hatten. Wenn Sie den von init.d initiierten Prozess beendet haben, übernimmt das systemd-Skript. Wenn Sie
service <service-name> stop
es jedoch ausführen, verweist es auf den alten Dienst init.d. Daher fand ich, dass der beste Weg darin bestand, den alten Dienst init.d zu löschen, und der Befehl service verwies stattdessen auf den Dienst systemd.Hoffe das hilft!
quelle