Project

General

Profile

DjangoClusterNginx » History » Version 6

Mark Caglienzi, 12/16/2013 05:54 PM
Aggiunta parte su memcached

1 1 Mark Caglienzi
h1. Clusterizzare un'applicazione Django con Nginx
2
3
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.
4
5
h2. Prerequisiti
6
7
* Applicazione Django nella directory @/home/utente/projects/django/myproject/@ (per semplicità la guida assume che l'applicazione sia servita da @./manage.py runserver $PORTA@)
8
* Nginx
9
10
h2. Avvio delle istanze Django
11
12 5 Mark Caglienzi
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.
13
14 1 Mark Caglienzi
Avviare 4 istanze della stessa applicazione in 4 terminali differenti (un comando per terminale):
15
16
<pre>
17
$ ./manage.py runserver 8000
18
$ ./manage.py runserver 8001
19
$ ./manage.py runserver 8002
20
$ ./manage.py runserver 8003
21
</pre>
22
23
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.
24
25 5 Mark Caglienzi
Oppure, in alternativa, usare Supervisor: [[SupervisorPerDjango|guida a questo link]].
26 4 Mark Caglienzi
27 1 Mark Caglienzi
h2. Configurazione base di nginx
28
29
Si crei il file di configurazione @/etc/nginx/sites-available/cluster@:
30
31
<pre>
32
upstream backend {
33
    server 127.0.0.1:8000;
34
    server 127.0.0.1:8001;
35
    server 127.0.0.1:8002;
36
    server 127.0.0.1:8003;
37
}
38
39
server {
40
    listen 80;
41
    root /home/utente/projects/django/;
42
    server_name cluster;
43
    access_log /home/utente/projects/django/access.log;
44
    error_log /home/utente/projects/django/error.log;
45
    location / {
46
        proxy_pass http://backend;
47
    }
48
}
49
</pre>
50
51
e si attivi con:
52
53
<pre>
54
# ln -s /etc/nginx/sites-available/cluster /etc/nginx/sites-enabled/cluster
55
# /etc/init.d/nginx restart
56
</pre>
57
58
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.
59
60 3 Mark Caglienzi
Perché l'URL @http://cluster@ funzioni però, bisogna aggiungere una riga al file @/etc/hosts@ (o configurare il DNS in modo appropriato):
61 1 Mark Caglienzi
62
<pre>
63
127.0.0.1 cluster
64
</pre>
65 5 Mark Caglienzi
66
h2. Gestione della session
67
68
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:
69
70
* File ("documentazione":https://docs.djangoproject.com/en/dev/topics/http/sessions/#using-file-based-sessions)
71
* Cookie firmati ("documentazione":https://docs.djangoproject.com/en/dev/topics/http/sessions/#using-cookie-based-sessions)
72 6 Mark Caglienzi
* Cache ("documentazione":https://docs.djangoproject.com/en/dev/topics/cache/), ad esempio usando @memcached@ ("documentazione":https://docs.djangoproject.com/en/dev/topics/cache/#memcached)
73 5 Mark Caglienzi
74
h3. Session backend basato su file
75
76
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@:
77
78
<pre><code class="python">
79
SESSION_ENGINE = "django.contrib.sessions.backends.file"
80
SESSION_FILE_PATH = "/home/utente/projects/sessions/"
81
</code></pre>
82
83 6 Mark Caglienzi
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@.
84
85 5 Mark Caglienzi
h3. Session backend basato su cookie firmati
86
87
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:
88
89
<pre><code class="python">
90
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
91
</code></pre>
92 6 Mark Caglienzi
93
h3. Session backend basato su cache (con @memcached@)
94
95
Django supporta diversi tipi di cache backend (come memcache, database, file), ma per la gestione della session "è consigliato":https://docs.djangoproject.com/en/dev/topics/http/sessions/#using-cached-sessions l'uso di @memcached@. Prima di tutto quindi bisognerà installare i seguenti pacchetti:
96
97
<pre>
98
# apt-get install memcached python-memcached
99
</pre>
100
101
Il pacchetto debian avvierà @memcached@ in ascolto sulla porta 11211.
102
103
Si configuri il framework cache di django modificando, al solito, il file @settings.py@:
104
105
<pre><code class="python">
106
CACHES = {
107
    'default': {
108
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
109
        'LOCATION': '127.0.0.1:11211',
110
    }
111
}
112
</code></pre>
113
114
In @LOCATION@ andranno messi l'IP e la porta dove risponde il demone @memcached@. Come scritto nella "documentazione ufficiale":https://code.google.com/p/memcached/wiki/NewConfiguringServer#Networking è 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@:
115
116
<pre>
117
-l <ip_addr>
118
              Listen on <ip_addr>; default to INDRR_ANY. This  is  an  important
119
              option  to  consider  as  <b>there  is  no  other  way</b>  to secure the
120
              installation.  Binding  to  an  internal  or  firewalled   network
121
              interface is suggested.
122
</pre>
123 1 Mark Caglienzi
124 2 Mark Caglienzi
h2. Test
125 1 Mark Caglienzi
126
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.
127 2 Mark Caglienzi
128
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":http://nginx.org/en/docs/http/ngx_http_upstream_module.html), ad esempio @weight@:
129
<pre>
130
upstream backend {
131
    server 127.0.0.1:8000;
132
    server 127.0.0.1:8001;
133
    server 127.0.0.1:8002 weight=3;
134
    server 127.0.0.1:8003;
135
}
136
</pre>
137
138
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@:
139
<pre>
140
upstream backend {
141
    server 127.0.0.1:8000;
142
    server 127.0.0.1:8001 max_fails=2 fail_timeout=20s;
143
    server 127.0.0.1:8002;
144
    server 127.0.0.1:8003;
145
}
146
</pre>
147
148
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@)
149
150
<pre>
151
upstream backend {
152
    server 127.0.0.1:8000 backup;
153
    server 127.0.0.1:8001;
154
    server 127.0.0.1:8002;
155
    server 127.0.0.1:8003;
156
}
157
</pre>
158
159
in questo modo il primo server è considerato di backup, e quindi verrà usato solo quando nessun altro server è disponibile.