Configurar NGIX con gunicorn en Django

En este artículo vamos a aprender a configurar nginx con gunicorn en Django sobre un servidor Ubuntu. Se presupone que el lector ya conoce Django y dispone de una aplicación configurada y arrancada correctamente.

Instalar Gunicorn y verificar su funcionamiento

Necesitarás instalar con pip el paquete gunicorn en tu proyecto para poder usarlo. Si estás utilizando un entorno virtual recuerda activarlo antes de lanzar el comando:

$ pip install gunicorn

Vamos a avanzar paso a paso para garantizar que cada parte del proceso funciona correctamente. En primer lugar, nos ubicaremos en el directorio raíz del proyecto y arrancaremos la aplicación con gunicorn:

$ gunicorn --bind 0.0.0.0:9000 <proyecto>.wsgi

Reemplaza <proyecto> por el nombre de tu proyecto, y si todo ha ido bien verás que la aplicación ha quedado arrancada. Ahora es posible hacer peticiones al puerto 9000 y ver su respuesta. Abre un terminal nuevo y lanza el comando:

$ curl -L localhost:9000

Te mostrará por consola la respuesta de tu aplicación.

Crear socket y un fichero de servicio para gunicorn

En este punto ya tenemos garantizado que la aplicación funciona y es capaz de arrancar con gunicorn. El siguiente paso es crear un socket que se encargará de escuchar si hay peticiones nuevas. Cuando se haga una petición, el sistema operativo de Ubuntu arrancará el proceso de gunicorn para gestionar la conexión.

Para crear un socket nuevo el comando es el siguiente:

$ sudo nano /etc/systemd/system/gunicorn_miproyecto.socket

Es posible que en tu servidor quieras configurar más de una aplicación, por lo que es importante personalizar el nombre del fichero de acuerdo al nombre de cada proyecto para una mejor diferenciación. El contenido del fichero será el siguiente:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn_miproyecto.sock

[Install]
WantedBy=sockets.targetLenguaje del código: JavaScript (javascript)

Guardamos los cambios y creamos un nuevo fichero para generar el servicio:

$ sudo nano /etc/systemd/system/gunicorn_miproyecto.service

El contenido del fichero será el siguiente:

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
PIDFile=/run/gunicorn.pid
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/miproyecto
ExecStart=/home/ubuntu/miproyecto/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn_miproyecto.sock \
          miproyecto.wsgi:application

[Install]
WantedBy=multi-user.targetLenguaje del código: JavaScript (javascript)

Hay varias consideraciones a tener en cuenta sobre la configuración de este fichero. «User» será cualquier usuario (preferible evitar root) de Ubuntu. El grupo debe ser «www-data» para que Django tenga permisos de acceso. «WorkingDirectory» es la ruta absoluta hasta nuestro proyecto de django. Se entiende que dentro de «miproyecto» ya se encuentra la raíz del mismo. Si quieres verificar que estás en la raíz del proyecto podrás ver que dentro se encuentra el archivo «manage.py». En el parámetro «bind» indicaremos la ruta al fichero «.sock» creado anteriormente, y por último, indicaremos el fichero wsgi para arrancar nuestra aplicación.

Por último, iniciaremos y habilitaremos el socket que acabamos de crear:

$ sudo systemctl daemon-reload

$ sudo systemctl enable gunicorn_miproyecto.socket

$ sudo systemctl enable gunicorn_miproyecto.service

$ sudo systemctl start gunicorn_miproyecto.socket

Para saber si todo ha ido bien, podemos comprobar el estado del socket con el comando:

$ sudo systemctl status gunicorn_miproyecto.socket

Output
● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn_miproyecto.service; disabled; vendor preset: enabled)
   Active: inactive (dead)Lenguaje del código: JavaScript (javascript)

La salida nos saldrá como que está inactivo, esto es normal ya que no ha recibido el socket todavía ninguna conexión. Para forzar una, podemos hacer lo siguiente:

