Comment servir les applications Flask avec uWSGI et Nginx sur Ubuntu 18.04

introduction

Dans ce guide, vous allez créer une application Python à l'aide du microframework Flask sur Ubuntu 18.04. L'essentiel de cet article portera sur la configuration desuWSGI application server et sur la façon de lancer l'application et de configurerNginx pour agir en tant que proxy inverse frontal.

Conditions préalables

Avant de commencer ce guide, vous devriez avoir:

  • Un serveur avec Ubuntu 18.04 installé et un utilisateur non root avec des privilèges sudo. Suivez nosinitial server setup guide pour obtenir des conseils.

  • Nginx installé, en suivant les étapes 1 et 2 deHow To Install Nginx on Ubuntu 18.04.

  • Un nom de domaine configuré pour pointer vers votre serveur. Vous pouvez en acheter un surNamecheap ou en obtenir un gratuitement surFreenom. Vous pouvez apprendre à pointer des domaines vers DigitalOcean en suivant lesdocumentation on domains and DNS appropriés. Assurez-vous de créer les enregistrements DNS suivants:

    • Un enregistrement A avecyour_domain pointant vers l'adresse IP publique de votre serveur.

    • Un enregistrement A avecwww.your_domain pointant vers l'adresse IP publique de votre serveur.

  • Familiarité avec uWSGI, notre serveur d'applications et la spécification WSGI. This discussion des définitions et des concepts passe en revue les deux en détail.

[[step-1 -—- installation-des-composants-from-the-ubuntu-repositories]] == Étape 1 - Installation des composants à partir des référentiels Ubuntu

Notre première étape consistera à installer toutes les pièces dont nous avons besoin à partir des référentiels Ubuntu. Nous installeronspip, le gestionnaire de packages Python, pour gérer nos composants Python. Nous aurons également les fichiers de développement Python nécessaires à la création de uWSGI.

Commençons par mettre à jour l’index de paquetage local et installons les paquetages qui nous permettront de construire notre environnement Python. Ceux-ci comprendrontpython3-pip, ainsi que quelques autres packages et outils de développement nécessaires à un environnement de programmation robuste:

sudo apt update
sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

Avec ces packages en place, passons à la création d’un environnement virtuel pour notre projet.

[[step-2 -—- creating-a-python-virtual-environment]] == Étape 2 - Création d'un environnement virtuel Python

Nous allons ensuite configurer un environnement virtuel afin d’isoler notre application Flask des autres fichiers Python du système.

Commencez par installer le packagepython3-venv, qui installera le modulevenv:

sudo apt install python3-venv

Ensuite, créons un répertoire parent pour notre projet Flask. Déplacez-vous dans le répertoire après l'avoir créé:

mkdir ~/myproject
cd ~/myproject

Créez un environnement virtuel pour stocker les exigences Python de votre projet Flask en tapant:

python3.6 -m venv myprojectenv

Cela installera une copie locale de Python et depip dans un répertoire appelémyprojectenv dans le répertoire de votre projet.

Avant d'installer des applications dans l'environnement virtuel, vous devez l'activer. Faites-le en tapant:

source myprojectenv/bin/activate

Votre invite changera pour indiquer que vous travaillez maintenant dans l'environnement virtuel. Cela ressemblera à quelque chose comme ceci(myprojectenv)user@host:~/myproject$.

[[step-3 -—- setting-up-a-flask-application]] == Étape 3 - Configuration d'une application Flask

Maintenant que vous êtes dans votre environnement virtuel, vous pouvez installer Flask et uWSGI et commencer à concevoir votre application.

Tout d’abord, installonswheel avec l’instance locale depip pour nous assurer que nos packages s’installeront même s’il manque des archives wheel:

pip install wheel

Note

[.note] # Quelle que soit la version de Python que vous utilisez, lorsque l'environnement virtuel est activé, vous devez utiliser la commandepip (et nonpip3).
#

Ensuite, installons Flask et uWSGI:

pip install uwsgi flask

Création d'un exemple d'application

Maintenant que Flask est disponible, vous pouvez créer une application simple. Flask est un microframework. Il n'inclut pas la plupart des outils que pourraient implémenter des infrastructures plus complètes, et existe principalement sous forme de module que vous pouvez importer dans vos projets pour vous aider à initialiser une application Web.

Bien que votre application soit plus complexe, nous créerons notre application Flask dans un seul fichier, appelémyproject.py:

nano ~/myproject/myproject.py

Le code de l'application vivra dans ce fichier. Il importera Flask et instanciera un objet Flask. Vous pouvez l'utiliser pour définir les fonctions à exécuter lorsqu'un itinéraire spécifique est demandé:

~/myproject/myproject.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "

Hello There!

" if __name__ == "__main__": app.run(host='0.0.0.0')

