Cómo correr Django (y Python) usando Apache y FastCGI
filed in Django, Python on Dec.16, 2006
Resumo en este post como hacer funcionar Django en un servidor Linux con CentOS 4.4 usando Apache 2 y FastCGI. Estas cosas están dispersas en Internet, la principal finalidad del post es tenerlas juntas en algún sitio.
Por qué FastCGI y no mod_python
Es la primera pregunta que uno se hace: si mod_python funciona, para qué quieres usar FastCGI. No es porque fastcgi vuelve a estar de moda, por supuesto.
El problema real es que mod_python y mod_php no se llevan bien, y de hecho en CentOS 4.4, usando mod_php, MySQL y mod_python, este último no funciona. Aquí y aquí (If you get a segmentation fault) se explican las posibles causas. Al parecer tiene que ver con el hecho de Apache usa la librería expat del sistema (y la precarga cuando se inicia el servidor de web), mientras que mod_python usa la versión de expat que fue compilada en la distribución de Python que tenga el sistema operativo. Si las dos versiones no coinciden, se produce un segmentation fault. También puede deberse a un cruce similar entre las librerías de MySQL, mod_php y mod_python, pero el resultado final es similar.
En los dos casos Apache sigue corriendo y mod_python se reinicia, pero nuestro programa en Python no se ejecuta. El segfault queda registrado en el log de errores de Apache (/var/log/httpd/error_log o algún lugar cercano, dependiendo de esté instalado Apache) y el navegador queda en blanco.
Y por eso la necesidad de usar fastcgi para correr Django.
Algunas consideraciones preliminares
- El sistema en el que he hecho la instalación es un servidor con CentOs 4.4 y Python 2.3.4. (Al parecer no hay modo fácil de actualizar el Python del CentOs a 2.5 o por lo menos a 2.4 sin que se rompan muchas cosas). Estamos usando Apache2.
- Se supone que este servidor ya tiene Django instalado, y que nuestra aplicación no tiene problemas con el servidor de desarrollo que trae Django. (Esto no es un tutorial de Django… la documentación de Django explica perfectamente como instalarlo).
- Si buscamos un poco en Google, muchas páginas insisten en que si queremos instalar fastcgi y python, necesitamos instalar una librería de python llamada flup; y, si la versión de python es 2.3 o menor, también necesitamos instalar una librería llamada eunuchs. Porque a Python 2.3 le falta algo… la capacidad de escribir socket pairs, un tema que no viene al caso ahora.
Bien, si lo que queremos es instalar Django, ninguna de estas dos librerías es necesaria. Django, a partir de la versión 0.95, trae su propia implementación de flup, y no necesita instalar eunuchs.
Aclarémonos dónde están las cosas
Estamos instalando django con la siguiente configuración, quizá no muy usual:
- El
DocumentRootde nuestro servidor Apache es/var/www/html. - El directorio con el proyecto de Django es
/home/django/prog. - El directorio con los archivos .css, imágenes, y demás que necesitan las páginas web es, para el proyecto de Django,
/home/django/progmedia. - El archivo
settings.pyestá en/home/django/prog/settings.py, que es lo normal. - Se accede a nuestro programa a través del URL
http://example.com/prog.
Instalar FastCGI
- FastCGI es un módulo de Apache que no viene con la distribución de Apache. Es necesario descargarlo del website de fastcgi: http://www.fastcgi.com.
- Las instrucciones sobre cómo compilar e instalar este módulo están muy claras en el archivo INSTALL.AP2 que viene con las fuentes de fastcgi. Pero (siempre hay un pero) lo que no dice el INSTALL.AP2 es que necesitamos además los paquetes de desarrollo de apache:
httpd-devely sus requisitos (apr-develyapr-util-devel). Podemos decir # rpm -qa httpd* para averiguar si los headers de desarrollo están o no instalados en nuestro servidor. Si es necesario, los instalamos con yum:# yum install httpd-devel. - Para fastcgi con CentOS es necesario usar la opción
top_dir, porque por defecto fastcgi (como debe ser) trata de instalarse en/usr/local/liben vez de en/usr/lib.
$ make top_dir=/usr/lib/httpd
- Si la compilación terminar sin errores, instalamos fastcgi con el comando
# make top_dir=/usr/lib/httpd install
- (credit where credit is due) fastcgi necesita un directorio para escribir datos de sus transacciones, pero no puede escribir a
/var/log/httpd/fastcgi, la opción por defecto, aunque este directorio tenga permisos 777 y sea propiedad de apache:apache. Según dicen los mitos tiene algo que ver con el manejo de symlinks dentro de fastcgi. En elerror_logde Apache aparece:[crit] (13)Permission denied: FastCGI: can’t create (dynamic) server “/var/www/html/prog.fcgi”: bind() failed [/var/log/httpd/fastcgi/dynamic/abcdef01020304]
La solución es crear un directorio en /tmp:
# mkdir -p /tmp/fcgi/dynamic # chown apache:apache -R /tmp/fcgi # chmod 755 -R /tmp/fcgi
- Ahora hay que decirle a Apache que queremos usar el módulo fastcgi que acambamos de instalar. Creamos el archivo
/etc/httpd/conf.d/fastcgi.conf:
LoadModule fastcgi_module modules/mod_fastcgi.so
<IfModule mod_fastcgi.c>
# Tenemos que decirle a fastcgi dónde puede guardar la información de sus conexiones, porque
# no estamos usando el directorio por defecto.
FastCgiIpcDir /tmp/fcgi/
# Asociamos los archivos de extensión .fcgi a fastcgi
AddHandler fastcgi-script .fcgi
</IfModule>
- Recargamos la configuración de Apache y vemos en el
/var/log/httpd/error_logsi todo ha ido bien.
# tail -f /var/log/httpd/error_log
(esta ventana se pude quedar abierta hasta que estemos seguros que el fastcgi funciona y bien)
En otra ventana:
# service httpd reload
RewriteEngine y ExecCGI
- Para poder usar el
RewriteEngineen el directorio raíz (más abajo se explica para qué lo necesitamos), añadimos la opciónFileInfoa la directiva AllowOverride del directorio raíz del servidor web. - También queremos ejecutar programas usando fastcgi en el directorio raíz del servidor de web. (repetimos: en el directorio raíz del servidor de web, el
DocumentRoot, no en el directorio en el que está nuestro proyecto Django). Para eso, modifico/etc/httpd/conf/httpd.conf
#
<Directory "/var/www/html">
#
# (... aquí he borrado los comentarios)
#
# Añadimos ExecCGI a las opciones
Options Indexes FollowSymLinks IncludesNoExec ExecCGI
# (... más comentarios)
# Añado el FileInfo para que me deje usar mod_rewrite en /home/www
AllowOverride AuthConfig FileInfo
Nuevamente recargo Apache y veo en el error_log si todo va bien.
Un puente entre Apache y Python
- Ahora necesitamos un puente (está bien, un proxy…) que permita a fastcgi comunicarse con nuestro programa en Python. Para suerte nuestra, alguien ya escribió ese puente:
fcgi.py. - Descargamos el módulo fcgi.py de internet. Ojo que si buscamos en Google hay dos resultados: uno es el módulo de Robin Dunn (el mismo de wxPython), y otro en el website de Saddi Enterprises. Este último es el que nos interesa:
http://svn.saddi.com/py-lib/trunk/fcgi.py. - Copiamos
fcgi.pya la raíz del servidor web, cambiarmos propietario a apache y nos aseguramos de que apache pueda leer el archivo:
# cp fcgi.py /var/www/html # chown apache:apache /var/www/html/fcgi.py # chmod 755 /var/www/html/fcgi.py
- Una vez que tenemos la herramientas, ahora hay que construir el puente. Creamos el siguiente script (sacado del website de Django), que es el que, usando las funciones de
fcgi.py, hace realmente de proxy entre fastcgi y nuestro proyecto Django. Lo guardo en el directorio raíz del servidor de web con el nombreprog.fcgi. (Volvemos a repetir: lo guardamos en la raíz del servidor de web, no en la raíz del directorio de nuestro proyecto).
#!/usr/bin/python
import sys, os
# Add a custom system path
sys.path.insert(0,"/home/django")
# switch to user project
os.chdir("/home/django/prog")
os.environ['DJANGO_SETTINGS_MODULE'] = "prog.settings"
from fcgi import WSGIServer
from django.core.handlers.wsgi import WSGIHandler
WSGIServer(WSGIHandler()).run()
- Idem, le cambio el propietario y permisos para que pueda ser ejecutado por Apache.
# chown apache:apache /var/www/html/prog.fcgi # chmod 755 /var/www/html/prog.fcgi
Rewrite rules
Como dice el manual de Apache, ”Despite the tons of examples and docs, mod_rewrite is voodoo. Damned cool voodoo, but still voodoo.” — Brian Moore, bem@news.cmc.net
A continuación vamos a escribir unas reglas (”Rewrite rules”) de modo que cuando el usuario escriba el URL de nuestro programa en el navegador, Apache realmente redireccione la llamada al script prog.fcgique acabamos de escribir. Este script, a su vez, llamará al programa escrito con Django en /home/django/prog.
Explico esto en detalle porque aunque al final el resultado sea solamente dos o tres líneas en un archivo de configuración, si no entendemos qué estamos haciendo, después tendremos problemas… causados por nosotros mismos.
-
Dentro del programa en Django, los distintos añadidos al URL a la derecha de sccr/ sirven para direccionar al usuario, a través del módulo urls.py, a las distintas partes de nuestro programa. Por ejemplo,
http://example.com/prog/admin http://example.com/prog/correo -
Lo que necesitamos es que Apache pase todos esos añadidos (
progincluido) al móduloprog.fcgi, que se encargará a su vez de reenviarlo a nuestro programa de modo transparente: el programa nunca sabrá si fue llamado a través de mod_python, con el servidor de desarrollo del mismo Django, etc. (de acuerdo, probablemente sí hay forma de que lo sepa… pero a los efectos que nos interesan, no lo sabe).http://tierramedia.oficinas/prog.fcgi/prog/admin/ http://tierramedia.oficinas/prog.fcgi/prog/correo/ -
Los rewrite rules se pueden poner en muchos sitios distintos. Por ejemplo, en el archivo
.htaccessen la raíz del servidor web:
RewriteEngine On RewriteRule ^(prog\.fcgi/.*)$ - [L] RewriteRule ^(prog/.*)$ prog.fcgi/$1 [L]
Con estas reglas, estamos diciendo dos cosas:
* la primera: si el URL contiene la cadena prog.fcgi/, simplemente se pasa tal cual. La [L] quiere decir que después de esta regla no seguimos aplicando más reglas.
* la segunda: si el URL contiene la cadena prog/, reescribe la cadena y todo lo que le siga insertando prog.fcgi/ por delante. Nuevamente la [L] quiere decir que después de esta regla no seguimos aplicando más reglas.
Como anteriormente hemos indicado a Apache en el archivo fastcgi.conf que toda extensión .fcgi en el directorio raíz debe procesarse con el módulo fastcgi, una vez reescrito el URL Apache ejecutará el script sccr.fcgi.
- Otra alternativa es crear un archivo
/etc/httpd/conf.d/prog.confcon los rewrite rules: es más eficiente que ponerlas en.htaccess(pero por otro lado no siempre vamos a poder modificar directamente la configuración del Apache). En ese caso, tenemos que especificar cláramente para qué directorio son las reglas:
<Directory /var/www/html> RewriteEngine On RewriteRule ^(prog\.fcgi/.*)$ - [L] RewriteRule ^(prog/.*)$ prog.fcgi/$1 [L] </Directory>
- Por supuesto, cada vez que cambimos la configuración necesitamos decirle a Apache que recargue los archivos de configuración:
# service httpd reload.
Otros directorios de Django
- Por último, si tenemos un directorio dentro del proyecto para el media, necesitamos decirle a Apache que asocie el URL con el directorio>. Por ejemplo,
http://example.com/progmediacon el directorio/home/django/progmedia. Para eso creamos o añadimos al archivo/etc/httpd/conf.d/prog.conf:
Alias /progmedia /home/django/progmedia
Y listo. Ahora deberíamos tener nuestro servidor Apache funcionando con Python, Django y FastCGI.




Leave a Reply