Comment puis-je écrire un script bash pour redémarrer un processus s’il meurt?

J’ai un script python qui vérifie une queue 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 sinon, lancez-le. À peu près 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 d’un crontab:

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

Évitez les fichiers PID, les crons ou tout autre élément 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 pouvez UNIQUEMENT attendre sur vos enfants. Toute méthode (parsing ps, pgrep, stockage d’un PID, …) qui essaie de contourner ce qui est défectueux et comporte des trous béants. Dis juste non .

Au lieu de cela, vous avez besoin du processus qui surveille votre processus pour devenir le parent du processus. Qu’est-ce que ça veut dire? Cela signifie que seul le processus qui démarre votre processus peut attendre avec certitude qu’il se termine. En bash, c’est absolument sortingvial.

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

Le morceau de code bash ci-dessus exécute myserver dans une boucle while. La première ligne démarre myserver et attend sa fin. A la fin, until son état de sortie until vérifié. Si le statut de sortie est 0 , cela signifie qu’il s’est terminé gracieusement (ce qui signifie que vous lui avez demandé de s’arrêter et qu’il a réussi). Dans ce cas, nous ne voulons pas le redémarrer (nous lui avons simplement demandé de l’éteindre!). Si le statut de sortie n’est pas 0 , le programme exécute le corps de la boucle, qui émet un message d’erreur sur STDERR et redémarre la boucle (retour à la ligne 1) après 1 seconde .

Pourquoi attendons-nous une seconde? Parce que si quelque chose ne va pas dans la séquence de démarrage de myserver et qu’il plante immédiatement, vous aurez une boucle très intense de redémarrage constant et de plantage sur vos mains. Le sleep 1 enlève la tension de cela.

Maintenant, tout ce que vous avez à faire est de démarrer ce script bash (de manière asynchrone, probablement), et il surveillera myserver et le redémarrera si nécessaire. Si vous voulez démarrer le moniteur au démarrage (en faisant en sorte que le serveur «survit» au redémarrage), vous pouvez le planifier dans le cron (1) 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 contrôle:

 @reboot /usr/local/bin/myservermonitor 

Ou bien regardez inittab (5) et / etc / inittab. Vous pouvez y append une ligne pour que myserver démarre à un certain niveau d’initialisation et soit réapparu automatiquement.


Modifier.

Permettez-moi d’append quelques informations sur pourquoi ne pas utiliser les fichiers PID. Bien qu’ils soient très populaires; ils sont également très défectueux et il n’y a aucune raison pour que vous ne fassiez pas cela correctement.

Considère ceci:

  1. Recyclage PID (tuer le mauvais processus):

    • /etc/init.d/foo start : lancez foo , écrivez le PID de /var/run/foo.pid dans /var/run/foo.pid
    • Un peu plus tard: foo meurt en quelque sorte.
    • Un peu plus tard: tout processus aléatoire qui démarre (appelez-le) 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 la bar , pense que c’est foo , 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 sortingviale) pour vérifier si le fichier PID est périmé et qu’une telle logique est à nouveau vulnérable à 1. ..

  3. Et si vous n’avez même pas access 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 ça du tout.

Voir aussi: Les fichiers PID sont-ils toujours imparfaits lorsqu’ils le font “correctement”?

Au fait; pire que les fichiers PID, c’est l’parsing ps ! Ne fais jamais ça.

  1. ps est très déplorable. Alors que vous le trouvez sur presque tous les systèmes UNIX; ses arguments varient grandement si vous souhaitez une sortie non standard. Et la sortie standard est UNIQUEMENT pour la consommation humaine, pas pour l’parsing de script!
  2. L’parsing ps conduit à beaucoup de faux positifs. Prenez le ps aux | grep PID Exemple de ps aux | grep PID , et maintenant, imaginez que quelqu’un lance un processus avec un nombre quelque part comme argument qui se trouve être le même que le PID avec lequel vous avez regardé votre démon! Imaginez que deux personnes commencent une session X et que vous souhaitiez que X tue le 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. Rechercher dans runit , par exemple.

Jetez un coup d’œil à 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 santé plus des redémarrages si nécessaire.

Ou faites un script simple:

 while true do /your/script sleep 1 done 

La manière la plus simple de le faire est d’utiliser flock on file. En script Python que 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() 

En shell, vous pouvez réellement tester s’il est en cours d’exécution:

 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à en cours d’exécution et que vous le redémarrez, il se terminera par 'other instance already running'

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

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

Vous devez utiliser monit, un outil Unix standard qui peut surveiller différentes choses sur le système et réagir en conséquence.

À partir des documents: http://mmonit.com/monit/documentation/monit.html#pid_testing

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

Vous pouvez également configurer monit pour vous envoyer un e-mail quand il redémarre.

J’ai utilisé le script suivant avec beaucoup de 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 cherche un processus Java, donc je peux utiliser jps, c’est beaucoup plus cohérent à travers les dissortingbutions que ps
  • $INSTALLATION contient suffisamment de chemin de processus qui est totalement sans ambiguïté
  • Utilisez le sumil en attendant que le processus meure, évitez les ressources accablantes 🙂

Ce script est en fait utilisé pour fermer une instance en cours d’exécution de tomcat, que je souhaite fermer (et attendre) sur la ligne de commande. Le lancer en tant que processus enfant n’est donc pas une option pour moi.