我有一个我用 python 编写的工具,通常应该作为守护进程运行。打包此工具以进行分发的最佳实践是什么,特别是应如何处理设置文件和守护程序可执行文件/脚本?
相关地,是否有任何通用工具用于设置适合给定平台的启动时运行的守护程序(即Linux 上的初始化脚本、Windows 上的服务、OS X 上的启动)?
我有一个我用 python 编写的工具,通常应该作为守护进程运行。打包此工具以进行分发的最佳实践是什么,特别是应如何处理设置文件和守护程序可执行文件/脚本?
相关地,是否有任何通用工具用于设置适合给定平台的启动时运行的守护程序(即Linux 上的初始化脚本、Windows 上的服务、OS X 上的启动)?
我发现帮助 init.d 脚本的最佳工具是“start-stop-daemon”。它将运行任何应用程序,监视运行/pid 文件,在必要时创建它们,提供停止守护进程的方法,设置进程用户/组 ID,甚至可以后台处理您的进程。
例如,这是一个可以启动/停止 wsgi 服务器的脚本:
#! /bin/bash
case "$1" in
start)
echo "Starting server"
# Activate the virtual environment
. /home/ali/wer-gcms/g-env/bin/activate
# Run start-stop-daemon, the $DAEMON variable contains the path to the
# application to run
start-stop-daemon --start --pidfile $WSGI_PIDFILE \
--user www-data --group www-data \
--chuid www-data \
--exec "$DAEMON"
;;
stop)
echo "Stopping WSGI Application"
# Start-stop daemon can also stop the application by sending sig 15
# (configurable) to the process id contained in the run/pid file
start-stop-daemon --stop --pidfile $WSGI_PIDFILE --verbose
;;
*)
# Refuse to do other stuff
echo "Usage: /etc/init.d/wsgi-application.sh {start|stop}"
exit 1
;;
esac
exit 0
您还可以看到一个如何将它与 virtualenv 一起使用的示例,我总是会推荐它。
要回答您的问题的一部分,我所知道的没有任何工具可以在 Linux 系统上进行守护程序设置,更不用说 Windows 或 Mac OS X。
大多数 Linux 发行版现在似乎都start-stop-daemon
在 init 脚本中使用,但在文件系统布局和打包方面仍然会有很大差异。使用 autotools/configure 或 distutils/easy_install 如果您的项目都是 Python,将大大有助于为不同的 Linux/BSD 发行版构建包。
Windows 是一个完全不同的游戏,需要Mark Hammond 的 win32扩展,也许还有Tim Golden 的 WMI扩展。
我不知道 Launchd,除了“以上都不相关”。
有关守护 Python 脚本的技巧,我会查看在现实世界中实际执行此操作的 Python 应用程序,例如在 Twisted 中。
互联网上有很多片段可以用纯 python 编写守护程序(没有 bash 脚本)
http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ 看起来很干净......
如果你想自己写,
原理和bash daemon函数一样。
基本上:
开始时:
停止时:
不过,我不知道有任何广泛使用的软件包可以做到这一点。
检查 Ben Finney 的守护进程模块。他已经开始编写针对 python 3.X 的 PEP:
http://www.python.org/dev/peps/pep-3143/
但是这里已经有一个实现:
对于您的要求,这不是灵丹妙药,但请查看supervisord。它处理管理流程的所有有趣部分。我在大型生产环境中大量使用它。此外,它是用 Python 编写的!
我不记得我在哪里下载了它......但这是我找到的最好的守护进程脚本。它运行良好(在 Mac 和 Linux 上)。(将其保存为 daemonize.py)
import sys, os
def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
# Perform first fork.
try:
pid = os.fork( )
if pid > 0:
sys.exit(0) # Exit first parent.
except OSError, e:
sys.stderr.write("fork #1 failed: (%d) %sn" % (e.errno, e.strerror))
sys.exit(1)
# Decouple from parent environment.
os.chdir("/")
os.umask(0)
os.setsid( )
# Perform second fork.
try:
pid = os.fork( )
if pid > 0:
sys.exit(0) # Exit second parent.
except OSError, e:
sys.stderr.write("fork #2 failed: (%d) %sn" % (e.errno, e.strerror))
sys.exit(1)
# The process is now daemonized, redirect standard file descriptors.
for f in sys.stdout, sys.stderr: f.flush( )
si = file(stdin, 'r')
so = file(stdout, 'a+')
se = file(stderr, 'a+', 0)
os.dup2(si.fileno( ), sys.stdin.fileno( ))
os.dup2(so.fileno( ), sys.stdout.fileno( ))
os.dup2(se.fileno( ), sys.stderr.fileno( ))
在您的脚本中,您只需:
from daemonize import daemonize
daemonize()
您还可以指定重定向 stdio、err 等的位置...
在 Linux 系统上,系统的包管理器(Gentoo 的 Portage、Ubuntu/Debian 的 Aptitude、Fedora 的 yum 等)通常负责安装程序,包括将 init 脚本放置在正确的位置。如果您想为 Linux 分发您的程序,您可能需要考虑将其捆绑成适合各种发行版包管理器的格式。
这个建议显然与没有包管理器的系统(我认为是 Windows 和 Mac)无关。
这篇博文让我清楚地知道,实际上有两种常见的方法可以让你的 Python 程序作为守护进程运行(我没有从现有答案中清楚地知道这一点):
有两种方法可以用 Python 编写像服务器这样的守护程序应用程序。
- 第一个是在 Python 代码本身中处理所有启动和停止守护进程的任务。最简单的方法是使用
python-daemon
最终可能会进入 Python 发行版的包。
Poeljapon 的答案是第一种方法的示例,虽然它不使用python-daemon
包,但链接到自定义但非常干净的 python 脚本。
- 另一种方法是使用操作系统提供的工具。对于 Debain,这意味着编写一个使用该
start-stop-daemon
程序的初始化脚本。
Ali Afshar 的答案是第二种方法的 shell 脚本示例,使用start-stop-daemon
.
我引用的博客条目有一个 shell 脚本示例,以及一些额外的细节,例如在系统启动时启动你的守护进程,以及在它因任何原因停止时自动重新启动你的守护进程。
如果错了,请纠正我,但我相信问题是如何部署守护进程。将您的应用设置为通过 pip 安装,然后将 entry_point 设为cli(daemon())
. 然后创建一个简单运行的初始化脚本$app_name &
“一般应该作为守护进程运行?”
从表面上看,这并没有多大意义。“一般”是不明智的。它要么是守护进程,要么不是。您可能想更新您的问题。
有关守护程序的示例,请阅读诸如 Apache 的 httpd 或任何数据库服务器(它们是守护程序)或 SMTPD 邮件守护程序之类的守护程序。
或者,也许,阅读一些更简单的东西,比如 FTP 守护进程、SSH 守护进程、Telnet 守护进程。
在 Linux 世界中,您将拥有应用程序安装目录、一些工作目录以及配置文件目录。
我们/opt/ourapp
用于应用程序(它是 Python,但我们不安装在 Python 中lib/site-packages
)
我们/var/ourapp
用于工作文件和我们的配置文件。
我们可以使用/etc/ourapp
配置文件——这将是一致的——但我们没有。
我们还没有使用init.d
脚本进行启动。但这是最后一块,自动启动。现在,我们让系统管理员启动守护进程。
这部分基于http://www.pathname.com/fhs/和http://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/Linux-Filesystem-Hierarchy.html。