Desplegar Django en una VPS Debian desde 0

Los servicios en la nube ofrecen varias ventajas por sobre los VPS ya que trabajan sobre una plataforma de recursos distribuidos. Al no formar parte de un servidor único son mucho más tolerantes a fallos. Sin embargo, hoy por hoy, esta infraestructura sigue siendo más cara.

Hay situaciones en las que nos puede interesar abaratar estos costes con una VPS, por ejemplo a la hora de arrancar proyectos personales o tener servidores de desarrollo en la nube.

En este sentido, la manera más barata de poner en producción pequeños proyectos personales sigue siendo alquilar una VPS con Linux y montarnos nuestro propio servidor desde cero. En tiempos en los que los Servicios Cloud nos solucionan muy bien la vida, esta práctica está cayendo en desuso, pero sigue siendo una alternativa muy económica que además nos permite tener todo el control sobre nuestro proyecto, algo que viene muy bien cuando éste arranca. Para que os hagáis una idea, con algunos proveedores se puede tener VPS con 2GB de RAM y disco SSD por poco más de 3 euros, IVA incluído. Y en una VPS podemos alojar varios proyectos (dependiendo de la carga que tengan que soportar, claro).

Así que voy a dedicar un árido artículo enumerando todos los pasos que hay que dar desde que se adquiere una VPS hasta que se pone a funcionar un proyecto en Django que hemos desarrollado. Espero que a alguien le resulte útil:

Configurar el servidor de nombres (BIND)

Berkeley Internet Name Domain (BIND) es una implementación del protocolo Domain Name System (DNS). Es lo que usaremos para configurar el dominio.

Para usar los DNS asignados por el ISP, edita el archivo /etc/bind/named.conf.options,

# nano /etc/bind/named.conf.options

para que lea,

options {
        directory "/var/cache/bind";

        // If there is a firewall between you and nameservers you want
        // to talk to, you may need to fix the firewall to allow multiple
        // ports to talk.  See http://www.kb.cert.org/vuls/id/800113

        // If your ISP provided one or more IP addresses for stable
        // nameservers, you probably want to use them as forwarders.
        // Uncomment the following block, and insert the addresses replacing
        // the all-0's placeholder.

        forwarders {
                100.10.0.3;
                100.10.0.7;
        };

        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };
};

En forwarders van los números IP de los servidores de nombre (DNS) de la red, los mismos del archivo /etc/resolv.conf original indicados en el instructivo nameserver.

El DNS ahora consulta los DNS indicados en forwarders y mantiene una tabla local.

La configuración avanzada de BIND que os voy a mostrar está pensada para un servidor Debian/Ubuntu con número IP estático y conectado permanentemente a Internet.

Para definir un nuevo dominio y hacer que el servidor actúe como su DNS autoritativo, necesitas primero haber comprado un dominio en un registro de nombres de dominio (NIC) y definir el servidor de nombres primario del nuevo dominio con el número IP y FQDN de tu servidor VPS que es donde irá el servidor BIND.

Vamos a usar de ejemplo el dominio MIDOMINIO.com.

Edita /etc/bind/named.conf.local y agrega al final del archivo,

zone "MIDOMINIO.com" {
        type master;
        file "/etc/bind/db.MIDOMINIO.com";
        allow-transfer { none; };
        allow-query { any; };
};

Ahora debes crear un archivo /etc/bind/db.MIDOMINIO.com y agregar lo siguiente,

;
; archivo BIND para zona MIDOMINIO.com
;
$TTL    604800
@       IN      SOA     MIDOMINIO.com. hostmaster.MIDOMINIO.com. (
                1       ; Serial
                           1200         ; Refresh
                            300         ; Retry
                        2419200         ; Expire
                           1200 )       ; Negative Cache TTL

MIDOMINIO.com.    IN      NS      ns1.MIDOMINIO.com.
MIDOMINIO.com.    IN      NS      ns2.MIDOMINIO.com.
MIDOMINIO.com.    IN      MX      1 mx1.MIDOMINIO.com.
MIDOMINIO.com.    IN      MX      2 mx2.MIDOMINIO.com.

