Comment redémarrer gracieusement django en exécutant fcgi derrière nginx?

J’exécute une instance de django derrière nginx connectée en utilisant fcgi (en utilisant la commande manage.py runfcgi). Puisque le code est chargé en mémoire, je ne peux pas recharger le nouveau code sans tuer ni redémarrer les processus django fcgi, interrompant ainsi le site Web en direct. Le redémarrage est très rapide. Mais en tuant les processus fcgi, les actions de certains utilisateurs seront interrompues, ce qui n’est pas bon. Je me demande comment puis-je recharger le nouveau code sans jamais causer d’interruption. Des conseils seront très appréciés!

Je commencerais un nouveau processus fcgi sur un nouveau port, changerais la configuration nginx pour utiliser le nouveau port, disposerais de la configuration nginx reload (qui est en elle-même gracieuse), puis éventuellement arrêter l’ancien processus (vous pouvez utiliser netstat pour savoir quand la dernière connexion à l’ancien port est fermée).

Alternativement, vous pouvez changer l’implémentation de fcgi en un nouveau processus, fermer tous les sockets dans le fils sauf le socket du serveur fcgi, fermer le socket du serveur fcgi dans parent, exécuter un nouveau processus django dans l’enfant (en utilisant le serveur fcgi) socket), et terminer le processus parent une fois que toutes les connexions fcgi sont fermées. IOW, implémente un redémarrage harmonieux pour runfcgi.

Je suis donc allé de l’avant et mis en œuvre la suggestion de Martin. Voici le script bash que j’ai trouvé.

pid_file=/path/to/pidfile port_file=/path/to/port_file old_pid=`cat $pid_file` if [[ -f $port_file ]]; then last_port=`cat $port_file` port_to_use=$(($last_port + 1)) else port_to_use=8000 fi # Reset so me don't go up forever if [[ $port_to_use -gt 8999 ]]; then port_to_use=8000 fi sed -i "s/$old_port/$port_to_use/g" /path/to/nginx.conf python manage.py runfcgi host=127.0.0.1 port=$port_to_use maxchildren=5 maxspare=5 minspare=2 method=prefork pidfile=$pid_file echo $port_to_use > $port_file kill -HUP `cat /var/run/nginx.pid` echo "Sleeping for 5 seconds" sleep 5s echo "Killing old processes on $last_port, pid $old_pid" kill $old_pid 

Je suis tombé sur cette page en cherchant une solution à ce problème. Tout le rest a échoué, alors j’ai regardé le code source 🙂

La solution semble être beaucoup plus simple. Le serveur Django fcgi utilise flup, qui gère correctement le signal HUP: il s’arrête normalement. Donc tout ce que vous avez à faire est de:

  1. envoyer le signal HUP au serveur fcgi (l’argument pidfile = de runserver sera utile)

  2. attendez un peu (flup permet aux enfants de traiter 10 secondes, attendez encore quelques instants; 15 ressemble à un bon nombre)

  3. envoyé le signal KILL au serveur fcgi, juste au cas où quelque chose le bloquerait

  4. redémarrer le serveur

C’est tout.

Vous pouvez utiliser le frai au lieu de FastCGI

http://www.eflorenzano.com/blog/post/spawning-django/

Nous avons finalement trouvé la solution appropriée à cela!

http://rambleon.usebox.net/post/3279121000/how-to-gracefully-restart-django-running-fastcgi

Commencez par envoyer un signal HUP par flup pour signaler un redémarrage. Flup le fera alors à tous ses enfants:

  1. ferme la prise qui arrêtera les enfants inactifs
  2. envoie un signal INT
  3. attend 10 secondes
  4. envoie un signal KILL

Quand tous les enfants seront partis, il en commencera de nouveaux.

Cela fonctionne presque tout le temps, sauf que si un enfant gère une requête lorsque flup exécute l’étape 2 votre serveur mourra avec KeyboardInterrupt , donnant à l’utilisateur une erreur 500.

La solution consiste à installer un gestionnaire SIGINT – voir la page ci-dessus pour plus de détails. Même en ignorant simplement SIGINT, votre processus prend 10 secondes pour sortir, ce qui est suffisant pour la plupart des requêtes.