Cómo crear un daemon en python

Una de los grandes problemas que suelen presentarse a nivel de programación de sistemas es la creación, de forma correcta, de un daemon o servicio que se ejecute en segundo plano y que haga el trabajo correctamente.
La forma más cutre que existe de hacer esto es ejecutando el comando y pasarlo luego a segundo plano, dejando así el software en cuestión trabajando, pero no en forma de daemon.
Hoy vamos a ver cómo programar un daemon en Python.
Para ello, lo primero que necesitamos es asegurarnos de que las librerías de Python necesarias están instaladas y en caso contrario, instalarlas. Una vez instaladas, ya podemos comenzar a programar el servicio de forma sencilla y eficiente.

shell> apt-get install python-daemon python-lockfile
En este caso, lo que vamos a realizar es un programa trivial y sin lógica, pero que nos servirá de ejemplo. A continuación os dejo el código.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import logging
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    =  '/var/run/test.pid'
      self.pidfile_timeout = 5

   def run(self):
      i = 0
      while True:
         logger.info("message %s" %i++)
         i += 1
         time.sleep(1)

if __name__ == '__main__':
   app = App()
   logger = logging.getLogger("testlog")
   logger.setLevel(logging.INFO)
   formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
   handler = logging.FileHandler("/var/log/test.log")
   handler.setFormatter(formatter)
   logger.addHandler(handler)

   serv = runner.DaemonRunner(app)
   serv.daemon_context.files_preserve=[handler.stream]
   serv.do_action()
Como veis, el código es sencillo y con él ya creamos un pequeño servicio que se ejecutará en segundo plano y que se encargará de escribir cada segundo un mensaje en el log (fichero /var/log/test.log). A mayores también controla perfectamente que sólo haya una instancia del servicio ejecutándose al mismo tiempo (fichero de bloqueo /var/run/test.pid). Y como era de esperar, están incluidos por defecto aquellos métodos de arranque y parada de servicio (start, stop y restart). Para probar el funcionamiento del daemon, podemos hacerlo tal que así,
shell> python /usr/local/bin/test.py
usage: test.py start|stop|restart

shell> python /usr/local/bin/test.py start
started with pid 32547

shell> tail -f /var/log/test.log
2014-04-04 10:00:17,342 - testlog - Info message 3
2014-04-04 10:00:18,343 - testlog - Info message 4
2014-04-04 10:00:19,344 - testlog - Info message 5
2014-04-04 10:00:20,346 - testlog - Info message 6
2014-04-04 10:00:21,347 - testlog - Info message 7

shell> python /usr/local/bin/test.py stop
Terminating on signal 15
Por supuesto, para dejarlo listo y poder tenerlo instalado como un servicio en nuestro sistema, lo único que no falta es un fichero de arranque (/etc/init.d/test). Para ello, creamos uno muy sencillo.
#!/bin/bash

# /etc/init.d/test

### BEGIN INIT INFO
...
### END INIT INFO

case "$1" in
   start)
      echo "Starting server"
      python /usr/local/bin/test.py start 
      ;;

   stop)
      echo "Stopping server"
      python /usr/local/bin/test.py stop
      ;;

   restart)
      echo "Restarting server"
      python /usr/local/bin/test.py restart
      ;;

   *)
      echo "Usage: /etc/init.d/demonioprueba.sh {start|stop|restart}"
      exit 1
      ;;
esac
exit 0
Y ya podemos levantar nuestro servicio cuando sea necesario. Lógicamente, dentro del método run, de la clase App habrá que meter el código necesario para que el servicio tenga sentido, pero eso ya es cosa de cada uno. Aquí la idea era enseñar a levantar un servicio escrito en Python.


8 comentarios :

  1. Tu blog que a servido mucho para una aplicación que estoy desarrollando en la plataforma Beaglebone www.beaglebone.org

    Saludos

    ResponderEliminar
  2. from daemon import runner
    Traceback (most recent call last):
    File "", line 1, in
    ImportError: cannot import name runner


    ayuda!!!!

    ResponderEliminar
    Respuestas
    1. Tienes las dependencias instaladas? Paré que falta la librería daemon de Python.

      Eliminar
  3. El primer còdigo que colocas, preguntò: Donde se guarda el archivo y con que nombre?

    ResponderEliminar
    Respuestas
    1. No entiendo tu pregunta. El nombre del fichero y labrula es la que tú quieras. No depende de eso el super de los módulos.

      Eliminar
  4. Gracias por tu aporte, me ha servido para entender el funcionamiento de los daemon. Pero al ejecutar el código (con start) me muestra el siguiente mensaje:

    daemon.runner.DaemonRunnerStopFailureError: PID file '/var/run/test.pid' not locked

    ResponderEliminar
  5. AYUDA......
    NECESITO CREAR UNO PARA CUANDO SE DETENGA O CONGELE EL CLIENTE O LA APLICACIÓN ESTE LE DE INICIO A NUEVAMENTE

    ResponderEliminar

Formulario de contacto

Nombre

Correo electrónico *

Mensaje *

Últimos comentarios