蟒蛇守护和systemd服务(Python daemon and systemd service)

2019-06-18 15:41发布

我有作品作为守护简单的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=syslogStandardError=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.dsudo systemctl daemon-reload重装systemd。

我希望我的服务,自动重启,因此重新启动选项。 我还发现,使用idleType不是更有意义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