2008-12-12 9 views
24

J'utilise une instance de django derrière nginx connectée en utilisant fcgi (en utilisant la commande manage.py runfcgi). Comme le code est chargé en mémoire, je ne peux pas recharger le nouveau code sans tuer et redémarrer les processus django fcgi, interrompant ainsi le site web en direct. Le redémarrage lui-même est très rapide. Mais en supprimant les processus fcgi, les actions de certains utilisateurs seront interrompues, ce qui n'est pas bon. Je me demande comment recharger un nouveau code sans jamais provoquer d'interruption. Les conseils seront très appréciés!Comment redémarrer avec élégance django en exécutant fcgi derrière nginx?

Répondre

15

Je voudrais commencer un nouveau processus de fcgi sur un nouveau port, changer la configuration de nginx pour utiliser le nouveau port, avoir la configuration de rechargement de nginx (qui est gracieuse en soi), puis 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 pour lancer un nouveau processus, fermer toutes les sockets de l'enfant sauf le socket serveur fcgi, fermer la socket serveur fcgi dans le parent, exec un nouveau processus django dans l'enfant (en le faisant utilisez le socket serveur fcgi), et terminez le processus parent une fois que toutes les connexions fcgi sont fermées. IOW, implémentez le redémarrage gracieux pour runfcgi.

+1

Si vous placez un nouveau fcgi sur un nouveau port, nginx ne redirigera-t-il pas les utilisateurs déjà connectés vers le nouveau processus? ce serait le même que le redémarrage à froid du processus fcgi – Javier

+1

Il transmettrait en effet tous les utilisateurs au nouveau processus; ça ne fait pas de mal. Le problème avec le redémarrage à froid est que le processus en cours est détruit, donc les requêtes HTTP * en cours * échouent. C'est le cas de l'OP qui s'inquiète (IIUC) –

+0

C'est utile. Merci! – evo

15

Alors je suis 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 
+0

wow, simple mais incroyablement utile. Merci beaucoup d'avoir partagé cela! – srchulo

10

Je suis tombé sur cette page en cherchant une solution à ce problème. Tout le reste a échoué, donc j'ai regardé dans 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, gracieusement. Donc, tout ce que vous avez à faire est de:

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

  2. attendre un peu (flup permet aux processus enfants 10 secondes, alors attendez un couple plus, 15 ressemble à un bon nombre)

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

  4. démarrer le serveur à nouveau

C'est tout.

+0

Cela fonctionne très bien si vous configurez votre serveur django fcgi sous 'upstart'. 'initctl reload ' enverra le HUP, et la directive 'respawn' dans la définition de votre travail gérera le redémarrage. Pas de muss, pas de problème. –

2

Nous avons finalement trouvé la bonne solution à cela!

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

d'abord envoyer FLUP un signal HUP pour signaler un redémarrage.Flup alors le faire à 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 alors votre serveur va mourir avec KeyboardInterrupt, ce qui donne à l'utilisateur une erreur 500.

La solution consiste à installer un gestionnaire SIGINT - voir la page ci-dessus pour plus de détails. Même ignorer simplement SIGINT donne à votre processus 10 secondes pour sortir ce qui est suffisant pour la plupart des demandes.

+0

Comme indiqué dans les commentaires sur ce post, cela ne fonctionne que pour flup 1.0.3. En outre, je ne pouvais pas obtenir ce travail avec prefork, seulement enfilé. – raylu

+0

Existe-t-il un problème ou une raison particulière de ne pas utiliser flup 1.0.3? Je l'utilise avec le mode prefork et ça marche bien. – gingerlime

Questions connexes