Une meilleure façon de redémarrer / recharger Gunicorn (via Upstart) après 'git pull'ing mes projets Django

Je cherche quelque chose de mieux que sudo restart projectname chaque fois que j'émet un git pull origin master, qui abaisse mes dernières modifications à un projet Django. Cette commande restart, je crois, est liée à Upstart, que j'utilise pour démarrer / top mon processus de serveur Gunicorn.

Ce redémarrage provoque une brève panne. Les utilisateurs qui frappent le serveur web (nginx) recevront un 500, Car Gunicorn redémarre toujours. En fait, il semble redémarrer instantanément, mais il faut quelques secondes pour le chargement des pages.

Des idées sur la façon de faire cela sans couture? Idéalement, je voudrais émettre mes git pull et Gunicorn rechargements automatiquement.

46
demandé sur Flowpoke 2012-03-27 04:34:55

5 réponses

Pour un rechargement gracieux, vous devriez plutôt utiliser la commande reload de Upstart, par exemple:

sudo reload jobname

Selon la page de manuel initctl (Upstart) , reload envoie un signal HUP au processus:

reload JOB [KEY=VALUE]...

       Sends the SIGHUP signal to running process of the named JOB instance.

...ce qui pour Gunicorn déclenchera un redémarrage gracieux (voir FAQ).

16
répondu Gary 2015-12-28 19:14:45

Vous pouvez dire à Gunicorn de recharger gracieusement en utilisant le signal HUP comme ceci:

kill -HUP <pid>

(voir la FAQ pour plus de détails)

J'utilise Supervisor pour contrôler mon serveur Gunicorn, ce qui me permet d'utiliser cette façon (légèrement hacky) de recharger Gunicorn après un déploiement:

supervisorctl status gunicorn | sed "s/.*[pid ]\([0-9]\+\)\,.*/\1/" | xargs kill -HUP

Vous pouvez évidemment réaliser quelque chose de similaire avec pidof, ou ps.

Ceci est en fait exécuté à partir d'un script Fabric , donc je n'ai même pas besoin de me connecter au serveur à tout.

74
répondu Rob Golding 2012-10-23 04:31:46

Pour ceux qui n'utilisent pas supervisord: ce que Rob a dit, cela fonctionne aussi avec ps,

ps aux |grep gunicorn |grep projectname | awk '{ print $2 }' |xargs kill -HUP
17
répondu thomaspaulb 2015-01-11 18:20:44

Systemd, gunicorn et Ubuntu

Voici le One-liner, si vous utilisez votre service gunicorn avec systemd .

systemctl status gunicorn |  sed -n 's/.*Main PID: \(.*\)$/\1/g p' | cut -f1 -d' ' | xargs kill -HUP

Détails étape par étape

Puisque les documents gunicorn indiquent que la bonne façon de recharger gracieusement les travailleurs est d'utiliser kill -HUP <Main PID>, où <Main PID> est l'id de processus du processus maître, nous extrayons le PID maître en utilisant systemctl, et exécutons kill -HUP <Main PID>.

1) Obtenir des informations sur le processus à partir de systemd en utilisant le nom de le service

systemctl status gunicorn 

gunicorn est le nom du service, située à /etc/systemd/system/.

Exemple de sortie:

ubuntu@ip-10-4-12-247:~$ systemctl status gunicorn
● gunicorn.service - Gunicorn server for yourproject.com
   Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2017-11-04 19:16:24 UTC; 1h 15min ago
 Main PID: 10673 (gunicorn)
   CGroup: /system.slice/gunicorn.service
           ├─10673 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
           ├─11069 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
           ├─11070 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application
           └─11071 /home/ubuntu/site/venv/bin/python3 /home/ubuntu/site/venv/bin/gunicorn --workers 3 --bind unix:/tmp/yourproject.socket config.wsgi:application

