Temps de lecture : 8 minutes — niveau débutant/intermédiaire
Il y a quelques semaines, je dockerise une petite API Flask. Deux routes, 80 lignes de code. Je lance docker build, j'attends, et quand je tape docker images — 445MB.
Quatre cent quarante-cinq mégaoctets. Pour une app qui tient dans un fichier.
Je me suis dit : quelque chose ne va pas. Et j'ai décidé de comprendre exactement d'où venait ce poids, et comment l'éliminer. Ce que j'ai trouvé a transformé ma façon d'écrire tous mes Dockerfiles depuis.
Voici la méthode complète, chiffres à l'appui.
Le point de départ : le Dockerfile "débutant"
Voici le Dockerfile que j'avais écrit — et que la majorité des tutoriels en ligne t'apprennent à écrire :
FROM python:3.11
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]
Propre. Lisible. Fonctionnel. Et catastrophiquement inutile en production.
Étape 1 : diagnostiquer avant de toucher quoi que ce soit
Avant d'optimiser, j'ai fait la radiographie. La commande docker history flask-fat affiche chaque couche de l'image avec son poids :
RUN pip install -r requirements.txt 111MB
COPY . . 37kB
[ couches de python:3.11 ] ~900MB
Le verdict est immédiat : mon code pèse 37 kilooctets. Le reste — 99% du poids — c'est l'infrastructure que j'avais choisie sans y réfléchir.
Optimisation 1 : le .dockerignore (quick win)
Première chose facile : empêcher Docker de copier des fichiers inutiles.
Sans .dockerignore, COPY . . embarque dans l'image : l'environnement virtuel Python local (.venv/ — souvent 200MB+), l'historique Git complet, les fichiers .env avec les mots de passe...
.venv/
__pycache__/
.git/
.env
.env.*
⚠️ Ce point dépasse la performance : un fichier
.envdans une image publiée sur Docker Hub, c'est tes clés API exposées à tout le monde. C'est l'une des fuites de secrets les plus fréquentes en entreprise.
Gain : marginal sur ce projet, critique en sécurité.
Optimisation 2 : changer l'image de base (-88%)
C'est lĂ que tout se joue. Il existe trois "tailles" d'images Python officielles :
| Image | Ce qu'elle contient | Poids |
|---|---|---|
python:3.11 |
Debian complet + Python + compilateurs + outils | ~900MB |
python:3.11-slim |
Debian minimal + Python | ~130MB |
python:3.11-alpine |
Alpine Linux + Python (rien d'autre) | ~50MB |
Un seul mot Ă changer dans le Dockerfile :
# Avant
FROM python:3.11
# Après
FROM python:3.11-alpine
Résultat : 445MB → 53MB. Un changement d'une ligne pour -88%.
Le piège à connaître : Alpine utilise une bibliothèque système différente de Debian (musl libc vs glibc). Certaines librairies Python avec des extensions C (psycopg2, Pillow, cryptography) nécessitent des outils de compilation supplémentaires sur Alpine :
RUN apk add --no-cache gcc musl-dev libffi-dev
Ce n'est pas compliqué — mais il faut le savoir avant de se retrouver avec des erreurs cryptiques.
Optimisation 3 : le multi-stage build
MĂŞme avec Alpine, si tu installes des outils de compilation pour certaines librairies, ils restent dans l'image finale. C'est lĂ qu'entre le multi-stage build.
L'analogie que j'utilise : imagine une maison en construction. Les maçons arrivent avec échafaudages, bétonnières, perceuses. Quand la maison est prête, tu n'emménages pas avec tout ça — tu livres la maison propre.
# Stage 1 : on construit ici, avec tous les outils nécessaires
FROM python:3.11-alpine AS builder
RUN apk add --no-cache gcc musl-dev libffi-dev
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# Stage 2 : image de production, table rase
FROM python:3.11-alpine AS runner
WORKDIR /app
# On copie UNIQUEMENT les packages installés — pas gcc, pas musl-dev
COPY --from=builder /install /usr/local
COPY . .
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
La ligne magique : COPY --from=builder /install /usr/local. Elle va chercher uniquement les packages Python compilés dans le stage précédent, sans les outils qui ont servi à les compiler.
Gain : 53MB → 45MB (modeste ici, mais 50-100MB sur des projets avec des dépendances C).
Optimisation 4 : épurer les dépendances
Mon requirements.txt de départ contenait pytest, black, ipython. Ces outils ne servent qu'au développement. En production, ils ne s'exécutent jamais — mais ils pèsent, et surtout ils représentent des surfaces d'attaque potentielles.
La solution : deux fichiers distincts.
requirements.txt (production) :
flask==3.0.0
gunicorn==21.2.0
Werkzeug==3.0.1
requirements-dev.txt (développement local) :
-r requirements.txt
pytest==7.4.0
black==23.9.1
ipython==8.16.1
Le -r requirements.txt dans le fichier dev signifie "installe d'abord les deps de prod". Un seul fichier à maintenir pour la prod, un seul pour le dev, et zéro duplication.
Gain : 45MB → 35MB.
Le bilan final
| Technique | Avant | Après | Gain |
|---|---|---|---|
.dockerignore |
445MB | 441MB | -1% |
| Image Alpine | 441MB | 53MB | -88% |
| Multi-stage build | 53MB | 45MB | -8% |
| Dépendances épurées | 45MB | 35MB | -8% |
| Total | 445MB | 35MB | -92% |
Les 3 règles que j'applique maintenant sur chaque projet
1. Commencer par -alpine ou -slim par défaut, et ne revenir à l'image complète que si une dépendance l'exige vraiment.
2. Multi-stage build systématique dès qu'une dépendance nécessite compilation.
3. .dockerignore dans chaque projet dès le premier commit — pas quand on pense à l'optimisation, dès le début.
Ce que ça change concrètement
- Déploiement plus rapide : 35MB à transférer au lieu de 445MB. Sur une connexion lente ou un budget bande passante limité (context africain : pertinent), c'est significatif.
- Moins de CVEs : moins de packages = moins de surface d'attaque = moins de vulnérabilités détectées par les scanners de sécurité comme Trivy.
- Coût réduit : sur AWS ECR, Docker Hub, ou tout registry cloud, le stockage et le transfert d'images se facturent au mégaoctet.
Pour aller plus loin
- Le repo GitHub complet avec tous les Dockerfiles (naïf → optimisé) : https://github.com/Bordley/flask-docker-optimization.git
- Le tutoriel complet étape par étape (niveau zéro débutant) : [TUTORIAL.md dans le repo]
- Prochaine étape : scanner l'image avec Trivy pour mesurer la réduction des CVEs
Tu travailles sur un projet DevOps ou Cloud ? Je publie régulièrement des contenus techniques en français, avec un angle contexte africain (contraintes réseau, budget VPS, stack locale). Retrouve-moi sur [www.linkedin.com/in/roméo-dossou3777] et [Roméo Bordley Tech].













