Déployer une application 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.
[cyklodev_summary]
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.
Merci pour ce tuto !
Grâce a lui j’ai pu tester mon webhook listener en 30 min.