2008-12-12 19 views
24

Estoy ejecutando una instancia de django detrás de nginx conectado usando fcgi (usando el comando manage.py runfcgi). Dado que el código se carga en la memoria, no puedo volver a cargar el nuevo código sin matar y reiniciar los procesos django fcgi, interrumpiendo así el sitio web en vivo. El reinicio en sí mismo es muy rápido. Pero al matar los procesos de fcgi, primero se interrumpirán las acciones de algunos usuarios, lo que no es bueno. Me pregunto cómo puedo volver a cargar el nuevo código sin causar ninguna interrupción. ¡Los consejos serán muy apreciados!Cómo reiniciar graciosamente django ejecutando fcgi detrás de nginx?

Respuesta

15

Comenzaría un nuevo proceso fcgi en un nuevo puerto, cambiaría la configuración nginx para usar el nuevo puerto, tendría la configuración de recarga nginx (que en sí misma es elegante), y luego detendría el proceso anterior (puede usar netstat para averiguar cuándo está cerrada la última conexión al puerto anterior).

Alternativamente, puede cambiar la implementación de fcgi para bifurcar un nuevo proceso, cerrar todos los sockets en el hijo excepto el socket del servidor fcgi, cerrar el socket del servidor fcgi en el padre, ejecutar un nuevo proceso django en el hijo (hacerlo use el socket del servidor fcgi) y finalice el proceso principal una vez que se cierren todas las conexiones de fcgi. IOW, implementa un elegante reinicio para runfcgi.

+1

si pone un nuevo fcgi en un nuevo puerto, ¿no reenviaría nginx los usuarios ya registrados al nuevo proceso? sería lo mismo que reiniciar en frío el proceso fcgi – Javier

+1

De hecho, reenviará a todos los usuarios al nuevo proceso; eso no hace daño El problema con el reinicio en frío es que el proceso en ejecución se cancela, por lo que las solicitudes HTTP * en progreso * fallan. Este es el caso en el que OP se preocupa (IIUC) –

+0

Eso es útil. ¡Gracias! – evo

15

Así que siguió adelante e implementado la sugerencia de Martin. Aquí está el script bash que se me ocurrió.

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 pero increíblemente útil. ¡Muchas gracias por compartir esto! – srchulo

10

Encontré esta página mientras buscaba una solución para este problema. Todo lo demás falló, así que busqué en el código fuente :)

La solución parece ser mucho más simple. El servidor Django fcgi usa flup, que maneja la señal HUP de la forma correcta: se apaga con gracia. Así que todo lo que tiene que hacer es:

  1. enviar la señal HUP al servidor fcgi (el archivo pid = argumento de ejecución del servidor será muy útil)

  2. esperar un poco (flup permite a los niños procesos 10 segundo, por lo que esperar un par más; 15 parece un buen número)

  3. envió la señal KILL al servidor fcgi, si acaso algo, estuvo muy

  4. empezar de nuevo el servidor

Eso es todo.

+0

Esto funciona muy bien si configura su servidor django fcgi bajo 'upstart'. 'initctl reload ' enviará el HUP, y la directiva 'respawn' en la definición de su trabajo se encargará del reinicio. Sin despeinarse sin problemas. –

2

¡Finalmente encontramos la solución adecuada para esto!

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

primero envía una señal HUP flup para señalar un reinicio.Flup entonces hacer esto a todos sus hijos:

  1. cierra el socket que evitar que los niños inactivos
  2. envía una señal INT
  3. espera 10 segundos
  4. envía una señal KILL

Cuando todos los niños se hayan ido, comenzará nuevos.

Esto funciona casi todo el tiempo, excepto que si un niño está manejando una solicitud cuando se ejecuta flup paso 2 entonces su servidor morirá con KeyboardInterrupt, dando al usuario un error 500.

La solución es instalar un controlador SIGINT; consulte la página anterior para obtener más información. Incluso ignorar SIGINT le da a su proceso 10 segundos para salir, lo cual es suficiente para la mayoría de las solicitudes.

+0

Como se señaló en los comentarios en esa publicación, esto solo funciona para flup 1.0.3. Además, no pude hacer que esto funcione con prefork, solo enhebrado. – raylu

+0

¿Existe algún problema o razón particular para no usar flup 1.0.3? Lo estoy usando con el modo prefork y funciona bien. – gingerlime

Cuestiones relacionadas