Introduzione

Django è un framework web che facilita l'avvio delle applicazione Python o di un sito web. Django include un server di sviluppo per testare il codice localmente.

In questo tutorial vedremo come installare e configurare Django con un database PostgreSQL su di un server Gunicorn che si interfaccia con le applicazioni scritte in Python. Infine installeremo Nginx come reverse proxy per Gunicorn.

Ubuntu 18.04 LTS.

Prerequisiti

Prima di iniziare con questa guida, dovresti avere un utente non root con i privilegi sudo impostati sul tuo server. Segui questa guida: Configurazione Iniziale su Ubuntu 18.04 LTS

Installare Python PostgreSQL Nginx

Scaricare tutti i pacchetti necessari per procedere con questo tutorial, è possibile utilizzare direttamente i repository ufficiali di Ubuntu.
Aggiornare l'indice del pacchetto apt locale e quindi scaricare e installare i pacchetti necessari. Da terminale:
sudo apt update
sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl

Configurare PostgreSQL

Durante la fase di installazione di Postgres verrà creato un nuovo utente di sistema postgres che avrà lo stesso nome dell'utente amministrativo di PostgreSQL.
Dobbiamo utilizzare questo utente per eseguire attività amministrative.
Effettuare quindi il login al nuovo utente con l'opzione -u:
sudo -u postgres psql

Creare un database per la nostra app:
CREATE DATABASE myapp;

Creare un utente per il nostro nuovo database:
CREATE USER myappuser WITH PASSWORD 'TUA_PASSWORD';

Impostare alcuni parametri consigliati per il nuovo database:
ALTER ROLE myappuser SET client_encoding TO 'utf8';
ALTER ROLE myappuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myappuser SET timezone TO 'UTC';

Dare al nuovo utente i privilegi di amministratore al database:
GRANT ALL PRIVILEGES ON DATABASE myapp TO myappuser;

Uscire dal prompt di PostgreSQL:
\q

Creare un ambiente virtuale in Python per gestire la nostra App

Installare virtualenv utilizzando pip:
sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

Creare una nuova cartella per la nostra App:
mkdir ~/myappdir

Entrare nella cartella appena creata:
cd ~/myappdir

Creare un ambiente virtuale in Python:
virtualenv myappenv

Questo comando creerà una directory chiamata myappenv all'interno della directory myappdir. All'interno, installerà una versione locale di Python e una versione locale di pip. Possiamo usarlo per installare e configurare un ambiente Python isolato per il nostro progetto.

Attivare l'ambiente virtuale:
source myappenv/bin/activate

Il tuo prompt dovrebbe cambiare per indicare che stai ora operando all'interno di un ambiente virtuale Python.
Esempio:
(myappenv) [email protected]:~/myappdir$.

Quando l'ambiente virtuale è attivato utilizzare pip invece di pip3, anche se si sta utilizzando Python 3.

Installare i requisiti Python del nostro progetto:
pip install django gunicorn psycopg2-binary

Creare un App Django

Dato che abbiamo già una directory di progetto, diremo a Django di installare i file nella stessa directory:
django-admin.py startproject myapp ~/myappdir

Cambiare le configurazioni nel file settings.py
nano ~/myappdir/myapp/settings.py

Cercare la voce ALLOWED_HOSTS, questa voce definisce un elenco di indirizzi IP del server o nomi dominio che possono essere usati per connettersi all'istanza Django. Qualsiasi richiesta in arrivo con un'intestazione Host non presente in questo elenco genererà un'eccezione.

Nelle parentesi quadre, elenca gli indirizzi IP oppure i nomi dominio associati al tuo server Django. Ogni indirizzo IP o nome dominio deve essere scritto tra virgolette con voci separate da una virgola.
Esempio:

# The simplest case: just add the domain name(s) and IP addresses of your Django server
# ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
# To respond to 'example.com' and any subdomains, start the domain with a dot
# ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
ALLOWED_HOSTS = ['NOME_DOMINIO', 'INDIRIZZO_IP', . . ., 'localhost']

Successivamente cercare la voce DATABASES per configurare l'accesso al nostro database PostgreSQL

Cambiare le impostazioni con le informazioni del tuo database PostgreSQL:

. . .
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myapp',
        'USER': 'myappuser',
        'PASSWORD': 'TUA_PASSWORD',
        'HOST': 'localhost',
        'PORT': '',
    }
}
. . .

Successivamente, andare in fondo al file e aggiungere un'impostazione che indica dove devono essere collocati i file statici. Questo è necessario affinché Nginx possa gestire le richieste:

. . .
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

Migrare lo schema del database iniziale al nostro database PostgreSQL:
~/myappdir/manage.py makemigrations
~/myappdir/manage.py migrate

Creare un utente amministratore per il nostro progetto:
~/myappdir/manage.py createsuperuser

Inserire tutte le informazioni necessarie per creare il nuovo utente amministratore.

Spostare tutti i file statici in una cartella chiamata static all'interno del nostro progetto:
~/myappdir/manage.py collectstatic

Se avete il firewall (UFW) attivo, è necessario abilitare la porta 8000:
sudo ufw allow 8000

Testare l'applicazione

Avviare l'applicazione da terminale:
~/myappdir/manage.py runserver 0.0.0.0:8000

Aprire il browser e connettersi all'indirizzo IP del server oppure al nome dominio:
http://DOMINIO_OPPURE_IP:8000

Per entrare nel pannello Admin, aggiungere /admin alla fine del link:
http://DOMINIO_OPPURE_IP:8000/admin

Effettuare il login con utente e password generati con il co mando createsuperuser.

