Heroku pour déployer votre application Python/Flask dans le cloud
Tags: python, cloud, heroku, programming
Published: 2021-09-02 07:12:26

Vous avez développé une application Python à l'aide du [framework Flask](https://en.wikipedia.org/wiki/Flask_(web_framework)). Tout se passe bien sur votre poste et maintenant, vous vous demandez comment rendre ça accessible au monde entier ? Avec [Heroku](https://www.heroku.com/), c'est trivial, et c'est ce qu'on va voir dans cet article !

Heroku est une _Platform as a Service (PaaS)_ dont le but est justement de déployer des applications le plus facilement possible dans le cloud. Ici, on ne configurera aucun serveur, on n'installera pas un environnement complet. On va juste écrire notre application avec Python et Flask, puis on va la pousser sur Heroku. On va l'accompagner de quelques petits fichiers de configuration pour expliquer à Heroku comment la lancer. Et c'est tout !

Le but de cet article n'est pas de faire un tutoriel complet sur Flask et Heroku. On va donc faire une application très simple, avec une seule route, juste pour afficher "hello, world". On va la tester en local sur notre PC avant de la déployer sur Heroku avec une configuration basique mais fonctionnelle.

## Créer son application Flask

### Installation

La première étape est d'installer Flask. Le plus simple est de faire :

```bash
pip install flask
```

