Bonjour !
Dans la leçon précédente, vous avez appris à implémenter la génération de site statique (SSG) avec Next.js, une stratégie de rendu ultra-performante pour les contenus qui ne changent pas fréquemment. Cela conclut notre module sur les fondamentaux du frontend moderne et du SEO technique.
Aujourd'hui, nous changeons de casquette pour revenir côté backend et aborder un sujet crucial pour le déploiement professionnel : la conteneurisation avec Docker. L'objectif de cette leçon est d'apprendre à écrire un Dockerfile optimisé (multi-stage) pour conteneuriser une application Spring Boot. Vous ne vous contenterez pas de créer une image qui fonctionne ; vous apprendrez à la rendre petite, sécurisée et rapide à reconstruire, des qualités essentielles en production.
1. Pourquoi un Dockerfile "simple" ne suffit pas ?
Lorsque l'on débute, il est tentant de créer un Dockerfile très simple qui copie tout le code source, installe les outils de build (comme Maven) et compile l'application à l'intérieur de l'image.
Voici un exemple typique de cette approche, que l'on trouve souvent dans les tutoriels d'introduction.
# Approche naïve - À ne PAS utiliser en production
FROM maven:3.8-jdk-17
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests
CMD ["java", "-jar", "target/my-app.jar"]
Bien que cela fonctionne, cette approche pose plusieurs problèmes majeurs en environnement professionnel :
- Taille de l'image : L'image finale contient le JDK complet, Maven, tout le code source et les fichiers intermédiaires de compilation. Une telle image peut facilement peser plus de 500 Mo, alors que l'application seule n'en fait que quelques dizaines.
- Sécurité : Embarquer des outils de build (JDK, Maven) dans une image de production augmente la surface d'attaque. Si un attaquant parvenait à pénétrer le conteneur, il aurait à sa disposition tout l'arsenal pour compiler et exécuter du code.
- Lenteur des builds : À chaque modification de votre code source, même d'une seule ligne, la commande
COPY . .invalide le cache de Docker. Le processus de build doit alors retélécharger toutes les dépendances Maven, ce qui est très lent.
Pour comprendre les meilleures pratiques et éviter ces écueils, l'article de Snyk est une excellente ressource.
10 best practices to build a Java container with Docker
Cet article, intitulé "10 best practices to build a Java container with Docker", est une référence sur le sujet. Il commence par présenter un Dockerfile déconseillé, très similaire à celui ci-dessus, avant d'expliquer comment l'améliorer pas à pas.
Lisez les sections "How NOT to build a Java container image" et "1. Use explicit and deterministic Docker base image tags". Concentrez-vous sur la critique de l'exemple de base et l'importance de ne pas utiliser le tag latest pour garantir des builds reproductibles.
La solution à ces problèmes réside dans une technique puissante : les builds multi-stage.
2. Le concept du Build Multi-Stage
Un Dockerfile multi-stage vous permet de définir plusieurs étapes (stages) dans un seul fichier. Chaque étape commence par une nouvelle instruction FROM et peut utiliser une image de base différente.
L'idée est de séparer complètement l'environnement de build de l'environnement d'exécution :
-
Étape 1 : Le "Builder"
- On utilise une image complète avec le JDK et Maven.
- Son rôle est de compiler le code source et de produire l'artefact final (le fichier
.jar).
-
Étape 2 : Le "Runtime"
- On part d'une image minimale ne contenant que l'environnement d'exécution Java (JRE), de préférence une version légère comme Alpine.
- On copie uniquement le fichier
.jargénéré à l'étape "Builder".
Ainsi, l'image finale est légère et ne contient que le strict nécessaire pour exécuter votre application.

