Tutorial

Comment servir des applications Flask avec Gunicorn et Nginx sur Ubuntu 20.04

NginxUbuntuPythonPython FrameworksUbuntu 20.04

Introduction

Dans ce guide, vous allez construire une application Python en utilisant le micro-framework Flask sur Ubuntu 20.04. L'essentiel de cet article portera sur la configuration du serveur d'application Gunicorn et sur la manière de lancer l'application et de configurer Nginx pour qu'il agisse comme un proxy inversé en amont.

Conditions préalables

Avant de démarrer ce guide, vous devriez avoir :

  • Un serveur avec Ubuntu 20.04 installé et un utilisateur non root avec des privilèges sudo. Suivez notre guide de configuration initiale du serveur pour vous aider.
  • Nginx installé, en suivant les étapes 1 et 2 de Comment installer Nginx sur Ubuntu 20.04.
  • Un nom de domaine configuré pour pointer vers votre serveur. Vous pouvez en acheter un sur Namecheap ou en obtenir un gratuitement sur Freenom.  Vous pouvez apprendre comment pointer des domaines vers DigitalOcean en suivant la documentation pertinente sur les domaines et le DNS. Veillez à créer les enregistrements DNS suivants :

    • Un enregistrement A avec your_domain pointant sur l'adresse IP publique de votre serveur.
    • Un enregistrement A avec www.your_domain​​​​​​ pointant à l'adresse IP publique de votre serveur.
  • Être familiarisé avec la spécification WSGI, que le serveur Gunicorn utilisera pour communiquer avec votre application Flask. Cette discussion couvre le WSGI plus en détail.

Étape 1 — Installation des composants depuis les référentiels Ubuntu

Notre première étape consistera à installer tous les éléments dont nous avons besoin depuis les référentiels Ubuntu. Cela inclut pip, le gestionnaire de paquets Python, qui gérera nos composants Python. Nous obtiendrons également les fichiers de développement Python nécessaires pour construire certains des composants de Gunicorn.

Tout d'abord, nous allons mettre à jour l'index local des paquets et installer les paquets qui nous permettront de construire notre environnement Python. Ceux-ci comprendront python3-pip, ainsi que quelques autres paquets et outils de développement nécessaires pour un environnement de programmation robuste :

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

Une fois ces paquets en place, passons à la création d'un environnement virtuel pour notre projet.

Étape 2 — Création d'un environnement virtuel Python

Ensuite, nous allons configurer un environnement virtuel pour isoler notre application Flask des autres fichiers Python du système.

Commencez par installer le paquet python3-venv, qui installera le module venv :

  • 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 -m venv myprojectenv

Cela installera une copie locale de Python et pip 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 à ce qui suit (myprojectenv)user@host:~/myproject$.

Étape 3 — Configuration d'une application Flask

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

Tout d'abord, installons wheel avec l'instance locale de pip pour nous assurer que nos paquets s'installeront même s'il leur manque des archives de wheel :

  • pip install wheel

Note
Quelle que soit la version de Python que vous utilisez, lorsque l'environnement virtuel est activé, vous devez utiliser la commande pip (et non pip3).

Ensuite, installons Flask et Gunicorn :

  • pip install gunicorn flask

Création d'un exemple d'application

Maintenant que vous disposez de Flask, vous pouvez créer une application simple. Flask est un microframework. Il n'inclut pas de nombreux outils que des frameworks plus complets pourraient inclure, et existe principalement sous la forme d'un module que vous pouvez importer dans vos projets pour vous aider à initialiser une application web.

Bien que votre application puisse être plus complexe, nous allons créer notre app Flask dans un seul fichier, appelé myproject.py :

  • nano ~/myproject/myproject.py

Le code de l'application se trouvera dans ce fichier. Il importera Flask et instanciera un objet Flask. Vous pouvez l'utiliser pour définir les fonctions qui doivent être exécutées lorsqu'un itinéraire spécifique est demandé :

~/myproject/myproject.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

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 devriez disposer d'un pare-feu UFW activé. Pour tester l'application, vous devez autoriser l'accès au port 5000 :

  • sudo ufw allow 5000

Vous pouvez maintenant tester votre application Flask en tapant :

  • python myproject.py

Vous obtiendrez un résultat comme suit, 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)

Visitez l'adresse IP de votre serveur, suivie de:5000 dans votre navigateur web :