localhost       IN      A       127.0.0.1
MIDOMINIO.com.    IN      A       199.199.199.199

ns1             IN      A       199.199.199.199
ns2             IN      A       100.10.20.20

mx1             IN      A       199.199.199.199
mx2             IN      A       100.10.20.20

www             IN      A       199.199.199.199
smtp            IN      A       100.10.10.11

A continuación la descripción de algunos campos de este fichero:

  • 199.199.199.199 es la IP de tu VPS.
  • El instructivo TTL (Time To Live) indica la validez (en segundos) de la consulta, tras la cual deberá ejecutarse una actualización. 604800 segundos equivalen a una semana.

  • El número Serial debería incrementarse cada vez que se modifica la zona.

  • El campo Refresh indica el intervalo de tiempo que los DNS secundarios deben refrescar la información del archivo de zona si han habido cambios (20 munutos).

  • Retry indica el intervalo de tiempo que los DNS secundarios deben reintentar actualizar la información si el DNS primario no responde (5 minutos).

  • Expire indica el tiempo que el DNS secundario expira como servidor de nombres de la zona en caso el DNS primario no responda a requerimiento de actualización (4 semanas).

  • Negative Cache TTL indica el TTL en caso de una consulta con respuesta negativa (20 minutos).

Algunas modificaciones que necesitas llevar a cabo para tu caso particular:

  • Modifica todas las ocurrencias de MIDOMINIO.com por tu propio dominio.
  • Todas ellas deben terminar con un punto. ns1 debe llevar el número IP del DNS primario, es decir, el servidor BIND.
  • ns2 define el DNS secundario. Puede ser el mismo servidor BIND, otro, o el que habilita el registro de nombres de dominio, generalmente en forma gratuita, por ejemplo, secundario.nic.cl. mx1 y mx2 son los registros MX (Mail eXchanger) e indican el destino del correo eletrónico del dominio (@MIDOMINIO.com), según la prioridad indicada por los números que preceden el nombre. En principio, es necesario solo un registro MX. El MTA de todo servidor MX debe aceptar correo-e a nombre del dominio.

Si tienes un firewall o lo quieres configurar, debes tener en cuenta que es muy importante habilitar el puerto 53 UDP y TCP para que BIND pueda funcionar.

Ahora sólo queda reiniciar el servicio:

# /etc/init.d/bind9 restart

Configurar el servidor web y el servidor de aplicaciones

Paso 1: actualiza paquetes

sudo apt-get update

sudo apt-get upgrade

Poco que comentar aquí, vamos a actualizar los paquetes de nuestra VPS para que estén en su versión más actualizada.

Paso 2: configura un Virtualenv

La instrucción para crear un virtualenv es la siguiente. Si quieres saber más, hace tiempo escribí este artículo.

virtualenv /opt/myproject

Ahora vamos a activar nuestro virtualenv y a instalar django dentro de él. Es importante instalar exactamente la misma versión en la que hemos desarrollado nuestro proyecto:

source /opt/myproject/bin/activate

pip install django

Paso 3: crea la base de datos

Puesto que hay muchos SGBD diferentes, dejaré este tema fuera porque hay muchos tutoriales por ahí mostrando cómo crear una base de datos desde 0. Símplemente busca uno para el SGBD que hayas utilizado tú.

Paso 4: Instalando el servidor web

Vamos a utilizar nginx, un servidor web que destaca por su flexibilidad y ligereza. Si vienes de Apache, te sonará esto: busca la carpeta que contiene los ficheros de configuración de cada sitio disponible, y crea uno nuevo para tu nuevo proyecto:

sudo nano /etc/nginx/sites-available/mydjangoproject

Ésta es la coraza de un fichero para tu proyecto:

server {
    server_name MIDOMINIO.com;

    access_log off;

    location /static/ {
        alias /opt/myenv/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
    }
}

Salva el fichero.

Loq ue dice este fichero de configuración a NGINX es que cada petición http que llegue a nuestro servidor sea redirigida al servidor Gunicorn (que será, como veremos dentro de nada, nuestro servidor de aplicaciones). Nginx en realidad es un proxy que recoge peticiones por el puerto 80 y se las proporciona al servidor de aplicaciones.

