¿Cómo escribo un script bash para reiniciar un proceso si se muere?

Tengo un script de python que verificará una cola y realizará una acción en cada elemento:

# checkqueue.py while True: check_queue() do_something() 

¿Cómo escribo un script bash que verificará si se está ejecutando? Si no, inícielo. Aproximadamente el siguiente pseudo código (o tal vez debería hacer algo como ps | grep ?):

 # keepalivescript.sh if processidfile exists: if processid is running: exit, all ok run checkqueue.py write processid to processidfile 

Llamaré eso desde un crontab:

 # crontab */5 * * * * /path/to/keepalivescript.sh 

Evite archivos PID, crones o cualquier otra cosa que intente evaluar procesos que no son sus hijos.

Hay una muy buena razón por la cual en UNIX, SOLO puede esperar a sus hijos. Cualquier método (ps parsing, pgrep, almacenamiento de un PID, …) que intente funcionar defectuoso y tenga agujeros en él. Solo di que no .

En su lugar, necesita que el proceso que supervisa su proceso sea el padre del proceso. ¿Qué significa esto? Significa que solo el proceso que inicia su proceso puede esperar a que termine. En bash, esto es absolutamente trivial.

 until myserver; do echo "Server 'myserver' crashed with exit code $?. Respawning.." >&2 sleep 1 done 

La pieza anterior de código bash ejecuta myserver en un ciclo until . La primera línea inicia myserver y espera a que termine. Cuando termina, until comprueba su estado de salida. Si el estado de salida es 0 , significa que terminó correctamente (lo que significa que usted pidió que se apague de alguna manera, y lo hizo con éxito). En ese caso, no queremos reiniciarlo (¡simplemente le pedimos que se apague!). Si el estado de salida no es 0 , until se ejecute el cuerpo del bucle, que emite un mensaje de error en STDERR y reinicia el bucle (volver a la línea 1) después de 1 segundo .

¿Por qué esperamos un segundo? Porque si algo anda mal con la secuencia de inicio de myserver y se bloquea inmediatamente, tendrás un ciclo muy intenso de reinicio constante y estrellarse en tus manos. El sleep 1 quita la tensión de eso.

Ahora todo lo que tiene que hacer es iniciar este script bash (de forma asíncrona, probablemente), y supervisará el myserver y lo reiniciará según sea necesario. Si desea iniciar el monitor al arrancar (haciendo que el servidor “sobreviva” se reinicie), puede progtwigrlo en el cron de su usuario (1) con una regla @reboot . Abra sus reglas de cron con crontab :

 crontab -e 

A continuación, agregue una regla para iniciar su secuencia de comandos del monitor:

 @reboot /usr/local/bin/myservermonitor 

Alternativamente; mira inittab (5) y / etc / inittab. Puede agregar una línea para que myserver comience en un cierto nivel de inicio y reaparecer automáticamente.


Editar.

Permítanme agregar algo de información sobre por qué no usar archivos PID. Si bien son muy populares; también son muy defectuosos y no hay razón por la cual no lo hagas de la manera correcta.

Considera esto:

  1. Reciclaje de PID (matando el proceso incorrecto):

    • /etc/init.d/foo start : start foo , escribe el PID de foo en /var/run/foo.pid
    • Un tiempo después: foo muere de alguna manera.
    • Un tiempo después: cualquier proceso aleatorio que comience (llámalo bar ) toma un PID aleatorio, imagínate tomando el viejo PID de foo .
    • Observa que foo ha ido: /etc/init.d/foo/restart lee /var/run/foo.pid , comprueba si todavía está activo, encuentra la bar , cree que es foo , lo elimina, inicia un nuevo foo .
  2. Los archivos PID quedan obsoletos. Necesita una lógica demasiado complicada (o debería decir, no trivial) para verificar si el archivo PID está obsoleto, y cualquier lógica de este tipo es nuevamente vulnerable a 1. ..

  3. ¿Qué sucede si ni siquiera tiene acceso de escritura o se encuentra en un entorno de solo lectura?

  4. Es una sobrecomplicación sin sentido; mira cuán simple es mi ejemplo anterior. No hay necesidad de complicar eso, en absoluto.

Ver también: ¿Los archivos PID siguen siendo defectuosos cuando se hace “correcto”?

Por cierto; ¡Incluso peor que los archivos PID es analizar ps ! Nunca hagas esto

  1. ps es muy poco práctico. Mientras lo encuentras en casi todos los sistemas UNIX; sus argumentos varían mucho si desea salida no estándar. ¡Y la salida estándar SOLAMENTE para el consumo humano, no para el análisis sintáctico!
  2. El análisis de ps conduce a MUCHOS positivos falsos. Toma el ps aux | grep PID Ejemplo de ps aux | grep PID , ¡y ahora imagina a alguien iniciando un proceso con un número en algún lugar como argumento que resulta ser el mismo que el PID con el que miraste a tu daemon! Imagina a dos personas comenzando una sesión X y agotando para que X mate al tuyo. Es todo tipo de cosas malas.

Si no desea administrar el proceso usted mismo; hay algunos sistemas perfectamente buenos que actuarán como monitores para sus procesos. Mire en runit , por ejemplo.

Eche un vistazo a monit ( http://mmonit.com/monit/ ). Maneja el inicio, la detención y el reinicio de la secuencia de comandos y puede realizar comprobaciones de estado y reinicios si es necesario.

O haz una secuencia de comandos simple:

 while true do /your/script sleep 1 done 

La forma más fácil de hacerlo es usar rebaño en el archivo. En el script de Python harías

 lf = open('/tmp/script.lock','w') if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): sys.exit('other instance already running') lf.write('%d\n'%os.getpid()) lf.flush() 

En shell, puedes probar si se está ejecutando:

 if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then echo 'it's not running' restart. else echo -n 'it's already running with PID ' cat /tmp/script.lock fi 

Pero, por supuesto, no tiene que probar, porque si ya se está ejecutando y lo reinicia, saldrá con 'other instance already running'

Cuando el proceso muere, todos sus descriptores de archivo se cierran y todos los lockings se eliminan automáticamente.

 if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then restart_process # Write PIDFILE echo $! >$PIDFILE fi 

Debería usar monit, una herramienta estándar de Unix que puede monitorear diferentes cosas en el sistema y reactjsr en consecuencia.

De los documentos: http://mmonit.com/monit/documentation/monit.html#pid_testing

 compruebe el proceso checkqueue.py con pidfile /var/run/checkqueue.pid
        si se cambia pid entonces ejecuta "checkqueue_restart.sh"

También puede configurar Mon para enviarle un correo electrónico cuando se reinicie.

He utilizado la siguiente secuencia de comandos con gran éxito en numerosos servidores:

 pid=`jps -v | grep $INSTALLATION | awk '{print $1}'` echo $INSTALLATION found at PID $pid while [ -e /proc/$pid ]; do sleep 0.1; done 

notas:

  • Está buscando un proceso de Java, entonces puedo usar jps, esto es mucho más consistente en distribuciones que ps
  • $INSTALLATION contiene suficiente de la ruta del proceso que es totalmente inequívoca
  • Use dormir mientras espera que el proceso muera, evite acaparar recursos 🙂

Este script se usa para cerrar una instancia en ejecución de tomcat, que quiero cerrar (y esperar) en la línea de comandos, por lo que iniciarlo como un proceso secundario simplemente no es una opción para mí.