Si vous voulez plus de finesse dans votre installation, comme par exemple utiliser un _virtual environment_, je vous laisse consulter la section [_Installation_](https://flask.palletsprojects.com/en/1.1.x/installation/) de la documentation officielle.

### Code

En s'inspirant du [_quickstart guide_](https://flask.palletsprojects.com/en/1.1.x/quickstart/) officiel, on écrit notre `main.py`:

```python
from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return 'hello, world'

```

Je suis sous Windows, j'utilise dans un _virtual environment_, je me suis donc créé un petit script `run.bat` pour lancer facilement mon application :

```bat
set FLASK_APP=main.py
.\venv\Scripts\flask run
```

C'est un peu surprenant quand on est habitué au développement en Python : on n'exécute pas directement notre script avec `python` mais on dit à `flask` quel fichier décrit notre application grâce à une variable d'environnement.

Quand on exécute `flask`, il démarre un serveur local, par défaut sur le port 5000, pour servir notre application :

```bash
C:\python_flask_heroku>.\venv\bin\flask run
 * Serving Flask app "main.py"
   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://127.0.0.1:5000/ (Press CTRL+C to quit)
```

Notez le _warning_ au lancement : par défaut, Flask utilise un serveur fait pour le développement mais [déconseillé](https://stackoverflow.com/questions/51025893/flask-at-first-run-do-not-use-the-development-server-in-a-production-environmen) pour la [production](https://flask.palletsprojects.com/en/1.1.x/deploying/). On verra dans la suite que ça impactera notre déploiement sur Heroku, puisqu'on voudra utiliser un serveur d'application plus adapté.

Pour vérifier que notre application fonctionne, il suffit d'ouvrir le lien donné dans les logs avec Firefox :

![flask en local sous firefox](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oz4vugwy2e31si74axgu.png)

Simple, efficace, fonctionnel.

Dans ma console, je vois les logs des requêtes faites au serveur :

```bash
127.0.0.1 - - [22/Apr/2021 11:56:20] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [22/Apr/2021 11:58:30] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [22/Apr/2021 11:58:38] "GET /try HTTP/1.1" 404 -
```

Ici, il y a eu 2 accès sur la racine `/` du serveur qui ont fonctionné avec le code 200, ainsi qu'un accès à `/try` qui a échoué avec le code 404. En effet, mon application définit bien une route vers `/`, mais pas vers `/try`.

Mon application est prête, il ne reste plus qu'à la rendre accessible sur l'internet mondial grâce à Heroku.

## Déploiement sur Heroku

### Inscription et installation

Pour commencer, il faut [créer un compte](https://signup.heroku.com/) et [installer l'interface en ligne de commande](https://devcenter.heroku.com/categories/command-line).

La commande `heroku` est désormais disponible depuis le terminal et nous pouvons nous connecter à notre compte avec `heroku login`. Une page internet s'ouvre dans notre navigateur par défaut pour entrer nos identifiants.

### Créer son application

Le concept est totalement génial : le déploiement consiste juste à faire un `git push` vers un dépôt distant qui est sur Heroku. Ce Git contient le code ainsi que des fichiers de configuration pour expliquer à Heroku comment lancer tout ça.

On a déjà une application fonctionnelle en local, on va donc créer un dépôt Git avec `git init`.

On crée ensuite l'[application Heroku](https://devcenter.heroku.com/articles/creating-apps), en lui donnant ou pas un nom. Dans mon cas, j'ai choisi de la nommer :

```bash
$ heroku apps
You have no apps.

$ heroku apps:create younup-flask
Creating ⬢ younup-flask... done
https://younup-flask.herokuapp.com/ | https://git.heroku.com/younup-flask.git

$ heroku apps
=== p.gradot@younup.fr Apps
younup-flask
```

Comme le dépôt existe déjà en local, les _remotes_ sont ajoutées automatiquement :

```bash
$ git remote -v
heroku  https://git.heroku.com/younup-flask.git (fetch)
heroku  https://git.heroku.com/younup-flask.git (push)
```

L'adresse de mon application est déjà accessible, mais il n'y a pas grand chose d'intéressant...

![app créée mais non deployée](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6im3q6kp020fpzbqpwfc.png)

D'ailleurs, elle est normalement toujours [accessible](https://younup-flask.herokuapp.com/) (mais peut-être qu'un jour je la désactiverai....).

### Configuration

Sur notre PC, l'interpréteur Python est installé ainsi que les paquets nécessaires. Par défaut, Heroku ne sait pas ce dont notre application a besoin, il faut donc lui préciser tout ça. Pour ça, il faut créer deux fichiers qu'on ajoute au dépôt Git.

Le premier est `runtime.txt` dans lequel on indique la version de Python qu'on souhaite utiliser. Il y a une seule ligne dans ce fichier :

```txt
python-3.9.2
```

Le second est `requirements.txt`, qui sert à `pip` pour connaitre [les paquets à installer](https://pip.pypa.io/en/stable/user_guide/#requirements-files) :

```txt
flask
gunicorn
```

`flask`, OK. Mais pourquoi `gunicorn` ? Souvenez-vous plus haut d'un _warning_ lors du lancement de Flask, nous conseillant de "_Use a production WSGI server instead"_. Et bien [c'est exactement ce qu'est `gunicorn`](https://gunicorn.org/) :

> Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork worker model. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.

Pour expliquer à Heroku qu'il faut utiliser `gunicorn` pour servir notre application Flask, on va utiliser [le fichier `Procfile`](https://devcenter.heroku.com/articles/procfile) :

> Heroku apps include a Procfile that specifies the commands that are executed by the app on startup. You can use a Procfile to declare a variety of process types, including your app’s web server".

Une ligne suffit pour notre `Procfile` :

```txt
web: gunicorn main:app
```

### Test en local

Heroku nous permet de tester en local avec la commande `heroku local`.

Malheureusement, ce n'est pas possible dans mon cas car je suis sous Windows et [gunicorn ne fonctionne pas Windows](https://stackoverflow.com/questions/11087682/does-gunicorn-run-on-windows). Si vous êtes sous Linux ou macOS, vous pouvez exécuter cette commande pour vérifier que tout est OK avant de passer au déploiement.

### Déploiment sur l'internet mondial

Tout est OK ? Il suffit de faire `git commit` et `git push` :

```bash
$ git push --set-upstream heroku master
Énumération des objets: 6, fait.
Décompte des objets: 100% (6/6), fait.
Compression par delta en utilisant jusqu'à 4 fils d'exécution
Compression des objets: 100% (3/3), fait.
Écriture des objets: 100% (6/6), 1.18 Kio | 1.18 Mio/s, fait.
Total 6 (delta 0), réutilisés 0 (delta 0), réutilisés du pack 0
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Building on the Heroku-20 stack
remote: -----> Determining which buildpack to use for this app
remote: -----> Python app detected
remote:  !     Python has released a security update! Please consider upgrading to python-3.9.4
remote:        Learn More: https://devcenter.heroku.com/articles/python-runtimes
remote: -----> Installing python-3.9.2
remote: -----> Installing pip 20.2.4, setuptools 47.1.1 and wheel 0.36.2
remote: -----> Installing SQLite3
remote: -----> Installing requirements with pip
remote:        Collecting flask
remote:          Downloading Flask-1.1.2-py2.py3-none-any.whl (94 kB)
remote:        Collecting gunicorn
remote:          Downloading gunicorn-20.1.0.tar.gz (370 kB)
remote:        Collecting itsdangerous>=0.24
remote:          Downloading itsdangerous-1.1.0-py2.py3-none-any.whl (16 kB)
remote:        Collecting Werkzeug>=0.15
remote:          Downloading Werkzeug-1.0.1-py2.py3-none-any.whl (298 kB)
remote:        Collecting click>=5.1
remote:          Downloading click-7.1.2-py2.py3-none-any.whl (82 kB)
remote:        Collecting Jinja2>=2.10.1
remote:          Downloading Jinja2-2.11.3-py2.py3-none-any.whl (125 kB)
remote:        Collecting MarkupSafe>=0.23
remote:          Downloading MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl (32 kB)
remote:        Building wheels for collected packages: gunicorn
remote:          Building wheel for gunicorn (setup.py): started
remote:          Building wheel for gunicorn (setup.py): finished with status 'done'
remote:          Created wheel for gunicorn: filename=gunicorn-20.1.0-py3-none-any.whl size=78917 sha256=389135bf7e57c6bee7f1675ce59482c4d7ab3500ab4debf1c633e23986c5326c
remote:          Stored in directory: /tmp/pip-ephem-wheel-cache-c0xwgcsz/wheels/ee/ca/72/3e9be4033d3993d4d78e2f4accdfcfff6c690921fef5ea0d57
remote:        Successfully built gunicorn
remote:        Installing collected packages: colorama, itsdangerous, Werkzeug, click, MarkupSafe, Jinja2, flask, gunicorn
remote:        Successfully installed Jinja2-2.11.3 MarkupSafe-1.1.1 Werkzeug-1.0.1 click-7.1.2 colorama-0.4.4 flask-1.1.2 gunicorn-20.1.0 itsdangerous-1.1.0
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: 
remote: -----> Compressing...
remote:        Done: 52.6M
remote: -----> Launching...
remote:        Released v3
remote:        https://younup-flask.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/younup-flask.git
 * [new branch]      master -> master
```

C'est magnifique. Heroku nous installe l'interpréteur Python et les paquets indiqués avec `pip`, puis il redémarre notre application, qui est maintenant disponible :

![app créee et deployée](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0wc6e2drbjlkzptba7y.png)

### Publier une mise à jour

En fait, notre premier _push_ était déjà une mise à jour. Les mises à jour suivantes se font de la même façon. Imaginons qu'on souhaite mettre à jour notre version de Python, pour suivre la recommandation donnée dans les logs d'installation : "_Python has released a security update! Please consider upgrading to python-3.9.4_". On modifie le fichier `runtime.txt`, on _commit_, on _push_, et le processus d'installation et de déploiement recommence. Cette fois, par contre, on lira :

```bash
remote: -----> Python app detected
remote: -----> Found python-3.9.2, removing
remote: -----> No change in requirements detected, installing from cache
remote: -----> Installing python-3.9.4
```

## Conclusion

Nous avons vu comment déployer très simplement notre application Flask sur Heroku et ainsi la rendre accessible au monde entier. Nous avons utilisé un compte gratuit, suffisant pour des tests, des hobbyists, des petites applications. Si vous avez des besoins plus importants, il existe [des versions payantes]( https://www.heroku.com/pricing).

Heroku supporte plusieurs [autres langages](https://www.heroku.com/languages) _out-of-the-box_ et fournit des [_getting started guides_](https://devcenter.heroku.com/start) pour chacun d'eux. Il y a des astuces pour utiliser d'autres langages. Certains se servent de Docker (par exemple pour faire du [C#](https://dev.to/alrobilliard/deploying-net-core-to-heroku-1lfe)) ; d'autres utilisent le [système de _buildpacks_](https://devcenter.heroku.com/articles/buildpack-api) (par exemple pour faire du [C](https://github.com/heroku/heroku-buildpack-c)).

Désormais, vous ne pourrez plus dire "je ne sais pas comment mettre mon application dans le cloud" !


---

Merci d'avoir lu cet article ! Il a été posté initialement sur le [blog de Younup](https://www.younup.fr/blog/heroku-pour-deployer-votre-application-python-flask-dans-le-cloud).