Despliegue en producción de una aplicación Django
En este post trato de explicar de la forma mas rápida y concisa cómo poner en producción una aplicación en Django en un VPS basado en Linux.
Tómese este post a modo de chuleta con todos los pasos necesarios para el despliegue: desde la instalación de la aplicación partiendo de un repositorio github, la instalación de postgresql, nginx, supervisor, cortafuegos, gunicorn…
Vamos manos a la obra.
1. Crear un usuario
adduser jair
Dar privilegios sudo al nuevo usuario
Editar con:
nano /etc/sudoers
Y añadir la línea:
2. Cambiar el puerto ssh del servidor
Editamos:
nano /etc/ssh/sshd_config
Reiniciar ssh:
service ssh restart
3. Configurar el firewall
ufw status
–> ver el estado
ufw enable
–> activar firewall
ufw allow 22
–> permite el paso al puerto 22
ufw allow ssh
–> permite ssh independientemente del puerto en el que esté configurado.
ufw status numbered
–> lista las reglas con el orden de aplicación.
ufw delete 1
–> borra la regla número 1.
ufw disable
–> desactiva el firewall.
4. Clonar repositorio del proyecto Django
https://github.com/andrestvp/ver.git
5. Preparar el entorno Python
Comprobar si se tiene instalado virtualenv:
e instalarlo en su caso:
sudo apt-get install virtualenv
Comprobar si está instalado Python
python3 --version
Crear el entorno virtual en la raíz de la aplicación
virtualenv env -ppython3
El argumento -p servirá para crear el entorno virtual en python3 aunque tuvieramos instalado también python2.
Activamos el entorno virtual con:
source env/bin/activate
Instalamos ahora los requerimientos:
pip install -r requirements.txt
Preparar la aplicación
python manage.py makemigrations python manage.py migrate python manage.py createsuperuser
6. Instalar Postgresql
sudo apt install postgresql postgresql-contrib
Configurar postgresql:
sudo -u postgres psql
Cambiar la contraseña del usuario postgres:
Cambiar la codificación de caracteres y la zona horaria:
ALTER ROLE postgres SET client_encoding TO 'utf8'; ALTER ROLE postgres SET timezone TO 'UTC';
Crear una base de datos:
create database apolo;
Verificamos en nuestro entorno virtual de python si tenemos instalada la librería de postgresql:
pip list
En caso de que no:
pip install psycopg2-binary
Cambiamos la configuración del proyecto django a postgresql.
nano config/db.py
verificando usuario y contraseña. De nuevo haremos migraciones y migrate.
7. Crear un fichero bash para ejecutar nuestra app python
Crearemos una carpeta ‘deploy’ en la estructura de nuestra app que contendrá el bash.
Para saber mas sobre bash hay un buen tutorial en Tutorial Bash
El script es ‘server.sh’:
#!/bin/bash
DJANGODIR=$(dirname $(cd `dirname $0` && pwd))
DJANGO_SETTINGS_MODULE=config.settings
cd $DJANGODIR
source env/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
exec python manage.py runserver 0:9000
Configurar Supervisor (gestor de procesos)
Se encargará de mantener nuestra aplicación siempre corriendo en nuestro servidor.
- Instalar supervidor
apt-get install supervisor
- Crear el siguiente archivo conf en la ruta /etc/supervisor/conf.d/apolo.conf
[program:apolo]
command=/home/jair/apolo/deploy/server.sh
autostart=true
autorestart=true
stderr_logfile=/home/jair/apolo/logs/err.log
stdout_logfile=/home/jair/apolo/logs/out.log
user=jair
environment=LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8
Comprobamos ahora si un fichero de supervisor es correcto:
sudo supervisorctl reread
Ahora actualizamos las tareas de supervisor:
sudo supervisorctl update
Podemos ver todo lo que se está ejecutando con supervisor:
sudo supervisorctl status
Para parar el servicio:
sudo supervisorctl stop apolo
Para iniciarlo:
sudo supervisorctl start apolo
Para reiniciar una tarea:
sudo supervisorctl restart apolo
o todas:
sudo supervisorctl restart all
Si hacemos un cambio al script debemos actualizarlo en supervisor:
sudo supervisorctl update
8. Configurar proxy gunicorn
Instalamos en nuestro entorno virtual:
pip install gunicorn
Creamos en la carpeta config un fichero settings llamado production.py que añadirá un par de variables a nuestro setting original:
from .settings import *
ALLOWED_HOSTS = ["192.168.0.9", "midominio.com", "www.midominio.com"]
DEBUG = False
Editamos el archivo wsgi.py para que apunte al nuevo production.py
Para comprobar que está funcionando gunicorn:
gunicorn config.wsgi
desde la ruta del programa:
Importante gunicorn servirá sobre el puerto 8000 por lo que tendremos que para el supervisor con:
sudo supervisorctl stop apolo
Necesitaremos un fichero bash para que que supervisor levante gunicorn:
#!/bin/bash
NAME="apolo"
DJANGODIR=$(dirname $(cd `dirname $0` && pwd))
SOCKFILE=/tmp/gunicorn-apolo.sock
LOGDIR=${DJANGODIR}/logs/gunicorn.log
USER=jair
GROUP=jair
NUM_WORKERS=5
DJANGO_WSGI_MODULE=config.wsgi
rm -frv $SOCKFILE
echo $DJANGODIR
cd $DJANGODIR
exec ${DJANGODIR}/env/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--bind=unix:$SOCKFILE \
--log-level=debug \
--log-file=$LOGDIR
Lo llamaremos gunicorn.sh y estará en la carpeta deploy con permisos de ejecución.
9. Configurar web server
sudo apt install nginx
Si usamos firewall habilitamos nginx:
sudo ufw allow 'Nginx HTTP'
sudo ufw allow 'Nginx HTTPS'
Verificamos que está corriendo:
systemctl status nginx
En el entorno virtual de python ejecutaremos:
python manage.py collectstatic --link --noinput
para organizar todas los recursos estáticos en una sola carpeta pero con enlaces simbólicos (sin ocupar espacio en disco).
Creamos el fichero de nginx en la ruta */etc/nginx/sites-available/apolo.conf’ con el contenido:
upstream apoloconn {
server unix:/tmp/gunicorn-apolo.sock fail_timeout=0;
}
server {
listen 80;
server_name www.algorisoftprojects.com algorisoftprojects.com 192.168.0.9;
access_log /home/jair/apolo/logs/nginx-access.log;
error_log /home/jair/apolo/logs/nginx-error.log;
location /media/ {
alias /home/jair/apolo/media/;
}
location /static/ {
alias /home/jair/apolo/staticfiles/;
}
location /static/admin/ {
alias /home/jair/apolo/staticfiles/admin/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://apoloconn;
}
error_page 500 502 503 504 /templates/500.html;
}
Habilitamos el sitio haciendo un enlace simbólico:
sudo ln -s /etc/nginx/sites-available/apolo.conf /etc/nginx/sites_enabled/
Comprobamos la configuración creada con:
sudo nginx -t
Cambiamos ahora nuesto apolo.conf de supervisor para que ahora apunto a gunicorn.sh en lugar de a server.sh como lo venía haciendo hasta este momento:
sudo nano /etc/supervisor/conf.d/apolo.conf
Actualizamos supervisor y reiniciamos nginx:
sudo supervisorctl update sudo systemctl restart nginx
Ahora es recomendable eliminar el puerto 8000 de las reglas del cortafueros:
sudo ufw status numbered
sudo ufw delete 2
10. Instalar los certificados SSL
Primero instalaremos el software certbot en el servidor:
sudo apt install certbot python3-certbot-nginx
Una vez instalado:
sudo certbot --nginx -d midominio.com -d www.midominio.com
A la pregunta sobre redireccionar http a https contestamos ‘2’ (si).
Actualizamos nuestro cortafuegos:
sudo ufw allow 'Nginx Full sudo ufw delete allow 'Nginx HTTP'
11. Backup de la Base de datos
Crearemos un script bash para hacer el backup de postgresql:
#!/bin/bash
export FECHA=`date +%d_%m_%Y_%H_%M_%S`
export NAME=apolo_${FECHA}.dump
export DIR=/home/jair/backup/
USER_DB=postgres
NAME_DB=apolo
cd $DIR
> ${NAME}
export PGPASSWORD=123
chmod 777 ${NAME}
echo "procesando la copia de la base de datos"
pg_dump -U $USER_DB -h localhost --port 5432 -f ${NAME} $NAME_DB
echo "backup terminado"
Añadimos una tarea a cron:
crontab -e
Programamos la tarea en un tiempo determinado:
* * * * * bash /home/jair/apollo/deploy/backup.sh
Verificamos la tarea creada:
crontab -l
Y hasta aquí la el despliegue en producción de una aplicación en Django.