Per chiudere l'app e spegnere il server premere CTRL-C nel terminale.

Testare l'applicazione utilizzando Gunicorn

Inserire la nostra directory di progetto e usare gunicorn per caricare il modulo WSGI del progetto:
cd ~/myappdir
gunicorn --bind 0.0.0.0:8000 myapp.wsgi

L'interfaccia di amministrazione non avrà alcuno stile poiché Gunicorn non sa come trovare il contenuto CSS statico.

Premi CTRL-C nella finestra del terminale per fermare Gunicorn.

Possiamo uscire dal nostro ambiente virtuale digitando:
deactivate

Creare i file socket e servizio systemd per Gunicorn
Il socket Gunicorn verrà creato all'avvio e ascolterà le connessioni. Quando si verifica una connessione, systemd avvierà automaticamente il processo Gunicorn per gestire la connessione.

Creare e aprire un file socket systemd per Gunicorn:
sudo nano /etc/systemd/system/gunicorn.socket

Inserire le seguenti righe di codice:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Salvare e chiudere il file.

Successivamente, creare e aprire un file di servizio systemd per Gunicorn. Il nome file del servizio deve corrispondere al nome file del socket con l'eccezione dell'estensione:
sudo nano /etc/systemd/system/gunicorn.service

Specificare l'utente e il gruppo che vogliamo utilizzare per l'esecuzione.
Inseire le seguenti righe di codice, modificando i campi necessari con i vostri dati:

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

[Service]
User=TUO_UTENTE
Group=www-data
WorkingDirectory=/home/TUO_UTENTE/myappdir
ExecStart=/home/TUO_UTENTE/myappdir/myappenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myapp.wsgi:application

[Install]
WantedBy=multi-user.target

Salvare e chiudere il file

Ora possiamo avviare e abilitare Gunicorn socket. Questo creerà il file socket in /run/gunicorn.sock ora e all'avvio. Quando viene effettuata una connessione a questo socket, systemd avvierà automaticamente gunicorn.service per gestirlo:
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

Verificare lo stato del file socket di Gunicorn:
sudo systemctl status gunicorn.socket

Verificare l'esistenza del file gunicorn.sock:
file /run/gunicorn.sock

Dovreste ricevere un messaggio di output simile al seguente:

/run/gunicorn.sock: socket

Se il comando di stato systemctl indica che si è verificato un errore o se non si trova il file gunicorn.sock nella directory, è un'indicazione che il socket Gunicorn non è stato possibile creare correttamente. Controlla i log della socket di Gunicorn digitando:

Per controllare i log del socket, anche per eventuali errori:
sudo journalctl -u gunicorn.socket

Successivamente, dopo aver quindi attivato gunicorn.socket è necessario attivare anche gunicorn.service con questo comando:
curl --unix-socket /run/gunicorn.sock localhost

Per verificare lo stato di Gunicorn service:
sudo systemctl status gunicorn

Dovreste ricevere un messaggio di output simile al seguente:

● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2018-07-09 20:00:40 UTC; 4s ago
 Main PID: 1157 (gunicorn)
    Tasks: 4 (limit: 1153)
   CGroup: /system.slice/gunicorn.service
           ├─1157 /home/noviello/myappdir/myappenv/bin/python3 /home/noviello/myappdir/myappenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myapp.wsgi:application
           ├─1178 /home/noviello/myappdir/myappenv/bin/python3 /home/noviello/myappdir/myappenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myapp.wsgi:application
           ├─1180 /home/noviello/myappdir/myappenv/bin/python3 /home/noviello/myappdir/myappenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myapp.wsgi:application
           └─1181 /home/noviello/myappdir/mypmyappenvrojectenv/bin/python3 /home/noviello/myappdir/myappenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myapp.wsgi:application

Jul 09 20:00:40 django1 systemd[1]: Started gunicorn daemon.
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Starting gunicorn 19.9.0
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Listening at: unix:/run/gunicorn.sock (1157)
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Using worker: sync
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1178] [INFO] Booting worker with pid: 1178
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1180] [INFO] Booting worker with pid: 1180
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1181] [INFO] Booting worker with pid: 1181
Jul 09 20:00:41 django1 gunicorn[1157]:  - - [09/Jul/2018:20:00:41 +0000] "GET / HTTP/1.1" 200 16348 "-" "curl/7.58.0"

Per verificare lo stato di eventuali errori:
sudo journalctl -u gunicorn

Se viene effettuata una modifica al file /etc/systemd/system/gunicorn.service sarà necessario riavviare i processi di Gunicorn:
sudo systemctl daemon-reload
sudo systemctl restart gunicorn

Configurare Nginx

Creare un nuovo blocco server (server block) in Nginx:
sudo nano /etc/nginx/sites-available/myapp

Configurare il file in questo modo, facendo attenzione a modificare i campi necessari con i vostri dati:

server {
    listen 80;
    server_name DOMINIO_OPPURE_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/TUO_UTENTE/myappdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Abbiamo impostato Nginx per trovare i file statici nella nostra directory creata precedentemente e abbiamo aggiunto i parametri proxy_params e proxy_pass per passare il traffico direttamente al socket di Gunicorn.

Salvare e chiudere il file.

Abilitare il blocco server appena creato:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Verificare le configurazioni di nginx:
sudo nginx -t

Se non ci sono errori, riavviare nginx:
sudo systemctl restart nginx

Se utilizzare il firewall di Ubuntu (Ufw) è necessario adesso cambiare le regole:
sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'

L'installazione e la configurazione di Django con Postgres Nginx e Gunicorn su Ubuntu 18.04 è terminata.