Introduzione
In questa guida, configureremo un’applicazione WSGI semplice servita da uWSGI. Utilizzeremo il server web Nginx come proxy inverso per il server dell’applicazione al fine di fornire una gestione delle connessioni più robusta. Installeremo e configureremo questi componenti su un server Ubuntu 14.04.
Definizioni e Concetti
Chiarezza su Alcuni Termini
Prima di iniziare, dovremmo affrontare alcuni termini confusi associati ai concetti interrelati con cui ci occuperemo. Questi tre termini separati sembrano interscambiabili, ma in realtà hanno significati distinti:
- WSGI: Uno standard di Python che definisce un’interfaccia standard per la comunicazione tra un’applicazione o framework e un server applicazioni/web. È stato creato per semplificare e standardizzare la comunicazione tra questi componenti per coerenza e interscambiabilità. Fondamentalmente definisce un’interfaccia API che può essere utilizzata su altri protocolli.
: Un contenitore del server delle applicazioni che mira a fornire uno stack completo per lo sviluppo e il rilascio di applicazioni e servizi web. Il componente principale è un server delle applicazioni in grado di gestire app di diverse lingue. Comunica con l’applicazione utilizzando i metodi definiti dalla specifica WSGI e con altri server web tramite una varietà di altri protocolli. Questo è il pezzo che traduce le richieste da un server web convenzionale in un formato che l’applicazione può elaborare. - uwsgi: Un protocollo binario veloce implementato dal server uWSGI per comunicare con un server web più completo. Questo è un protocollo di trasmissione, non un protocollo di trasporto. È il modo preferito per parlare con i server web che inoltrano le richieste a uWSGI.
Requisiti dell’applicazione WSGI
La specifica WSGI definisce l’interfaccia tra il server web e le porzioni dell’applicazione dello stack. In questo contesto, “server web” si riferisce al server uWSGI, che è responsabile della traduzione delle richieste dei client all’applicazione utilizzando la specifica WSGI. Questo semplifica la comunicazione e crea componenti debolmente accoppiati in modo che tu possa facilmente sostituire uno dei due lati senza troppi problemi.
Il server web (uWSGI) deve avere la capacità di inviare richieste all’applicazione attivando una “chiamata” definita. La chiamata è semplicemente un punto di ingresso nell’applicazione in cui il server web può chiamare una funzione con alcuni parametri. I parametri attesi sono un dizionario di variabili ambientali e una chiamata fornita dal componente del server web (uWSGI).
In risposta, l’applicazione restituisce un iterabile che verrà utilizzato per generare il corpo della risposta del client. Chiamerà anche la chiamata del componente del server web che ha ricevuto come parametro. Il primo parametro quando si attiva la chiamata del server web sarà il codice di stato HTTP e il secondo sarà un elenco di tuple, ciascuna delle quali definisce un’intestazione di risposta e valore da inviare al client.
Con il componente “server web” di questa interazione fornito da uWSGI in questa occasione, dobbiamo solo assicurarci che le nostre applicazioni abbiano le qualità descritte sopra. Configureremo inoltre Nginx per gestire le richieste dei client effettivi e periferizzarle al server uWSGI.
Installare i componenti
Per iniziare, dovremo installare i componenti necessari sul nostro server Ubuntu 14.04. Possiamo principalmente farlo utilizzando apt
e pip
.
Per prima cosa, aggiorna il tuo indice apt
e quindi installa i pacchetti di sviluppo Python e i headers, il gestore di pacchetti Python pip
e il server web e reverse proxy Nginx:
sudo apt-get update
sudo apt-get install python-dev python-pip nginx
Una volta completata l’installazione del pacchetto, avrai accesso al gestore di pacchetti Python pip
. Possiamo utilizzarlo per installare il pacchetto virtualenv
, che useremo per isolare l’ambiente Python della nostra applicazione da qualsiasi altro che potrebbe esistere nel sistema:
sudo pip install virtualenv
Una volta completato questo passaggio, possiamo iniziare a creare la struttura generale per la nostra applicazione. Creeremo l’ambiente virtuale discusso in precedenza e installeremo il server dell’applicazione uWSGI all’interno di questo ambiente.
Configura una Directory dell’App e un Virtualenv
Inizieremo creando una cartella per la nostra app. Questa può contenere una cartella nidificata contenente il codice effettivo dell’applicazione in un’applicazione più completa. Per i nostri scopi, questa directory conterrà semplicemente il nostro ambiente virtuale e il nostro punto di ingresso WSGI:
mkdir ~/myapp/
Successivamente, spostati nella directory in modo da poter configurare l’ambiente per la nostra applicazione:
cd ~/myapp
Crea un ambiente virtuale con il comando virtualenv
. Lo chiameremo myappenv
per semplicità:
virtualenv myappenv
A new Python environment will be set up under a directory called myappenv
. We can activate this environment by typing:
source myappenv/bin/activate
Il tuo prompt dovrebbe cambiare per indicare che stai operando all’interno dell’ambiente virtuale. Apparirà qualcosa del genere:
(myappenv)username@host:~/my_app$
Se desideri uscire da questo ambiente in qualsiasi momento, puoi semplicemente digitare:
deactivate
Se hai disattivato il tuo ambiente, riattivalo di nuovo per continuare con la guida.
Con questo ambiente attivo, tutti i pacchetti Python installati saranno contenuti all’interno di questa gerarchia di directory. Non interferiranno con l’ambiente Python del sistema. Con questo in mente, possiamo ora installare il server uWSGI nel nostro ambiente usando pip
. Il pacchetto per questo si chiama uwsgi
(questo è ancora il server uWSGI e non il protocollo uwsgi
):
pip install uwsgi
Puoi verificare che ora sia disponibile digitando:
uwsgi --version
Se restituisce un numero di versione, il server uWSGI è disponibile per l’uso.
Crea un’applicazione WSGI
Successivamente, creeremo un’applicazione WSGI incredibilmente semplice utilizzando i requisiti di specifica WSGI che abbiamo discusso in precedenza. Per ribadire, il componente dell’applicazione che dobbiamo fornire dovrebbe avere le seguenti proprietà:
- Deve fornire un’interfaccia attraverso una chiamabile (una funzione o un altro costrutto di linguaggio che può essere chiamato)
- La chiamabile deve prendere come parametri un dizionario contenente coppie chiave-valore simili a variabili ambientali e una chiamabile che è accessibile sul server (uWSGI).
- La chiamabile dell’applicazione dovrebbe restituire un iterabile che produrrà il corpo da inviare al client.
- L’applicazione dovrebbe chiamare la chiamabile del server web con lo stato HTTP e gli header della richiesta.
Scriveremo la nostra applicazione in un file chiamato wsgi.py
nella nostra directory dell’applicazione:
nano ~/myapp/wsgi.py
All’interno di questo file, creeremo l’applicazione conforme a WSGI più semplice possibile. Come per tutto il codice Python, assicurati di prestare attenzione all’indentazione:
Il codice sopra costituisce un’applicazione WSGI completa. Per impostazione predefinita, uWSGI cercherà una funzione chiamata application
, motivo per cui abbiamo chiamato la nostra funzione application
. Come puoi vedere, accetta due parametri.
Il primo l’abbiamo chiamato environ
perché sarà un dizionario chiave-valore simile a una variabile ambientale. Il secondo è chiamato start_response
ed è il nome che l’app utilizzerà internamente per fare riferimento alla chiamata del server Web (uWSGI) che viene inviata. Entrambi i nomi dei parametri sono stati selezionati semplicemente a causa del loro utilizzo negli esempi nella specifica PEP 333 che definisce le interazioni WSGI.
La nostra applicazione deve prendere queste informazioni e fare due cose. Innanzitutto, deve chiamare la chiamata ricevuta con un codice di stato HTTP e eventuali intestazioni che vuole inviare. In questo caso, stiamo inviando una risposta “200 OK” e impostando l’intestazione Content-Type
su text/html
.
In secondo luogo, deve ritornare con un iterabile da utilizzare come corpo della risposta. Qui abbiamo usato semplicemente una lista contenente una singola stringa HTML. Anche le stringhe sono iterabili, ma all’interno di una lista, uWSGI sarà in grado di elaborare l’intera stringa con un’iterazione.
In uno scenario del mondo reale, questo file verosimilmente sarebbe utilizzato come collegamento al resto del codice dell’applicazione. Ad esempio, i progetti Django includono un file wsgi.py
di default che traduce le richieste dal server web (uWSGI) all’applicazione (Django). L’interfaccia semplificata WSGI rimane la stessa indipendentemente da quanto complesso sia il codice dell’applicazione effettiva. Questa è una delle forze dell’interfaccia.
Salva e chiudi il file quando hai finito.
Per testare il codice, possiamo avviare uWSGI. Gli diremo di utilizzare HTTP per il momento e di ascoltare sulla porta 8080
. Gli passeremo il nome dello script (suffix rimosso):
uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi
Ora, se visiti l’indirizzo IP del tuo server o il nome di dominio nel tuo browser web seguito da :8080
, dovresti vedere il testo dell’intestazione di primo livello che abbiamo passato come corpo nel nostro file wsgi.py
:
Arresta il server con CTRL-C quando hai verificato che funziona.
Abbiamo finito di progettare la nostra applicazione effettiva a questo punto. Puoi disattivare il nostro ambiente virtuale se desideri:
deactivate
Configura un File di Configurazione uWSGI
Nell’esempio precedente, abbiamo avviato manualmente il server uWSGI e gli abbiamo passato alcuni parametri sulla riga di comando. Possiamo evitarlo creando un file di configurazione. Il server uWSGI può leggere configurazioni in una varietà di formati, ma useremo il formato .ini
per semplicità.
Per continuare con la denominazione che abbiamo utilizzato finora, chiameremo il file myapp.ini
e lo collocheremo nella nostra cartella dell’applicazione:
nano ~/myapp/myapp.ini
All’interno, dobbiamo stabilire una sezione chiamata [uwsgi]
. Questa sezione è dove risiederanno tutti i nostri elementi di configurazione. Inizieremo identificando la nostra applicazione. Il server uWSGI deve sapere dove si trova la funzione invocabile dell’applicazione. Possiamo indicare il file e la funzione all’interno:
[uwsgi]
module = wsgi:application
Vogliamo contrassegnare il processo uwsgi
iniziale come master e quindi avviare un certo numero di processi worker. Inizieremo con cinque worker:
[uwsgi]
module = wsgi:application
master = true
processes = 5
In realtà cambieremo il protocollo che uWSGI utilizza per comunicare con il mondo esterno. Quando stavamo testando la nostra applicazione, abbiamo specificato --protocol=http
in modo da poterla vedere da un browser web. Poiché configureremo Nginx come proxy inverso di fronte a uWSGI, possiamo cambiarlo. Nginx implementa un meccanismo di proxying uwsgi
, che è un protocollo binario veloce che uWSGI può utilizzare per comunicare con altri server. Il protocollo uwsgi
è effettivamente il protocollo predefinito di uWSGI, quindi semplicemente omettendo una specifica di protocollo, verrà usato uwsgi
.
Dal momento che stiamo progettando questa configurazione per l’uso con Nginx, passeremo anche dall’uso di una porta di rete a un socket Unix. Questo è più sicuro e veloce. Il socket verrà creato nella directory corrente se utilizziamo un percorso relativo. Lo chiameremo `myapp.sock`. Modificheremo i permessi in “664” in modo che Nginx possa scriverci (avvieremo uWSGI con il gruppo `www-data` che Nginx utilizza). Aggiungeremo anche l’opzione `vacuum`, che rimuoverà il socket quando il processo si interrompe.
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
Abbiamo bisogno di un’ultima opzione poiché creeremo un file di Upstart per avviare la nostra applicazione all’avvio. Upstart e uWSGI hanno idee diverse su cosa dovrebbe fare il segnale SIGTERM a un’applicazione. Per risolvere questa discrepanza in modo che i processi possano essere gestiti come previsto con Upstart, è sufficiente aggiungere un’opzione chiamata `die-on-term` in modo che uWSGI uccida il processo anziché ricaricarlo.
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
die-on-term = true
Salva e chiudi il file quando hai finito. Questo file di configurazione è ora pronto per essere utilizzato con uno script Upstart.
Crea un file Upstart per gestire l’applicazione
Possiamo avviare un’istanza di uWSGI all’avvio in modo che la nostra applicazione sia sempre disponibile. Metteremo questo nella directory `/etc/init` che Upstart controlla. Lo chiameremo `myapp.conf`:
sudo nano /etc/init/myapp.conf
Prima, possiamo iniziare con una descrizione del servizio e scegliere i livelli di runlevel del sistema in cui dovrebbe essere eseguito automaticamente. I livelli di runlevel utente standard sono da 2 a 5. Diremo a Upstart di fermare il servizio quando è su un qualsiasi runlevel al di fuori di questo gruppo (come quando il sistema si riavvia o è in modalità utente singolo):
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
Successivamente, diremo a Upstart quale utente e gruppo utilizzare per eseguire il processo. Vogliamo eseguire l’applicazione con il nostro account (stiamo usando demo
in questa guida, ma dovresti sostituire il tuo utente). Vogliamo impostare il gruppo sull’utente www-data
che utilizza Nginx. Questo è necessario perché il server web deve essere in grado di leggere e scrivere sul socket che il nostro file .ini
creerà:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
setuid demo
setgid www-data
Successivamente, eseguiremo i comandi effettivi per avviare uWSGI. Poiché abbiamo installato uWSGI in un ambiente virtuale, dobbiamo fare un lavoro extra. Potremmo semplicemente fornire il percorso completo dell’eseguibile uWSGI, ma invece attiveremo l’ambiente virtuale. Questo renderebbe più semplice se fossimo basati su software aggiuntivo installato nell’ambiente.
Per fare ciò, utilizzeremo un blocco script
. All’interno, cambieremo nella nostra directory dell’applicazione, attiveremo l’ambiente virtuale (dobbiamo utilizzare .
negli script invece di source
), e avvieremo l’istanza di uWSGI puntando al nostro file .ini
:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
setuid demo
setgid www-data
script
cd /home/demo/myapp
. myappenv/bin/activate
uwsgi --ini myapp.ini
end script
Con questo, il nostro script Upstart è completo. Salva e chiudi il file quando hai finito.
Ora, possiamo avviare il servizio digitando:
sudo start myapp
Possiamo verificare che sia stato avviato digitando:
ps aux | grep myapp
demo 14618 0.0 0.5 35868 5996 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14619 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14620 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14621 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14622 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14623 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 15520 0.0 0.0 11740 936 pts/0 S+ 15:53 0:00 grep --color=auto myapp
Questo avvierà automaticamente all’avvio. È possibile interrompere il servizio in qualsiasi momento digitando:
sudo stop myapp
Configura Nginx per fare da proxy a uWSGI
A questo punto, abbiamo un’applicazione WSGI e abbiamo verificato che uWSGI possa leggerla e servirla. Abbiamo creato un file di configurazione e uno script Upstart. Il nostro processo uWSGI ascolterà su un socket e comunicherà utilizzando il protocollo uwsgi
.
Siamo ora al punto in cui possiamo lavorare alla configurazione di Nginx come proxy inverso. Nginx ha la capacità di fare da proxy utilizzando il protocollo uwsgi
per comunicare con uWSGI. Questo è un protocollo più veloce rispetto a HTTP e avrà prestazioni migliori.
La configurazione di Nginx che configureremo è estremamente semplice. Crea un nuovo file all’interno della directory sites-available
nell’organizzazione della configurazione di Nginx. Chiameremo il nostro file myapp
per corrispondere al nome dell’applicazione che stiamo utilizzando:
sudo nano /etc/nginx/sites-available/myapp
All’interno di questo file, possiamo specificare il numero di porta e il nome di dominio a cui questo blocco server dovrebbe rispondere. Nel nostro caso, utilizzeremo la porta predefinita 80:
server {
listen 80;
server_name server_domain_or_IP;
}
Poiché desideriamo inviare tutte le richieste su questo dominio o indirizzo IP alla nostra applicazione WSGI, creeremo un singolo blocco di posizione per le richieste che iniziano con /
, il che dovrebbe corrispondere a tutto. All’interno, utilizzeremo la direttiva include
per includere un certo numero di parametri con valori predefiniti ragionevoli da un file nel nostro directory di configurazione Nginx. Il file che contiene questi si chiama uwsgi_params
. Successivamente, passeremo il traffico alla nostra istanza uWSGI tramite il protocollo uwsgi
. Utilizzeremo il socket Unix che abbiamo configurato in precedenza:
server {
listen 80;
server_name server_domain_or_IP;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/demo/myapp/myapp.sock;
}
}
In realtà è tutto ciò di cui abbiamo bisogno per un’applicazione semplice. Ci sono alcuni miglioramenti che potrebbero essere apportati per un’applicazione più completa. Ad esempio, potremmo definire un certo numero di server uWSGI upstream al di fuori di questo blocco e poi passarli a quello. Potremmo includere alcuni parametri uWSGI aggiuntivi. Potremmo anche gestire eventuali file statici direttamente da Nginx e passare solo le richieste dinamiche all’istanza uWSGI.
Non abbiamo bisogno di nessuna di queste funzionalità nella nostra applicazione di tre righe, quindi possiamo salvare e chiudere il file.
Abilita la configurazione del server che abbiamo appena creato collegandola alla directory sites-enabled
:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled
Controlla il file di configurazione per gli errori di sintassi:
sudo service nginx configtest
Se riporta che non sono stati rilevati problemi, riavvia il server per implementare le tue modifiche:
sudo service nginx restart
Una volta riavviato Nginx, dovresti essere in grado di andare al nome del dominio del tuo server o all’indirizzo IP (senza numero di porta) e vedere l’applicazione che hai configurato:
Conclusione
Se sei arrivato fin qui, hai creato un’applicazione WSGI semplice e hai qualche idea su come dovrebbero essere progettate applicazioni più complesse. Abbiamo installato il contenitore/server dell’applicazione uWSGI in un ambiente virtuale appositamente creato per servire la nostra applicazione. Abbiamo creato un file di configurazione e uno script Upstart per automatizzare questo processo. Davanti al server uWSGI, abbiamo configurato un proxy inverso Nginx che può comunicare con il processo uWSGI utilizzando il protocollo wire uwsgi
.
Puoi facilmente vedere come questo può essere ampliato durante la configurazione di un ambiente di produzione effettivo. Ad esempio, uWSGI ha la capacità di gestire più applicazioni utilizzando qualcosa chiamato “modalità imperatore”. Puoi espandere la configurazione di Nginx per il bilanciamento del carico tra le istanze di uWSGI, o per gestire file statici per la tua applicazione. Quando si servono più applicazioni, potrebbe essere nel tuo interesse installare uWSGI globalmente invece che in un ambiente virtuale, a seconda delle tue esigenze. I componenti sono tutti abbastanza flessibili, quindi dovresti essere in grado di regolare la loro configurazione per adattarli a molti scenari diversi.