Introduction
Dans ce guide, nous allons mettre en place une application WSGI simple servie par uWSGI. Nous utiliserons le serveur web Nginx comme proxy inverse vers le serveur d’application pour fournir une gestion de connexion plus robuste. Nous installerons et configurerons ces composants sur un serveur Ubuntu 14.04.
Définitions et concepts
Clarification de quelques termes
Avant de commencer, nous devrions aborder quelques termes confus associés aux concepts interdépendants avec lesquels nous allons travailler. Ces trois termes distincts qui semblent interchangeables, mais ont en fait des significations distinctes:
- WSGI: Une spécification Python qui définit une interface standard pour la communication entre une application ou un framework et un serveur d’application/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.
: Un conteneur de serveur d’application 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’application qui peut gérer des applications de différents langages. 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 demandes d’un serveur web conventionnel dans un format que l’application peut traiter. - uwsgi : Un protocole binaire rapide implémenté par le serveur uWSGI pour communiquer avec un serveur web plus complet. Il s’agit d’un protocole de transmission, et non d’un protocole de transport. C’est la méthode préférée pour parler aux serveurs web qui transmettent les demandes à uWSGI.
Exigences de l’application WSGI
La spécification WSGI définit l’interface entre le serveur web et les parties de l’application 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 en utilisant la spécification WSGI. Cela simplifie la communication et crée des composants faiblement couplés afin que vous puissiez facilement échanger l’un ou l’autre côté sans trop de difficulté.
Le serveur web (uWSGI) doit avoir la capacité de transmettre 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 appelable 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. Elle appellera également l’appelable du composant du serveur web qu’elle a reçu en paramètre. Le premier paramètre lors du déclenchement de l’appelable du serveur web 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 cet exemple, nous n’aurons besoin que de nous assurer que nos applications possèdent les qualités décrites ci-dessus. Nous configurerons également Nginx pour gérer les requêtes des clients réels et les faire relayer vers le 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, rafraîchissez votre index de paquets apt
puis installez les bibliothèques et les en-têtes de développement Python, le gestionnaire de paquets Python pip
, et le serveur web et proxy inverse Nginx :
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 utiliser cela pour installer le package virtualenv
, que nous utiliserons pour isoler l’environnement Python de notre application de tout autre qui pourrait exister sur le système :
sudo pip install virtualenv
Une fois cela fait, nous pouvons commencer à créer la structure générale de notre application. Nous allons créer l’environnement virtuel discuté précédemment et installer le serveur d’application uWSGI dans cet environnement.
Configurer un répertoire d’application et un Virtualenv
Nous commencerons 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 de pouvoir configurer l’environnement pour notre application :
cd ~/myapp
Créez un environnement virtuel avec la commande virtualenv
. Nous l’appellerons myappenv
pour simplifier :
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
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 à nouveau pour continuer le guide.
Avec cet environnement actif, tous les paquets Python installés seront contenus dans cette hiérarchie de répertoires. Ils n’interféreront pas avec l’environnement Python du système. Avec ceci à l’esprit, nous pouvons maintenant installer le serveur uWSGI dans notre environnement en utilisant pip
. Le paquet pour cela 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 désormais disponible en tapant :
uwsgi --version
Si cela renvoie un numéro de version, le serveur uWSGI est disponible pour une utilisation.
Créer une Application WSGI
Ensuite, nous allons créer une application WSGI incroyablement simple en utilisant les exigences de spécification WSGI que nous avons discutées 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 callable (une fonction ou autre construction de langage pouvant être appelée)
- Le callable doit prendre en paramètres un dictionnaire contenant des paires clé-valeur semblables à des variables d’environnement et un callable accessible sur le serveur (uWSGI).
- Le callable de l’application devrait renvoyer un itérable qui produira le corps à envoyer au client.
- L’application devrait appeler le callable du serveur web avec le statut HTTP et les en-têtes de requête.
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 créerons l’application la plus simple conforme à WSGI que nous pouvons. Comme pour tout code Python, assurez-vous de bien faire attention à l’indentation :
Le code ci-dessus constitue une application WSGI complète. Par défaut, uWSGI recherchera une fonction appelable appelée application
, c’est pourquoi nous avons appelé notre fonction application
. Comme vous pouvez le voir, elle prend deux paramètres.
Le premier que nous avons appelé environ
car il s’agira d’un dictionnaire de type clé-valeur similaire à une variable environnementale. Le deuxième est appelé start_response
et c’est le nom que l’application utilisera en interne pour faire référence à l’appelable du serveur web (uWSGI) qui est envoyé. Les noms de ces paramètres ont simplement été choisis 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. Premièrement, elle doit appeler l’appelable qu’elle a reçu avec un code d’état HTTP et les en-têtes qu’elle 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, elle doit retourner un itérable à utiliser comme corps de réponse. Ici, nous avons simplement utilisé une liste contenant une seule chaîne HTML. Les chaînes sont également itérables, mais à l’intérieur d’une liste, uWSGI pourra traiter l’ensemble de la chaîne en une seule itération.
Dans un scénario réel, ce fichier serait probablement utilisé comme un 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 quel que soit le degré de complexité du code de l’application réelle. C’est l’une des forces de l’interface.
Sauvegardez 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
. Nous lui passerons 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 fichier wsgi.py
:
Arrêtez le serveur avec CTRL-C lorsque vous avez vérifié que cela fonctionne.
Nous avons terminé 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 passé certains paramètres en 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 sa simplicité.
Pour continuer avec la dénomination que nous avons utilisée jusqu’à présent, nous appellerons le fichier myapp.ini
et le placerons 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ù résideront tous nos éléments de configuration. Nous commencerons par identifier notre application. Le serveur uWSGI doit savoir où se trouve l’appelable de l’application. Nous pouvons spécifier le fichier et la fonction à l’intérieur :
[uwsgi]
module = wsgi:application
Nous voulons marquer le processus uwsgi
initial comme maître et ensuite en générer plusieurs processus ouvriers. Nous commencerons avec cinq ouvriers :
[uwsgi]
module = wsgi:application
master = true
processes = 5
En fait, nous allons changer le protocole que uWSGI utilise pour communiquer avec le monde extérieur. Lorsque nous testions notre application, nous avons spécifié --protocol=http
afin de pouvoir la voir depuis un navigateur web. Puisque nous allons configurer Nginx comme 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 de uWSGI, donc en omettant simplement une spécification de protocole, il basculera vers uwsgi
.
Depuis que nous concevons cette configuration pour une utilisation avec Nginx, nous allons également passer de l’utilisation d’un port réseau à l’utilisation d’un socket Unix. C’est plus sécurisé et plus rapide. Le socket sera créé dans le répertoire actuel si nous utilisons un chemin relatif. Nous l’appellerons myapp.sock
. Nous allons modifier les autorisations en « 664 » afin que Nginx puisse écrire dedans (nous allons démarrer uWSGI avec le groupe www-data
que Nginx utilise). 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 option finale 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 à une application. Pour résoudre cette divergence afin que les processus puissent être gérés comme prévu avec Upstart, nous devons simplement 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 prêt à être utilisé avec un script Upstart.
Créez un fichier Upstart pour gérer l’application
Nous pouvons lancer une instance uWSGI au démarrage pour que notre application soit toujours disponible. Nous placerons cela dans le répertoire /etc/init
que vérifie Upstart. Nous l’appellerons myapp.conf
:
sudo nano /etc/init/myapp.conf
Tout d’abord, nous pouvons commencer par une description du service et choisir les niveaux d’exécution du système où il devrait s’exécuter automatiquement. Les niveaux d’exécution utilisateur standard sont de 2 à 5. Nous dirons à Upstart d’arrêter le service lorsqu’il est sur un niveau d’exécution en dehors de ce groupe (comme lors du redémarrage du système ou en mode utilisateur unique) :
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 voulons exécuter l’application sous notre propre compte (nous utilisons demo
dans ce guide, mais vous devez substituer votre propre utilisateur). Nous voulons définir le groupe sur l’utilisateur www-data
que Nginx utilise cependant. C’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. Comme nous avons installé uWSGI dans un environnement virtuel, nous avons un peu plus de travail à faire. Nous pourrions simplement fournir le chemin complet vers l’exécutable uWSGI, mais au lieu de cela, nous allons activer l’environnement virtuel. Cela serait plus facile si nous dépendions de logiciels supplémentaires installés dans l’environnement.
Pour ce faire, nous utiliserons un bloc script
. À l’intérieur, nous changerons de répertoire vers notre répertoire d’application, activerons l’environnement virtuel (nous devons utiliser .
dans les scripts au lieu de source
), et démarrerons l’instance uWSGI en pointant vers 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 complet. 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 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 en sommes maintenant au point où nous pouvons travailler sur la configuration de Nginx en tant que proxy inverse. Nginx a la capacité de proxy à l’aide du protocole uwsgi
pour communiquer avec uWSGI. C’est un protocole plus rapide que HTTP et offrira de meilleures performances.
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 correspondre 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 auquel 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;
}
Depuis que nous souhaitons envoyer toutes les demandes sur ce domaine ou cette adresse IP à notre application WSGI, nous allons créer un seul bloc de localisation pour les demandes commençant par /
, qui devrait 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 contenant ceux-ci est appelé uwsgi_params
. Ensuite, nous redirigerons le trafic vers notre instance uWSGI via le protocole uwsgi
. Nous utiliserons 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. Il y a quelques améliorations qui pourraient être apportées pour une application plus complète. Par exemple, nous pourrions définir un certain nombre de serveurs uWSGI amont en dehors de ce bloc et ensuite les transmettre à celui-ci. Nous pourrions inclure certains autres paramètres uWSGI. Nous pourrions également gérer les fichiers statiques directement depuis Nginx et ne transmettre que les demandes dynamiques à l’instance uWSGI.
Nous n’avons pas besoin de ces fonctionnalités dans notre application de trois lignes cependant, donc nous pouvons 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 détecter 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 mettre en œuvre 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 un aperçu 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 capable de communiquer avec le processus uWSGI en utilisant le protocole de communication 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 quelque chose appelé « 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 au lieu de dans un environnement virtuel, en fonction de vos besoins. Les composants sont tous assez flexibles, donc vous devriez pouvoir ajuster leur configuration pour répondre à de nombreux scénarios différents.