Python守护进程和systemd servi

2024-04-28 06:51:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个简单的Python脚本作为守护进程。我正在尝试创建systemd脚本,以便能够在启动期间启动此脚本。

当前systemd脚本:

[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包含while True循环。

我试图用systemctl start zebra-node.service运行此服务。不幸的是,服务从未完成顺序说明-我必须按Ctrl+C。 脚本正在运行,但状态为“正在激活”,一段时间后将变为“正在停用”。 现在我正在使用python守护进程(但在我尝试不使用它之前,症状是相似的)。

我应该实现一些额外的功能到我的脚本还是系统文件不正确?


Tags: runtextpy脚本nodetargethome进程
3条回答

它没有完成启动序列的原因是,对于类型forking,您的启动过程需要分叉并退出(请参阅$man systemd.service-search for forking)。

只使用主进程,不进行守护

一种选择是少做。使用systemd,通常不需要创建守护进程,您可以直接运行代码,而无需守护进程。

#!/usr/bin/python -u
from somewhere import Node
check = Node()
check.run()

这允许使用名为simple的更简单的服务类型,因此您的单元文件看起来像。

[Unit]
Description=Simplified simple zebra service
After=syslog.target

[Service]
Type=simple
User=node
Group=node
WorkingDirectory=/home/node/Node/
ExecStart=/home/node/Node/node.py
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target

注意,python shebang中的-u不是必需的,但是如果您将某些内容输出到stdout或stderr,那么-u将确保没有输出缓冲,并且打印的行将立即被systemd捕获并记录在日志中。如果没有它,它会延迟出现。

为此,我在单元文件中添加了StandardOutput=syslogStandardError=syslog行。如果你不关心日记中的打印输出,就不要关心这些行(它们不必出现)。

systemd使守护进程过时

虽然您的问题的标题明确询问了守护进程,但我猜,问题的核心是“如何使我的服务运行”,虽然使用主进程似乎简单得多(您根本不必关心守护进程),但它可以被视为您问题的答案。

我认为,很多人使用守护只是因为“每个人都这么做”。对于systemd,守护的原因通常是过时的。使用守护化可能有一些原因,但现在这种情况很少见。

编辑:将python -p修复为正确的python -u。谢谢KMFTZ

您没有创建PID文件。

systemd希望您的程序在/var/run/zebra.pid中写入其PID。如果您不这样做,systemd可能会认为您的程序正在失败,因此将其停用。

要添加PID文件,请安装lockfile并将代码更改为:

import daemon
import daemon.pidlockfile 

pidfile = daemon.pidlockfile.PIDLockFile("/var/run/zebra.pid")
with daemon.DaemonContext(pidfile=pidfile):
    check = Node()
    check.run()

(简要说明:最近对lockfile的一些更新更改了其API并使其与python守护进程不兼容。要修复它,请编辑daemon/pidlockfile.py,从导入中删除LinkFileLock,然后添加from lockfile.linklockfile import LinkLockFile as LinkFileLock。)

注意另一件事:DaemonContext将程序的工作目录更改为/,使服务文件的WorkingDirectory无用。如果希望DaemonContext将chdir放入另一个目录,请使用DaemonContext(pidfile=pidfile, working_directory="/path/to/dir")

可以像Schnouki和Amit所描述的那样进行大名化。但对于systemd,这是不必要的。有两种更好的方法来初始化守护进程:socket激活和使用sd_notify()的显式通知。

Socket激活适用于希望在网络端口或UNIX Socket或类似设备上侦听的守护进程。Systemd会打开套接字,监听它,然后在连接进入时生成守护进程。这是首选方法,因为它为管理员提供了最大的灵活性。[1] [2]做了很好的介绍,[3]描述了C API,而[4]描述了PythonAPI。

[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

显式通知意味着守护进程打开套接字本身和/或执行任何其他初始化,然后通知init它已经准备好并可以为请求提供服务。这可以通过“forking protocol”来实现,但实际上,使用sd_notify()向systemd发送通知更好。 Python包装被称为systemd.daemon.notify,它将是使用[5]的一行。

[5]http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify

在这种情况下,单元文件将具有Type=notify,并调用 在建立套接字之后,systemd.daemon.notify(“READY=1”)。不需要分叉或守护。

相关问题 更多 >