Come fare il deploy di un progetto Django su una VPS

Usare un server virtuale per l'host di un progetto Django

01 Febbraio 2017

Qualche nota da tenere a mente prima di iniziare:

  • quella che stai leggendo è una vecchia guida scritta attorno al 2018 (per me stesso, usavo Django da appena qualche mese), la riporto qui perché penso possa essere utile anche ad altri, nonostante sia datata.
  • questa guida spiega l'installazione diretta del progetto sulla macchina. In futuro - forse - scriverò la versione con Docker.
  • anche se installi direttamente sulla macchina, meglio evitare l'interprete Python di sistema, usa un ambiente virtuale.
  • la guida è pensata per far capire il processo in modo semplice, non per un deploy in un ambiente professionale.
  • non è una guida esaustiva, soprattutto per ambienti di produzione pubblici e/o ad alto traffico (mancano considerazioni su sicurezza, load testing e altro).

Come prima cosa, installare il Web Server Nginx sulla macchina:

sudo apt-get update 
sudo apt-get install nginx

Una volta installato verificare che stia girando correttamente:

sudo systemctl status nginx

Se sta girando correttamente, aprire una pagina in incognito di Chrome e recarsi al seguente indirizzo: http://<ip della tua VPS>
Dovrebbe vedersi una pagina di presentazione, se così non fosse potrebbe essere necessario aprire alcune porte nel firewall del Server: come impostare il firewall.

Creare un percorso sul server dove poter mettere un file personalizzato da far servire al Virtual Host (Server Block) di Nginx:

sudo mkdir -p /var/www/example.com/html 
sudo chown -R $USER:$USER /var/www/example.com/html 
sudo chmod -R 755 /var/www
cd /var/www/example.com/html  
nano index.html

Procedere con la creazione del primo file per il Server Block (maggiori info sul file di configurazione in questa guida):

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com

Svuotare il seguente file e scrivere all'interno di esso una configurazione come di seguito:

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

        root /var/www/example.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name example.com www.example.com;

        location / {
                try_files $uri $uri/ =404;
        }
}

Abilitare il nuovo Server Block di Nginx:

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

Aprire il file di configurazione principale di Nginx:

sudo nano /etc/nginx/nginx.conf

Aggiungere alla configurazione un parametro per evitare problemi futuri riguardanti i Server Blocks:

http {
    . . .

    server_names_hash_bucket_size 64;

    . . .
}

Controllare che non ci siano errori di sintassi nei files di configurazione e riavviare il Web Server:

sudo nginx –t; sudo systemctl restart nginx

Adesso bisogna implementare l'HTTPS, per prima cosa installare il Certbot:

sudo add-apt-repository ppa:certbot/certbot
sudo apt install python-certbot-nginx

L'utilizzo del Certbot richiede che sia configurato un Server Block, quindi che almeno un file di configurazione di Nginx abbia la seguente direttiva:

...

server_name example.com www.example.com;

...

A questo punto non resta che utilizzare il Certbot per creare i certificati per i domini specificati configurando il tutto per il Web Server specificato:

sudo certbot --nginx -d example.com -d www.example.com

Il oltre a creare e configurare i certificati, il software si occuperà anche di aggiungere uno script di rinnovo a /etc/cron.d, è possibile testarlo con il seguente comando:

sudo certbot renew --dry-run

Infine riavviare Nginx

sudo systemctl restart nginx

A questo punto bisogna configurare il database, per prima cosa andare nel settings.py del progetto e assicurarsi di aver impostato la variabile TIME_ZONE in modo corretto. Infine installiamo il necessario:

sudo apt-get install python3-dev libmysqlclient-dev mysql-server
pip install mysqlclient

Accertarsi che il MySQL server stia girando in modo corretto prima di continuare:

sudo systemctl status mysql.service

Entrare nel terminale del MySQL, creare un nuovo Database e creare un utente con tutti i permessi su quel database:

CREATE DATABASE progetto_db; 
GRANT ALL
ON progetto_db.*
TO 'utente_db'@'localhost'
IDENTIFIED BY '1234567';

Adesso bisogna configurare il progetto per utizzare il database appena creato, per farlo andare nel file settings.py e inserire una configurazione di questo tipo al posto di quella standard:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/etc/mysql/progetto.cnf',
        },
    }
}

La configurazione di sopra non fa altro che far puntare il progetto a un file con la configurazione per il MySQL, quindi per procedere creare quel file:

sudo nano /etc/mysql/progetto.cnf

Scrivere all'interno del file appena creato tutti i parametri necessari:

[client]
database = progetto_db
user = utente_db
password = 1234567
default-character-set = utf8

Riavviare il MySQL e provare il progetto in modo da verificarne il corretto funzionamento:

sudo systemctl daemon-reload
sudo systemctl restart mysql
python manage.py makemigrations
python manage.py migrate
python manage.py runserver 0.0.0.0:8010

Aggiustare gli host consentiti per il progetto e definire dove collezionare i file statici:

ALLOWED_HOSTS = ['domino_o_ip'] 
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

Collezionare tutti i file statici nella cartella STATIC_ROOT specificata di sopra (il web server andrà a prenderli lì):

python manage.py collectstatic

Accertarsi che il Gunicorn sia in grado di servire il progetto senza errori, eseguendo il comando seguente sarà possibile visitare il sito (per il momento ancora privo di files statici):

gunicorn --bind 0.0.0.0:8000 progetto.wsgi

A questo punto, corretti tutti gli errori se ce ne sono stati, bisogna creare il file SystemD per la gestione del processo:

sudo nano /etc/systemd/system/progetto.service

Nel file appena creato inserire una configurazione di questo tipo:

[Unit]
Description=Progetto
After=network.target

[Service]
User=utente
Group=www-data
WorkingDirectory=/directory/del/progetto
ExecStart=gunicorn --access-logfile - --workers 3 --bind unix:/home/utente/progetto/progetto.sock progetto.wsgi:application

[Install]
WantedBy=multi-user.target

Attenzione: nell'esempio è stata indicata una configurazione di Gunicorn molto semplice, con semplicemente 3 workers non asincroni.

La configurazione ideale per avere un'applicazione web scalabile è probabilmente abbastanza diversa e dipende dal progetto: ecco un articolo interessante a riguardo.

Abilitare il processo (per lo start all'avvio) ed avviarlo per la prima volta, infine verificare che stia girando in modo corretto:

sudo systemctl daemon-reload
sudo systemctl start progetto
sudo systemctl enable progetto
sudo systemctl status progetto

Il seguente comando permette invece di vedere tutto l'output del progetto come errori, eccezioni, print, log..:

sudo journalctl –b –u progetto

Ogni volta che il progetto verrà modificato sarà necessario effettuare un reload e un restart:

sudo systemctl restart progetto

Nel file di configurazione di Nginx per il Server Block del sito inserire una configurazione di questo tipo:

....
location /static/ {
        alias /path/to/staticfiles/here/;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/utente/progetto/progetto.sock;
    }
....

Infine riavviare il Web Server per concludere l'intera procedura. Il sito dovrebbe essere visualizzabile senza problemi in HTTPS:

sudo nginx –t; sudo systemctl restart nginx

Attenzione: visto che l'app è online ed esposta al pubblico, esegui questa utility di Django per assicurarti di aver rispettato la checklist per il deploy.


Return to wiki index