Comment configurer uWSGI et Nginx pour servir des applications Python sur Ubuntu 14.04

introduction

Dans ce guide, nous allons configurer une application WSGI simple servie par uWSGI. Nous allons utiliser le serveur Web Nginx en tant que proxy inverse du serveur d'applications afin de fournir une gestion de connexion plus robuste. Nous allons installer et configurer ces composants sur un serveur Ubuntu 14.04.

Définitions et concepts

Clarifier certains termes

Avant d’intervenir, nous devrions aborder une terminologie déroutante associée aux concepts interdépendants que nous traiterons. Ces trois termes distincts qui semblent interchangeables, mais ont en réalité une signification distincte:

  • WSGI: UnPython spec qui définit une interface standard pour la communication entre une application ou un framework et une application / serveur Web. Ceci a été créé afin de simplifier et normaliser la communication entre ces composants pour la cohérence et l'interchangeabilité. Cela définit fondamentalement une interface API qui peut être utilisée sur d'autres protocoles.

  • uWSGI: conteneur de serveur d'applications qui vise à fournir une pile complète pour le développement et le déploiement d'applications et de services Web. Le composant principal est un serveur d'applications capable de gérer des applications de différentes langues. Il communique avec l'application à l'aide des méthodes définies par la spécification WSGI et avec d'autres serveurs Web via divers protocoles. C'est l'élément qui traduit les demandes d'un serveur Web conventionnel dans un format que l'application peut traiter.

  • uwsgi: protocole binaire rapide implémenté par le serveur uWSGI pour communiquer avec un serveur Web plus complet. Ceci est unwire protocol, pas un protocole de transport. C'est le moyen préféré de parler aux serveurs Web qui envoient des requêtes à uWSGI.

Conditions requises pour l'application WSGI

La spécification WSGI définit l'interface entre le serveur Web et les parties d'application de la pile. Dans ce contexte, «serveur Web» fait référence au serveur uWSGI, qui est chargé de la traduction des demandes client vers l'application à l'aide de la spécification WSGI. Cela simplifie la communication et crée des composants à couplage lâche afin que vous puissiez facilement échanger les deux côtés sans trop de peine.

Le serveur Web (uWSGI) doit pouvoir envoyer des demandes à l'application en déclenchant un «appelable» défini. L'appelable est simplement un point d'entrée dans l'application où le serveur Web peut appeler une fonction avec certains paramètres. Les paramètres attendus sont un dictionnaire de variables d'environnement et un appelable fournis par le composant serveur Web (uWSGI).

En réponse, l'application retourne une valeur itérable qui sera utilisée pour générer le corps de la réponse du client. Il appellera également le composant de serveur Web appelable qu'il a reçu en tant que paramètre. Le premier paramètre lors du déclenchement du serveur Web appelable sera le code d'état HTTP et le second sera une liste de n-uplets, chacun définissant un en-tête de réponse et une valeur à renvoyer au client.

Avec le composant «serveur Web» de cette interaction fourni par uWSGI, nous devrons simplement nous assurer que nos applications possèdent les qualités décrites ci-dessus. Nous allons également configurer Nginx pour gérer les demandes des clients et les envoyer par proxy au serveur uWSGI.

Installer les composants

Pour commencer, nous devrons installer les composants nécessaires sur notre serveur Ubuntu 14.04. Nous pouvons principalement le faire en utilisantapt etpip.

Tout d'abord, actualisez votre index de packageapt, puis installez les bibliothèques et les en-têtes de développement Python, le gestionnaire de packages Pythonpip, le serveur Web Nginx et le proxy inverse:

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

Une fois l'installation du package terminée, vous aurez accès au gestionnaire de packages Python depip. Nous pouvons l'utiliser pour installer le packagevirtualenv, que nous utiliserons pour isoler l'environnement Python de notre application de tous les autres qui peuvent exister sur le système:

sudo pip install virtualenv

Une fois cette opération terminée, nous pouvons commencer à créer la structure générale de notre application. Nous allons créer l'environnement virtuel décrit ci-dessus et installer le serveur d'applications uWSGI dans cet environnement.

Configurer un répertoire d'applications et un serveur virtuel

Nous allons commencer par créer un dossier pour notre application. Cela peut contenir un dossier imbriqué contenant le code de l'application réelle dans une application plus complète. Pour nos besoins, ce répertoire contiendra simplement notre environnement virtuel et notre point d’entrée WSGI:

mkdir ~/myapp/

Ensuite, déplacez-vous dans le répertoire afin que nous puissions configurer l'environnement pour notre application:

cd ~/myapp

Créez un environnement virtuel avec la commandevirtualenv. Nous appellerons celamyappenv pour plus de simplicité:

virtualenv myappenv

