Intro
cavote est une application de vote en ligne open source développée par la fédération FDN.
L’installation paraissait simple (pip install -r requirements.txt
, etc.) mais je me suis rapidement rendu compte que la petite mention "testé avec python 2.7" allait rapidement me bloquer avec mon python 3.6 et 3.7 si je ne voulais pas réécrire l’application.
C’est là que rentre Docker en jeu avec ses capacités à supporter des systèmes hétérogènes, voire ancien comme dans mon cas. En effet, je ne tenais pas à installer un 2.7 sur mon Windows 10 dernier cri.
Même si le fichier Docker ne fait qu’une dizaine de ligne, c’est l’occasion de rappeler quelques pratiques.
Le fichier complet est disponible à la fin
Image
L’image de départ est extrêmement importante. L’objectif est d’avoir une image de départ :
-
Qui soit fiable, d’un point de vue sécurité : pour ce faire préférer les images officielles
-
Qui soit la plus petite possible : pour ce faire, préférer les versions à base d’alpine plutôt qu’à base d’Ubuntu ou Debian
FROM python:2.7-alpine
Comme on peut le voir ci-dessous, l’image python:2.7-alpine
ne fait que 61,68MB.
> docker system df -v
Images space usage:
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
...
python 2.7-alpine df73112425b5 6 weeks ago 61.68MB 61.68MB 0B 0
...
Installation des dépendances
Afin de pouvoir générer le plus rapidement possible une image, tout en profitant du mécanisme de couche, il est recommandé de :
-
Copier en premier le fichier de dépendance pip
requirements.txt
et exécuter la commandepip install
-
Copier les fichiers python de l’application à la fin
Ainsi, tant que le fichier de dépendance n’est pas mis à jour, la construction de l’image pourra se faire en repartant de l’ancienne image.
Flask dans Docker
Flask est un framework Web python répandu.
Par défaut, Flask écoute sur l’interface 127.0.0.1. Le port n’est donc pas accessible quand publie le port avec Docker.
Il faut donc modifier la ligne suivante
app.run()
par
app.run(host= '0.0.0.0')
Ignorer des fichiers
Il est possible d’ignorer des fichiers — à la manière de git avec .gitignore
— avec le fichier .dockerignore
.
J’ai donc ignoré le répertoire git et la configuration d’exemple avec le fichier suivant :
Dockerfile de développement
Le fichier Docker pour la version "développement" complète est disponible ci-dessous :
Construction d’une image
Lors de la construction d’une image, il est très fortement recommandé de la tagué :
docker build . -t cavote:latest
Et c’est parti!
docker run -d --rm -p 5000:5000 cavote:latest
-d
-
va automatiquement rendre la main et transformer le docker en démon
--rm
-
supprimera le container à la fin
-p
-
va mapper le port 5000 du container sur le port 5000 de l’hôte (donc en local)
La commande suivante va donner la liste des containers en cours d’exécution :
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b08233412e9b cavote:latest "gunicorn -w 4 --bin…" 11 minutes ago Up 11 minutes 0.0.0.0:5000->5000/tcp elastic_keller
La commande suivante va l’arrêter (et le supprimer si l’option --rm
avait été précisé initialement) :
docker stop b08233412e9b
Mise en production avec gunicorn
La documentation précise que, pour une installation en production, on peut utiliser un serveur comme gunicorn.
Pour ce faire, j’ai donc ajouté gunicorn
dans les dépendances :
La commande à exécuter dans docker est modifiée pour démarrer gunicorn
:
CMD ["gunicorn", "-w", "4", "--bind", "0.0.0.0:5000","main:app"]
Ce qui donne :
Conclusion
Il s’agissait d’une petite application et d’un fichier Docker pour la tester mais il convient de respecter certaines bonnes pratiques pour accélérer la construction des images, leurs tests et leurs déploiements.
Ainsi, dans mon exemple, la configuration est chargée directement dans l’image. Il s’agit évidemment d’une mauvaise pratique. Il faudra donc s’intéresser aux volumes pour s’assurer que les fichiers de configuration ne fassent pas partie de l’image.
Egalement, gunicorn est "exposé" directement. L’application est accessible en HTTP, ce qui est bien suffisant pour un test local. Pour une mise en production, on pourrait s’appuyer sur un reverse-proxy comme NGINX qui assurerait la terminaison SSL. On pourrait donc imaginer embarquer NGINX et cavote ensemble grâce à Docker Compose.