$ curl --unix-socket /run/gunicorn_miproyecto.sock localhost

Verás que devuelve por consola la salida HTML de tu aplicación, al igual que pasó anteriormente cuando validamos gunicorn. Ahora si repetimos el comando para mostrar el estado del socket, ya se mostrará activo:

$ sudo systemctl status gunicorn_miproyecto.socket

Llegados a este punto, ya tenemos instalado y configurado Gunicorn con éxito, ahora para terminar de completar el proceso solo nos quedará configurar Nginx para que al entrar una petición a nuestra aplicación sepa redirigir la petición al socket.

Configurar Nginx para enlazarlo con nuestro proceso Gunicorn

Necesitaremos instalar Nginx en nuestro servidor antes de continuar, si todavía no lo tienes instalado puedes seguir los pasos de este artículo.

Con Nginx ya instalado modificaremos su fichero de configuración:

$ sudo nano /etc/nginx/sites-enabled/default

Para este tutorial, asociaremos el dominio www.miproyecto.es a nuestra aplicación Django. Por tanto, crearemos un nuevo bloque «server» dentro del fichero:

server {
    listen 80;
    server_name www.miproyecto.es;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/ubuntu/miproyecto;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn_miproyecto.sock;
    }
}Lenguaje del código: PHP (php)

Mediante esta configuración estamos indicando a Nginx que escuche peticiones por el puerto 80, y en caso de realizarse al dns www.miproyecto.es hará una redirección (proxy_pass) a nuestro socket que ya tenemos creado. También podemos configurar la ruta de los ficheros estáticos en caso de necesitarlo indicando su ubicación en «root».

Antes de reiniciar el proceso de Nginx para que tenga en cuenta los cambios, podemos validar que la sintaxis del fichero sigue siendo correcta. Para ello puedes lanzar el comando:

$ sudo nginx -t

Si no hay errores, ya podemos reiniciar el proceso:

$ sudo systemctl restart nginx

Y ¡listo! ya hemos conseguido configurar NGINX con gunicorn en Django. Si abrimos un navegador e introducimos http://www.miproyecto.es, se mostará la página principal de vuestra aplicación Django. En caso de error, revisa detenidamente los pasos que hemos ido haciendo. Y si utilizas algún firewall, tendrás que tenerlo en cuenta para permitir conexiones al puerto 80.

Este artículo está basado en el artículo de Digital Ocean que puedes encontrar aquí

Script de automatización

#/bin/bash

proyecto=<nombre>
full_path="<ruta completa a carpeta del manage.py>"
target=<carpeta que contiene wsgi.py>

echo "Generando entorno"
mkdir /root/nginx-logs/${proyecto}
mkdir /root/virtualenvs/${proyecto}
virtualenv -p python3 /root/virtualenvs/${proyecto}/venv
source /root/virtualenvs/${proyecto}/venv/bin/activate
pip install -r ${full_path}/requirements.txt

echo "Creando fichero de servicio"

echo "
[Unit]
Description=gunicorn daemon
Requires=gunicorn_${proyecto}.socket
After=network.target

[Service]
User=root
Group=psaserv
WorkingDirectory=${full_path}
ExecStart=/root/virtualenvs/${proyecto}/venv/bin/gunicorn --access-logfile /root/nginx-logs/${proyecto}/logfile.log --workers 3 --bind unix:/run/gunicorn_${proyecto}.sock ${target}.wsgi

[Install]
WantedBy=multi-user.target
" > /etc/systemd/system/gunicorn_${proyecto}.service


echo "Creando fichero socket"
echo "
[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn_${proyecto}.sock

[Install]
WantedBy=sockets.target
" > /etc/systemd/system/gunicorn_${proyecto}.socket

echo "Creando servicio"
systemctl start gunicorn_${proyecto}.socket
systemctl enable gunicorn_${proyecto}.socket

curl --unix-socket /run/gunicorn_{$proyecto}.sock localhostLenguaje del código: PHP (php)