Déployer une application Flask

fpython flask

Pour sortir un peu des sentiers battus des frameworks php Symfony ou Cake, mais surtout pour avoir accès à toutes les librairies présentes sur python. Je vous propose une méthode de déploiement des applications Flask avec gunicorn.



Application basique

On commence par une toute petite application qui ne répond qu’à une requête.

Créer l’environnement

Pour commencer, on installe l’environnement virtuel python pour ne pas interférer avec les modules python du système.

pip3.6 install --user virtualenv
virtualenv -p python3 env
source env/bin/activate

Une fois que je vous avez tapé la dernière ligne vous devriez voir ce prompt avec le (env)

(env) [www2@eye ~]$

Installer les dépendances

Pour gérer les dépendances, je vais tout regrouper dans un fichier requirements.txt qui contient:

flask
python-dotenv

Et on installe ces dépendances dans l’environnement virtuel.

pip install -r requirements.txt

Le code

Voici l’application monapp.py en question. Elle ne fait que répondre « Hello, bot » dans le navigateur.

#!/bin/python3.6

from flask import Flask

from dotenv import load_dotenv
import os

load_dotenv()
app = Flask(__name__)
name = os.getenv('NAME')

@app.route('/')

def hello():
    return 'Hello, ' + name 

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

Je crée aussi le fichier d’environnement .env qui permettra de différencier les environnements (dev,prod). On peut imaginer y mettre les informations de connexion d’une base de données mysql. On va juste mettre une variable de test dans cet exemple.

NAME='bot'

L’exécution

Il ne nous reste plus qu’a faire tourner cette application:

(env) [www2@eye flask-app]$ python monapp.py
 * Serving Flask app "monapp" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Et a acceder à l’application avec un navigateur sur le port 5000 pour voir afficher : Hello , bot

Attention !

Comme le dit le serveur web flask, et malgré que le debug est à off, il ne faut pas utiliser ce déploiement en production. Pour plusieurs raisons dont les principales sont :

  • le serveur ne servir qu’une requête à la fois
  • le serveur Werkzeug que Flask utilise n’est pas sécurisé ( exemple d’exploit )

Servir Flask avec un vrai serveur

Il existe une multitude de serveur de type WSGI (Web Server Gateway Interface) comme uwsgi ou waitress, mais je vais utiliser gunicorn .

Installation de Gunicorn

On va mettre a jour notre fichier requirements.txt

flask
python-dotenv
gunicorn

Comme avant, on installe les dépendances:

pip install -r requirements.txt

Point d’entrée de l’application

Pour servir l’application Flask il va nous falloir un point d’entrée pour gunicorn, ce sera le fichier wsgi.py, qui s’occupe d’importer l’application.

#!/bin/python3.6

from monapp import app

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

Il suffit ensuite de lancer gunicron avec la commande suivante:

gunicorn --bind 0.0.0.0:5000 wsgi:app

On peut maintenant voir le lancement de l’application.

(env) [www2@eye flask-app]$ gunicorn --bind 0.0.0.0:5000 wsgi:app
[2020-07-24 09:34:35 +0200] [21929] [INFO] Starting gunicorn 20.0.4
[2020-07-24 09:34:35 +0200] [21929] [INFO] Listening at: http://0.0.0.0:5000 (21929)
[2020-07-24 09:34:35 +0200] [21929] [INFO] Using worker: sync
[2020-07-24 09:34:35 +0200] [21932] [INFO] Booting worker with pid: 21932

De la même façon qu’avant, l’application est disponible sur le navigateur mais de manière « sécurisée » avec un vrai serveur web.

Faire un service pour lancer l’application au boot

Je vais maintenant vous montrer comment on peut faire démarrer son application au boot en définissant un service systemd ( et non ne troll pas sur systemd dans les commentaires ).

vi /etc/systemd/system/monapp.service
[Unit]
Description=Gunicorn monapp 
After=network.target

[Service]
User=www2
Group=www2
WorkingDirectory=/home/www2/flask-app
Environment="PATH=/home/www2/flask-app/env/bin"
ExecStart=/home/www2/flask-app/env/bin/gunicorn --workers 3 --bind 0.0.0.0:5000 wsgi:app

[Install]
WantedBy=multi-user.target

Dans ce service on va utiliser le même port (5000), et surtout on utilise l’environnement virtuel qu’on a spécifie au départ.

A partir de la on peut démarrer le service et le mettre active au boot.

systemctl enable monapp.service
systemctl start monapp.service

Utiliser un socket à la place d’un port réseau

Pour éviter d’utiliser plein de ports sur le serveur de déploiement, j’ai choisi de présenter l’application à travers un socket et en le servant avec le reverse proxy Nginx.

On va déjà changer le service pour qu’il utilise un socket

vi /etc/systemd/system/monapp.service
[Unit]
Description=Gunicorn monapp 
After=network.target

[Service]
User=www2
Group=www2
WorkingDirectory=/home/www2/flask-app
Environment="PATH=/home/www2/flask-app/env/bin"
ExecStart=/home/www2/flask-app/env/bin/gunicorn --workers 3 --bind unix:monapp.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Configurer le virtual host nginx

Je me rends compte que je n’ai toujours pas faire d’article sur nginx, alors que ca va faire 10 ans que je l’utilise … mais bon ce n’est pas le sujet. Je pars du principe que vous avez déjà utiliser la bête.

vi /etc/nginx/conf.d/monapp.conf

Avec le port réseau on aurait cette location :

location / {
  proxy_pass http://0.0.0.0:5000/ ;
}

Pour l’utilisation de la socket:

location / {
  proxy_pass http://unix://home/www2/flask-app/monapp.sock;
}

Épilogue

Voila votre application Flask est en ligne avec un stack propre et performante. N’hésitez pas à commenter sur vos astuces sur gunicorn.

Zephilou

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Post comment