Un nouvel environnement Python sera mis en place sous un répertoire appelémyappenv. Nous pouvons activer cet environnement en tapant:

source myappenv/bin/activate

Votre invite devrait changer pour indiquer que vous opérez maintenant dans l'environnement virtuel. Cela ressemblera à ceci:

(myappenv)username@host:~/my_app$

Si vous souhaitez quitter cet environnement à tout moment, vous pouvez simplement taper:

deactivate

Si vous avez désactivé votre environnement, réactivez-le pour continuer avec le guide.

Lorsque cet environnement est actif, tous les packages Python installés seront contenus dans cette hiérarchie de répertoires. Ils n’interféreront pas avec l’environnement Python du système. Dans cet esprit, nous pouvons maintenant installer le serveur uWSGI dans notre environnement en utilisantpip. Le package pour cela s'appelleuwsgi (il s'agit toujours du serveur uWSGI et non du protocoleuwsgi):

pip install uwsgi

Vous pouvez vérifier qu'il est maintenant disponible en tapant:

uwsgi --version

S'il renvoie un numéro de version, le serveur uWSGI est disponible.

Créer une application WSGI

Ensuite, nous allons créer une application WSGI incroyablement simple en utilisant les exigences de la spécification WSGI décrites précédemment. Pour réitérer, le composant d'application que nous devons fournir devrait avoir les propriétés suivantes:

  • Il doit fournir une interface via un appelable (une fonction ou une autre construction de langage pouvant être appelée)

  • L'appelable doit prendre comme paramètres un dictionnaire contenant des paires clé-valeur de type variable d'environnement et un appelable accessible sur le serveur (uWSGI).

  • L’appelable de l’application doit renvoyer un itérable qui produira le corps à envoyer au client.

  • L’application doit appeler le serveur Web appelable avec les en-têtes de statut et de demande HTTP.

Nous écrirons notre application dans un fichier appeléwsgi.py dans notre répertoire d'application:

nano ~/myapp/wsgi.py

À l'intérieur de ce fichier, nous allons créer l'application compatible WSGI la plus simple possible. Comme pour tout code Python, assurez-vous de faire attention à l'indentation:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["

Hello There!

"]

Le code ci-dessus constitue une application WSGI complète. Par défaut, uWSGI recherchera un appelable appeléapplication, c'est pourquoi nous avons appelé notre fonctionapplication. Comme vous pouvez le constater, il faut deux paramètres.

Le premier que nous avons appeléenviron car il s'agira d'un dictionnaire clé-valeur de type variable d'environnement. Le second est appeléstart_response et est le nom que l'application utilisera en interne pour faire référence au serveur Web (uWSGI) appelable qui est envoyé. Ces deux noms de paramètres ont été simplement sélectionnés en raison de leur utilisation dans les exemples de la spécificationPEP 333 qui définit les interactions WSGI.

Notre application doit prendre cette information et faire deux choses. Tout d'abord, il doit appeler l'appelable qu'il a reçu avec un code d'état HTTP et les en-têtes qu'il souhaite renvoyer. Dans ce cas, nous envoyons une réponse «200 OK» et définissons l'en-têteContent-Type surtext/html.

Deuxièmement, il doit être renvoyé avec un itératif à utiliser comme corps de réponse. Ici, nous venons d’utiliser une liste contenant une seule chaîne de code HTML. Les chaînes sont également éditables, mais à l'intérieur d'une liste, uWSGI sera en mesure de traiter la chaîne entière en une seule itération.

Dans un scénario réel, ce fichier serait probablement utilisé comme lien vers le reste du code de votre application. Par exemple, les projets Django incluent un fichierwsgi.py par défaut qui traduit les requêtes du serveur Web (uWSGI) vers l'application (Django). L'interface WSGI simplifiée reste la même quelle que soit la complexité du code de l'application. C'est l'un des points forts de l'interface.

Enregistrez et fermez le fichier lorsque vous avez terminé.

Pour tester le code, nous pouvons démarrer uWSGI. Nous lui dirons d'utiliser HTTP pour le moment et d'écouter sur le port8080. Nous allons lui passer le nom du script (suffixe supprimé):

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

Maintenant, si vous visitez l’adresse IP ou le nom de domaine de votre serveur dans votre navigateur Web suivi de:8080, vous devriez voir le texte d’en-tête de premier niveau que nous avons passé comme corps dans notre fichierwsgi.py:

wsgi application example

Arrêtez le serveur avec CTRL-C lorsque vous avez vérifié que cela fonctionne.

Nous en avons terminé avec la conception de notre application réelle à ce stade. Vous pouvez désactiver notre environnement virtuel si vous le souhaitez:

deactivate

Configurer un fichier de configuration uWSGI

