Introducción al despliegue de aplicaciones Django con Apache2 y el módulo WSGI

Contexto: del entorno de desarrollo al entorno de producción

Hasta ahora hemos trabajado con Django usando el servidor de desarrollo que se ejecuta con el comando:

python3 manage.py runserver

Este servidor es muy útil para pruebas locales, pero no está diseñado para entornos de producción, ya que no ofrece:

  • Rendimiento suficiente para múltiples clientes.
  • Seguridad y aislamiento adecuados.
  • Gestión eficiente de procesos.

En producción, normalmente usamos un servidor web real como Apache2 o Nginx, que se encarga de:

  • Atender las peticiones HTTP de los clientes.
  • Servir directamente el contenido estático (imágenes, CSS, JavaScript).
  • Delegar la ejecución de la aplicación Django a un módulo intermedio, en este caso mod_wsgi, que permite ejecutar aplicaciones Python dentro de Apache.

El protocolo WSGI

WSGI (Web Server Gateway Interface) es una especificación que define cómo un servidor web (como Apache o Nginx) comunica peticiones HTTP a una aplicación Python.

En pocas palabras:

  • Apache recibe una petición HTTP.
  • A través del módulo mod_wsgi, la envía a la aplicación Django.
  • Django procesa la petición (por ejemplo, consultando la base de datos y renderizando una plantilla HTML).
  • El resultado se devuelve a Apache, que responde al cliente.

Este mecanismo permite integrar aplicaciones Python con servidores web de propósito general de forma estándar y eficiente.

El archivo wsgi.py en Django

Cada proyecto Django incluye un archivo llamado wsgi.py, normalmente ubicado en el directorio del proyecto (por ejemplo, guestbook_project/guestbook_project/wsgi.py).

Este archivo define un objeto llamado application, que es el punto de entrada que el servidor Apache (a través de mod_wsgi) utilizará para comunicarse con Django.

Ejemplo simplificado del contenido del archivo:

import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'guestbook_project.settings')
application = get_wsgi_application()

En resumen: wsgi.py es el enlace entre Apache y Django, y su correcta configuración es imprescindible para que la aplicación funcione en producción.

Módulos necesarios en Apache2

Para que Apache2 pueda ejecutar aplicaciones Python, debe tener instalado el módulo mod_wsgi.

En sistemas basados en Debian/Ubuntu, se instala con:

sudo apt install libapache2-mod-wsgi-py3

Una vez instalado, el módulo se activa automáticamente. Si no es así, puede activarse manualmente con:

sudo a2enmod wsgi
sudo systemctl reload apache2

Configuración del VirtualHost

A continuación se muestra una configuración típica para desplegar un proyecto Django llamado guestbook_project, ubicado en /home/debian/guestbook_project, con un entorno virtual en /home/debian/venv/django/.

Para que el servidor web apache2 sea capaz de acceder a los ficheros de nuestra aplicación, tenemos que configurar los permisos:

chmod o+x /home/debian/guestbook_project

Archivo: /etc/apache2/sites-available/guestbook.conf

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName guestbook.local
    DocumentRoot /home/debian/guestbook_project

    # === Configuración del proceso WSGI ===
    WSGIDaemonProcess django_guestbook python-path=/home/debian/guestbook_project:/home/debian/venv/django/lib/python3.X/site-packages
    WSGIProcessGroup django_guestbook
    WSGIScriptAlias / /home/debian/guestbook_project/guestbook_project/wsgi.py process-group=django_guestbook

    # === Archivos estáticos (CSS, imágenes, JS) ===
    Alias /static/ /home/debian/guestbook_project/static/
    <Directory /home/debian/guestbook_project/static>
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/guestbook_error.log
    CustomLog ${APACHE_LOG_DIR}/guestbook_access.log combined
</VirtualHost>

Nota: Tienes que mirar la versión de python3 para sustituir el nombre del directorio pythonX.X.

Vamos a explicar la configuración:

  • El DocumentRootse indica el directorio donde está la aplicación. Realmente el servidor web siempre va a llamar al fichero WSGI wsgi.py, pero el DocumentRoot es necesario para servir el contenido estático.
  • La directiva WSGIDaemonProcess: Se define un grupo de procesos que se van a encargar de ejecutar la aplicación (servidor de aplicaciones). A estos procesos se le ponen un nombre (django_guestbook) y se indica los directorios donde se encuentran la aplicación y los paquetes necesarios (python-path), como puedes observar se pone el directorio donde esta la aplicación y el directorio donde se encuentran los paquetes en el entorno virtual, separados por dos puntos.
  • WSGIProcessGroup: Nos permite agrupar procesos. Se pone el mismo nombre que hemos definido en la directiva anterior.
  • La directiva WSGIScriptAlias nos permite indicar que programa se va a ejecutar (el fichero WSGI: /home/debian/guestbook/wsgi.py) cuando se haga una petición a la url / y que proceso lo va a ejecutar.
  • Alias /static/: Nos permite indicar la ruta de contenidos estáticos. Lo explicaremos a continuación.

El problema del contenido estático

En Django, muchos recursos estáticos (CSS, imágenes, JavaScript) no están ubicados en la carpeta del proyecto, sino distribuidos dentro de las aplicaciones del framework. Por ejemplo:

  • Los estilos del panel de administración (/admin/) se encuentran en los paquetes internos de Django.
  • Nuestros propios archivos estáticos pueden estar en static/.

En un entorno de desarrollo, Django los sirve automáticamente. Pero en producción, Apache no los conoce si no los copiamos a un único lugar accesible.

Solución: collectstatic

Django proporciona el comando collectstatic, que recopila todos los archivos estáticos (de las apps y del proyecto) en una única carpeta, normalmente /static/.

source /home/debian/venv/django/bin/activate
cd /home/debian/guestbook_project
python3 manage.py collectstatic

Este comando crea o actualiza el directorio /home/debian/guestbook_project/static/ con todos los archivos necesarios.

El bloque Alias /static/ del VirtualHost permite que Apache los sirva directamente a los clientes, sin pasar por Django.

Nota: Si esta operación da un error, tendremos que definir de manera adecuada la variable STATIC_ROOT en el fichero setting.py indicando el directorio donde se guardan los contenidos estáticos.

:::caution[Ejercicio]

  1. Realiza la implementación de la aplicación guestbook_django con apache2 y el mod_wsgi. A la aplicación se acceder con el nombre guestbook.example.org.
  2. Realiza la implementación de la aplicación tutorial_django con apache2 y el mod_wsgi. A la aplicación se acceder con el nombre poll.example.org. :::