web-dev-qa-db-fra.com

Comment écrire un script bash pour redémarrer un processus s'il meurt?

J'ai un script python qui vérifie une file d'attente et effectue une action sur chaque élément:

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

Comment puis-je écrire un script bash qui vérifiera s'il est en cours d'exécution, et si ce n'est pas le cas, démarrez-le. En gros, le pseudo-code suivant (ou peut-être devrait-il faire quelque chose comme ps | grep?):

# keepalivescript.sh
if processidfile exists:
  if processid is running:
     exit, all ok

run checkqueue.py
write processid to processidfile

Je vais appeler ça depuis une crontab:

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

Évitez les fichiers PID, les fichiers crons ou toute autre chose qui tente d'évaluer des processus qui ne sont pas leurs enfants.

Il y a une très bonne raison pour laquelle, sous UNIX, vous ne pouvez attendre que vos enfants. Toute méthode (ps analyse, pgrep, stockage d'un PID, ...) qui tente de contourner le problème est défectueuse et comporte des trous béants. Dites simplement non .

Au lieu de cela, vous avez besoin que le processus qui surveille votre processus soit le parent du processus. Qu'est-ce que ça veut dire? Cela signifie que seul le processus que commence votre processus peut attendre de manière fiable qu'il se termine. En bash, c'est absolument trivial.

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

Le code de bash ci-dessus exécute myserver dans une boucle until. La première ligne commence myserver et attend la fin. Quand il se termine, until vérifie son statut de sortie. Si le statut de sortie est 0, cela signifie qu'il s'est terminé normalement (ce qui signifie que vous avez demandé de l'éteindre d'une manière ou d'une autre et que l'opération s'est bien déroulée). Dans ce cas, nous ne voulons pas le redémarrer (nous lui avons simplement demandé de l'éteindre!). Si le statut de sortie est , not 0, until exécute le corps de la boucle, qui émet un message d'erreur sur STDERR et relance la boucle (retour à la ligne). 1) après 1 seconde .

Pourquoi attendons-nous une seconde? Parce que si quelque chose ne va pas avec la séquence de démarrage de myserver et que celle-ci se bloque immédiatement, vous aurez une boucle très intensive de redémarrage et de plantage constants. Le sleep 1 enlève la tension.

Il ne vous reste plus qu'à démarrer ce script bash (probablement de manière asynchrone). Il surveillera myserver et le redémarrera si nécessaire. Si vous souhaitez démarrer le moniteur au démarrage (pour que le serveur "survivre" au redémarrage), vous pouvez le planifier dans le cron de votre utilisateur avec une règle @reboot. Ouvrez vos règles cron avec crontab:

crontab -e

Ajoutez ensuite une règle pour démarrer votre script de moniteur:

@reboot /usr/local/bin/myservermonitor

Alternativement Regardez inittab (5) et/etc/inittab. Vous pouvez y ajouter une ligne pour que myserver démarre à un certain niveau d'init et soit réapparu automatiquement.


Modifier.

Permettez-moi d'ajouter quelques informations sur les raisons pour lesquelles pas d'utiliser des fichiers PID. Bien qu'ils soient très populaires; ils sont également très imparfaits et il n'y a aucune raison pour que vous ne le fassiez pas correctement.

Considère ceci:

  1. Recyclage PID (élimination du mauvais processus):

    • /etc/init.d/foo start: démarrer foo, écrire le PID de foo dans /var/run/foo.pid
    • Un peu plus tard: foo meurt en quelque sorte.
    • Un peu plus tard: tout processus aléatoire qui commence (appelez-le bar) prend un PID aléatoire, imaginez-le en prenant l'ancien PID de foo.
    • Vous remarquez que foo est parti: /etc/init.d/foo/restart lit /var/run/foo.pid, vérifie s'il est toujours en vie, trouve bar, pense que c'est foo, le tue, lance un nouveau foo.
  2. Les fichiers PID sont périmés. Vous avez besoin d'une logique trop compliquée (ou devrais-je dire, non triviale) pour vérifier si le fichier PID est périmé, et une telle logique est à nouveau vulnérable à 1..

  3. Que se passe-t-il si vous n'avez même pas d'accès en écriture ou si vous êtes dans un environnement en lecture seule?

  4. C'est une complication inutile. voyez comme mon exemple ci-dessus est simple. Pas besoin de compliquer cela du tout.

Voir aussi: Les fichiers PID sont-ils toujours défectueux quand on le fait 'bien'?

Au fait; encore pire que les fichiers PID, c'est l'analyse ps! Ne faites jamais cela.

  1. ps est très instable. Alors que vous le trouvez sur presque tous les systèmes UNIX; ses arguments varient grandement si vous voulez une sortie non standard. Et la sortie standard est UNIQUEMENT destinée à la consommation humaine et non à l'analyse syntaxique!
  2. L'analyse ps conduit à beaucoup de faux positifs. Prenons l'exemple ps aux | grep PID et imaginons maintenant que quelqu'un lance un processus avec un nombre quelque part comme argument qui est identique au PID avec lequel vous avez surveillé votre démon! Imaginez que deux personnes commencent une session X et que vous cherchiez X à tuer la vôtre. C'est juste toutes sortes de mauvaises.

Si vous ne voulez pas gérer le processus vous-même; Il existe de très bons systèmes qui serviront de moniteur pour vos processus. Regardez dans runit , par exemple.

586
lhunath

Regardez monit ( http://mmonit.com/monit/ ). Il gère le démarrage, l'arrêt et le redémarrage de votre script et peut effectuer des vérifications de l'état et des redémarrages si nécessaire.

Ou faites un script simple:

while true
do
/your/script
sleep 1
done
24
Bernd

La façon la plus simple de le faire consiste à utiliser flock on file. Dans le script Python, vous feriez

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()

Dans Shell, vous pouvez réellement tester son fonctionnement:

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

Mais bien sûr, vous n'avez pas à tester, car s'il est déjà lancé et que vous le redémarrez, il se terminera avec 'other instance already running'

À la fin du processus, tous les descripteurs de fichier sont fermés et tous les verrous sont automatiquement supprimés.

8
vartec

Vous devez utiliser monit, un outil standard Unix capable de surveiller différents éléments du système et de réagir en conséquence.

À partir de la documentation: http://mmonit.com/monit/documentation/monit.html#pid_testing

 vérifier le processus checkqueue.py avec pidfile /var/run/checkqueue.pid
 si modifié pid puis exec "checkqueue_restart.sh" 

Vous pouvez également configurer monit pour qu'il vous envoie un e-mail lors d'un redémarrage.

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

Je ne sais pas si c'est portable sur tous les systèmes d'exploitation, mais vous pouvez vérifier si votre système contient la commande 'run-one', c'est-à-dire "man run-one". Plus précisément, cet ensemble de commandes inclut "run-one-constant", ce qui semble être exactement ce dont vous avez besoin.

De la page de manuel:

run-one-constant COMMAND [ARGS]

Remarque: ceci peut évidemment être appelé depuis votre script, mais évite également d'avoir un script.

2
Daniel Bradley

J'ai utilisé le script suivant avec un grand succès sur de nombreux serveurs:

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

remarques:

  • Il recherche un processus Java afin que je puisse utiliser jps, ce qui est beaucoup plus cohérent entre les distributions que ps
  • $INSTALLATION contient suffisamment de chemin du processus qui est totalement non ambigu
  • Utilisez le sommeil en attendant la fin du processus, évitez de monopoliser les ressources :)

Ce script est en fait utilisé pour arrêter une instance en cours d'exécution de Tomcat, que je veux fermer (et attendre) sur la ligne de commande. Par conséquent, le lancer en tant que processus enfant n'est tout simplement pas une option pour moi.

1
Kevin Wright