example daemonize python packaging setuptools distutils

daemonize - Python Daemon Packaging Buenas Prácticas



python daemonize (10)

Tengo una herramienta que he escrito en Python y generalmente debería ejecutarse como un demonio. ¿Cuáles son las mejores prácticas para empaquetar esta herramienta para su distribución, en particular, cómo deben manejarse los archivos de configuración y el ejecutable / script del daemon?

¿Existen herramientas comunes para configurar el demonio para que se ejecute en el arranque según sea apropiado para la plataforma dada (es decir, scripts de inicio en linux, servicios en windows, launchd en os x)?


"En general, se debe ejecutar como un demonio?"

No tiene, en la superficie, mucho sentido. "Generalmente" no es sensato. Es un daemon o no. Es posible que desee actualizar su pregunta.

Para ver ejemplos de demonios, lea sobre demonios como el httpd de Apache o cualquier servidor de base de datos (son demonios) o el demonio de correo SMTPD.

O, quizás, lea algo más simple, como el demonio FTP, SSH, Telnet.

En el mundo de Linux, tendrá el directorio de instalación de la aplicación, algunos directorios de trabajo, más los directorios de archivos de configuración.

Usamos /opt/ourapp para la aplicación (es Python, pero no instalamos en los lib/site-packages Python)

Usamos /var/ourapp para archivos de trabajo y nuestros archivos de configuración.

Podríamos usar /etc/ourapp para los archivos de configuración, sería consistente, pero no lo hacemos.

Todavía no utilizamos los scripts init.d para el inicio. Pero esa es la pieza final, el arranque automatizado. Por ahora, tenemos administradores de sistemas que inician los demonios.

Esto se basa, en parte, en http://www.pathname.com/fhs/ y http://tldp.org/LDP/Linux-Filesystem-Hierarchy/html/Linux-Filesystem-Hierarchy.html .



En sistemas Linux, el administrador de paquetes del sistema (Portage para Gentoo, Aptitude para Ubuntu / Debian, yum para Fedora, etc.) generalmente se encarga de instalar el programa, incluida la colocación de scripts de inicio en los lugares correctos. Si desea distribuir su programa para Linux, es posible que desee analizar el empaquetado en el formato adecuado para los gestores de paquetes de varias distribuciones.

Este consejo es obviamente irrelevante en sistemas que no tienen administradores de paquetes (creo que en Windows y Mac).


Esta entrada de blog me dejó en claro que en realidad hay dos formas comunes de hacer que tu programa Python se ejecute como un demonio (no lo había descubierto tan claramente a partir de las respuestas existentes):

Hay dos enfoques para escribir aplicaciones de daemon como servidores en Python.

  • El primero es manejar todas las tareas de inicio y detención de demonios en el propio código de Python . La forma más fácil de hacer esto es con el paquete python-daemon que eventualmente podría abrirse camino en la distribución de Python.

La respuesta de Poeljapon es un ejemplo de este primer enfoque, aunque no usa el paquete python-daemon , sino que se vincula a un script de python personalizado pero muy limpio.

  • El otro enfoque es utilizar las herramientas suministradas por el sistema operativo . En el caso de Debain, esto significa escribir un script de inicio que haga uso del programa start-stop-daemon .

La respuesta de Ali Afshar es un ejemplo de script de shell del segundo enfoque, que utiliza el start-stop-daemon .

La entrada del blog que cité tiene un ejemplo de script de shell, y algunos detalles adicionales sobre cosas como iniciar su daemon al inicio del sistema y reiniciar su daemon automáticamente cuando se detuvo por cualquier motivo.


Hay muchos fragmentos en Internet que ofrecen escribir un demonio en python puro (sin scripts de bash)

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

Si quieres escribir el tuyo,
El principio es el mismo que con la función del demonio bash.

Básicamente:

Al inicio:

  • te bifurcas a otro proceso
  • abra un archivo de registro para redireccionar su stdout y stderr
  • Guarde el pid en algún lugar.

En la parada:

  • Envía SIGTERM al proceso con pid almacenado en su archivo pid.
  • Con signal.signal (signal.SIGTERM, sigtermhandler) puede vincular un procedimiento de parada a la señal SIGTERM.

Sin embargo, no conozco ningún paquete ampliamente utilizado haciendo esto.


La mejor herramienta que encontré para ayudar con los scripts init.d es "start-stop-daemon". Ejecutará cualquier aplicación, monitoreará los archivos ejecutados / pid, los creará cuando sea necesario, proporcionará formas de detener el demonio, establecerá los identificadores de usuario / grupo del proceso e incluso podrá hacer un fondo del proceso.

Por ejemplo, este es un script que puede iniciar / detener un servidor 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

También puede ver un ejemplo de cómo usarlo con un virtualenv, que siempre recomendaría.


No es una bala de plata para lo que está pidiendo, pero eche un vistazo a supervisord . Maneja todos los bits de diversión de los procesos de gestión. Lo uso mucho en un entorno de gran producción. Además, está escrito en Python!


No recuerdo dónde lo descargué ... pero este es el mejor script de demonización que he encontrado. Funciona a la perfección (en Mac y Linux). (Guárdelo como 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( ))

En su guión, usted simplemente:

from daemonize import daemonize daemonize()

Y también puede especificar lugares para redireccionar el stdio, err, etc ...


Para responder a una parte de su pregunta, no conozco ninguna herramienta que pueda hacer la configuración del daemon de forma portátil incluso en sistemas Linux, y mucho menos en Windows o Mac OS X.

La mayoría de las distribuciones de Linux parecen estar usando start-stop-daemon dentro de los scripts de inicio ahora, pero todavía tendrá una pequeña diferencia en el diseño del sistema de archivos y grandes diferencias en el empaquetado. El uso de autotools / configure, o distutils / easy_install si su proyecto es todo de Python, hará mucho más fácil la creación de paquetes para diferentes distribuciones de Linux / BSD.

Windows es un juego completamente diferente y requerirá las extensiones win32 de Mark Hammond y quizás las extensiones WMI de Tim Golden .

No sé Launchd, excepto que "ninguno de los anteriores" son relevantes.

Para obtener sugerencias sobre cómo demonizar los scripts de Python, me gustaría ver las aplicaciones de Python que realmente lo hacen en el mundo real, por ejemplo, dentro de Twisted.


corríjame si está mal, pero creo que la pregunta es cómo DESPLEGAR el daemon. Configure su aplicación para que se instale a través de pip y luego haga que entry_point sea cli(daemon()) . Luego cree un script de inicio que simplemente ejecute $app_name &