Ahora tienes que crear un enlace simbólico de este fichero en la carpeta /etc/nginx/sites-enabled:

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

Y ahora símplemente reinicia el servicio Nginx:

sudo service nginx restart

Paso 5: Sube a producción tu proyecto Django

create una carpeta para tu proyecto Django, la que quieras, en tu VPS y con FTP subes los ficheros. Si usas git, puedes simplemente hacer un clone

git clone https://github.com/spereas/ifeifdife .

Paso 6: Configura tu servidor de aplicaciones

Como he comentado antes, nuestra elección será Gunicorn, la más extendida para proyectos en Django. Para instalarla, primero activamos nuestro virtualenv, que hemos creado antes:

source /opt/myproject/bin/activate

Una vez dentro, instala en él Gunicorn:

pip install gunicorn

Ahora símplemente arranca el servidor Gunicorn para que escuche en el puerto 8001 y probar que funciona OK:

gunicorn_django --bind MIDOMINIO.com:8001

Configura tu aplicación en Django

Ahora vamos a ir al fichero de configuración de nuestro proyecto en DJANGO para cambiar algunas cosillas:

sudo nano /opt/myproject/myproject/settings.py

De momento, ponemos DEBUG en False. Si es un entorno de producción no necesitamos tener esto activo ya que afecta al rendimiento:

DEBUG = False

Ahora configura la base de datos:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'mydb',                      # Or path to database file if using sqlite3.
        # The following settings are not used with sqlite3:
        'USER': 'myuser',
        'PASSWORD': 'password',
        'HOST': '',                      # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
        'PORT': '',                      # Set to empty string for default.
    }
}

Por supuesto es necesario que edites la ruta en la que están tus ficheros estáticos:

STATIC_ROOT = '/opt/myproject/static/'

STATIC_URL = '/static/'

Y ahora, vamos a ejecutar esta instrucción. Lo que hace es recoger todos los archivos estáticos y moverlos a la carpeta que acabamos de configurar:

python manage.py collectstatic

Ale, pues ya debería rular!!!! :)

Configurar HTTPS con Let'sEncryp

La mejor manera de instalar Letsencrypt en Ubuntu es descargándolo de la web oficial directamente con esta instrucción:

cd ~
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt

Antes de nada, para nginx, pues vamos a cambiar su configuración:

service nginx stop

Ahora tenemos instalado letsencrypt, pero no tenemos ningún certificado. Así que vamos a instalarlo

./letsencrypt-auto certonly --standalone --email admin@MIEMAIL.com -d MIDOMINIO.com

Esto lo que hace es poner el certificado y todos sus ficheros asociados en esta ruta:

/etc/letsencrypt/live/[MIDOMINIO.com]

De esta carpeta vamos a necesitar dos ficheros: fullchain.pem y privkey.pem.

Ahora vamos al fichero .conf de nuestro site, buscamos la directiva server y añadimos lo siguiente:

listen [::]:80;
listen 80;

listen 443 ssl;

ssl_certificate /etc/letsencrypt/live/[MIDOMINIO.com]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[MIDOMINIO.com]/privkey.pem;

Estoq ue acabo de poner permite acceder a nuestra aplicación indistintamente con http y https. Es muy probable ququieras activar la web con https de modo que vamos a modificar la directiva server para obligar a nginx a redireccionar las peticiones a https:

server {
    listen [::]:80;
    listen 80;

    # listen on the www and non-www host
    server_name www.[MIDOMINIO.com] [MIDOMINIO.com];

    # and redirect to the https host (declared below)
    return 301 https://[MIDOMINIO.com]$request_uri;
}

Para el SSL sólo tenemos que dejarlo así:

listen [::]:443 ssl;
listen 443 ssl;

ssl_certificate /etc/letsencrypt/live/[MIDOMINIO.com]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[MIDOMINIO.com]/privkey.pem;

Y ahora podemos arrancar nginx!

service nginx start