Dans l'exemple ci-dessus, nous avons démarré manuellement le serveur uWSGI et lui avons transmis certains paramètres sur la ligne de commande. Nous pouvons éviter cela en créant un fichier de configuration. Le serveur uWSGI peut lire des configurations dans une variété de formats, mais nous utiliserons le format.ini pour plus de simplicité.

Pour continuer avec la dénomination que nous utilisons jusqu'à présent, nous allons appeler le fichiermyapp.ini et le placer dans notre dossier d'application:

nano ~/myapp/myapp.ini

À l'intérieur, nous devons établir une section appelée[uwsgi]. Cette section est où vivront tous nos éléments de configuration. Nous allons commencer par identifier notre application. Le serveur uWSGI doit savoir où se trouve l’appelable de l’application. Nous pouvons donner le fichier et la fonction dans:

[uwsgi]
module = wsgi:application

Nous voulons marquer le processus initialuwsgi comme un maître, puis générer un certain nombre de processus de travail. Nous allons commencer avec cinq travailleurs:

[uwsgi]
module = wsgi:application

master = true
processes = 5

Nous allons en fait changer le protocole utilisé par uWSGI pour parler avec le monde extérieur. Lorsque nous testions notre application, nous avons spécifié--protocol=http afin de pouvoir la voir à partir d'un navigateur Web. Puisque nous allons configurer Nginx en tant que proxy inverse devant uWSGI, nous pouvons changer cela. Nginx implémente un mécanisme de proxy deuwsgi, qui est un protocole binaire rapide que uWSGI peut utiliser pour parler avec d'autres serveurs. Le protocoleuwsgi est en fait le protocole par défaut de uWSGI, donc simplement en omettant une spécification de protocole, il reviendra àuwsgi.

Puisque nous concevons cette configuration pour une utilisation avec Nginx, nous allons également utiliser un port réseau et utiliser un socket Unix à la place. Ceci est plus sécurisé et plus rapide. Le socket sera créé dans le répertoire en cours si nous utilisons un chemin relatif. Nous l'appelleronsmyapp.sock. Nous changerons les permissions en «664» pour que Nginx puisse y écrire (nous allons démarrer uWSGI avec le groupewww-data que Nginx utilise. Nous ajouterons également l'optionvacuum, qui supprimera le socket à l'arrêt du processus:

[uwsgi]
module = wsgi:application

master = true
processes = 5

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

Nous avons besoin d’une dernière option car nous allons créer un fichier Upstart pour démarrer notre application au démarrage. Upstart et uWSGI ont des idées différentes sur ce que le signal SIGTERM devrait faire pour une application. Pour trier cet écart afin que les processus puissent être traités comme prévu avec Upstart, nous avons juste besoin d'ajouter une option appeléedie-on-term afin que uWSGI tue le processus au lieu de le recharger:

[uwsgi]
module = wsgi:application

master = true
processes = 5

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

die-on-term = true

Enregistrez et fermez le fichier lorsque vous avez terminé. Ce fichier de configuration est maintenant configuré pour être utilisé avec un script Upstart.

Créer un fichier Upstart pour gérer l'application

Nous pouvons lancer une instance uWSGI au démarrage afin que notre application soit toujours disponible. Nous placerons ceci dans le répertoire/etc/init que Upstart vérifie. Nous allons appeler celamyapp.conf:

sudo nano /etc/init/myapp.conf

Premièrement, nous pouvons commencer par décrire le service et choisir les niveaux d’exécution du système où il doit s’exécuter automatiquement. Les niveaux d’utilisation standard sont compris entre 2 et 5. Nous dirons à Upstart d’arrêter le service quand il s’agit d’un niveau d’exécution en dehors de ce groupe (par exemple, lorsque le système redémarre ou en mode mono-utilisateur):

description "uWSGI instance to serve myapp"

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

Ensuite, nous indiquerons à Upstart quel utilisateur et quel groupe exécuter le processus. Nous souhaitons exécuter l'application sous notre propre compte (nous utilisonsdemo dans ce guide, mais vous devez remplacer votre propre utilisateur). Nous voulons définir le groupe sur l'utilisateurwww-data que Nginx utilise cependant. Ceci est nécessaire car le serveur Web doit pouvoir lire et écrire sur le socket que notre fichier.ini créera:

description "uWSGI instance to serve myapp"

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

setuid demo
setgid www-data

Ensuite, nous lancerons les commandes pour lancer uWSGI. Depuis que nous avons installé uWSGI dans un environnement virtuel, nous avons encore du travail à faire. Nous pourrions simplement fournir le chemin d'accès complet à l'exécutable uWSGI, mais au lieu de cela, nous activerons l'environnement virtuel. Cela faciliterait la tâche si nous utilisions des logiciels supplémentaires installés dans l'environnement.