http://your_server_ip:5000

Vous devriez voir quelque chose comme ceci :

Exemple d'application Flask

Lorsque vous avez terminé, appuyez sur CTRL-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 Gunicorn comment interagir avec l'application.

Appelons le fichier wsgi.py :

  • nano ~/myproject/wsgi.py

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

~/myproject/wsgi.py
from myproject import app

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

Enregistrez et fermez le fichier lorsque vous avez terminé.

Étape 4 — Configuration de Gunicorn

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

Avant de continuer, nous devrions vérifier que Gunicorn peut servir correctement l'application.

Nous pouvons le faire en lui passant simplement le nom de notre point d'entrée. Celui-ci est construit comme le nom du module (moins l'extension .py), plus le nom de l'appelable dans l'application. Dans notre cas, il s'agit de wsgi:app.

Nous spécifierons également l'interface et le port à utiliser pour que l'application soit lancée sur une interface accessible au public :

  • cd ~/myproject
  • gunicorn --bind 0.0.0.0:5000 wsgi:app

Vous devriez voir une sortie similaire à la suivante :

Output
[2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

Visitez à nouveau dans votre navigateur web l'adresse IP de votre serveur avec :5000 ajouté à la fin :

http://your_server_ip:5000

Vous devriez voir la sortie de votre application :

Exemple d'application Flask

Lorsque vous avez la confirmation qu'il fonctionne correctement, appuyez sur CTRL-C dans la fenêtre de votre terminal.

Nous en avons maintenant fini avec notre environnement virtuel, nous pouvons donc le désactiver :

  • deactivate

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

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

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

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

Dans celui-ci, nous commencerons par la section [Unit], qui est utilisée pour spécifier les métadonnées et les dépendances. Ajoutons ici une description de notre service et disons au système d'initialisation de ne le lancer qu'une fois que l'objectif de mise en réseau a été atteint :

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

Ensuite, ouvrons la section [Service]. Celle-ci indiquera l'utilisateur et le groupe sous lequel nous voulons que le processus s'exécute. Donnons à notre compte utilisateur habituel la propriété du processus puisqu'il possède tous les fichiers pertinents. Donnons également la propriété du groupe au groupe www-data afin que Nginx puisse facilement communiquer avec les processus Gunicorn.  N'oubliez pas de remplacer le nom d'utilisateur ici par votre nom d'utilisateur :

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

Ensuite, définissons le répertoire de travail et la variable d'environnement PATH afin que le système d'initialisation sache que les exécutables du processus sont situés dans notre environnement virtuel. Précisons également la commande de démarrage du service. Cette commande fera ce qui suit :

  • Démarrez 3 processus de travail (mais vous devez ajuster cela si nécessaire)
  • Créez et reliez à un fichier socket Unix, myproject.sock, dans notre répertoire de projet. Nous fixerons une valeur d'umask de 007 pour que le fichier socket soit créé en donnant l'accès au propriétaire et au groupe, tout en limitant tout autre accès
  • Précisez le nom du fichier du point d'entrée du WSGI, ainsi que le nom de l'appel Python dans ce fichier (wsgi:app)

Systemd exige que nous donnons le chemin complet à l'exécutable Gunicorn, qui est installé dans notre environnement virtuel.

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=Gunicorn 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/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

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

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn 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/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

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

Nous pouvons maintenant démarrer le service Gunicorn que nous avons créé et l'activer afin qu'il démarre au boot :

  • sudo systemctl start myproject
  • sudo systemctl enable myproject

Vérifions l'état :

  • sudo systemctl status myproject

Vous devriez voir une sortie comme celle-ci :

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 14:15:18 UTC; 1s ago Main PID: 46430 (gunicorn) Tasks: 4 (limit: 2344) Memory: 51.3M CGroup: /system.slice/myproject.service ├─46430 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46449 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46450 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─46451 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Si vous constatez des erreurs, veillez à les résoudre avant de poursuivre le tutoriel.

Étape 5 — Configuration de Nginx pour les demandes de proxy

Notre serveur d'application Gunicorn devrait maintenant être opérationnel, en attendant les requêtes sur le fichier socket dans le répertoire du projet. Configurons maintenant Nginx pour qu'il transmette les requêtes web à cette socket en effectuant quelques petits ajouts à son fichier de configuration.

Commencez par créer un nouveau fichier de configuration du bloc serveur dans le répertoire sites-available de Nginx. Appelons cela myproject pour rester en phase avec le reste du guide :

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

Ouvrez un bloc serveur et indiquez à Nginx d'écouter sur le port 80 par défaut. Demandons également à Nginx 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 de localisation qui corresponde à chaque requête. Dans ce bloc, nous allons inclure le fichier proxy_params qui spécifie certains paramètres généraux de proxy devant être définis. Nous transmettrons ensuite les demandes à la socket que nous avons définie à l'aide de la directive proxy_pass :

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
    }
}

