Lab 9.2 : Réduire et comparer des images¶
| Durée | 60 min |
| Niveau | Semi-guidé |
| Prérequis | Lab 9.1 : Construire avec multi-stage, sections 2, 3 et 6 du cours |
| Acquis visés | AA7, AA3 |
Objectifs
- Comparer la taille, la durée de construction et le fonctionnement d'une même application sur trois images de base
- Repérer, avec
docker history, ce qui pèse dans une image - Mesurer l'effet d'un nettoyage fait dans la même instruction ou dans une instruction séparée
- Constater qu'une image plus légère n'est pas toujours compatible avec les dépendances de l'application
- Choisir une image en s'appuyant sur des mesures
Contexte et schéma¶
Vous prenez la petite application Flask du chapitre 3 et vous la construisez sur trois images de base. Vous mesurez, vous testez, puis vous décidez. Dans ce lab, seules les commandes clés vous sont données : à vous de les adapter et de déduire le reste à l'aide du cours.
flowchart LR
A["Application Flask<br/>app.py, requirements.txt"] --> F["python:3.12<br/>image complète"]
A --> S["python:3.12-slim"]
A --> L["python:3.12-alpine"]
F --> M["Mesures : taille, durée,<br/>couches, fonctionnement"]
S --> M
L --> M
M --> D["Décision"]
Selon votre option
Sous PowerShell, utilisez curl.exe et Measure-Command { ... } à la place de time. Les téléchargements des trois images de base peuvent être longs : lancez-les dès l'étape 2.
Étapes¶
Étape 1 : Préparer le projet¶
- Objectif : disposer de l'application et de trois Dockerfile qui ne diffèrent que par l'image de base.
- Indices : créez un dossier
tp-compareet placez-vous dedans, puis créez les fichiers suivants.
requirements.txt
app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def accueil():
return "Bonjour depuis un conteneur Docker !\n"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Dockerfile.slim
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Créez Dockerfile.full et Dockerfile.alpine en changeant uniquement la première ligne (python:3.12 et python:3.12-alpine). Ajoutez un .dockerignore contenant .git et *.md.
Point de contrôle
- Le dossier contient
app.py,requirements.txt,.dockerignoreet les trois Dockerfile - Les trois Dockerfile ne diffèrent que par la ligne
FROM
Étape 2 : Construire et mesurer¶
- Objectif : obtenir trois images et relever leurs tailles et durées de construction.
- Indices :
- Construisez
flask:full,flask:slimetflask:alpineavec l'option-fpour choisir le Dockerfile, en mesurant la durée de chaque construction. - Les images de base sont téléchargées pendant la première construction : notez que la durée dépend de votre connexion.
- Relevez les tailles avec
docker images flask. - Pour une comparaison équitable de la construction, refaites la mesure avec
--no-cachequand toutes les images de base sont présentes.
- Construisez
Point de contrôle
- Les trois images apparaissent dans
docker images flask - Vous avez noté, pour chacune, la taille et la durée de construction
- Vous avez classé les trois images par taille
Étape 3 : Vérifier le fonctionnement¶
- Objectif : vérifier que la plus petite image fonctionne aussi bien que les autres.
- Indices : lancez un conteneur par image, sur les ports 5001, 5002 et 5003 de l'hôte (port 5000 dans le conteneur), puis testez chaque port avec
curl.
Point de contrôle
- Les trois conteneurs répondent par le même message
-
docker psmontre les trois conteneurs en fonctionnement
Étape 4 : Analyser les couches¶
- Objectif : repérer ce qui pèse.
- Indices :
docker history flask:full,docker history flask:slimetdocker history flask:alpine.- Pour chaque image, relevez la couche la plus volumineuse et celle qui correspond à l'installation de Flask.
Point de contrôle
- Pour chaque image, vous avez identifié la couche la plus volumineuse (l'image de base)
- Vous avez comparé la taille de la couche d'installation de Flask d'une image à l'autre
Étape 5 : Réduire en nettoyant dans la bonne instruction¶
- Objectif : mesurer l'effet d'un nettoyage mal placé et bien placé (section 2.1 du cours).
- Indices :
- Écrivez trois Dockerfile qui partent de
python:3.12-slimet installent seulementcurl:Dockerfile.apt1: installation et suppression dans deuxRUNséparés.
- Écrivez trois Dockerfile qui partent de
2. **`Dockerfile.apt2`** : installation et suppression dans un **seul** `RUN`.
3. **`Dockerfile.apt3`** : comme `apt2`, avec en plus l'option `--no-install-recommends` d'`apt-get install`.
- Construisez `outils:apt1`, `outils:apt2` et `outils:apt3`, puis comparez les tailles avec `docker images outils` et les couches avec `docker history`.
Point de contrôle
-
outils:apt2est plus petite queoutils:apt1 -
outils:apt3est la plus petite des trois - Vous pouvez expliquer, avec
docker history, pourquoiapt1garde le poids des listes de paquets
Étape 6 : Tester une dépendance native sur Alpine¶
- Objectif : vérifier qu'une image plus légère accepte les dépendances de votre application.
- Indices :
- Créez un dossier
tp-nativeavec un fichierrequirements.txtcontenantpsycopg2-binary==2.9.9(la bibliothèque de connexion à PostgreSQL utilisée au Lab 7.2). - Écrivez un Dockerfile
Dockerfile.slimà partir depython:3.12-slimet unDockerfile.alpineà partir depython:3.12-alpine: copie derequirements.txt, installation avecpip install --no-cache-dir -r requirements.txt, puis la commande :
- Créez un dossier
- Construisez les deux images, lancez chaque conteneur et notez le résultat **tel que vous l'observez** : réussite ou échec de la construction, durée, message d'erreur éventuel.
Point de contrôle
- Vous avez le résultat de la construction et de l'exécution pour
slimet pouralpine - En cas d'échec, vous avez relevé la ligne du message d'erreur qui indique la cause
- Vous savez dire si cette dépendance est compatible avec l'image Alpine, d'après votre observation
Étape 7 (facultative) : Mesurer l'effet du .dockerignore¶
- Objectif : voir qu'un fichier inutile peut entrer dans l'image.
- Indices :
- Dans
tp-compare, créez un fichier volumineux de 50 Mo (sous Linux :head -c 50M /dev/zero > gros.bin; sous PowerShell :fsutil file createnew gros.bin 52428800). - Écrivez un Dockerfile
Dockerfile.copiequi part depython:3.12-slimet copie tout le dossier avecCOPY . .; construisezcopie:1et relevez la taille. - Ajoutez
gros.binau.dockerignore, reconstruisezcopie:2et comparez.
- Dans
Point de contrôle
-
copie:1est plus grosse quecopie:2d'environ 50 Mo - Vous pouvez expliquer le rôle du
.dockerignoredans la taille de l'image
Étape 8 : Choisir et justifier¶
- Objectif : décider avec des mesures.
- Indices : complétez le tableau avec vos mesures (reprenez, si vous l'avez, le résultat du serveur Go du Lab 9.1).
| Image | Taille | Durée de construction | Fonctionne ? | Dépendance native (étape 6) | Shell disponible ? |
|---|---|---|---|---|---|
python:3.12 |
non testé | ||||
python:3.12-slim |
|||||
python:3.12-alpine |
Concluez en quelques lignes : quelle image choisissez-vous pour l'application du Lab 7.2 (Flask et psycopg2-binary), et pourquoi ? Que feriez-vous différemment pour une application sans dépendance native ?
Point de contrôle
- Le tableau est complété avec des valeurs mesurées
- Votre choix tient compte de la taille, du fonctionnement et de la compatibilité des dépendances
Questions de réflexion¶
Pourquoi l'image complète est-elle bien plus grosse que slim pour exactement la même application ?
L'image complète contient une distribution avec de nombreux outils (compilateurs, en-têtes de bibliothèques, utilitaires) qui sont inutiles à l'exécution de l'application. L'image slim ne conserve que le nécessaire.
Pourquoi outils:apt1 n'est-elle pas plus légère malgré la suppression des listes de paquets ?
Les listes sont créées par la couche de l'installation et y restent : la suppression dans une couche ultérieure ne fait que les masquer. Seul un nettoyage dans la même instruction empêche de les enregistrer.
Qu'apprend-on de l'étape 6 sur le choix d'une image de base ?
Qu'une image plus légère n'est pas toujours compatible : une dépendance précompilée pour un système peut ne pas exister pour un autre, et il faut alors la compiler (ce qui exige des outils et alourdit l'image) ou changer d'image. D'où la règle : tester l'application, pas seulement mesurer la taille.
Quand préférez-vous slim à alpine ?
Quand l'application a des dépendances natives ou que la compatibilité prime sur quelques dizaines de Mo : slim est plus compatible. On choisit alpine quand la taille minimale est une priorité et que l'application a été testée dessus.
Dépannage¶
| Symptôme | Pistes |
|---|---|
error: command 'gcc' failed ou Failed building wheel à l'étape 6 |
Résultat à noter : la dépendance doit être compilée et l'image ne contient pas les outils nécessaires |
E: Unable to locate package curl |
apt-get update est manquant ou exécuté dans une instruction séparée de l'installation |
could not resolve host pendant apt-get ou pip |
Problème réseau du conteneur de construction : vérifiez votre connexion et la configuration de Docker |
Les tailles de slim et full sont presque identiques |
Vous avez construit deux fois la même image : vérifiez la ligne FROM de chaque Dockerfile |
port is already allocated |
Un conteneur d'une étape précédente occupe le port : supprimez-le |
| La construction est très lente | Premier téléchargement des images de base : relancez après coup pour une mesure comparable |
fsutil demande des droits d'administrateur |
Ouvrez PowerShell en administrateur, ou créez le fichier avec [IO.File]::WriteAllBytes("gros.bin", (New-Object byte[] 52428800)) |
Nettoyage et reprise¶
docker rm -f <les trois conteneurs de l'étape 3>
docker rmi flask:full flask:slim flask:alpine outils:apt1 outils:apt2 outils:apt3
Remplacez la partie entre chevrons par les noms que vous avez choisis (docker ps -a), puis supprimez aussi les images des étapes 6 et 7. Mesurez ensuite l'espace récupéré avec docker system df.
Retour au chapitre¶
Chapitre 9 : Optimisation des images · Quiz : Quiz du chapitre 9