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

De Get Docs
Aller à :navigation, rechercher

Introduction

Dans ce guide, nous allons configurer une application WSGI simple servie par uWSGI. Nous utiliserons le serveur Web Nginx comme proxy inverse vers le serveur d'applications pour fournir une gestion de connexion plus robuste. Nous allons installer et configurer ces composants sur un serveur Ubuntu 14.04.

Définitions et concepts

Clarification de certains termes

Avant de nous lancer, nous devrions aborder une terminologie confuse associée aux concepts interdépendants dont nous allons traiter. Ces trois termes distincts qui semblent interchangeables, mais ont en réalité des significations distinctes :

  • WSGI : une spécification Python qui définit une interface standard pour la communication entre une application ou un framework et une application/un serveur Web. Cela a été créé afin de simplifier et de standardiser la communication entre ces composants pour la cohérence et l'interchangeabilité. Cela définit essentiellement une interface API qui peut être utilisée sur d'autres protocoles.
  • uWSGI : un 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 qui peut gérer des applications de différentes langues. Il communique avec l'application en utilisant les méthodes définies par la spécification WSGI et avec d'autres serveurs Web via une variété d'autres protocoles. C'est la pièce qui traduit les requêtes d'un serveur Web conventionnel dans un format que l'application peut traiter.
  • uwsgi : protocole binaire rapide mis en œuvre par le serveur uWSGI pour communiquer avec un serveur Web plus complet. Il s'agit d'un wire protocol, pas d'un protocole de transport. C'est le moyen préféré de parler aux serveurs Web qui envoient des requêtes par proxy à uWSGI.

Exigences de candidature WSGI

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

Le serveur Web (uWSGI) doit avoir la capacité d'envoyer des requêtes à 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 callable fourni par le composant du serveur web (uWSGI).

En réponse, l'application renvoie un itérable qui sera utilisé 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 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 tuples, 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 dans ce cas, nous n'aurons qu'à nous assurer que nos applications ont les qualités décrites ci-dessus. Nous allons également configurer Nginx pour gérer les demandes réelles 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 utilisant apt et pip.

Tout d'abord, actualisez votre index de package apt, puis installez les bibliothèques et les en-têtes de développement Python, le gestionnaire de packages Python pip, ainsi que 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 pip. Nous pouvons l'utiliser pour installer le package virtualenv, que nous utiliserons pour isoler l'environnement Python de notre application de tout autre pouvant exister sur le système :

sudo pip install virtualenv

Une fois ceci terminé, 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 App Directory et un Virtualenv

Nous allons commencer par créer un dossier pour notre application. Cela peut contenir un dossier imbriqué contenant le code d'application réel 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 de notre application :

cd ~/myapp

Créez un environnement virtuel avec la commande virtualenv. Nous l'appellerons myappenv pour plus de simplicité :

virtualenv myappenv

Un nouvel environnement Python sera configuré dans un répertoire appelé myappenv. On peut activer cet environnement en tapant :

source myappenv/bin/activate

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

(myappenv)[email protected]:~/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 à nouveau pour continuer avec le guide.

Avec cet environnement 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 utilisant pip. Le package correspondant s'appelle uwsgi (il s'agit toujours du serveur uWSGI et non du protocole uwsgi) :

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 spécification WSGI dont nous avons parlé précédemment. Pour rappel, le composant applicatif que nous devons fournir doit 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 l'appelable du serveur Web avec le statut HTTP et les en-têtes de requête.

Nous allons écrire 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 conforme 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 ["<h1 style='color:blue'>Hello There!</h1>"]

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 fonction application. Comme vous pouvez le voir, il faut deux paramètres.

Le premier que nous avons appelé environ car il s'agira d'un dictionnaire clé-valeur de type variable environnementale. Le second s'appelle 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 simplement été sélectionnés en raison de leur utilisation dans les exemples de la spécification PEP 333 qui définit les interactions WSGI.

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

Deuxièmement, il doit revenir avec un itérable à 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 itérables, mais à l'intérieur d'une liste, uWSGI pourra 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 par défaut un fichier wsgi.py 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 d'application réel. 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 port 8080. On lui passera 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 transmis comme corps dans notre fichier wsgi.py :

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

Nous en avons fini 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 les 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 avons utilisée jusqu'à présent, nous allons appeler le fichier myapp.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 l'endroit où tous nos éléments de configuration vivront. 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 initial uwsgi comme 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 que nous puissions le 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 uwsgi, qui est un protocole binaire rapide que uWSGI peut utiliser pour communiquer avec d'autres serveurs. Le protocole uwsgi est en fait le protocole par défaut d'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 passer d'un port réseau à un socket Unix. C'est plus sûr et plus rapide. La socket sera créée dans le répertoire courant si nous utilisons un chemin relatif. Nous l'appellerons myapp.sock. Nous allons changer les autorisations en "664" afin que Nginx puisse y écrire (nous allons démarrer uWSGI avec le groupe www-data utilisé par Nginx. Nous ajouterons également l'option vacuum, qui supprimera le socket lorsque le processus s'arrêtera :