La vidéo suivante explique très clairement la logique et les avantages de cette approche.
DOCKER - 17. DOCKERFILE : MULTISTAGE BUILD D'UNE IMAGE
Dans cette vidéo, xavki explique le concept du build multi-stage. Bien que l'exemple utilise Tomcat, le principe est universel et parfaitement applicable à notre cas Spring Boot.
Regardez de 00:21 à 05:20. L'auteur explique d'abord le problème (dépendances de build dans l'environnement de production), puis montre comment le résoudre en structurant le Dockerfile en plusieurs étapes (une pour cloner le code, une pour builder, une pour l'exécution finale). Faites attention à la syntaxe COPY --from=<nom_de_l_etape> qui est la clé du processus.
3. Mettre en pratique : un Dockerfile optimisé pour Spring Boot
Maintenant que vous comprenez le concept, voyons comment l'appliquer concrètement à une application Spring Boot. Nous allons construire un Dockerfile qui non seulement utilise le multi-stage, mais intègre aussi d'autres optimisations spécifiques à Spring Boot et aux bonnes pratiques de sécurité.
La vidéo ci-dessous montre une première implémentation pratique du build multi-stage.
Création d'une Image Docker à Partir d'une Application Spring Boot : Guide Étape par Étape
Cette vidéo de chillotech vous guide dans la création d'un Dockerfile pour Spring Boot, en commençant par une approche simple avant d'introduire le multi-stage pour l'optimiser.
Regardez la partie sur le multi-stage, de 29:03 à 33:29. Observez comment le fichier est divisé en deux étapes : une première FROM maven:... AS builder pour construire le projet, et une seconde FROM ... qui copie l'artefact depuis l'étape builder.
Cette approche est déjà bien meilleure, mais nous pouvons l'amener à un niveau professionnel en y ajoutant plusieurs optimisations clés. L'article suivant présente ce qui peut être considéré comme un Dockerfile de référence pour une application Spring Boot moderne.
Déployer une application Spring Boot sur Kubernetes : guide complet
Ce guide du blog Artisan Développeur propose un Dockerfile multi-stage hautement optimisé et explique chaque optimisation. C'est l'objectif que nous visons.
Concentrez-vous sur la section "Étape 2 : Créer un Dockerfile optimisé". Étudiez attentivement le code du Dockerfile fourni, puis lisez le tableau "Pourquoi cette structure ?" qui résume les bénéfices de chaque optimisation.
Analysons ensemble les optimisations les plus importantes de ce Dockerfile de référence :
a. Optimisation du cache avec les "layers" Spring Boot
Vous avez vu cette ligne dans l'étape de build :RUN java -Djarmode=layertools -jar target/*.jar extract
Depuis la version 2.3, Spring Boot peut créer des JAR "stratifiés" (layered). Cette commande analyse le JAR et l'extrait en plusieurs répertoires (dépendances, chargeur Spring, dépendances de snapshot, et code de l'application).
Dans l'étape finale, au lieu de copier un seul gros JAR, on copie ces couches séparément :
# Stage 2 : Runtime
...
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
L'avantage est énorme pour le cache Docker. Les dépendances changent rarement. Votre code applicatif, lui, change tout le temps. Avec cette structure, si vous ne modifiez que votre code (/app/application/), seul ce petit calque (layer) sera reconstruit. Les calques des dépendances, qui sont beaucoup plus lourds, resteront en cache. Vos builds seront ainsi quasi-instantanés !
b. Sécurité : exécution en tant qu'utilisateur non-root
Par défaut, les processus dans un conteneur s'exécutent avec l'utilisateur root, ce qui est un risque de sécurité majeur. Il est impératif de créer un utilisateur dédié avec des privilèges limités.
# Stage 2 : Runtime
...
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
...
Ces lignes créent un groupe et un utilisateur système spring, puis indiquent à Docker d'exécuter le reste des commandes (et le processus final) avec cet utilisateur. C'est une application directe du principe de moindre privilège.
c. Optimisations JVM pour les conteneurs
L'instruction ENTRYPOINT contient des options importantes :ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", ...]
-XX:+UseContainerSupport(activé par défaut depuis Java 10) : Indique à la JVM de prendre en compte les limites de mémoire et de CPU définies par le conteneur, et non celles de la machine hôte. C'est essentiel pour que votre application se comporte correctement dans un environnement orchestré comme Kubernetes.-XX:MaxRAMPercentage=75.0: Alloue un pourcentage de la RAM du conteneur à la heap Java. C'est une manière plus flexible de gérer la mémoire que de fixer une valeur absolue avec-Xmx.
Conclusion
Vous avez maintenant toutes les clés pour créer des images Docker pour vos applications Spring Boot qui ne sont pas seulement fonctionnelles, mais véritablement prêtes pour la production : légères, sécurisées et optimisées.

Points clés à retenir :
- Un
Dockerfilede production doit être optimisé pour la taille, la sécurité et la vitesse de build. - La technique fondamentale pour y parvenir est le build multi-stage, qui sépare l'environnement de compilation de l'environnement d'exécution.
- Pour Spring Boot, l'utilisation des JAR stratifiés (
layertools) optimise drastiquement l'utilisation du cache Docker, rendant les builds beaucoup plus rapides. - Les bonnes pratiques de sécurité, comme l'exécution avec un utilisateur non-root, sont non négociables.
- Configurer la JVM pour qu'elle soit consciente de son environnement conteneurisé (
UseContainerSupport) est crucial pour la stabilité.
Prochaine étape :
Maintenant que vous savez comment "empaqueter" votre application Spring Boot dans une image Docker optimisée, une question se pose : comment gérer les autres services dont elle dépend, comme la base de données MySQL ? Dans la prochaine leçon, nous allons découvrir Docker Compose, un outil qui vous permettra de définir et d'orchestrer des applications multi-conteneurs de manière simple et déclarative.