Nov 04 20:27:04 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:27:04 +0000] [11047] [INFO] Booting worker with pid: 11047
Nov 04 20:27:04 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:27:04 +0000] [11048] [INFO] Booting worker with pid: 11048
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [10673] [INFO] Handling signal: hup
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [10673] [INFO] Hang up: Master
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11046] [INFO] Worker exiting (pid: 11046)
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11047] [INFO] Worker exiting (pid: 11047)
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11048] [INFO] Worker exiting (pid: 11048)
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11069] [INFO] Booting worker with pid: 11069
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11070] [INFO] Booting worker with pid: 11070
Nov 04 20:32:16 ip-10-4-12-247 gunicorn[10673]: [2017-11-04 20:32:16 +0000] [11071] [INFO] Booting worker with pid: 11071

2) récupère l'ID de processus (PID) du processus gunicorn principal

Le sed commande fonctionne comme suit: sed 's/<search this>/<replace with this>/g'

  • s signifie pour l' remplacer commande, et g signifie que la recherche de l'ensemble de l'input mondiale.
  • l'indicateur -n indique à sed pas à d'imprimer chaque ligne (ou en fait, de ne pas imprimer quoi que ce soit.)
  • Le p à la fin indique à sed de imprimer le contenu de la ligne.
  • nous recherchons .*Main PID: \(.*\)$, qui est un modèle d'expression régulière, qui a les parties suivantes: .* correspond à n'importe quel caractère (.) zéro fois ou plus (*). Ensuite, nous recherchons Main PID: suivi de tous les caractères, répétés zéro ou plusieurs fois (.*). Pour capturer tous les caractères après le Main PID: - text, nous enfermons le .* entre parenthèses, qui sont échappés avec barre oblique inverse: \(.*\). $ indique la fin de la ligne.
  • la partie" remplacer par cette " de la commande sed est juste \1, ce qui signifie le premier ensemble de caractères capturé.

Exemple de sortie:

ubuntu@ip-10-4-12-247:~$ systemctl status gunicorn |  sed -n 's/.*Main PID: \(.*\)$/\1/g p'
10673 (gunicorn)

3) débarrassez - vous des caractères supplémentaires

Acheminez la sortie vers cut . Le cut -f1 -d' ' signifie, que

  • la chaîne est délimitée par un espace: ici -d détermine le délimiteur, qui est le caractère juste après le -d. Puisque le délimiteur est espace, nous joignons cela entre guillemets.
  • -f signifie juste que la coupe est faite en utilisant le délimiteur (et non en octets), et -f1 signifie que nous voulons prendre le premier élément de la liste.

Exemple de sortie:

ubuntu@ip-10-4-12-247:~$ systemctl status gunicorn |  sed -n 's/.*Main PID: \(.*\)$/\1/g p' | cut -f1 -d' '
10673

4) Utilisez le PID principal

Piping à xargs signifie simplement exécuter la commande avec des arguments du tuyau sur le côté gauche. Puisque nous sifflons juste le PID principal à xargs,

 systemctl status gunicorn-django |  sed -n 's/.*Main PID: \(.*\)$/\1/g p' | cut -f1 -d' ' | xargs kill -HUP

Est fondamentalement la même chose chose comme

echo <Main PID > | xargs kill -HUP

Qui se traduit par

kill -HUP <Main PID >

Modifier

Une solution un peu plus robuste serait d'utiliser cut -f1 -d$'\n' ou grep -m1 "" devant cut -f1 -d' ', pour ne choisir que la première ligne du match. Je ne peux pas comprendre les circonstances, où il y aurait deux matches pour le Main PID:, cependant.

6
répondu np8 2017-11-05 08:27:50

Nous exécutons Gunicorn sous superviseur, mais c'est le moyen le plus simple et le plus propre que nous ayons trouvé pour recharger gracieusement Gunicorn quand il est confus:

sudo pkill -HUP -f gunicorn.*master
1
répondu johntellsall 2015-12-28 19:12:52