[uwsgi]
module = wsgi:application

master = true
processes = 5

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

Nous avons besoin d'une dernière option puisque 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 régler cet écart afin que les processus puissent être gérés comme prévu avec Upstart, nous avons juste besoin d'ajouter une option appelée die-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 le placerons dans le répertoire /etc/init que Upstart vérifie. Nous appellerons cela myapp.conf :

sudo nano /etc/init/myapp.conf

Tout d'abord, nous pouvons commencer par une description du service et sélectionner les niveaux d'exécution du système où il doit s'exécuter automatiquement. Les niveaux d'exécution utilisateur standard vont de 2 à 5. Nous dirons à Upstart d'arrêter le service lorsqu'il se trouve à n'importe quel 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, indiquera à Upstart l'utilisateur et le groupe sous lesquels exécuter le processus. Nous voulons exécuter l'application sous notre propre compte (nous utilisons demo dans ce guide, mais vous devez remplacer votre propre utilisateur). Nous voulons définir le groupe sur l'utilisateur www-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 va créer :

description "uWSGI instance to serve myapp"

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

setuid demo
setgid www-data

Ensuite, nous exécuterons les commandes réelles pour démarrer uWSGI. Depuis que nous avons installé uWSGI dans un environnement virtuel, nous avons du travail supplémentaire à faire. Nous pourrions simplement fournir le chemin complet vers l'exécutable uWSGI, mais à la place, nous activerons l'environnement virtuel. Cela faciliterait la tâche si nous comptions sur des logiciels supplémentaires installés dans l'environnement.

Pour ce faire, nous allons utiliser un bloc script. À l'intérieur, nous allons passer à notre répertoire d'application, activer l'environnement virtuel (nous devons utiliser . dans les scripts au lieu de source), et démarrer l'instance uWSGI pointant vers notre [X190X ] dossier:

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 bien été lancé 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 pour proxy vers uWSGI

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

Nous sommes maintenant au point où nous pouvons travailler sur la configuration de Nginx en tant que proxy inverse. Nginx a la capacité de proxy en utilisant le protocole uwsgi pour communiquer avec uWSGI. C'est un protocole plus rapide que HTTP et il fonctionnera mieux.

La configuration Nginx que nous allons mettre en place est extrêmement simple. Créez un nouveau fichier dans le répertoire sites-available dans la hiérarchie de configuration de Nginx. Nous appellerons notre fichier myapp pour qu'il corresponde au nom de l'application que nous avons utilisé :

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

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

server {
    listen 80;
    server_name server_domain_or_IP;
}

Étant donné que nous souhaitons envoyer toutes les requêtes sur ce domaine ou cette adresse IP à notre application WSGI, nous allons créer un bloc de localisation unique pour les requêtes commençant par /, qui doit correspondre à tout. À l'intérieur, nous utiliserons la directive include 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 qui les contient s'appelle uwsgi_params. Ensuite, nous transmettrons le trafic à notre instance uWSGI via le protocole uwsgi. 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 en fait 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 certain 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 tous les fichiers statiques de Nginx et transmettre uniquement les requêtes dynamiques à l'instance uWSGI.

Cependant, nous n'avons besoin d'aucune de ces fonctionnalités dans notre application à trois lignes, nous pouvons donc enregistrer et fermer le fichier.

Activez la configuration du serveur que nous venons de créer en la liant au répertoire sites-enabled :

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

Vérifiez le fichier de configuration pour les erreurs de syntaxe :

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 Nginx 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 :

Conclusion

Si vous êtes arrivé jusqu'ici, 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'application 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. Devant le serveur uWSGI, nous avons configuré un proxy inverse Nginx qui peut communiquer avec le processus uWSGI à l'aide du protocole filaire uwsgi.

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 qu'on appelle le "mode empereur". Vous pouvez étendre la configuration Nginx pour équilibrer la charge entre les instances uWSGI ou pour gérer les fichiers statiques de votre application. Lorsque vous servez plusieurs applications, il peut être dans votre intérêt d'installer uWSGI globalement plutôt que dans un environnement virtuel, selon vos besoins. Les composants sont tous assez flexibles, vous devriez donc pouvoir modifier leur configuration pour s'adapter à de nombreux scénarios différents.