Come configurare uWSGI e Nginx per servire app Python su Ubuntu 14.04

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:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["<h1 style='color:blue'>Hello There!</h1>"]

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.

Source:
https://www.digitalocean.com/community/tutorials/how-to-set-up-uwsgi-and-nginx-to-serve-python-apps-on-ubuntu-14-04