perl - sinonimos - ¿Cómo demonizo una secuencia de comandos arbitraria en Unix?
desdemonizar (12)
Como alternativa a las daemonize
y daemontools
ya mencionadas, está el comando daemon del paquete libslack.
daemon
es bastante configurable y se preocupa por todo el tedioso daemon, como el reinicio automático, el registro o el manejo de archivos pidfile.
Me gustaría un daemonizador que pueda convertir un script o comando arbitrario y genérico en un daemon .
Hay dos casos comunes con los que me gustaría tratar:
Tengo un script que debería funcionar para siempre. Si alguna vez muere (o al reiniciar), reinícielo. No permita que haya dos copias ejecutándose a la vez (detecte si una copia ya se está ejecutando y no la ejecute en ese caso).
Tengo un script simple o un comando de línea de comando que me gustaría seguir ejecutando repetidamente para siempre (con una breve pausa entre ejecuciones). Nuevamente, no permita que se ejecuten dos copias del guión a la vez.
Por supuesto, es trivial escribir un ciclo "while (true)" alrededor del script en el caso 2 y luego aplicar una solución para el caso 1, pero una solución más general resolverá el caso 2 directamente, ya que eso aplica al script en el caso 1 como bien (es posible que desee una pausa más corta o ninguna si el guión no está destinado a morir nunca (por supuesto, si el guión realmente nunca muere, entonces la pausa en realidad no importa)).
Tenga en cuenta que la solución no debe incluir, por ejemplo, agregar código de bloqueo de archivos o grabación PID a las secuencias de comandos existentes.
Más específicamente, me gustaría un programa "daemonize" que pueda ejecutar como
% daemonize myscript arg1 arg2
o, por ejemplo,
% daemonize ''echo `date` >> /tmp/times.txt''
que mantendría una lista creciente de fechas añadidas a times.txt. (Tenga en cuenta que si el argumento (s) a demonizar es un script que se ejecuta para siempre como en el caso 1 anterior, entonces daemonize seguirá haciendo lo correcto, reiniciándolo cuando sea necesario.) Entonces podría poner un comando como el de arriba en mi .login y / o cron por hora o minuciosamente (dependiendo de lo preocupado que estaba por la muerte inesperada).
NB: El script daemonize necesitará recordar la cadena de comandos que está demonizando, de modo que si la misma cadena de comandos se daemoniza nuevamente, no se inicia una segunda copia.
Además, la solución ideal debería funcionar tanto en OS X como en Linux, pero las soluciones para uno u otro son bienvenidas.
EDITAR: Está bien si tienes que invocarlo con sudo daemonize myscript myargs
.
(Si estoy pensando que todo está mal o que hay soluciones parciales rápidas y sucias, también me gustaría escuchar eso).
PD: en caso de que sea útil, here''s una pregunta similar específica para Python.
Y this respuesta a una pregunta similar tiene lo que parece ser una expresión útil para una demonización rápida y sucia de un guión arbitrario:
Creo que es posible que desee probar start-stop-daemon(8)
. Consulte los scripts en /etc/init.d
en cualquier distribución de Linux para ver ejemplos. Puede encontrar procesos iniciados por línea de comando invocada o archivo PID, por lo que coincide con todos sus requisitos, excepto como un perro guardián para su secuencia de comandos. Pero siempre puede iniciar otra secuencia de comandos de vigilancia demonio que solo reinicia su secuencia de comandos si es necesario.
Daemontools ( daemontools ) es un conjunto de utilidades bastante duras que se utilizan para hacer esto, escrito por dj bernstein. Lo he usado con algo de éxito. Lo molesto es que ninguno de los scripts devuelve resultados visibles cuando los ejecuta, solo códigos de retorno invisibles. Pero una vez que se está ejecutando es a prueba de balas.
Deberías echarle un vistazo a daemonize . Permite detectar una segunda copia (pero usa un mecanismo de bloqueo de archivos). También funciona en diferentes distribuciones de UNIX y Linux.
Si necesita iniciar su aplicación automáticamente como daemon, entonces necesita crear el script de inicio apropiado.
Puedes usar la siguiente plantilla:
#!/bin/sh
#
# mydaemon This shell script takes care of starting and stopping
# the <mydaemon>
#
# Source function library
. /etc/rc.d/init.d/functions
# Do preliminary checks here, if any
#### START of preliminary checks #########
##### END of preliminary checks #######
# Handle manual control parameters like start, stop, status, restart, etc.
case "$1" in
start)
# Start daemons.
echo -n $"Starting <mydaemon> daemon: "
echo
daemon <mydaemon>
echo
;;
stop)
# Stop daemons.
echo -n $"Shutting down <mydaemon>: "
killproc <mydaemon>
echo
# Do clean-up works here like removing pid files from /var/run, etc.
;;
status)
status <mydaemon>
;;
restart)
$0 stop
$0 start
;;
*)
echo $"Usage: $0 {start|stop|status|restart}"
exit 1
esac
exit 0
Esta es una versión de trabajo completa con un ejemplo que puede copiar en un directorio vacío y probar (después de instalar las dependencias de CPAN, que son Getopt::Long , File::Spec , File::Pid e IPC::System::Simple : todo bastante estándar y muy recomendable para cualquier hacker: puede instalarlos todos a la vez con cpan <modulename> <modulename> ...
).
keepAlive.pl:
#!/usr/bin/perl
# Usage:
# 1. put this in your crontab, to run every minute:
# keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
# where $pidfile is the same value as used in the cron job above:
# use File::Pid;
# File::Pid->new({file => $pidfile})->write;
# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.
use strict;
use warnings;
use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);
my ($pid_file, $command);
GetOptions("pidfile=s" => /$pid_file,
"command=s" => /$command)
or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>/n", exit;
my @arguments = @ARGV;
# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});
if ($pid_obj->running())
{
# process is still running; nothing to do!
exit 0;
}
# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments/n";
system($command, @arguments);
example.pl:
#!/usr/bin/perl
use strict;
use warnings;
use File::Pid;
File::Pid->new({file => "pidfile"})->write;
print "$0 got arguments: @ARGV/n";
Ahora puede invocar el ejemplo anterior con: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3
y se pidfile
el archivo pidfile
, y verá el resultado:
Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3
He hecho una serie de mejoras en la otra respuesta .
- stdout fuera de este script está compuesto puramente de stdout proveniente de su hijo A MENOS QUE se cierre debido a la detección de que el comando ya se está ejecutando
- se limpia después de su archivo pid cuando termina
- Período de tiempo de espera configurable opcional (Acepta cualquier argumento numérico positivo, envía a
sleep
) - indicador de uso en
-h
- ejecución de comandos arbitrarios, en lugar de la ejecución de comandos individuales. Los últimos arg OR args restantes (si hay más de un último arg) se envían a
eval
, por lo que puede construir cualquier tipo de script de shell como una cadena para enviar a este script como último arg (o args finales) para que se daemonize - las comparaciones de recuentos de argumentos hechas con
-lt
lugar de<
Aquí está el guión:
#!/bin/sh
# this script builds a mini-daemon, which isn''t a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.
if [ "$1" = ''-h'' ]; then
echo "timeout defaults to 1 sec./nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
exit
fi
if [ $# -lt 2 ]; then
echo "No command given."
exit
fi
PIDFILE=$1
shift
TIMEOUT=1
if [[ $1 =~ ^[0-9]+(/.[0-9]+)?$ ]]; then
TIMEOUT=$1
[ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
shift
fi
echo "Checking pid in file ${PIDFILE}." >&2
#Check to see if process running.
if [ -f "$PIDFILE" ]; then
PID=$(< $PIDFILE)
if [ $? = 0 ]; then
ps -p $PID >/dev/null 2>&1
if [ $? = 0 ]; then
echo "This script is (probably) already running as PID ${PID}."
exit
fi
fi
fi
# Write our pid to file.
echo $$ >$PIDFILE
cleanup() {
rm $PIDFILE
}
trap cleanup EXIT
# Run command until we''re killed.
while true; do
eval "$@"
echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
sleep $TIMEOUT
done
Uso:
$ term-daemonize.sh pidfilefortesting 0.5 ''echo abcd | sed s/b/zzz/''
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C
$ term-daemonize.sh pidfilefortesting 0.5 ''echo abcd | sed s/b/zzz/'' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C
Tenga en cuenta que si ejecuta esta secuencia de comandos desde diferentes directorios, puede usar diferentes archivos pid y no detectar ninguna instancia existente en ejecución. Dado que está diseñado para ejecutar y reiniciar comandos efímeros proporcionados a través de un argumento, no hay manera de saber si algo ya se ha iniciado, porque ¿quién puede decir si es el mismo comando o no? Para mejorar esta aplicación de solo ejecutar una instancia única de algo, se requiere una solución específica para la situación.
Además, para que funcione como un daemon apropiado, debe usar (en el mínimo) nohup como la otra respuesta menciona. No he hecho ningún esfuerzo para proporcionar resistencia a las señales que el proceso puede recibir.
Un punto más a tener en cuenta es que matar este script (si fue llamado desde otro script que se eliminó, o con una señal) puede no tener éxito en matar al niño, especialmente si el niño es otro script. No estoy seguro de por qué esto es así, pero parece ser algo relacionado con la forma en que eval
trabaja, lo cual es misterioso para mí. Por lo tanto, puede ser prudente reemplazar esa línea con algo que acepte solo un comando como en la otra respuesta.
Me disculpo por la respuesta larga (por favor, consulte los comentarios sobre cómo mi respuesta da en el clavo). Estoy tratando de ser comprensivo, así que tienes la mejor oportunidad posible. :-)
Si puede instalar programas (tiene acceso de administrador) y está dispuesto a hacer un trabajo de una sola vez para configurar su script para la ejecución de daemon (es decir, más complicado que simplemente especificar los argumentos de la línea de comando para ejecutar en la línea de comando, pero solo necesita hacerse una vez por servicio), tengo una forma más robusta.
Implica el uso de daemontools . El resto de la publicación describe cómo configurar servicios utilizando daemontools.
Configuración inicial
- Siga las instrucciones en Cómo instalar daemontools . Algunas distribuciones (p. Ej., Debian, Ubuntu) ya tienen paquetes, así que solo use eso.
- Haga un directorio llamado
/service
. El instalador ya debería haber hecho esto, pero solo verifique o si lo instala manualmente. Si no le gusta esta ubicación, puede cambiarla en su scriptsvscanboot
, aunque la mayoría de los usuarios de daemontools están acostumbrados a usar/service
y se confundirán si no lo usa. - Si está utilizando Ubuntu u otra distribución que no utiliza
init
estándar (es decir, no utiliza/etc/inittab
), necesitará usar elinittab
preinstalado como base para organizarsvscanboot
para ser llamado porinit
. No es difícil, pero necesita saber cómo configurar elinit
que usa su sistema operativo.svscanboot
es un script que llama asvscan
, que hace el trabajo principal de buscar servicios; se llama desdeinit
por lo queinit
se encargará de reiniciarlo si fallece por algún motivo.
Configuración por servicio
- Cada servicio necesita un directorio de servicios , que almacena información sobre el servicio de limpieza. También puede hacer una ubicación para albergar estos directorios de servicios para que estén todos en un solo lugar; usualmente uso
/var/lib/svscan
, pero cualquier ubicación nueva estará bien. Usualmente uso un script para configurar el directorio del servicio, para ahorrar mucho trabajo manual repetitivo. p.ej,
sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
donde
some-service-name
es el nombre que desea dar a su servicio, eluser
es el usuario para ejecutar ese servicio como, yloguser
es el usuario para ejecutar el registrador como. (El registro se explica en solo un poco).- Tu servicio tiene que ejecutarse en primer plano . Si el fondo de su programa está predeterminado, pero tiene una opción para desactivarlo, hágalo. Si su programa tiene un fondo sin una forma de desactivarlo, lea
fghack
, aunque esto tiene unfghack
: ya no puede controlar el programa usandosvc
. - Edite el script de
run
para asegurarse de que está haciendo lo que quiere. Es posible que deba realizar una llamada de espera en la parte superior, si espera que su servicio salga con frecuencia. - Cuando todo esté configurado correctamente, crea un enlace simbólico en
/service
apuntando a tu directorio de servicio. (No coloque directorios de servicio directamente dentro de/service
; hace que sea más difícil eliminar el servicio delsvscan
desvscan
).
Explotación florestal
- La forma de registro de daemontools es hacer que el servicio escriba los mensajes de registro en la salida estándar (o error estándar, si usa scripts generados con
mkservice
);svscan
se encarga de enviar mensajes de registro al servicio de registro. - El servicio de registro toma los mensajes de registro de la entrada estándar. La secuencia de comandos de servicio de registro generada por
mkservice
creará automáticamente los archivos de registro con marcas de tiempo en ellog/main
directoriolog/main
. El archivo de registro actual se llamacurrent
. - El servicio de registro puede iniciarse y detenerse independientemente del servicio principal.
- Tubería de los archivos de registro a través de
tai64nlocal
traducirá las marcas de tiempo en un formato legible por humanos. (TAI64N es una marca de tiempo atómica de 64 bits con un recuento de nanosegundos).
Servicios de control
- Use
svstat
para obtener el estado de un servicio. Tenga en cuenta que el servicio de registro es independiente y tiene su propio estado. - Usted controla su servicio (inicio, detención, reinicio, etc.) utilizando
svc
. Por ejemplo, para reiniciar su servicio, usesvc -t /service/some-service-name
;-t
significa "enviarSIGTERM
". - Otras señales disponibles incluyen
-h
(SIGHUP
),-a
(SIGALRM
),-1
(SIGUSR1
),-2
(SIGUSR2
) y-k
(SIGKILL
). - Para bajar el servicio, use
-d
. También puede evitar que un servicio se inicie automáticamente durante el arranque creando un archivo con el nombredown
en el directorio del servicio. - Para comenzar el servicio, use
-u
. Esto no es necesario a menos que lo hayas bajado previamente (o lo hayas configurado para que no se inicie automáticamente). - Para pedirle al supervisor que salga, use
-x
; usualmente se usa con-d
para terminar el servicio también. Esta es la forma habitual de permitir que se elimine un servicio, pero primero debe desvincular el servicio de/service
o, de lo contrario,svscan
reiniciará el supervisor. Además, si creó su servicio con un servicio de registro (mkservice -l
), recuerde también salir del supervisor de registro (por ejemplo,svc -dx /var/lib/svscan/some-service-name/log
) antes de eliminar el directorio de servicio .
Resumen
Pros:
- daemontools proporciona una forma a prueba de balas para crear y administrar servicios. Lo uso para mis servidores, y lo recomiendo encarecidamente.
- Su sistema de registro es muy robusto, como lo es el servicio de reinicio automático.
- Debido a que inicia los servicios con un script de shell que usted escribe / ajusta, puede personalizar su servicio de la forma que desee.
- Potentes herramientas de control de servicio: puede enviar casi cualquier señal a un servicio y puede subir y bajar servicios de manera confiable.
- Sus servicios tienen garantizado un entorno de ejecución limpio: se ejecutarán con el mismo entorno, límites de proceso, etc., que proporciona
init
.
Contras:
- Cada servicio requiere un poco de configuración. Afortunadamente, esto solo necesita hacer una vez por servicio.
- Los servicios deben configurarse para ejecutarse en primer plano. Además, para obtener los mejores resultados, se deben configurar para iniciar sesión en la salida estándar / error estándar, en lugar de syslog u otros archivos.
- Empinada curva de aprendizaje si eres nuevo en la manera de hacer las cosas de daemontools.
svc
reiniciar los servicios utilizandosvc
, y no puede ejecutar los scripts de ejecución directamente (ya que no estarían bajo el control del supervisor). - Muchos archivos de limpieza, y muchos procesos de limpieza. Cada servicio necesita su propio directorio de servicios, y cada servicio utiliza un proceso de supervisor para reiniciar automáticamente el servicio si se muere. (Si tiene muchos servicios, verá muchos procesos de
supervise
en su tabla de procesos).
En balance, creo que daemontools es un sistema excelente para sus necesidades. Acepto cualquier pregunta sobre cómo configurarlo y mantenerlo.
Podrías tratar de inmortal. Es un supervisor * nix multiplataforma (OS agnóstico).
Para una prueba rápida en macOS:
brew install immortal
En caso de que esté utilizando FreeBSD desde los puertos o mediante el uso de pkg:
pkg install immortal
Para Linux descargando los archivos binarios precompilados o desde la fuente: https://immortal.run/source/
Puedes usarlo así:
immortal -l /var/log/date.log date
O mediante un archivo de configuración YAML que le brinda más opciones, por ejemplo:
cmd: date
log:
file: /var/log/date.log
age: 86400 # seconds
num: 7 # int
size: 1 # MegaBytes
timestamp: true # will add timesamp to log
Si desea mantener también el resultado de error estándar en un archivo separado, podría usar algo como:
cmd: date
log:
file: /var/log/date.log
age: 86400 # seconds
num: 7 # int
size: 1 # MegaBytes
stderr:
file: /var/log/date-error.log
age: 86400 # seconds
num: 7 # int
size: 1 # MegaBytes
timestamp: true # will add timesamp to log
Primero obtén createDaemon()
de http://code.activestate.com/recipes/278731/
Luego el código principal:
import subprocess
import time
createDaemon()
while True:
subprocess.call(" ".join(sys.argv[1:]),shell=True)
time.sleep(10)
Puede demonizar cualquier ejecutable en Unix utilizando nohup y el operador &:
nohup yourScript.sh script args&
El comando nohup le permite cerrar su sesión de shell sin que se elimine su script, mientras que & & coloca su script en segundo plano para que obtenga un aviso de shell para continuar su sesión. El único problema menor con esto es la salida estándar y el error estándar ambos se envían a ./nohup.out, por lo que si inicia varios scripts en este feudo, su salida se entrelazará. Un mejor comando sería:
nohup yourScript.sh script args >script.out 2>script.error&
Esto enviará el estándar al archivo de su elección y el error estándar a un archivo diferente de su elección. Si desea utilizar solo un archivo para la salida estándar y el error estándar, puede hacernos esto:
nohup yourScript.sh script args >script.out 2>&1 &
El 2> y 1 le dice al shell que redirija el error estándar (descriptor de archivo 2) al mismo archivo que el estándar (descriptor de archivo 1).
Para ejecutar un comando solo una vez y reiniciarlo si se muere, puede usar este script:
#!/bin/bash
if [[ $# < 1 ]]; then
echo "Name of pid file not given."
exit
fi
# Get the pid file''s name.
PIDFILE=$1
shift
if [[ $# < 1 ]]; then
echo "No command given."
exit
fi
echo "Checking pid in file $PIDFILE."
#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
ps -p $PID >/dev/null 2>&1
if [[ $? = 0 ]]; then
echo "Command $1 already running."
exit
fi
fi
# Write our pid to file.
echo $$ >$PIDFILE
# Get command.
COMMAND=$1
shift
# Run command until we''re killed.
while true; do
$COMMAND "$@"
sleep 10 # if command dies immediately, don''t go into un-ctrl-c-able loop
done
El primer argumento es el nombre del archivo pid para usar. El segundo argumento es el comando. Y todos los demás argumentos son los argumentos del comando.
Si llama a este script restart.sh, así es como lo llamaría:
nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &
Si está utilizando OS X específicamente, le sugiero que eche un vistazo a cómo funciona launchd. Verificará automáticamente si su script se está ejecutando y lo relanzará si es necesario. También incluye todo tipo de características de programación, etc. Debe satisfacer los requisitos 1 y 2.
En cuanto a asegurar que solo se puede ejecutar una copia de su script, necesita usar un archivo PID. En general, escribo un archivo en /var/run/.pid que contiene un PID de la instancia actual en ejecución. si el archivo existe cuando el programa se ejecuta, verifica si el PID en el archivo se está ejecutando realmente (el programa puede haberse bloqueado u olvidado de algún otro modo para eliminar el archivo PID). Si es así, aborta. De lo contrario, comience a ejecutar y sobrescriba el archivo PID.
También puedes probar Monit . Monit es un servicio que monitorea e informa sobre otros servicios. Si bien se utiliza principalmente como una forma de notificar (a través de correo electrónico y sms) sobre los problemas de tiempo de ejecución, también puede hacer lo que la mayoría de las otras sugerencias han recomendado. Puede auto (re) iniciar y detener programas, enviar correos electrónicos, iniciar otros scripts y mantener un registro de salida que puede recoger. Además, he encontrado que es fácil de instalar y mantener ya que hay documentación sólida.