Clusterizzare un'applicazione Django con Nginx¶
Si suppone di avere un'applicazione django da distribuire su più istanze server, e di volere che il carico sia distribuito fra queste istanze e che anche in caso di malfunzionamento di una o più istanze, il servizio non sia negato, finché almeno un server resta funzionante.
Prerequisiti¶
- Applicazione Django nella directory
/home/utente/projects/django/myproject/
(per semplicità la guida assume che l'applicazione sia servita da./manage.py runserver $PORTA
) - Nginx
Avvio delle istanze Django¶
Innanzitutto assicurarsi che la SECRET_KEY
contenuta nei rispettivi file settings.py
sia la stessa per tutte le istanze, in modo che i backend di firma digitale e di controllo interni a django funzionino correttamente.
Avviare 4 istanze della stessa applicazione in 4 terminali differenti (un comando per terminale):
$ ./manage.py runserver 8000 $ ./manage.py runserver 8001 $ ./manage.py runserver 8002 $ ./manage.py runserver 8003
In questo modo si avranno 4 istanze della stessa applicazione, e accedendo con il browser agli indirizzi 127.0.0.1:8000
, 127.0.0.1:8001
, 127.0.0.1:8002
, 127.0.0.1:8003
, si vedrà che ognuna funziona indipendentemente.
Oppure, in alternativa, usare Supervisor: guida a questo link.
Configurazione base di nginx¶
Si crei il file di configurazione /etc/nginx/sites-available/cluster
:
upstream backend { server 127.0.0.1:8000; server 127.0.0.1:8001; server 127.0.0.1:8002; server 127.0.0.1:8003; } server { listen 80; root /home/utente/projects/django/; server_name cluster; access_log /home/utente/projects/django/access.log; error_log /home/utente/projects/django/error.log; location / { proxy_pass http://backend; } }
e si attivi con:
# ln -s /etc/nginx/sites-available/cluster /etc/nginx/sites-enabled/cluster # /etc/init.d/nginx restart
In questo modo si dice a nginx di redirigere tutte le richieste che giungono a http://cluster/
verso il cluster chiamato backend
, che è il gruppo di istanze di django.
Perché l'URL http://cluster
funzioni però, bisogna aggiungere una riga al file /etc/hosts
(o configurare il DNS in modo appropriato):
127.0.0.1 cluster
Gestione della session¶
Nel caso dell'esempio, con tutte le istanze in locale e col database condiviso, la sessione di login funziona in modo trasparente dato che django usa la session con database backend. In produzione invece si vorranno cluster formati da più server, e il database potrebbe essere condiviso oppure no, a seconda dell'applicazione specifica. Django mette a disposizione diverse alternative per la gestione della session, ad esempio:
- File (documentazione)
- Cookie firmati (documentazione)
- Cache (documentazione), ad esempio usando
memcached
(documentazione)
Session backend basato su file¶
Per impostare il file backend bisogna avere una directory raggiungibile e scrivibile da tutti i server che partecipano al cluster, e configurare django in modo che usi quella directory per scrivere i file che contengono i dati di sessione. Come al solito nell'esempio si userà una directory locale, per semplicità, ma in produzione potrebbe essere ad esempio un path remoto montato tramite sshfs
. Aggiungere quindi queste due righe al file settings.py
:
SESSION_ENGINE = "django.contrib.sessions.backends.file"
SESSION_FILE_PATH = "/home/utente/projects/sessions/"
Di default Django usa come SESSION_FILE_PATH
l'output di tempfile.gettempdir()
, che solitamente è /tmp
, ma in questo modo il path non è condiviso tra i server del cluster, per questo è necessario indicare SESSION_FILE_PATH
.
Session backend basato su cookie firmati¶
Django può anche usare cookie per i dati di sessione, firmati usando SECRET_KEY
come chiave (che dovrà essere la stessa per tutte le istanze) per evitare che l'utente che accede al sito possa modificarli. In questo modo qualsiasi sia il server che risponde alla singola request, django potrà controllare mediante il cookie firmato se l'utente è loggato oppure no, e tutti gli altri dati di sessione che l'applicazione avrà settato nell'oggetto request.session
. Il file settings.py
dovrà contenere questa riga:
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
Session backend basato su cache (con memcached
)¶
Django supporta diversi tipi di cache backend (come memcache, database, file), ma per la gestione della session è consigliato l'uso di memcached
. Prima di tutto quindi bisognerà installare i seguenti pacchetti:
# apt-get install memcached python-memcached
Il pacchetto debian avvierà memcached
in ascolto sulla porta 11211.
Si configuri il framework cache di django modificando, al solito, il file settings.py
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
In LOCATION
andranno messi l'IP e la porta dove risponde il demone memcached
. Come scritto nella documentazione ufficiale è importante che il demone non sia esposto su internet, ma che risponda soltanto su un'interfaccia interna, configurabile con l'opzione -l
. Citando la pagina man di memcached
:
-l <ip_addr> Listen on <ip_addr>; default to INDRR_ANY. This is an important option to consider as <b>there is no other way</b> to secure the installation. Binding to an internal or firewalled network interface is suggested.
Test¶
A questo punto si può accedere a http://cluster/
e vedere come le richieste vengano divise fra i 4 server, e fermandone alcuni e/o riavviandoli, il sito funziona sempre, a patto che ovviamente almeno un'istanza sia attiva.
Con questa configurazione di base il carico viene distribuito uniformemente fra i componenti del cluster, ma il modulo upstream
di nginx supporta diverse opzioni (documentazione), ad esempio weight
:
upstream backend { server 127.0.0.1:8000; server 127.0.0.1:8001; server 127.0.0.1:8002 weight=3; server 127.0.0.1:8003; }
in questo modo nginx redirigerà sul terzo server statisticamente il triplo delle richieste rispetto agli altri (può essere utile se uno dei server del cluster ha performance hardware maggiori, o se ha più banda a disposizione), oppure max_fails
e fail_timeout
:
upstream backend { server 127.0.0.1:8000; server 127.0.0.1:8001 max_fails=2 fail_timeout=20s; server 127.0.0.1:8002; server 127.0.0.1:8003; }
se nginx vedrà negate più di 2 richieste in 20 secondi dal secondo server, lo considererà non disponibile per 20 secondi (i default sono max_fails=1
e fail_timeout=10s
)
upstream backend { server 127.0.0.1:8000 backup; server 127.0.0.1:8001; server 127.0.0.1:8002; server 127.0.0.1:8003; }
in questo modo il primo server è considerato di backup, e quindi verrà usato solo quando nessun altro server è disponibile.
Updated by Mark Caglienzi almost 11 years ago · 6 revisions