Hoe uWSGI en Nginx instellen om Python-apps te bedienen op Ubuntu 14.04

Introductie

In deze handleiding zullen we een eenvoudige WSGI-toepassing opzetten die wordt bediend door uWSGI. We zullen de Nginx-webserver gebruiken als een omgekeerde proxy naar de toepassingsserver om robuustere verbindingsafhandeling te bieden. We zullen deze componenten installeren en configureren op een Ubuntu 14.04-server.

Definities en Concepten

Verduidelijking van enkele termen

Voordat we beginnen, moeten we enkele verwarrende terminologieën bespreken die verband houden met de onderling verbonden concepten waarmee we te maken zullen krijgen. Deze drie afzonderlijke termen lijken uitwisselbaar, maar hebben eigenlijk verschillende betekenissen:

  • WSGI: Een Python-specificatie die een standaardinterface definieert voor communicatie tussen een toepassing of framework en een toepassings-/webserver. Dit werd gecreëerd om de communicatie tussen deze componenten te vereenvoudigen en te standaardiseren voor consistentie en uitwisselbaarheid. Dit definieert in feite een API-interface die kan worden gebruikt via andere protocollen.
  • uWSGI: Een toepassingsservercontainer die tot doel heeft een volledige stack te bieden voor het ontwikkelen en implementeren van webapplicaties en -diensten. Het belangrijkste onderdeel is een toepassingsserver die apps van verschillende talen kan verwerken. Het communiceert met de toepassing via de methoden gedefinieerd door de WSGI-specificatie, en met andere webservers via een verscheidenheid aan andere protocollen. Dit is het onderdeel dat verzoeken van een conventionele webserver vertaalt naar een formaat dat de toepassing kan verwerken.
  • uwsgi: Een snel, binair protocol geïmplementeerd door de uWSGI-server om te communiceren met een meer geavanceerde webserver. Dit is een draadprotocol, geen transportprotocol. Het is de voorkeursmanier om te communiceren met webservers die verzoeken doorsturen naar uWSGI.

WSGI-toepassingsvereisten

De WSGI-specificatie definieert de interface tussen het gedeelte van de webserver en de toepassing van de stack. In deze context verwijst “webserver” naar de uWSGI-server, die verantwoordelijk is voor het vertalen van clientverzoeken naar de toepassing met behulp van de WSGI-specificatie. Dit vereenvoudigt de communicatie en creëert losjes gekoppelde componenten, zodat u gemakkelijk beide kanten kunt vervangen zonder al te veel moeite.

De webserver (uWSGI) moet in staat zijn om verzoeken naar de toepassing te sturen door een gedefinieerde “callable” te activeren. De callable is eenvoudigweg een ingangspunt in de toepassing waar de webserver een functie kan aanroepen met enkele parameters. De verwachte parameters zijn een woordenboek van omgevingsvariabelen en een callable die wordt geleverd door het webserver (uWSGI) component.

Als reactie hierop retourneert de toepassing een iterable die zal worden gebruikt om de body van de clientreactie te genereren. Het zal ook de webservercomponent callable aanroepen die het als parameter heeft ontvangen. Het eerste parameter bij het activeren van de webserver callable zal de HTTP-statuscode zijn en de tweede zal een lijst zijn van tuples, waarin elk een responsheader en -waarde definieert om terug te sturen naar de client.

Met het “webserver” component van deze interactie geleverd door uWSGI in dit geval, hoeven we alleen maar ervoor te zorgen dat onze toepassingen de hierboven beschreven eigenschappen hebben. We zullen ook Nginx instellen om daadwerkelijke clientverzoeken af te handelen en deze door te sturen naar de uWSGI-server.

Installeer de componenten

Om te beginnen moeten we de nodige componenten installeren op onze Ubuntu 14.04-server. We kunnen dit voornamelijk doen met behulp van apt en pip.

Vernieuw eerst uw apt-pakketindex en installeer vervolgens de Python-ontwikkelingsbibliotheken en -headers, de pip-Python-pakketbeheerder, en de Nginx-webserver en omgekeerde proxy:

