library - python daemonize
¿Cómo se crea un demonio en Python? (15)
Aquí está mi demonio de Python ''Howdy World'' básico con el que comienzo, cuando estoy desarrollando una nueva aplicación de daemon.
#!/usr/bin/python
import time
from daemon import runner
class App():
def __init__(self):
self.stdin_path = ''/dev/null''
self.stdout_path = ''/dev/tty''
self.stderr_path = ''/dev/tty''
self.pidfile_path = ''/tmp/foo.pid''
self.pidfile_timeout = 5
def run(self):
while True:
print("Howdy! Gig''em! Whoop!")
time.sleep(10)
app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()
Tenga en cuenta que necesitará la biblioteca del python-daemon
. Puedes instalarlo por:
pip install python-daemon
Luego simplemente comience con ./howdy.py start
, y deténgalo con ./howdy.py stop
.
La búsqueda en Google revela fragmentos de código x2. El primer resultado es esta receta de código que tiene mucha documentación y explicación, junto con algunas discusiones útiles debajo.
Sin embargo, otro ejemplo de código , aunque no contiene tanta documentación, incluye código de ejemplo para pasar comandos como iniciar, detener y reiniciar. También crea un archivo PID que puede ser útil para comprobar si el demonio ya se está ejecutando, etc.
Estas muestras explican cómo crear el demonio. ¿Hay cosas adicionales que deben ser consideradas? ¿Es una muestra mejor que la otra, y por qué?
Después de algunos años y muchos intentos, ahora me doy cuenta de que hay una mejor manera de querer comenzar, detener, reiniciar un demonio directamente desde Python: ¡use las herramientas del sistema operativo en su lugar!
En resumen, en lugar de hacer python myapp start
y python myapp stop
, hago esto para iniciar la aplicación:
screen -S myapp python myapp.py
CTRL+A, D to detach
o screen -dmS myapp python myapp.py
para iniciarlo y desconectarlo en un solo comando .
Entonces:
screen -r myapp
para adjuntar a este terminal de nuevo. Una vez en el terminal, es posible usar CTRL + C para detenerlo.
El 80% de las veces, cuando la gente dice "demonio", solo quieren un servidor. Dado que la pregunta no está clara en este punto, es difícil decir cuál podría ser el posible dominio de respuestas. Dado que un servidor es adecuado, comience allí. Si realmente se necesita un "demonio" real (esto es raro), lea nohup
como una forma de demonizar un servidor.
Hasta que se requiera un demonio real, simplemente escriba un servidor simple.
También mira la implementación de referencia WSGI .
También mira el servidor HTTP simple .
"¿Hay cosas adicionales que deban ser consideradas?" Sí. Alrededor de un millón de cosas. ¿Qué protocolo? ¿Cuántas peticiones? ¿Cuánto tiempo para atender cada solicitud? ¿Con qué frecuencia llegarán? ¿Usarás un proceso dedicado? ¿Trapos? Subprocesos? Escribir un demonio es un gran trabajo.
Esta función transformará una aplicación a un demonio:
import sys
import os
def daemonize():
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write(''_Fork #1 failed: {0}/n''.format(err))
sys.exit(1)
# decouple from parent environment
os.chdir(''/'')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write(''_Fork #2 failed: {0}/n''.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, ''r'')
so = open(os.devnull, ''w'')
se = open(os.devnull, ''w'')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
Hay muchas cosas difíciles de cuidar cuando se convierte en un proceso daemon de buen comportamiento :
evitar volcados de núcleo (muchos demonios se ejecutan como root, y los volcados de núcleo pueden contener información confidencial)
comportarse correctamente dentro de una
chroot
configurar UID, GID, directorio de trabajo, umask y otros parámetros de proceso de manera apropiada para el caso de uso
renunciar a
suid
elevados, privilegios desgid
cerrar todos los descriptores de archivos abiertos, con exclusiones según el caso de uso
comportarse correctamente si se inicia dentro de un contexto ya separado, como
init
,inetd
, etc.configure los manejadores de señales para el comportamiento razonable de los demonios, pero también con manejadores específicos determinados por el caso de uso
redirigir los flujos estándar
stdin
,stdout
,stderr
ya que un proceso daemon ya no tiene un terminal de controlmaneja un archivo PID como un bloqueo de asesoramiento cooperativo, que es una lata de gusanos en sí misma con muchas formas contradictorias pero válidas de comportarse
Permitir la limpieza adecuada cuando el proceso se termina
En realidad se convierte en un proceso de daemon sin conducir a zombies
Algunos de estos son estándar , como se describe en la literatura canónica de Unix ( Programación avanzada en el entorno UNIX , por el difunto W. Richard Stevens, Addison-Wesley, 1992). Otros, como la redirección de flujos y el manejo de archivos PID , son comportamientos convencionales que la mayoría de los usuarios de demonios esperan, pero están menos estandarizados.
Todo esto está cubierto por la especificación PEP 3143 "Biblioteca de procesos de daemon estándar" . La implementación de referencia de pypi.python.org/pypi/python-daemon funciona en Python 2.7 o posterior, y en Python 3.2 o posterior.
La forma más fácil de crear un demonio con Python es usar el marco basado en eventos Twisted . Se encarga de todas las cosas necesarias para la demonización para usted. Utiliza el patrón Reactor para manejar solicitudes concurrentes.
Me temo que el módulo de daemon mencionado por @Dustin no funcionó para mí. En lugar de eso, instalé pypi.python.org/pypi/python-daemon y utilicé el siguiente código:
# filename myDaemon.py
import sys
import daemon
sys.path.append(''/home/ubuntu/samplemodule'') # till __init__.py
from samplemodule import moduleclass
with daemon.DaemonContext():
moduleclass.do_running() # I have do_running() function and whatever I was doing in __main__() in module.py I copied in it.
Correr es facil
> python myDaemon.py
sólo para completar aquí es samplemodule contenido del directorio
>ls samplemodule
__init__.py __init__.pyc moduleclass.py
El contenido de moduleclass.py puede ser
class moduleclass():
...
def do_running():
m = moduleclass()
# do whatever daemon is required to do.
Modifiqué algunas líneas en el ejemplo de código de Sander Marechal (mencionado por @JeffBauer en la respuesta aceptada ) para agregar un método quit()
que se ejecuta antes de que el daemon se detenga. Esto es a veces muy útil.
Nota: no uso el módulo "daemon de python" porque todavía falta la documentación (consulte también muchas otras preguntas de SO) y es bastante oscuro (¿cómo iniciar / detener correctamente un demonio desde la línea de comandos con este módulo?)
Probablemente no sea una respuesta directa a la pregunta, pero se puede usar systemd para ejecutar su aplicación como un demonio. Aquí hay un ejemplo:
[Unit]
Description=Python daemon
After=syslog.target
After=network.target
[Service]
Type=simple
User=<run as user>
Group=<run as group group>
ExecStart=/usr/bin/python <python script home>/script.py
# Give the script some time to startup
TimeoutSec=300
[Install]
WantedBy=multi-user.target
Prefiero este método porque gran parte del trabajo está hecho para usted, y luego su script de daemon se comporta de manera similar al resto de su sistema.
-O por
Tenga en cuenta el paquete pypi.python.org/pypi/python-daemon que resuelve muchos problemas detrás de los demonios fuera de la caja.
Entre otras características, permite (a partir de la descripción del paquete Debian):
- Desacoplar el proceso en su propio grupo de procesos.
- Establecer el entorno de proceso apropiado para ejecutarse dentro de un chroot.
- Renuncia a los privilegios sididos y sididos.
- Cierre todos los descriptores de archivos abiertos.
- Cambie el directorio de trabajo, uid, gid y umask.
- Establecer manejadores de señal apropiados.
- Abra nuevos descriptores de archivo para stdin, stdout y stderr.
- Administrar un archivo de bloqueo PID especificado.
- Registre las funciones de limpieza para el procesamiento de salida.
Una alternativa: crear un programa de Python normal, no demonizado, y luego demonizarlo externamente usando supervisord.org . Esto puede ahorrar muchos dolores de cabeza, y es * nix- y lenguaje portátil.
Una cosa más para pensar cuando se daemoniza en python:
Si está utilizando el registro de Python y desea continuar usándolo después de la demonización, asegúrese de llamar a close()
en los manejadores (particularmente los manejadores de archivos).
Si no lo hace, el manejador aún puede pensar que tiene archivos abiertos y sus mensajes simplemente desaparecerán; en otras palabras, ¡asegúrese de que el registrador sepa que sus archivos están cerrados!
Esto supone que cuando se desmonta, se están cerrando TODOS los descriptores de archivos abiertos de manera indiscriminada; en lugar de eso, se puede intentar cerrar todos los archivos de registro menos los archivos de registro (pero, por lo general, es más sencillo cerrar todos y luego volver a abrir los que desee).
ya que python-daemon aún no es compatible con python 3.x, y de lo que se puede leer en la lista de correo, puede que nunca lo haga, he escrito una nueva implementación de PEP 3143: pep3143daemon
pep3143daemon debería admitir al menos Python 2.6, 2.7 y 3.x
También contiene una clase PidFile.
La biblioteca solo depende de la biblioteca estándar y del módulo seis.
Se puede utilizar como una gota en el reemplazo de python-daemon.
Aquí está la documentation .
YapDi es un módulo de python relativamente nuevo que apareció en Hacker News. Parece bastante útil, se puede usar para convertir un script de python en modo daemon desde dentro del script.
Solución actual
Una implementación de referencia de PEP 3143 (biblioteca de procesos del daemon estándar) ahora está disponible como python-daemon .
Respuesta historica
El ejemplo del código de Sander Marechal es superior al original, que se publicó originalmente en 2004. Una vez contribuí con un daemonizer para Pyro, pero probablemente usaría el código de Sander si tuviera que hacerlo.