run - Cómo hacer que un script de Python se ejecute como un servicio o daemon en Linux
python-daemon example (15)
He escrito un script de Python que verifica una cierta dirección de correo electrónico y pasa nuevos correos electrónicos a un programa externo. ¿Cómo puedo hacer que este script se ejecute 24/7, como convertirlo en daemon o servicio en Linux? ¿También necesitaría un bucle que nunca termina en el programa, o puede hacerse simplemente haciendo que el código se ejecute varias veces?
¿Qué hay de usar el comando $nohup
en Linux?
Lo uso para ejecutar mis comandos en mi servidor Bluehost.
Por favor, consejo si estoy equivocado.
Aquí hay una buena clase que se toma de here :
#!/usr/bin/env python
import sys, os, time, atexit
from signal import SIGTERM
class Daemon:
"""
A generic daemon class.
Usage: subclass the Daemon class and override the run() method
"""
def __init__(self, pidfile, stdin=''/dev/null'', stdout=''/dev/null'', stderr=''/dev/null''):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def daemonize(self):
"""
do the UNIX double-fork magic, see Stevens'' "Advanced
Programming in the UNIX Environment" for details (ISBN 0201563177)
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
"""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError, e:
sys.stderr.write("fork #1 failed: %d (%s)/n" % (e.errno, e.strerror))
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, e:
sys.stderr.write("fork #2 failed: %d (%s)/n" % (e.errno, e.strerror))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = file(self.stdin, ''r'')
so = file(self.stdout, ''a+'')
se = file(self.stderr, ''a+'', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
file(self.pidfile,''w+'').write("%s/n" % pid)
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""
Start the daemon
"""
# Check for a pidfile to see if the daemon already runs
try:
pf = file(self.pidfile,''r'')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if pid:
message = "pidfile %s already exist. Daemon already running?/n"
sys.stderr.write(message % self.pidfile)
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
"""
Stop the daemon
"""
# Get the pid from the pidfile
try:
pf = file(self.pidfile,''r'')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if not pid:
message = "pidfile %s does not exist. Daemon not running?/n"
sys.stderr.write(message % self.pidfile)
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, SIGTERM)
time.sleep(0.1)
except OSError, err:
err = str(err)
if err.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print str(err)
sys.exit(1)
def restart(self):
"""
Restart the daemon
"""
self.stop()
self.start()
def run(self):
"""
You should override this method when you subclass Daemon. It will be called after the process has been
daemonized by start() or restart().
"""
Deberías usar la biblioteca python-daemon , se ocupa de todo.
Desde PyPI: Library para implementar un proceso de demonio Unix con buen comportamiento.
Para crear algo que se ejecuta como servicio, puede usar esta cosa:
Lo primero que debe hacer es instalar el marco Cement : el trabajo de marco de cemento es un trabajo de marco CLI que puede implementar su aplicación en él.
interfaz de línea de comando de la aplicación:
interface.py
from cement.core.foundation import CementApp
from cement.core.controller import CementBaseController, expose
from YourApp import yourApp
class Meta:
label = ''base''
description = "your application description"
arguments = [
([''-r'' , ''--run''],
dict(action=''store_true'', help=''Run your application'')),
([''-v'', ''--version''],
dict(action=''version'', version="Your app version")),
]
([''-s'', ''--stop''],
dict(action=''store_true'', help="Stop your application")),
]
@expose(hide=True)
def default(self):
if self.app.pargs.run:
#Start to running the your app from there !
YourApp.yourApp()
if self.app.pargs.stop:
#Stop your application
YourApp.yourApp.stop()
class App(CementApp):
class Meta:
label = ''Uptime''
base_controller = ''base''
handlers = [MyBaseController]
with App() as app:
app.run()
Clase YourApp.py:
import threading
class yourApp:
def __init__:
self.loger = log_exception.exception_loger()
thread = threading.Thread(target=self.start, args=())
thread.daemon = True
thread.start()
def start(self):
#Do every thing you want
pass
def stop(self):
#Do some things to stop your application
Tenga en cuenta que su aplicación debe ejecutarse en un hilo para ser demonio
Para ejecutar la aplicación solo haz esto en la línea de comando
python interface.py --ayuda
Primero, lee los alias del correo. Un alias de correo hará esto dentro del sistema de correo sin que tengas que perder el tiempo con daemons o servicios o algo por el estilo.
Puede escribir un script simple que sendmail ejecutará cada vez que se envíe un mensaje de correo a un buzón específico.
Ver http://www.feep.net/sendmail/tutorial/intro/aliases.html
Si realmente desea escribir un servidor innecesariamente complejo, puede hacerlo.
nohup python myscript.py &
Eso es todo lo que se necesita. Tu script simplemente se repite y duerme.
import time
def do_the_work():
# one round of polling -- checking email, whatever.
while True:
time.sleep( 600 ) # 10 min.
try:
do_the_work()
except:
pass
Puede usar fork () para separar su script del tty y hacer que continúe ejecutándose, de esta forma:
import os, sys
fpid = os.fork()
if fpid!=0:
# Running as daemon now. PID is fpid
sys.exit(0)
Por supuesto, también necesita implementar un bucle infinito, como
while 1:
do_your_check()
sleep(5)
Espero que esto empiece.
Si está usando terminal (ssh o algo así) y desea mantener un script de larga duración en funcionamiento después de cerrar la sesión desde el terminal, puede intentar esto:
screen
apt-get install screen
crear una terminal virtual dentro (a saber, abc): screen -dmS abc
ahora nos conectamos a abc: screen -r abc
Entonces, ahora podemos ejecutar el script de python Keep_sending_mail.py
: python Keep_sending_mail.py
a partir de ahora, puede cerrar directamente su terminal, sin embargo, la secuencia de comandos python seguirá ejecutándose en lugar de cerrarse
Dado que este PID de
Keep_sending_mail.py
pertenece a la pantalla virtual en lugar de a la terminal (ssh)
Si quieres volver verifica el estado de ejecución de tu script, puedes usar screen -r abc
otra vez
También puede hacer que el script python se ejecute como un servicio utilizando un script de shell. Primero crea un script de shell para ejecutar el script de python así (nombre de script nombre arbitario)
#!/bin/sh
script=''/home/.. full path to script''
/usr/bin/python $script &
ahora crea un archivo en /etc/init.d/scriptname
#! /bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/home/.. path to shell script scriptname created to run python script
PIDFILE=/var/run/scriptname.pid
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting feedparser"
start_daemon -p $PIDFILE $DAEMON
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping feedparser"
killproc -p $PIDFILE $DAEMON
PID=`ps x |grep feed | head -1 | awk ''{print $1}''`
kill -9 $PID
log_end_msg $?
;;
force-reload|restart)
$0 stop
$0 start
;;
status)
status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $?
;;
*)
echo "Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}"
exit 1
;;
esac
exit 0
Ahora puede iniciar y detener su script de python usando el comando /etc/init.d/scriptname start o stop.
Tienes dos opciones aquí.
Haga un trabajo cron apropiado que llame a su script. Cron es un nombre común para un daemon de GNU / Linux que periódicamente lanza secuencias de comandos de acuerdo con un programa establecido. Agregue su secuencia de comandos en un crontab o coloque un enlace simbólico en un directorio especial y el daemon maneja el trabajo de iniciarlo en segundo plano. Puedes leer más en wikipedia. Existe una variedad de diferentes daemons cron, pero su sistema GNU / Linux debería tenerlo ya instalado.
Utilice algún tipo de enfoque de Python (una biblioteca, por ejemplo) para que su script pueda demonizarse a sí mismo. Sí, requerirá un bucle de evento simple (donde sus eventos son disparadores de temporizador, posiblemente proporcionados por la función de suspensión).
No recomendaría que eligieras 2. porque de hecho estás repitiendo la funcionalidad de cron. El paradigma del sistema Linux es permitir que múltiples herramientas simples interactúen y resuelvan sus problemas. A menos que haya razones adicionales por las que debe crear un daemon (además de activarse periódicamente), elija el otro enfoque.
Además, si utiliza daemonize con un bucle y se produce un bloqueo, nadie revisará el correo después de eso (como señaló Ivan Nevostruev en los comentarios a this respuesta). Si bien la secuencia de comandos se agrega como una tarea cron, simplemente se activará nuevamente.
Tuve un problema similar, el siguiente enlace funcionó bien para mí: http://werxltd.com/wp/2012/01/05/simple-init-d-script-template/#footnote_0_1077
No utiliza nada específico para ninguna distribución, si se usa chkconfig , se puede iniciar al inicio del sistema.
Una versión simple y supported es Deamonize. Instálala desde el Python Package Index (PyPI):
$ pip install daemonize
y luego usar como:
...
import os, sys
from daemonize import Daemonize
...
def main()
# your code here
if __name__ == ''__main__'':
myname=os.path.basename(sys.argv[0])
pidfile=''/tmp/%s'' % myname # any name
daemon = Daemonize(app=myname,pid=pidfile, action=main)
daemon.start()
Utilice el administrador de servicios que su sistema le ofrezca, por ejemplo, en Ubuntu use upstart . Esto manejará todos los detalles para usted, como inicio al arrancar, reinicio en crash, etc.
Yo recomendaría esta solución. Debe heredar y anular la run
método.
import sys
import os
from signal import SIGTERM
from abc import ABCMeta, abstractmethod
class Daemon(object):
__metaclass__ = ABCMeta
def __init__(self, pidfile):
self._pidfile = pidfile
@abstractmethod
def run(self):
pass
def _daemonize(self):
# decouple threads
pid = os.fork()
# stop first thread
if pid > 0:
sys.exit(0)
# write pid into a pidfile
with open(self._pidfile, ''w'') as f:
print >> f, os.getpid()
def start(self):
# if daemon is started throw an error
if os.path.exists(self._pidfile):
raise Exception("Daemon is already started")
# create and switch to daemon thread
self._daemonize()
# run the body of the daemon
self.run()
def stop(self):
# check the pidfile existing
if os.path.exists(self._pidfile):
# read pid from the file
with open(self._pidfile, ''r'') as f:
pid = int(f.read().strip())
# remove the pidfile
os.remove(self._pidfile)
# kill daemon
os.kill(pid, SIGTERM)
else:
raise Exception("Daemon is not started")
def restart(self):
self.stop()
self.start()
cron
es claramente una gran opción para muchos propósitos. Sin embargo, no crea un servicio o daemon como solicitó en el OP. cron
simplemente ejecuta trabajos periódicamente (lo que significa que el trabajo comienza y se detiene), y no más de una vez / minuto. Hay problemas con cron
: por ejemplo, si una instancia anterior de su script aún se está ejecutando la próxima vez que aparece el cronograma cron
y se inicia una nueva instancia, ¿está bien? cron
no maneja dependencias; solo trata de comenzar un trabajo cuando el horario lo dice.
Si encuentra una situación en la que realmente necesita un daemon (un proceso que nunca deja de ejecutarse), eche un vistazo a supervisord
. Proporciona una forma simple de envolver un script o programa normal no dañado y hacerlo funcionar como un daemon. Esta es una forma mucho mejor que crear un demonio Python nativo.