sudo apt-get update
sudo apt-get install python-dev python-pip nginx

Zodra de pakketinstallatie is voltooid, heeft u toegang tot de pip Python-pakketbeheerder. We kunnen dit gebruiken om het virtualenv-pakket te installeren, dat we zullen gebruiken om de Python-omgeving van onze toepassing te isoleren van andere die mogelijk op het systeem aanwezig zijn:

sudo pip install virtualenv

Zodra dit is voltooid, kunnen we beginnen met het maken van de algemene structuur voor onze toepassing. We zullen de bovenstaande virtuele omgeving maken en de uWSGI-toepassingsserver binnen deze omgeving installeren.

Stel een App-map en een Virtualenv in

We beginnen met het maken van een map voor onze app. Deze kan een geneste map bevatten met de werkelijke toepassingscode in een meer complete toepassing. Voor onze doeleinden zal deze map eenvoudigweg onze virtuele omgeving en ons WSGI-ingangspunt bevatten:

mkdir ~/myapp/

Ga vervolgens naar de map zodat we de omgeving voor onze toepassing kunnen instellen:

cd ~/myapp

Maak een virtuele omgeving aan met het virtualenv-commando. We zullen dit myappenv voor eenvoud noemen:

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

Uw prompt zou moeten veranderen om aan te geven dat u nu binnen de virtuele omgeving werkt. Het zal er ongeveer zo uitzien:

(myappenv)username@host:~/my_app$

Als u op elk moment deze omgeving wilt verlaten, typt u eenvoudigweg:

deactivate

Als u uw omgeving heeft gedeactiveerd, activeert u deze opnieuw om door te gaan met de handleiding.

Met deze omgeving actief worden alle geïnstalleerde Python-pakketten binnen deze directory-hiërarchie geplaatst. Ze zullen niet interfereren met de Python-omgeving van het systeem. Met dit in gedachten kunnen we nu de uWSGI-server installeren in onze omgeving met behulp van pip. Het pakket hiervoor heet uwsgi (dit is nog steeds de uWSGI-server en niet het uwsgi-protocol):

pip install uwsgi

U kunt controleren of het nu beschikbaar is door te typen:

uwsgi --version

Als het een versienummer retourneert, is de uWSGI-server beschikbaar voor gebruik.

Maak een WSGI-toepassing

Vervolgens zullen we een ongelooflijk eenvoudige WSGI-toepassing maken met behulp van de WSGI-specificatievereisten die we eerder hebben besproken. Om te herhalen, het toepassingscomponent dat we moeten leveren, moet de volgende eigenschappen hebben:

  • Het moet een interface bieden via een oproepbaar (een functie of andere taalconstructie die kan worden aangeroepen)
  • De oproepbare moet als parameters een woordenboek bevatten met omgevingsvariabele-achtige sleutel-waardeparen en een oproepbaar dat toegankelijk is op de server (uWSGI).
  • De oproepbare van de toepassing moet een iterabel retourneren dat de inhoud produceert om naar de client te sturen.
  • De toepassing moet het oproepbare van de webserver aanroepen met de HTTP-status en de verzoekheaders.

We zullen onze toepassing schrijven in een bestand genaamd wsgi.py in onze toepassingsdirectory:

nano ~/myapp/wsgi.py

Binnen dit bestand zullen we de eenvoudigste WSGI-compatibele applicatie creëren die we kunnen. Zoals bij alle Python-code, let goed op de inspringing:

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

De bovenstaande code vormt een volledige WSGI-applicatie. Standaard zal uWSGI op zoek gaan naar een aanroepbare genaamd application, daarom hebben we onze functie application genoemd. Zoals je kunt zien, neemt deze twee parameters.

De eerste noemen we environ omdat het een omgevingsvariabele-achtige key-value-dictionary zal zijn. De tweede wordt start_response genoemd en is de naam die de app intern zal gebruiken om te verwijzen naar de webserver (uWSGI) die wordt meegezonden. Beide parameter namen zijn eenvoudig gekozen vanwege hun gebruik in de voorbeelden in de PEP 333 specificatie die WSGI-interacties definieert.