Cela définit essentiellement le contenu à présenter lors de l'accès au domaine racine. Enregistrez et fermez le fichier lorsque vous avez terminé.

Si vous avez suivi le guide de configuration initiale du serveur, vous devez activer un pare-feu UFW. Pour tester l'application, vous devez autoriser l'accès au port5000:

sudo ufw allow 5000

Maintenant, vous pouvez tester votre application Flask en tapant:

python myproject.py

Vous verrez une sortie comme celle-ci, avec un avertissement utile vous rappelant de ne pas utiliser cette configuration de serveur en production:

Output* Serving Flask app "myproject" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Accédez à l'adresse IP de votre serveur suivie de:5000 dans votre navigateur Web:

http://your_server_ip:5000

Vous devriez voir quelque chose comme ça:

Flask sample app

Lorsque vous avez terminé, appuyez surCTRL-C dans la fenêtre de votre terminal pour arrêter le serveur de développement Flask.

Création du point d'entrée WSGI

Ensuite, créons un fichier qui servira de point d’entrée pour notre application. Cela indiquera à notre serveur uWSGI comment interagir avec celui-ci.

Appelons le fichierwsgi.py:

nano ~/myproject/wsgi.py

Dans ce fichier, importons l’instance Flask de notre application, puis exécutons-la:

~/myproject/wsgi.py

from myproject import app

if __name__ == "__main__":
    app.run()

Enregistrez et fermez le fichier lorsque vous avez terminé.

[[step-4 -—- configuring-uwsgi]] == Étape 4 - Configuration d'uWSGI

Votre application est maintenant écrite avec un point d’entrée établi. Nous pouvons maintenant passer à la configuration de uWSGI.

Tester UWSGI Serving

Faisons le test pour nous assurer que uWSGI peut servir notre application.

