Django en Ubuntu Server 18.04 con Nginx, Gunicorn y Supervisor

Hoy camparto con vosotros este tutorial para los interesados en aprender a desplegar Django en GNU/Linux de una forma cómoda y sencilla.

Introducción

A continuación os resumo para qué sirve cada uno de los componentes que se utilizan en un despliegue genérico.

  • Gunicorn: Green Unicorn es un servidor WSGI HTTP para Python (pre-fork de unicorn de ruby). Consume poco y es bastante rápido.
  • Nginx: Es un servidor web/proxy inverso ligero de alto rendimiento y un proxy para protocolos de correo electrónico. Nos ayudará a servir ficheros estáticos.
  • Supervisor: Es un gestor de procesos para Linux. Nos permitirá crear un proceso en segundo plano de nuestro servidor gunicorn.

Requisitos previos

sudo apt install python3
  • Un proyecto Django ya listo para desplegar.
  • Borrar del fichero urls.py que Django sirva los ficheros estáticos al desactivar el DEBUG.
  • Tener configurada la variable con el directorio de los ficheros estáticos STATIC_ROOT en el settings.py:

proyecto/settings.py

# Añadir esta línea abajo del todo dependiendo de vuestro directorio
STATIC_ROOT = os.path.join(BASE_DIR, 'static') 

Directorio del proyecto

Suponiendo que vuestro proyecto lo tendréis en dominio.com os recomiendo clonar el repositorio en la ruta /var/www/dominio.com/.

Ahora aseguraos de tener un fichero requirements.txt con las dependencias del proyecto en /var/www/dominio.com/requirements.txt y si no tenéis dependencias como mínimo que contenga a Django:

requirements.txt

django

Entorno virtual con Pipenv

Ahora vamos a instalar Pipenv para crear nuestro entorno virtual, para ello necesitamos Pip en Python 3:

sudo apt install python3-pip
pip3 install pipenv

El siguiente paso es crear el entorno e instalar las dependencias, fácil:

cd /var/www/dominio.com
pipenv install -r requirements.txt

Con esto ya deberíamos tener nuestro entorno creado, vamos a dejar anotada la ruta del python del entorno porque más adelante la necesitaremos, podemos consultar haciendo:

pipenv run which python

Os debería aparecer algo de este estilo dependiendo de vuestro usuario, que debería ser el administrador (aunque yo estoy haciendo el tutorial directamente con root):

/root/.local/share/virtualenvs/dominio.com-EZwa4jqa/bin/python

Dejadlo copiado en alguna parte.

En este punto deberíamos tener el proyecto funcionando, quizá tendréis que configurar una base de datos pero en eso no me voy a meter, en este tutorial daremos por hecho que usamos SQLite para hacerlo más sencillo:

pipenv run python manage.py migrate

Recopilamos los ficheros estáticos de las diferentes apps en el directorio static del proyecto (hay que hacerlo siempre que modifiquemos alguno), recordad tener configurada la variable STATIC_ROOT tal como indico arriba en los requisitos. Esto es necesario para que Nginx pueda servirlos correctamente:

pipenv run python manage.py collectstatic

Cualquier comando que debáis ejecutar recordad hacerlo con pipenv run para hacer referencia al Python del entorno virtual.

Gunicorn

Tenemos el proyecto de Django preparado pero necesitamos un servidor para manejarlo, para ello vamos a utilizar gunicorn.

cd /var/www/dominio.com
pipenv install gunicorn

Vamos a probar si se lanza correctamente desde la raíz, justo donde está el manage.py (el puerto podéis cambiarlo):

cd /var/www/dominio.com
pipenv run gunicorn proyecto.wsgi:application --bind=127.0.0.1:8000

Si se muestra el mensaje típico de Listening at: http://127.0.0.1:8000 podemos hacer Control + C y confirmar que está funcionando bien.

Supervisor

El siguiente paso es lograr mantener activo ese servidor de gunicorn en segundo plano, para ello usaremos el gestor de procesos Supervisor.

sudo apt install supervisor

Ahora tenemos que crear un fichero de configuración para nuestro proyecto:

sudo nano /etc/supervisor/conf.d/dominio.com.conf

En él añadiremos esta simple configuración, recuperando ya la ruta al python de nuestro entorno virtual, la que copiamos anteriormente:

/etc/supervisor/conf.d/dominio.com.conf

[program:dominio.com]
command = /root/.local/share/virtualenvs/dominio.com-EZwa4jqa/bin/python /root/.local/share/virtualenvs/dominio.com-EZwa4jqa/bin/gunicorn proyecto.wsgi:application --bind=127.0.0.1:8000
directory = /var/www/dominio.com/proyecto
user = root

Es muy importante poner correctamente las rutas a python y a gunicorn del entorno virtual para crear correctamente el comando con los ejecutables de ambos programas en el entorno. El usuario tiene que ser el vuestro o root, pero debe tener los permisos adecuados. Además si queremos tener varios Django funcionando tendremos que ponerlos en puertos diferentes, por ejemplo 8000, 8001, 8002 o los que queramos.

Para manejar el proceso debemos actualizar los cambios y activar el proyecto en supervisor:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start dominio.com

Con esto deberíamos tener gunicorn ejecutando Django en el puerto 8000 del sistema, ya sólo nos queda configurar un server block de Nginx haciendo de proxy reverso para enlazarlo a un dominio/subdominio y servir los ficheros estáticos.

Todas las acciones para manejar el proceso que ejecuta nuestro proyecto se pueden encontrar en la documentación de Supervisor. Los más comunes son:

  • reread: Recarga las configuraciones de los procesos.
  • update: Rearga las configuraciones y reinicia los procesos afectados.
  • start: Para iniciar un proceso.
  • stop: Para detener un proceso.
  • restart: Para reiniciar un proceso, algo necesario para actualizar los cambios al modificar el proyecto de Django:
sudo supervisorctl restart dominio.com

En este punto deberías poder hacer una petición con cURL y ver que efectivamente os devuelve índice de vuestra página:

curl http://127.0.0.1:8000

Server block de Nginx

Os voy a dejar la configuración genérica del sitio funcionando en un dominio en el puerto 80 no seguro y sirviendo los ficheros estáticos. Es vuestra tarea adaptarla y añadir un certificado tal como explico en el curso de configuración básica de Ubuntu Server.

Primero vamos a crear un directorio para almacenar los logs de nginx:

cd /var/www/dominio.com
mkdir logs

Ahora creamos el server block del sitio:

sudo nano /etc/nginx/sites-available/dominio.com

Esta es la configuración básica:

/etc/nginx/sites-available/dominio.com

server {

    # Puerto y nombre
    listen 80;
    server_name dominio.com www.dominio.com;

    # Logs de nginx
    access_log /var/www/dominio.com/logs/nginx.access.log;
    error_log  /var/www/dominio.com/logs/nginx.error.log;

    # Ficheros estáticos
    location /static/ {
        alias /var/www/dominio.com/static/;
        expires 365d;
    }

    # Proxy reverso del puerto 8000
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 1m;
        proxy_connect_timeout 1m;
        proxy_pass http://127.0.0.1:8000;
    }

}

Guardamos y creamos un enlace simbólico:

cd /etc/nginx/sites-enabled
sudo ln -s ../sites-available/dominio.com

Finalmente reiniciamos nginx:

sudo service nginx restart

Y ya deberíamos tener funcionando Django en http://dominio.com cargando correctamente los ficheros estáticos.

Documentación

Para profundizar más sobre las opciones de configuración, como crear registros de errores y todo éso os dejo las documentaciones oficiales:

¡Espero que os sirva!

 

Django 2.2
17/08/2019