Desplegar aplicaciones Django con Nginx y Gunicorn
Si estás desarrollando una aplicación web, algún día tendrás que dar el paso y hacerla pública. Inicialmente puedes usar un servicio como Heroku pero si tienes éxito, no te quedará mas remedio que gestionar tus propios servidores o VPS.
Por fortuna desplegar una aplicación django usando nginx y gunicorn, es más sencillo de lo que podría parecer. En esta pequeña guia trato de describir el proceso paso a paso:
1 - Crear un usuario para la aplicación django
El primer paso es crear un usuario con el que ejecutar la aplicación Django, esto nos permite organizar con facilidad un servidor donde estemos ejecutando varias aplicaciones, proporciona separación de privilegios, y limita el posible daño que pueda hacerse al sistema si la aplicación es comprometida.
Creamos un grupo al que pertenecerán todos los usuarios de las aplicaciones django, y un usuario al que le asignamos el nombre de la aplicación.
$ sudo addgroup --system webapps
$ sudo adduser --system --ingroup webapps --home /webapps/appname appname
Al usar en modificador --system, al usuario se le asigna /bin/false como shell, y no tiene clave, por lo que no puede hacer login. Aunque esto es una buena medida de seguridad, es muy incomodo mientras se está configurando el sistema, así que asignamos un shell temporalmente:
$ sudo chsh -s /bin/bash appname
Esto nos permite usar sudo su appname para seguir con la instalación como el nuevo usuario.
2 - Instalar y configurar virtualenv
Virtualenv es la herramienta que nos permite aislar los paquetes requeridos por las aplicaciones, de manera que si dos aplicaciones necesitan paquetes que están en conflicto, no interfieran la una con la otra como ocurriría si instalásemos todos los paquetes directamente en el sistema. Instalamos virtualenv con:
$ sudo apt-get install python-virtualenv python-pip
Una vez instalado, hacemos login con el usuario que hemos creado, y dentro de su directorio usamos virtualenv para generar un nuevo entorno virtual:
$ sudo su appname
$ cd
$ mkdir virtualenvs
$ virtualenv --no-site-packages virtualenvs/app_env
$ source virtualenvs/app_env/bin/activate
Dentro del entorno, hay que instalar todos los paquetes que sean necesarios para la aplicación, puedes hacerlo uno a uno, o en bloque si tienes el archivo requirements.txt:
(app_env)$ pip install django
(app_env)$ pip install django-countries
(app_env)$ pip install django-mptt
(app_env)$ ....
(app_env)$ pip install -r requirements.txt
Si quieres profundizar en el funcionamiento de virtualenv sigue este tutorial
3 - Gunicorn + Supervisor
Gunicorn es el servidor WSGI que se encarga de servir la aplicación, pero necesita un programa que lo inicie al arranque, y lo monitorice para reiniciarlo si hay algún problema. La mejor solución es usar el gestor de procesos como supervisor. La instalación es sencilla:
$ sudo apt-get install supervisor
Para instalar gunicorn el método más sencillo es hacerlo dentro del entorno virtual de tu aplicación django.
$ source virtualenvs/app_env/bin/activate
(app_env)$ pip install gunicorn
Una vez todo está instalado, creamos un archivo de configuración para gunicorn gunicorn_conf.py en el directorio HOME del usuario, en el que indicamos la dirección y puerto en los que estará escuchando gunicorn:
# gunicorn_conf.py
workers = 3
bind = '127.0.0.1:9000'
A partir de aquí ya podemos salir del entorno virtual de la aplicación:
(app_env)$ deactivate
El siguiente paso es crear el archivo de configuración de supervisor en /etc/supervisor/conf.d/appname.conf:
[program:appname]
command=/webapps/appname/django_app/run_gunicorn.sh
directory = /webapps/appname/django_app/
user=appname
autostart=true
autorestart=true
priority=991
stopsignal=KILL
Tras esto creamos en el directorio de la aplicación el script run_gunicorn.sh que etablece el entorno virtualenv para la aplicación y despuer ejecuta gunicorn:
#!/bin/bash
source /webapp/appname/virtualenvs/app_env/bin/activate
exec /webapp/appname/virtualenvs/app_env/bin/gunicorn -c /webapps/appname/gunicorn_conf.py django_app.wsgi:application
Una vez configurado indicamos a supervisor que debe iniciar el nuevo servicio:
$ sudo supervisorctl start reread
appname: available
$ sudo supervisorctl update
appname: added process group
$ sudo supervisorctl status appname
appname RUNNING pid 21710, uptime 0:00:07
Para detener e iniciar la aplicación podemos usar:
$ sudo supervisorctl stop appname
appname: stopped
$ sudo supervisorctl start appname
appname: started
Llegado a este punto si todo funciona correctamente, ya podemos desactivar el shell del usuario creado para la aplicación.
$ sudo -s /bin/false appname
Si prefieres que sea posible hacer login con el usuario, es recomendable que le asignes una clave.
4 - Nginx
Usamos Nginx para hacer de pasarela entre los clientes y gunicorn, y para servir los archivos estáticos de la aplicación. Para instalarlo:
$ sudo apt-get install nginx
Para configurar nginx, hay que crear un archivo /etc/ngix/sites-available/, en el especificaremos donde se debe conectar a gunicorn en la sección upstream, y la ruta a los archivos estáticos de tu aplicación en la sección server:
upstream django_app_server { # Dirección en la que está escuchando gunicorn server 127.0.0.1:9000 fail_timeout=0; } server { # listen 80 default deferred; # for Linux # listen 80 default accept_filter=httpready; # for FreeBSD listen 80 default; client_max_body_size 4G; server_name www.dominio.com; # ~2 seconds is often enough for most folks to parse HTML/CSS and # retrieve needed images/icons/frames, connections are cheap in # nginx so increasing this is generally safe... keepalive_timeout 5; # Ruta a tus archivos estaticos. location /static/ { alias /webapps/appname/django_app/static/; autoindex on; } location / { # an HTTP header important enough to have its own Wikipedia entry: # http://en.wikipedia.org/wiki/X-Forwarded-For proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # enable this if and only if you use HTTPS, this helps Rack # set the proper protocol for doing redirects: # proxy_set_header X-Forwarded-Proto https; # pass the Host: header from the client right along so redirects # can be set properly within the Rack application proxy_set_header Host $http_host; # we don't want nginx trying to do something clever with # redirects, we set the Host: header above already. proxy_redirect off; # set "proxy_buffering off" *only* for Rainbows! when doing # Comet/long-poll stuff. It's also safe to set if you're # using only serving fast clients with Unicorn + nginx. # Otherwise you _want_ nginx to buffer responses to slow # clients, really. # proxy_buffering off; # Try to serve static files from nginx, no point in making an # *application* server like Unicorn/Rainbows! serve static files. proxy_pass http://django_app_server; } }
Una vez configurado, hay que enlazar el archivo que hemos creado en /etc/nginx/sites-available/ a /etc/nginx/sites-enabled/, de esta manera nginx empezará a usar la configuración:
$ sudo ln -s /etc/nginx/sites-available/midominio /etc/nginx/sites-enabled/midominio
Por último reiniciamos nginx:
$ sudo service nginx restart
Nota: Los autores de gunicorn tiene un ejemplo más completo de un archivo de configuración nginx.conf
Alternativas
Por supuesto esta no es la única opción para desplegar django, ni es la mejor para todas las situaciones. Si estas administrando múltiples aplicaciones, y/o múltiples servidores quizás Docker o Dokku se ajuste mejor a tus necesidades. Y como comenté al inicio Heroku es una buena alternativa para páginas con poco tráfico.