Python脚本作为Linux服务/守护进程

72

你好,

我正在尝试让一个Python脚本在(Ubuntu)Linux上作为服务(守护进程)运行。

网络上存在几种解决方案,如下:

http://pypi.python.org/pypi/python-daemon/

编写一个良好的Unix守护进程是很棘手的,但每个守护进程程序所需的步骤都大致相同。 DaemonContext实例保存程序的行为和配置的进程环境;使用该实例作为上下文管理器以进入守护进程状态。

http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/

然而,由于我想将我的Python脚本与Ubuntu Linux特别集成,因此我的解决方案是使用init.d脚本的组合。

#!/bin/bash

WORK_DIR="/var/lib/foo"
DAEMON="/usr/bin/python"
ARGS="/opt/foo/linux_service.py"
PIDFILE="/var/run/foo.pid"
USER="foo"

case "$1" in
  start)
    echo "Starting server"
    mkdir -p "$WORK_DIR"
    /sbin/start-stop-daemon --start --pidfile $PIDFILE \
        --user $USER --group $USER \
        -b --make-pidfile \
        --chuid $USER \
        --exec $DAEMON $ARGS
    ;;
  stop)
    echo "Stopping server"
    /sbin/start-stop-daemon --stop --pidfile $PIDFILE --verbose
    ;;
  *)
    echo "Usage: /etc/init.d/$USER {start|stop}"
    exit 1
    ;;
esac

exit 0

并且在Python中:

import signal
import time
import multiprocessing

stop_event = multiprocessing.Event()

def stop(signum, frame):
    stop_event.set()

signal.signal(signal.SIGTERM, stop)

if __name__ == '__main__':
    while not stop_event.is_set():
        time.sleep(3)

我现在的问题是这种方法是否正确。我需要处理任何额外的信号吗?这将成为"一个表现良好的Unix守护进程"吗?

2个回答

90

假设您的守护进程有一种持续运行的方式(某些事件循环,twisted等),您可以尝试使用upstart

这是一个假想的Python服务的示例upstart配置:

description "My service"
author  "Some Dude <blah@foo.com>"

start on runlevel [234]
stop on runlevel [0156]

chdir /some/dir
exec /some/dir/script.py
respawn
如果你将这个保存为 script.conf 到 /etc/init 目录下,你只需要执行一次。
$ sudo initctl reload-configuration
$ sudo start script
你可以使用 stop script 停止它。上述的 Upstart 配置文件会在重启时启动此服务,如果它死了就重新启动。
至于信号处理 - 你的进程应该自然地响应 SIGTERM。默认情况下,除非你特别安装了自己的信号处理程序,否则应该会被处理。

2
你说得对,现在upstart是标准!由于上述脚本处理了SIGTERM,所以它应该可以与你的配置文件兼容 :) - tauran
11
刚刚我做了一个额外的微调。如果你的Python脚本运行在虚拟环境下,你只需要更改upstart使用该环境中的Python可执行文件即可:exec /home/user/.env/environ/bin/python /some/dir/script.py - Anthony Briggs
很棒的信息。/etc/init文件的文档在哪里? - Scott Willeke
从upstart版本1.4开始,您可以使用"setid"和"setgid"。该参数是用户/组名称。 - tiktak
4
Upstart似乎不再是标准了。维基百科列出了许多"Linux发行版[...]自此抛弃或不再使用upstart作为其默认init系统",他们改用systemd代替。更多有关Upstream和Systemd的信息来自unix.stackexchange - Hibuki

11
Rloton的回答很好。这里只是一些轻微的改进,因为我花了很多时间调试。我需要做一个新的回答,以便我可以正确地格式化。
还有一些其他的要点,这让我花费了很长时间来调试:
1.当它失败时,首先检查/var/log/upstart/.log
2.如果您的脚本使用python-daemon实现守护程序,则不要使用“expect daemon”部分。没有“expect”也可以。我不知道为什么。(如果有人知道为什么,请发帖!)
3.此外,继续检查“initctl status script”,以确保您处于启动/运行状态。(并在更新配置文件时重新加载)
这是我的版本:
description "My service"
author  "Some Dude <blah@foo.com>"

env PYTHON_HOME=/<pathtovirtualenv>
env PATH=$PYTHON_HOME:$PATH

start on runlevel [2345]
stop on runlevel [016]

chdir <directory>

# NO expect stanza if your script uses python-daemon
exec $PYTHON_HOME/bin/python script.py

# Only turn on respawn after you've debugged getting it to start and stop properly
respawn

当我将这个配置复制到 /etc/init 中,然后键入 service myservicename,它找不到它。出了什么问题? - David
1
你是否执行了 initctl reload-configuration 命令,然后再执行 service myservice start 命令? - Ross R
抱歉,现在它可以工作了。我一直在输入“service servicename”,以为它会给我选项,但显然不是这样的...你必须输入“servicename start stop”或“restart”。无论如何,感谢您的回复。 - David
2
非常感谢。关于“无expect stanza”的提示是非常宝贵的。不幸的是,upstart在重启之间保留了一些状态,并且有时会因为没有原因而挂起正确的文件。这使得调试非常困难。 - yhager

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接