Cómo correr Django (y Python) usando Apache y FastCGI

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

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 DocumentRoot de 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.py está en /home/django/prog/settings.py, que es lo normal.
  • Se accede a nuestro programa a través del URL https://example.com/prog.

Instalar FastCGI

  • 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/lib en vez de en /usr/lib.
$ make top_dir=/usr/lib/httpd[/bash]
  • Si la compilación terminar sin errores, instalamos fastcgi con el comando
# make top_dir=/usr/lib/httpd install

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_log si 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 RewriteEngine en el directorio raíz (más abajo se explica para qué lo necesitamos), añadimos la opción FileInfo a la directiva AllowOverride del directorio raíz del servidor web.

#
<Directory "/var/www/html">
#
# (... aquí he borrado los comentarios)
#
#  Añadimos ExecCGI a las opciones
     Options Indexes FollowSymLinks IncludesNoExec <strong>ExecCGI</strong>
# (... más comentarios)
# Añado el FileInfo para que me deje usar mod_rewrite en /home/www
     AllowOverride AuthConfig <strong>FileInfo</strong>

Nuevamente recargo Apache y veo en el error_log si todo va bien.

Un puente entre Apache y Python

  • Copiamos fcgi.py a 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
#!/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,

        https://example.com/prog/admin
        https://example.com/prog/correo
        https://tierramedia.oficinas/prog.fcgi/prog/admin/
        https://tierramedia.oficinas/prog.fcgi/prog/correo/

Los rewrite rules se pueden poner en muchos sitios distintos. Por ejemplo, en el archivo .htaccess en 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: 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.

<Directory /var/www/html>
   RewriteEngine On
   RewriteRule ^(prog\.fcgi/.*)$ - [L]
   RewriteRule ^(prog/.*)$ prog.fcgi/$1 [L]
</Directory>

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, https://example.com/progmedia con 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.

Join my free newsletter and receive updates directly to your inbox.