Pour ce faire, nous allons utiliser un blocscript. À l'intérieur, nous allons passer à notre répertoire d'application, activer l'environnement virtuel (nous devons utiliser. dans les scripts au lieu desource), et démarrer l'instance uWSGI pointant sur notre fichier.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

Avec cela, notre script Upstart est terminé. Enregistrez et fermez le fichier lorsque vous avez terminé.

Maintenant, nous pouvons démarrer le service en tapant:

sudo start myapp

Nous pouvons vérifier qu'il a été démarré en tapant:

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

Cela démarrera automatiquement au démarrage. Vous pouvez arrêter le service à tout moment en tapant:

sudo stop myapp

Configurer Nginx sur Proxy sur uWSGI

À ce stade, nous avons une application WSGI et nous avons vérifié qu’UWSGI peut la lire et la servir. Nous avons créé un fichier de configuration et un script Upstart. Notre processus uWSGI écoutera sur une socket et communiquera en utilisant le protocoleuwsgi.

Nous sommes maintenant sur le point de pouvoir configurer Nginx en tant que proxy inverse. Nginx a la possibilité d'effectuer un proxy en utilisant le protocoleuwsgi pour communiquer avec uWSGI. C'est un protocole plus rapide que HTTP et donnera de meilleurs résultats.

La configuration de Nginx que nous allons configurer est extrêmement simple. Créez un nouveau fichier dans le répertoiresites-available dans la hiérarchie de configuration de Nginx. Nous appellerons notre fichiermyapp pour correspondre au nom de l'application que nous utilisons:

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

Dans ce fichier, nous pouvons spécifier le numéro de port et le nom de domaine auquel ce bloc de serveur doit répondre. Dans notre cas, nous utiliserons le port par défaut 80:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Puisque nous souhaitons envoyer toutes les demandes sur ce domaine ou cette adresse IP à notre application WSGI, nous allons créer un bloc d'emplacement unique pour les demandes commençant par/, qui devrait tout correspondre. À l'intérieur, nous utiliserons la directiveinclude pour inclure un certain nombre de paramètres avec des valeurs par défaut raisonnables à partir d'un fichier dans notre répertoire de configuration Nginx. Le fichier les contenant est appeléuwsgi_params. Ensuite, nous passerons le trafic à notre instance uWSGI via le protocoleuwsgi. Nous allons utiliser le socket Unix que nous avons configuré précédemment:

server {
    listen 80;
    server_name server_domain_or_IP;

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

C'est tout ce dont nous avons besoin pour une application simple. Certaines améliorations pourraient être apportées pour une application plus complète. Par exemple, nous pourrions définir un nombre de serveurs uWSGI en amont en dehors de ce bloc, puis les transmettre à celui-ci. Nous pourrions inclure d'autres paramètres uWSGI. Nous pouvons également gérer directement les fichiers statiques de Nginx et transmettre uniquement les requêtes dynamiques à l'instance uWSGI.

Nous n'avons cependant besoin d'aucune de ces fonctionnalités dans notre application à trois lignes afin de pouvoir enregistrer et fermer le fichier.

Activez la configuration du serveur que nous venons de faire en la liant au répertoiresites-enabled:

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

Recherchez des erreurs de syntaxe dans le fichier de configuration:

sudo service nginx configtest

S'il signale qu'aucun problème n'a été détecté, redémarrez le serveur pour appliquer vos modifications:

sudo service nginx restart

Une fois que Nginx a redémarré, vous devriez pouvoir accéder au nom de domaine ou à l'adresse IP de votre serveur (sans numéro de port) et voir l'application que vous avez configurée:

full WSGI app

Conclusion

Si vous avez réussi jusque-là, vous avez créé une application WSGI simple et avez une idée de la manière dont des applications plus complexes devraient être conçues. Nous avons installé le conteneur / serveur d'applications uWSGI dans un environnement virtuel spécialement conçu pour servir notre application. Nous avons créé un fichier de configuration et un script Upstart pour automatiser ce processus. En face du serveur uWSGI, nous avons configuré un proxy inverse Nginx qui peut parler au processus uWSGI à l'aide du protocole filaireuwsgi.

Vous pouvez facilement voir comment cela peut être étendu lors de la configuration d'un environnement de production réel. Par exemple, uWSGI a la capacité de gérer plusieurs applications en utilisant ce que l’on appelle le «mode empereur». Vous pouvez développer la configuration de Nginx pour équilibrer la charge entre les instances uWSGI ou pour gérer les fichiers statiques de votre application. Lorsque vous utilisez plusieurs applications, il peut être dans votre intérêt d'installer uWSGI globalement plutôt que dans un environnement virtuel, en fonction de vos besoins. Les composants sont tous assez flexibles, vous devriez donc pouvoir ajuster leur configuration pour s'adapter à de nombreux scénarios différents.