TP 2a - Images et Dockerfile

Découverte d’une application web flask

  • Récupérez d’abord une application Flask exemple en la clonant :
git clone https://github.com/uptime-formation/microblog/
  • Si VSCode n’est pas installé : sudo snap install --classic code

  • Ouvrez VSCode avec le dossier microblog en tapant code microblog ou bien en lançant VSCode avec code puis en cliquant sur Open Folder.

  • Dans VSCode, vous pouvez faire Terminal > New Terminal pour obtenir un terminal en bas de l’écran.

  • Observons ensemble le code dans VSCode.

Passons à Docker

Déployer une application Flask manuellement à chaque fois est relativement pénible. Pour que les dépendances de deux projets Python ne se perturbent pas, il faut normalement utiliser un environnement virtuel virtualenv pour séparer ces deux apps. Avec Docker, les projets sont déjà isolés dans des conteneurs. Nous allons donc construire une image de conteneur pour empaqueter l’application et la manipuler plus facilement. Assurez-vous que Docker est installé.

Pour connaître la liste des instructions des Dockerfiles et leur usage, se référer au manuel de référence sur les Dockerfiles.

  • Dans le dossier du projet ajoutez un fichier nommé Dockerfile et sauvegardez-le

  • Normalement, VSCode vous propose d’ajouter l’extension Docker. Il va nous faciliter la vie, installez-le. Une nouvelle icône apparaît dans la barre latérale de gauche, vous pouvez y voir les images téléchargées et les conteneurs existants. L’extension ajoute aussi des informations utiles aux instructions Dockerfile quand vous survolez un mot-clé avec la souris.

  • Ajoutez en haut du fichier : FROM python:3.9 Cette commande indique que notre image de base est la version 3.9 de Python. Quel OS est utilisé ? Vérifier en examinant l’image ou via le Docker Hub.

  • Nous pouvons déjà contruire un conteneur à partir de ce modèle Ubuntu vide : docker build -t microblog .

  • Une fois la construction terminée lancez le conteneur.

  • Le conteneur s’arrête immédiatement. En effet il ne contient aucune commande bloquante et nous n’avons précisé aucune commande au lancement.

Il s’agit d’un Linux standard, mais il n’est pas conçu pour être utilisé comme un système complet, juste pour une application isolée. Il faut maintenant ajouter notre application Flask à l’intérieur.

  • Pour installer les dépendances python et configurer la variable d’environnement Flask, il va falloir :
    • ajouter le fichier requirements.txt avec COPY
    • lancer pip3 install -r requirements.txt
    • initialiser la variable d’environnement FLASK_APP à microblog.py
Solution :
  • Reconstruisez votre image. Si tout se passe bien, poursuivez.

  • Ensuite, copions le code de l’application à l’intérieur du conteneur.

Solution :

Cette ligne indique de copier tout le contenu du dossier courant sur l’hôte dans un dossier /microblog à l’intérieur du conteneur. Nous n’avons pas copié les requirements en même temps pour pouvoir tirer partie des fonctionnalités de cache de Docker, et ne pas avoir à retélécharger les dépendances de l’application à chaque fois que l’on modifie le contenu de l’app.

Puis, faites que le dossier courant dans le conteneur est déplacé à /microblog.

Solution :
  • Reconstruisez votre image. Observons que le build recommence à partir de l’instruction modifiée. Les layers précédents avaient été mis en cache par le Docker Engine.

  • Si tout se passe bien, poursuivez.

  • Enfin, ajoutons la section de démarrage à la fin du Dockerfile, c’est un script appelé boot.sh :

Solution :
  • Reconstruisez l’image et lancez un conteneur basé sur l’image en ouvrant le port 5000 avec la commande : docker run -p 5000:5000 microblog

  • Naviguez dans le navigateur à l’adresse localhost:5000 pour admirer le prototype microblog.

  • Lancez un deuxième container cette fois avec : docker run -d -p 5001:5000 microblog

  • Une deuxième instance de l’app est maintenant en fonctionnement et accessible à l’adresse localhost:5001

Améliorer le Dockerfile

Une image plus simple

  • A l’aide de l’image python:3.9-alpine et en remplaçant les instructions nécessaires, repackagez l’app microblog en une image taggée microblog:slim ou microblog:light. Comparez la taille entre les deux images ainsi construites.

Ne pas faire tourner l’app en root

Solution :

Construire l’application avec docker build, la lancer et vérifier avec docker exec, whoami et id l’utilisateur avec lequel tourne le conteneur.

Réponse :

Documenter les ports utilisés

  • Ajoutons l’instruction EXPOSE 5000 pour indiquer à Docker que cette app est censée être accédée via son port 5000.
  • NB : Publier le port grâce à l’option -p port_de_l-hote:port_du_container reste nécessaire, l’instruction EXPOSE n’est là qu’à titre de documentation de l’image.

Faire varier la configuration en fonction de l’environnement

Le serveur de développement Flask est bien pratique pour debugger en situation de développement, mais n’est pas adapté à la production. Nous pourrions créer deux images pour les deux situations mais ce serait aller contre l’impératif DevOps de rapprochement du dev et de la prod.

Pour démarrer l’application, nous avons fait appel à un script de boot boot.sh avec à l’intérieur :

#!/bin/bash

# ...

set -e
if [ "$CONTEXT" = 'DEV' ]; then
    echo "Running Development Server"
    FLASK_ENV=development exec flask run -h 0.0.0.0
else
    echo "Running Production Server"
    exec gunicorn -b :5000 --access-logfile - --error-logfile - app_name:app
fi
  • Déclarez maintenant dans le Dockerfile la variable d’environnement CONTEXT avec comme valeur par défaut PROD.

  • Construisez l’image avec build.

  • Puis, grâce aux bons arguments allant avec docker run, lancez une instance de l’app en configuration PROD et une instance en environnement DEV (joignables sur deux ports différents).

  • Avec docker ps ou en lisant les logs, vérifiez qu’il existe bien une différence dans le programme lancé.

Dockerfile amélioré

`Dockerfile` final :