Enregistrez et fermez le fichier lorsque vous avez terminé.

Pour activer la configuration du bloc serveur Nginx que vous venez de créer, reliez le fichier au répertoire sites-enabled :

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

Avec le fichier dans ce répertoire, vous pouvez tester les erreurs de syntaxe :

  • sudo nginx -t

Si cela ne révèle aucun problème, relancez le processus Nginx pour lire la nouvelle configuration :

  • sudo systemctl restart nginx

Enfin, ajustons à nouveau le pare-feu. Nous n'avons plus besoin d'un accès via le port 5000, nous pouvons donc supprimer cette règle. Nous pouvons alors autoriser l'accès complet au serveur Nginx :

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

Vous devriez maintenant être en mesure de naviguer sur le nom de domaine de votre serveur dans votre navigateur web :

http://your_domain

Vous devriez voir la sortie de votre application :

Exemple d'application Flask

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

  • sudo less /var/log/nginx/error.log : vérifie les journaux d'erreurs de Nginx.
  • sudo less /var/log/nginx/access.log : vérifie les journaux d'accès Nginx.
  • sudo journalctl -u nginx : vérifie les journaux des processus Nginx.
  • sudo journalctl -u myproject : vérifie les journaux Gunicorn de votre application Flask.

Étape 6 — Sécurisation de l'application

Pour garantir que le trafic vers votre serveur reste sécurisé, obtenons un certificat SSL pour votre domaine. Il existe plusieurs façons de le faire, notamment en obtenant un certificat gratuit auprès de Let’s Encrypt, en générant un certificat auto-signé ou en en achetant un auprès d'un autre fournisseur et en configurant Nginx pour l'utiliser en suivant les étapes 2 à 6 de Comment créer un certificat SSL auto-signé pour Nginx dans Ubuntu 20.04. Nous allons choisir l'option 1 pour des raisons de commodité.

Installez le paquet Nginx de Certbot avec apt :

  • sudo apt install python3-certbot-nginx

Certbot propose différents moyens d'obtenir des certificats SSL par le biais de 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écute certbot avec le plugin --nginx, en utilisant -d pour spécifier les noms pour lesquels nous aimerions que le certificat soit valide.

Si vous utilisez certbot pour la première fois, vous serez invité à saisir une adresse électronique et à accepter les conditions régissant le service. Après avoir fait cela, certbot communiquera avec le serveur Let’s Encrypt, puis exécutera un défi pour vérifier que vous contrôlez le domaine pour lequel vous demandez un certificat.

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

Output
Please 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 ensuite appuyez sur ENTER. La configuration sera mise à jour, et Nginx se rechargera pour récupérer les nouveaux paramètres. certbot terminera par un message vous indiquant que le processus a réussi et où sont stockés vos certificats :

Output
IMPORTANT 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 2020-08-18. 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 Nginx dans les conditions préalables, vous n'aurez plus besoin de l'autorisation de profil HTTP redondant :

  • sudo ufw delete allow 'Nginx HTTP'

Pour vérifier la configuration, naviguez à nouveau sur votre domaine, en utilisant https:// :

https://your_domain

Vous devriez voir à nouveau la sortie de votre application, ainsi que l'indicateur de sécurité de votre navigateur, 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'application compatible WSGI puisse s'interfacer avec lui, puis vous avez configuré le serveur d'application Gunicorn pour qu'il assure cette fonction. Ensuite, vous avez créé un fichier de service systemd pour lancer automatiquement le serveur de l'application au démarrage. Vous avez également créé un bloc serveur Nginx qui transmet le trafic du client web au serveur d'applications, relayant les requêtes externes, et qui sécurise le trafic vers votre serveur avec Let’s Encrypt.

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

Creative Commons License