我有作品作为守护简单的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。 脚本运行,但状态被激活并经过一段时间它更改为停用。 我现在用的蟒蛇守护进程(但在此之前我想没有它,症状相似)。
我应该实现一些附加功能给我的脚本或systemd文件不正确的?
Answer 1:
究其原因,它并没有完成启动顺序,对于类型forking
的启动过程,预计到餐桌和退出(见$男人systemd.service -搜索分叉)。
只需使用只有主过程中,不要守护进程
一种选择是少做。 随着systemd,通常没有必要创建守护进程,并可能直接运行代码,无需daemonizing。
#!/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
请注意,该-u
在python家当是没有必要的,但如果你打印出来的东西到标准输出或标准错误,则-u
确保,有一个地方没有输出缓冲和印刷线将通过systemd立即捕获并记录在杂志上。 没有它,它会出现一些延迟。
为了这个目的,我加入到单元文件中的行StandardOutput=syslog
和StandardError=syslog
。 如果你没有在你的日记在意打印输出,不关心这些行(他们没有必须存在)。
systemd
使系统守护进程过时
当你的问题的标题明确地询问daemonizing,我想,这个问题的核心是“如何让我的服务运行”,并同时采用主过程似乎简单得多 (您不必关心在所有守护进程),它可以考虑回答你的问题。
我想,很多人用daemonizing只是因为“每个人都没有它”。 随着systemd为daemonizing的原因往往是过时的。 可能有一些理由使用系统守护进程,但它现在将是罕见的情况。
编辑:固定python -p
适当的python -u
。 感谢kmftzg
Answer 2:
它可以像守护进程和Schnouki阿米特描述。 但随着systemd这是没有必要的。 有两种更好的方法来初始化进程:插座激活和明确的通知,sd_notify()。
插座激活适用于哪些要听一个网络端口或UNIX插座或类似的守护进程。 Systemd将打开插座,听就可以了,然后产卵守护当连接进来,这是首选的计算策略,因为它提供了最大的灵活性给管理员。 [1]和[2]给出一个很好的介绍,[3]描述了C API,而[4]描述了Python API。
[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,它已准备就绪,可以服务请求。 这可以用“分岔协议”来实现的,但实际上它是更好的,只是发送通知与sd_notify到systemd()。 Python包装被称为systemd.daemon.notify和将一个行到[5]使用。
[5] http://www.freedesktop.org/software/systemd/python-systemd/daemon.html#systemd.daemon.notify
在这种情况下,单元文件将具有类型=通知和呼叫systemd.daemon.notify(“READY = 1”)已经建立套接字之后。 无分叉或系统守护进程是必要的。
Answer 3:
你不是创建PID文件。
systemd期待你的程序写入其PID在/var/run/zebra.pid
。 当你不这样做,可能systemd认为你的程序失败,因此停用了。
要添加PID文件,安装锁文件和你的代码改成这样:
import daemon
import daemon.pidlockfile
pidfile = daemon.pidlockfile.PIDLockFile("/var/run/zebra.pid")
with daemon.DaemonContext(pidfile=pidfile):
check = Node()
check.run()
(快速注:最近的一些更新lockfile
改变了它的API,并使其与蟒蛇守护不相容要修复它,编辑。 daemon/pidlockfile.py
,删除LinkFileLock
来自进口,并添加from lockfile.linklockfile import LinkLockFile as LinkFileLock
。)
要小心的另一件事情: DaemonContext
改变你的程序的工作目录到/
,使得WorkingDirectory
你的服务文件的无用。 如果你想DaemonContext
到CHDIR到另一个目录,使用DaemonContext(pidfile=pidfile, working_directory="/path/to/dir")
Answer 4:
此外,您最有可能需要设置daemon_context=True
创建时DaemonContext()
这是因为,如果python-daemon
检测到如果下init系统的运行,它不从父进程分离。 systemd
预计,与运行的守护进程Type=forking
会这样做。 因此,你需要的,否则systemd
将继续等待,终于杀死进程。
如果你很好奇,在python-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
希望这个解释要好。
Answer 5:
尝试,通过将该文件转换成一些Python的init.d服务的CentOS 7.这似乎是工作非常适合我下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
我然后放弃我的旧的init.d服务文件/etc/init.d
跑sudo systemctl daemon-reload
重装systemd。
我希望我的服务,自动重启,因此重新启动选项。 我还发现,使用idle
的Type
不是更有意义simple
。
空闲的行为非常相似,简单; 然而,服务二进制文件的实际执行被延迟,直到所有活动的作业调度。 这可以用来避免与控制台上的状态输出的壳服务输出的交错。
在我使用的选项的更多细节在这里 。
我也尝试保留旧的服务并具有systemd resart的服务,但我遇到了一些问题。
[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
我遇到的问题是,init.d下的服务脚本来代替的systemd服务,如果这两个命名相同。 如果你杀了的init.d启动过程中,systemd脚本将随后接管。 但是,如果你跑了service <service-name> stop
它会引用旧的init.d服务。 于是,我找到了最好的方法是删除旧的init.d服务,并提到了systemd服务,而不是服务命令。
希望这可以帮助!
文章来源: Python daemon and systemd service