Onze applicatie moet deze informatie gebruiken en twee dingen doen. Ten eerste moet het de ontvangen aanroepbare aanroepen met een HTTP-statuscode en eventuele headers die het terug wil sturen. In dit geval sturen we een “200 OK” reactie en zetten we de Content-Type header op text/html.

Ten tweede moet het terugkeren met een iteratie om te gebruiken als het antwoordlichaam. Hier hebben we gewoon een lijst gebruikt die een enkele string van HTML bevat. Strings zijn ook iterabel, maar binnen een lijst zal uWSGI de hele string kunnen verwerken met één iteratie.

In een echt scenario zou dit bestand waarschijnlijk worden gebruikt als een link naar de rest van de applicatiecode. Bijvoorbeeld, Django-projecten bevatten standaard een wsgi.py bestand dat verzoeken van de webserver (uWSGI) naar de applicatie (Django) vertaalt. De vereenvoudigde WSGI-interface blijft hetzelfde, ongeacht hoe complex de daadwerkelijke applicatiecode is. Dit is een van de sterke punten van de interface.

Sla het bestand op en sluit het af wanneer je klaar bent.

Om de code uit te testen, kunnen we uWSGI starten. We vertellen het om voorlopig HTTP te gebruiken en te luisteren op poort 8080. We geven het de naam van het script door (zonder suffix):

uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi

Nu, als je het IP-adres of domeinnaam van je server in je webbrowser bezoekt, gevolgd door :8080, zou je de tekst van de eerste niveauskop moeten zien die we als inhoud hebben doorgegeven in ons wsgi.py bestand:

Stop de server met CTRL-C wanneer je hebt geverifieerd dat dit werkt.

We zijn klaar met het ontwerpen van onze daadwerkelijke applicatie op dit moment. Je kunt onze virtuele omgeving deactiveren indien gewenst:

deactivate

Configureer een uWSGI-configuratiebestand

In het bovenstaande voorbeeld zijn we handmatig de uWSGI-server gestart en hebben we het wat parameters meegegeven via de opdrachtregel. Dit kunnen we vermijden door een configuratiebestand te maken. De uWSGI-server kan configuraties lezen in verschillende formaten, maar we zullen het .ini-formaat gebruiken voor eenvoud.

Om door te gaan met de benaming die we tot nu toe hebben gebruikt, zullen we het bestand myapp.ini noemen en het plaatsen in onze applicatiemap:

nano ~/myapp/myapp.ini

Binnenin moeten we een sectie genaamd [uwsgi] instellen. In deze sectie zullen al onze configuratie-items leven. We beginnen met het identificeren van onze applicatie. De uWSGI-server moet weten waar de aanroepbare functie van de applicatie zich bevindt. We kunnen het bestand en de functie aangeven:

[uwsgi]
module = wsgi:application

We willen het initiële uwsgi-proces markeren als een master en vervolgens een aantal worker-processen spawnen. We beginnen met vijf workers:

[uwsgi]
module = wsgi:application

master = true
processes = 5

We gaan eigenlijk het protocol wijzigen dat uWSGI gebruikt om te communiceren met de buitenwereld. Toen we onze applicatie aan het testen waren, hebben we --protocol=http gespecificeerd zodat we het konden zien vanuit een webbrowser. Aangezien we Nginx gaan configureren als een reverse proxy voor uWSGI, kunnen we dit wijzigen. Nginx implementeert een uwsgi-proxy mechanisme, wat een snel binair protocol is dat uWSGI kan gebruiken om met andere servers te communiceren. Het uwsgi-protocol is eigenlijk het standaardprotocol van uWSGI, dus door simpelweg geen protocol specificatie op te geven, zal het terugvallen op uwsgi.

