[英]Python daemon and systemd service
I have a simple Python script working as a daemon.我有一个简单的 Python 脚本作为守护进程工作。 I am trying to create systemd script to be able to start this script during startup.
我正在尝试创建 systemd 脚本以便能够在启动期间启动此脚本。
Current systemd script:当前系统脚本:
[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:节点.py:
if __name__ == '__main__':
with daemon.DaemonContext():
check = Node()
check.run()
run
contains while True
loop. run
包含while True
循环。
I try to run this service with systemctl start zebra-node.service
.我尝试使用
systemctl start zebra-node.service
运行此服务。 Unfortunately service never finished stating sequence - I have to press Ctrl+C.不幸的是,服务从未完成说明顺序 - 我必须按 Ctrl+C。 Script is running, but status is activating and after a while it change to deactivating.
脚本正在运行,但状态为激活,一段时间后变为停用。 Now I am using python-daemon (but before I tried without it and the symptoms were similar).
现在我正在使用 python-daemon(但在我尝试没有它并且症状相似之前)。
Should I implement some additional features to my script or is systemd file incorrect?我应该为我的脚本实现一些附加功能还是 systemd 文件不正确?
The reason, it does not complete the startup sequence is, that for Type forking
your startup process is expected to fork and exit (see $ man systemd.service - search for forking).它没有完成启动顺序的原因是,对于类型
forking
,您的启动过程预计会分叉并退出(请参阅 $ man systemd.service - 搜索分叉)。
One option is to do less.一种选择是少做。 With systemd, there is often no need to create daemons and you may directly run the code without daemonizing.
使用 systemd,通常不需要创建守护进程,您可以直接运行代码而无需守护进程。
#!/usr/bin/python -u
from somewhere import Node
check = Node()
check.run()
This allows using simpler Type of service called simple
, so your unit file would look like.这允许使用称为
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
Note, that the -u
in python shebang is not necessary, but in case you print something out to the stdout or stderr, the -u
makes sure, there is no output buffering in place and printed lines will be immediately caught by systemd and recorded in journal.请注意,python shebang 中的
-u
不是必需的,但是如果您将某些内容打印到 stdout 或 stderr,则-u
确保没有输出缓冲到位,并且打印的行将立即被 systemd 捕获并记录在日记中。 Without it, it would appear with some delay.没有它,它会出现一些延迟。
For this purpose I added into unit file the lines StandardOutput=syslog
and StandardError=syslog
.为此,我在单元文件中添加了
StandardOutput=syslog
和StandardError=syslog
行。 If you do not care about printed output in your journal, do not care about these lines (they do not have to be present).如果您不关心日记中的打印输出,请不要关心这些行(它们不必存在)。
systemd
makes daemonization obsolete systemd
使守护进程过时While the title of your question explicitly asks about daemonizing, I guess, the core of the question is "how to make my service running" and while using main process seems much simpler (you do not have to care about daemons at all), it could be considered answer to your question.虽然您的问题的标题明确询问了守护进程,但我想,问题的核心是“如何使我的服务运行”,而使用主进程似乎要简单得多(您根本不必关心守护进程),它可以考虑回答你的问题。
I think, that many people use daemonizing just because "everybody does it".我认为,很多人使用守护进程只是因为“每个人都这样做”。 With systemd the reasons for daemonizing are often obsolete.
使用 systemd 守护进程的原因通常是过时的。 There might be some reasons to use daemonization, but it will be rare case now.
使用守护进程可能有一些原因,但现在这种情况很少见。
EDIT: fixed python -p
to proper python -u
.编辑:将
python -p
修复为正确的python -u
。 thanks kmftzg谢谢kmftzg
It is possible to daemonize like Schnouki and Amit describe.可以像 Schnouki 和 Amit 描述的那样进行守护进程。 But with systemd this is not necessary.
但是对于 systemd,这不是必需的。 There are two nicer ways to initialize the daemon: socket-activation and explicit notification with sd_notify().
有两种更好的方式来初始化守护进程:socket-activation 和使用 sd_notify() 的显式通知。
Socket activation works for daemons which want to listen on a network port or UNIX socket or similar.套接字激活适用于希望在网络端口或 UNIX 套接字或类似端口上侦听的守护进程。 Systemd would open the socket, listen on it, and then spawn the daemon when a connection comes in. This is the preferred approch because it gives the most flexibility to the administrator.
Systemd 将打开套接字,侦听它,然后在连接进入时生成守护进程。这是首选方法,因为它为管理员提供了最大的灵活性。 [1] and [2] give a nice introduction, [3] describes the C API, while [4] describes the Python API.
[1] 和 [2] 给出了很好的介绍,[3] 描述了 C API,而 [4] 描述了 Python API。
[1] http://0pointer.de/blog/projects/socket-activation.html [1] http://0pointer.de/blog/projects/socket-activation.html
[2] http://0pointer.de/blog/projects/socket-activation2.html [2] http://0pointer.de/blog/projects/socket-activation2.html
[3] http://www.freedesktop.org/software/systemd/man/sd_listen_fds.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 [4] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.listen_fds
Explicit notification means that the daemon opens the sockets itself and/or does any other initialization, and then notifies init that it is ready and can serve requests.显式通知意味着守护程序自己打开套接字和/或进行任何其他初始化,然后通知 init 它已准备好并可以为请求提供服务。 This can be implemented with the "forking protocol", but actually it is nicer to just send a notification to systemd with sd_notify().
这可以通过“分叉协议”来实现,但实际上最好使用 sd_notify() 向 systemd 发送通知。 Python wrapper is called systemd.daemon.notify and will be one line to use [5].
Python 包装器称为 systemd.daemon.notify 并且将是使用 [5] 的一行。
[5] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify [5] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify
In this case the unit file would have Type=notify, and call systemd.daemon.notify("READY=1") after it has established the sockets.在这种情况下,单元文件将具有 Type=notify,并在建立套接字后调用 systemd.daemon.notify("READY=1")。 No forking or daemonization is necessary.
不需要分叉或守护进程。
You're not creating the PID file.您没有创建 PID 文件。
systemd expects your program to write its PID in /var/run/zebra.pid
. systemd 期望您的程序将其 PID 写入
/var/run/zebra.pid
。 As you don't do it, systemd probably thinks that your program is failing, hence deactivating it.如果您不这样做,systemd 可能会认为您的程序失败,因此将其停用。
To add the PID file, install lockfile and change your code to this:要添加 PID 文件,请安装lockfile并将您的代码更改为:
import daemon
import daemon.pidlockfile
pidfile = daemon.pidlockfile.PIDLockFile("/var/run/zebra.pid")
with daemon.DaemonContext(pidfile=pidfile):
check = Node()
check.run()
(Quick note: some recent update of lockfile
changed its API and made it incompatible with python-daemon. To fix it, edit daemon/pidlockfile.py
, remove LinkFileLock
from the imports, and add from lockfile.linklockfile import LinkLockFile as LinkFileLock
.) (快速说明:最近对
lockfile
的一些更新更改了它的 API,使其与 python-daemon 不兼容。要修复它,请编辑daemon/pidlockfile.py
,从导入中删除LinkFileLock
,然后添加from lockfile.linklockfile import LinkLockFile as LinkFileLock
。)
Be careful of one other thing: DaemonContext
changes the working dir of your program to /
, making the WorkingDirectory
of your service file useless.注意另一件事:
DaemonContext
将程序的工作目录更改为/
,使服务文件的WorkingDirectory
无用。 If you want DaemonContext
to chdir into another directory, use DaemonContext(pidfile=pidfile, working_directory="/path/to/dir")
.如果您希望
DaemonContext
chdir 进入另一个目录,请使用DaemonContext(pidfile=pidfile, working_directory="/path/to/dir")
。
Also, you most likely need to set daemon_context=True
when creating the DaemonContext()
.此外,您很可能需要在创建
DaemonContext()
时设置daemon_context=True
。
This is because, if python-daemon
detects that if it is running under a init system, it doesn't detach from the parent process.这是因为,如果
python-daemon
检测到它在 init 系统下运行,它不会与父进程分离。 systemd
expects that the daemon process running with Type=forking
will do so. systemd
期望以Type=forking
运行的守护进程会这样做。 Hence, you need that, else systemd
will keep waiting, and finally kill the process.因此,您需要它,否则
systemd
将继续等待,并最终终止该进程。
If you are curious, in python-daemon
's daemon module, you will see this code:如果你好奇,在
python-daemon
的 daemon 模块中,你会看到这段代码:
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
Hopefully this explains better.希望这能更好地解释。
I came across this question when trying to convert some python init.d services to systemd under CentOS 7. This seems to work great for me, by placing this file in /etc/systemd/system/
:我在 CentOS 7 下尝试将一些 python init.d 服务转换为 systemd 时遇到了这个问题。通过将此文件放在
/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
I then dropped my old init.d service file from /etc/init.d
and ran sudo systemctl daemon-reload
to reload systemd.然后我从
/etc/init.d
中删除了旧的 init.d 服务文件并运行sudo systemctl daemon-reload
以重新加载 systemd。
I wanted my service to auto restart, hence the restart options.我希望我的服务自动重启,因此有重启选项。 I also found using
idle
for Type
made more sense than simple
.我还发现对
Type
使用idle
比simple
更有意义。
Behavior of idle is very similar to simple;
idle 的行为与 simple 非常相似; however, actual execution of the service binary is delayed until all active jobs are dispatched.
但是,服务二进制文件的实际执行会延迟,直到所有活动作业都被调度。 This may be used to avoid interleaving of output of shell services with the status output on the console.
这可用于避免 shell 服务的输出与控制台上的状态输出交错。
More details on the options I used here .有关我在此处使用的选项的更多详细信息。
I also experimented with keeping the old service and having systemd resart the service but I ran into some issues.我还尝试保留旧服务并让 systemd 重新启动服务,但我遇到了一些问题。
[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
The issues I experienced was that the init.d service script was used instead of the systemd service if both were named the same.我遇到的问题是,如果两者的名称相同,则使用 init.d 服务脚本而不是 systemd 服务。 If you killed the init.d initiated process, the systemd script would then take over.
如果你杀死了 init.d 启动的进程,systemd 脚本就会接管。 But if you ran
service <service-name> stop
it would refer to the old init.d service.但是如果你运行
service <service-name> stop
它将引用旧的 init.d 服务。 So I found the best way was to drop the old init.d service and the service command referred to the systemd service instead.所以我发现最好的方法是删除旧的 init.d 服务,而 service 命令引用 systemd 服务。
Hope this helps!希望这可以帮助!
You MAY demonise a service.你可以妖魔化一个服务。 This is ok.
还行吧。
What's more important is that you do not demonise other people.更重要的是你不要妖魔化别人。
A service won't care about being demonised.服务不会在乎被妖魔化。 A human being will.
一个人会。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.