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 :

  1. Copier en premier le fichier de dépendance pip requirements.txt et exécuter la commande pip install

  2. 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.