Aangezien we deze configuratie ontwerpen voor gebruik met Nginx, gaan we ook over van het gebruik van een netwerkpoort en gebruiken we in plaats daarvan een Unix-socket. Dit is veiliger en sneller. De socket wordt aangemaakt in de huidige directory als we een relatief pad gebruiken. We zullen het myapp.sock noemen. We zullen de rechten wijzigen naar “664” zodat Nginx ernaar kan schrijven (we zullen uWSGI starten met de www-data-groep die Nginx gebruikt). We zullen ook de vacuum-optie toevoegen, die de socket zal verwijderen wanneer het proces stopt:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

We hebben nog één laatste optie nodig omdat we een Upstart-bestand zullen maken om onze toepassing bij het opstarten te starten. Upstart en uWSGI hebben verschillende ideeën over wat het SIGTERM-signaal met een toepassing moet doen. Om dit verschil op te lossen zodat de processen zoals verwacht kunnen worden afgehandeld met Upstart, hoeven we alleen maar een optie toe te voegen genaamd die-on-term, zodat uWSGI het proces zal doden in plaats van het opnieuw te laden:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

Sla het bestand op en sluit het af wanneer u klaar bent. Dit configuratiebestand is nu ingesteld om te worden gebruikt met een Upstart-script.

Maak een Upstart-bestand om de app te beheren

We kunnen een uWSGI-instantie starten bij het opstarten zodat onze toepassing altijd beschikbaar is. We zullen dit plaatsen in de /etc/init-directory die Upstart controleert. We noemen dit myapp.conf:

sudo nano /etc/init/myapp.conf

Eerst kunnen we beginnen met een beschrijving van de service en de systeem runlevels selecteren waarin deze automatisch moet worden uitgevoerd. De standaard gebruikersrunlevels zijn 2 tot en met 5. We zullen Upstart vertellen om de service te stoppen wanneer deze op een runlevel buiten deze groep draait (bijvoorbeeld bij het herstarten van het systeem of in de single-usermodus):

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

Vervolgens zullen we Upstart informeren over welke gebruiker en groep het proces moet uitvoeren. We willen de toepassing uitvoeren onder ons eigen account (we gebruiken demo in deze handleiding, maar je moet je eigen gebruiker vervangen). We willen echter de groep instellen op de www-data-gebruiker die Nginx gebruikt. Dit is noodzakelijk omdat de webserver moet kunnen lezen en schrijven naar de socket die ons .ini-bestand zal aanmaken:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

Vervolgens zullen we de daadwerkelijke commando’s uitvoeren om uWSGI te starten. Omdat we uWSGI hebben geïnstalleerd in een virtuele omgeving, hebben we wat extra werk te doen. We zouden gewoon het volledige pad naar het uWSGI-uitvoerbaar bestand kunnen opgeven, maar in plaats daarvan zullen we de virtuele omgeving activeren. Dit zou het gemakkelijker maken als we vertrouwen op extra software die is geïnstalleerd in de omgeving.

Hiervoor zullen we een script-blok gebruiken. Binnenin zullen we naar onze toepassingsmap gaan, de virtuele omgeving activeren (we moeten in scripts . gebruiken in plaats van source), en de uWSGI-instantie starten die naar ons .ini-bestand wijst:

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

Daarmee is ons Upstart-script voltooid. Sla het bestand op en sluit het af wanneer je klaar bent.

Nu kunnen we de service starten door te typen:

sudo start myapp

We kunnen controleren of het is gestart door te typen:

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

Dit zal automatisch starten bij het opstarten. U kunt de service op elk moment stoppen door het volgende te typen:

sudo stop myapp

Configureer Nginx om te proxyen naar uWSGI

Op dit punt hebben we een WSGI-app en hebben we geverifieerd dat uWSGI deze kan lezen en serveren. We hebben een configuratiebestand en een Upstart-script gemaakt. Ons uWSGI-proces zal luisteren op een socket en communiceren via het uwsgi-protocol.