Nous pouvons le faire en lui donnant simplement le nom de notre point d’entrée. Ceci est construit par le nom du module (moins l'extension.py) plus le nom de l'appelable dans l'application. Dans notre cas, il s'agit dewsgi:app.

Spécifions également le socket, afin qu'il soit démarré sur une interface accessible au public, ainsi que le protocole, afin qu'il utilise HTTP au lieu du protocole binaireuwsgi. Nous utiliserons le même numéro de port,5000, que nous avons ouvert précédemment:

uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app

Accédez à nouveau à l'adresse IP de votre serveur avec:5000 ajouté à la fin dans votre navigateur Web:

http://your_server_ip:5000

Vous devriez voir à nouveau la sortie de votre application:

Flask sample app

Lorsque vous avez confirmé qu'il fonctionne correctement, appuyez surCTRL-C dans la fenêtre de votre terminal.

Nous en avons terminé avec notre environnement virtuel. Nous pouvons donc le désactiver:

deactivate

Toutes les commandes Python utiliseront à nouveau l’environnement Python du système.

Création d'un fichier de configuration uWSGI

Vous avez testé la capacité de uWSGI à servir votre application, mais vous souhaiterez finalement quelque chose de plus robuste pour une utilisation à long terme. Vous pouvez créer un fichier de configuration uWSGI avec les options appropriées à cet effet.

Plaçons ce fichier dans notre répertoire de projet et appelons-lemyproject.ini:

nano ~/myproject/myproject.ini

À l'intérieur, nous commencerons par l'en-tête[uwsgi] afin que uWSGI sache appliquer les paramètres. Nous allons spécifier deux choses: le module lui-même, en faisant référence au fichierwsgi.py moins l'extension, et l'appelable dans le fichier,app:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

Ensuite, nous dirons à uWSGI de démarrer en mode maître et d’engendrer cinq processus de travail pour répondre aux demandes réelles:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

Lors de vos tests, vous avez exposé uWSGI sur un port réseau. Cependant, vous allez utiliser Nginx pour gérer les connexions client réelles, qui transmettront ensuite les demandes à uWSGI. Étant donné que ces composants fonctionnent sur le même ordinateur, une prise Unix est préférable car elle est plus rapide et plus sécurisée. Appelons le socketmyproject.sock et placez-le dans ce répertoire.

Modifions également les autorisations sur le socket. Nous attribuerons ultérieurement au groupe Nginx la propriété du processus uWSGI. Nous devons donc nous assurer que le propriétaire du groupe du socket peut lire les informations et écrire. Nous allons également nettoyer le socket lorsque le processus s'arrête en ajoutant l'optionvacuum:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

La dernière chose que nous allons faire est de définir l'optiondie-on-term. Cela peut aider à garantir que le système init et uWSGI ont les mêmes hypothèses sur la signification de chaque signal de processus. Ce paramètre aligne les deux composants du système et implémente le comportement attendu:

~/myproject/myproject.ini

[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = myproject.sock
chmod-socket = 660
vacuum = true

die-on-term = true

Vous avez peut-être remarqué que nous n’avons pas spécifié de protocole comme nous l’avons fait en ligne de commande. En effet, par défaut, uWSGI parle en utilisant le protocoleuwsgi, un protocole binaire rapide conçu pour communiquer avec d'autres serveurs. Nginx peut parler ce protocole de manière native, il est donc préférable de l’utiliser plutôt que de forcer la communication par HTTP.

Lorsque vous avez terminé, enregistrez et fermez le fichier.

[[step-5 -—- creation-a-systemd-unit-file]] == Étape 5 - Création d'un fichier d'unité systemd

Ensuite, créons le fichier d’unité de service systemd. La création d’un fichier unité systemd permettra au système init d’Ubuntu de démarrer automatiquement uWSGI et de servir l’application Flask à chaque démarrage du serveur.

Créez un fichier unité se terminant par.service dans le répertoire/etc/systemd/system pour commencer:

sudo nano /etc/systemd/system/myproject.service

À l'intérieur, nous commencerons par la section[Unit], qui est utilisée pour spécifier les métadonnées et les dépendances. Mettons ici une description de notre service et disons au système init de ne le démarrer que lorsque la cible réseau aura été atteinte:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

Ensuite, ouvrons la section[Service]. Cela spécifiera l'utilisateur et le groupe sous lesquels nous voulons que le processus s'exécute. Donnons à notre compte utilisateur régulier la propriété du processus car il possède tous les fichiers pertinents. Donnons également la propriété du groupe au groupewww-data afin que Nginx puisse communiquer facilement avec les processus uWSGI. N'oubliez pas de remplacer le nom d'utilisateur ici par votre nom d'utilisateur:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

Ensuite, cartographions le répertoire de travail et définissons la variable d'environnementPATH afin que le système init sache que les exécutables du processus se trouvent dans notre environnement virtuel. Spécifions également la commande pour démarrer le service. Systemd nécessite que nous donnions le chemin complet à l'exécutable uWSGI, qui est installé dans notre environnement virtuel. Nous passerons le nom du fichier de configuration.ini que nous avons créé dans notre répertoire de projet.

N'oubliez pas de remplacer le nom d'utilisateur et les chemins du projet par vos propres informations:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

Enfin, ajoutons une section[Install]. Cela indiquera à systemd à quoi lier ce service si nous lui permettons de démarrer au démarrage. Nous voulons que ce service démarre lorsque le système multi-utilisateurs normal est opérationnel:

/etc/systemd/system/myproject.service

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

[Install]
WantedBy=multi-user.target

Avec cela, notre fichier de service systemd est complet. Enregistrez et fermez-le maintenant.

Nous pouvons maintenant démarrer le service uWSGI que nous avons créé et l'activer pour qu'il démarre au démarrage:

sudo systemctl start myproject
sudo systemctl enable myproject

Voyons l’état:

sudo systemctl status myproject

Vous devriez voir la sortie comme ceci:

Output● myproject.service - uWSGI instance to serve myproject
   Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2018-07-13 14:28:39 UTC; 46s ago
 Main PID: 30360 (uwsgi)
    Tasks: 6 (limit: 1153)
   CGroup: /system.slice/myproject.service
           ├─30360 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
           ├─30378 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
           ├─30379 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
           ├─30380 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
           ├─30381 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini
           └─30382 /home/sammy/myproject/myprojectenv/bin/uwsgi --ini myproject.ini

Si vous voyez des erreurs, assurez-vous de les résoudre avant de poursuivre le didacticiel.

[[step-6 -—- configuring-nginx-to-proxy-requests]] == Étape 6 - Configuration de Nginx en requêtes proxy

Notre serveur d'applications uWSGI devrait maintenant être opérationnel et attendre les demandes sur le fichier de socket dans le répertoire du projet. Configurons Nginx pour transmettre les requêtes Web à ce socket en utilisant le protocoleuwsgi.

Commencez par créer un nouveau fichier de configuration de bloc serveur dans le répertoiresites-available de Nginx. Appelons cecimyproject pour rester en ligne avec le reste du guide:

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

Ouvrez un bloc serveur et dites à Nginx d'écouter sur le port par défaut80. Dites-lui également d’utiliser ce bloc pour les demandes de nom de domaine de notre serveur:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name your_domain www.your_domain;
}

Ensuite, ajoutons un bloc d’emplacement correspondant à chaque demande. Dans ce bloc, nous inclurons le fichieruwsgi_params qui spécifie certains paramètres uWSGI généraux qui doivent être définis. Nous passerons ensuite les requêtes au socket que nous avons défini à l'aide de la directiveuwsgi_pass:

/etc/nginx/sites-available/myproject

server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:/home/sammy/myproject/myproject.sock;
    }
}

Enregistrez et fermez le fichier lorsque vous avez terminé.