We zijn nu op het punt gekomen waar we Nginx kunnen configureren als een omgekeerde proxy. Nginx heeft de mogelijkheid om te proxyen met behulp van het uwsgi-protocol om te communiceren met uWSGI. Dit is een sneller protocol dan HTTP en zal beter presteren.

De Nginx-configuratie die we gaan instellen, is uiterst eenvoudig. Maak een nieuw bestand aan in de sites-available-directory binnen de configuratiehiërarchie van Nginx. We zullen ons bestand myapp noemen om overeen te komen met de app-naam die we hebben gebruikt:

sudo nano /etc/nginx/sites-available/myapp

In dit bestand kunnen we het poortnummer en de domeinnaam specificeren waarnaar dit serverblok moet reageren. In ons geval zullen we de standaard poort 80 gebruiken:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Aangezien we alle verzoeken op dit domein of IP-adres naar onze WSGI-toepassing willen sturen, zullen we een enkel locatieblok maken voor verzoeken die beginnen met /, wat alles zou moeten matchen. Binnenin zullen we de include-instructie gebruiken om een aantal parameters met redelijke standaardwaarden op te nemen vanuit een bestand in onze Nginx-configuratiemap. Het bestand dat deze bevat heet uwsgi_params. Daarna zullen we het verkeer doorsturen naar onze uWSGI-instantie via het uwsgi-protocol. We zullen het Unix-socket gebruiken dat we eerder hebben geconfigureerd:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

Dat is eigenlijk alles wat we nodig hebben voor een eenvoudige toepassing. Er zijn enkele verbeteringen mogelijk voor een meer complete toepassing. Zo zouden we bijvoorbeeld een aantal upstream uWSGI-servers buiten dit blok kunnen definiëren en deze vervolgens doorgeven. We zouden wat meer uWSGI-parameters kunnen opnemen. We kunnen ook eventuele statische bestanden rechtstreeks vanuit Nginx behandelen en alleen dynamische verzoeken doorgeven aan de uWSGI-instantie.

We hebben echter geen van die functies nodig in onze toepassing van drie regels, dus we kunnen het bestand opslaan en sluiten.

Activeer de serverconfiguratie die we zojuist hebben gemaakt door deze te koppelen aan de sites-enabled-map:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Controleer het configuratiebestand op syntaxisfouten:

sudo service nginx configtest

Als het meldt dat er geen problemen zijn gedetecteerd, herstart dan de server om uw wijzigingen door te voeren:

sudo service nginx restart

Zodra Nginx opnieuw is opgestart, zou u naar het domein of IP-adres van uw server moeten kunnen gaan (zonder poortnummer) en de geconfigureerde toepassing moeten zien:

Conclusie

Als je het tot zover hebt gemaakt, heb je een eenvoudige WSGI-toepassing gemaakt en heb je inzicht in hoe complexere toepassingen ontworpen zouden moeten worden. We hebben de uWSGI-toepassingscontainer/-server geïnstalleerd in een speciaal gemaakte virtuele omgeving om onze toepassing te bedienen. We hebben een configuratiebestand en een Upstart-script gemaakt om dit proces te automatiseren. Voor de uWSGI-server hebben we een Nginx-reverse proxy opgezet die met het uWSGI-proces kan communiceren via het uwsgi-protocol.

Je kunt gemakkelijk zien hoe dit kan worden uitgebreid bij het opzetten van een daadwerkelijke productieomgeving. Zo heeft uWSGI de mogelijkheid om meerdere toepassingen te beheren met iets dat “emperor mode” wordt genoemd. Je kunt de Nginx-configuratie uitbreiden om de belasting te verdelen tussen uWSGI-instanties, of om statische bestanden voor je toepassing te verwerken. Bij het bedienen van meerdere toepassingen is het misschien in uw beste belang om uWSGI wereldwijd te installeren in plaats van in een virtuele omgeving, afhankelijk van uw behoeften. De componenten zijn allemaal redelijk flexibel, dus je zou hun configuratie moeten kunnen aanpassen om veel verschillende scenario’s te accommoderen.

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