Pour activer la configuration de bloc de serveur Nginx que vous venez de créer, liez le fichier au répertoiresites-enabled:

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

Avec le fichier dans ce répertoire, nous pouvons tester les erreurs de syntaxe en tapant:

sudo nginx -t

Si cela retourne sans indiquer de problème, redémarrez le processus Nginx pour lire la nouvelle configuration:

sudo systemctl restart nginx

Enfin, ajustons à nouveau le pare-feu. Nous n'avons plus besoin d'accéder via le port5000, nous pouvons donc supprimer cette règle. Nous pouvons ensuite autoriser l'accès au serveur Nginx:

sudo ufw delete allow 5000
sudo ufw allow 'Nginx Full'

Vous devriez maintenant pouvoir accéder au nom de domaine de votre serveur dans votre navigateur Web:

http://your_domain

Vous devriez voir la sortie de votre application:

Flask sample app

Si vous rencontrez des erreurs, essayez de vérifier les éléments suivants:

  • sudo less /var/log/nginx/error.log: vérifie les journaux d'erreurs Nginx.

  • sudo less /var/log/nginx/access.log: vérifie les journaux d'accès Nginx.

  • sudo journalctl -u nginx: vérifie les journaux de processus Nginx.

  • sudo journalctl -u myproject: vérifie les journaux uWSGI de votre application Flask.

[[step-7 -—- secure-the-application]] == Étape 7 - Sécurisation de l'application

Pour vous assurer que le trafic sur votre serveur reste sécurisé, obtenons un certificat SSL pour votre domaine. Il existe plusieurs façons de le faire, y compris l'obtention d'un certificat gratuit deLet’s Encrypt,generating a self-signed certificate oubuying one from another provider et la configuration de Nginx pour l'utiliser en suivant les étapes 2 à 6 deHow to Create a Self-signed SSL Certificate for Nginx in Ubuntu 18.04 . Nous allons choisir l’option 1 par souci d’opportunité.

Premièrement, ajoutez le référentiel Certbot Ubuntu:

sudo add-apt-repository ppa:certbot/certbot

Vous devrez appuyer surENTER pour accepter.

Ensuite, installez le package Nginx de Certbot avecapt:

sudo apt install python-certbot-nginx

Certbot propose diverses méthodes pour obtenir des certificats SSL via des plugins. Le plugin Nginx se chargera de reconfigurer Nginx et de recharger la configuration chaque fois que nécessaire. Pour utiliser ce plugin, tapez ce qui suit:

sudo certbot --nginx -d your_domain -d www.your_domain

Cela exécutecertbot avec le plugin--nginx, en utilisant-d pour spécifier les noms pour lesquels nous aimerions que le certificat soit valide.

Si c'est la première fois que vous exécutezcertbot, vous serez invité à saisir une adresse e-mail et à accepter les conditions d'utilisation. Après cela,certbot communiquera avec le serveur Let’s Encrypt, puis lancera un défi pour vérifier que vous contrôlez le domaine pour lequel vous demandez un certificat.

Si cela réussit,certbot vous demandera comment vous souhaitez configurer vos paramètres HTTPS.

OutputPlease choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Sélectionnez votre choix puis appuyez surENTER. La configuration sera mise à jour et Nginx se rechargera pour récupérer les nouveaux paramètres. certbot se terminera par un message vous indiquant que le processus a réussi et où vos certificats sont stockés:

OutputIMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/your_domain/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/your_domain/privkey.pem
   Your cert will expire on 2018-07-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Si vous avez suivi les instructions d'installation de Nginx dans les conditions préalables, vous n'aurez plus besoin de la tolérance de profil HTTP redondant:

sudo ufw delete allow 'Nginx HTTP'

Pour vérifier la configuration, revenons à votre domaine à l'aide dehttps://:

https://your_domain

Vous devriez voir à nouveau la sortie de votre application, ainsi que l'indicateur de sécurité de votre navigateur, ce qui devrait indiquer que le site est sécurisé.

Conclusion

Dans ce guide, vous avez créé et sécurisé une application Flask simple dans un environnement virtuel Python. Vous avez créé un point d'entrée WSGI afin que tout serveur d'applications prenant en charge WSGI puisse s'y connecter, puis vous avez configuré le serveur d'applications uWSGI pour fournir cette fonction. Ensuite, vous avez créé un fichier de service systemd pour lancer automatiquement le serveur d'applications au démarrage. Vous avez également créé un bloc de serveur Nginx qui transfère le trafic du client Web au serveur d'applications, en relayant les demandes externes, et en sécurisant le trafic sur votre serveur avec Let’s Encrypt.

Flask est un framework très simple, mais extrêmement flexible, destiné à fournir des fonctionnalités à vos applications sans être trop restrictif en termes de structure et de conception. Vous pouvez utiliser la pile générale décrite dans ce guide pour servir les applications de flacon que vous concevez.