docker image build \
--tag javahello:mavenimage \
--file Dockerfile.10.maven .Conteneurs — Application à Java
Construire des images Docker Java prêtes pour l’orchestration
Objectifs
Comment transformer une application Java en image Docker exploitable en production.
À l’issue, vous devez comprendre :
- L’impact de l’ordre des instructions sur le cache de build.
- Pourquoi séparer l’environnement de build du runtime (Multi-stage).
- Comment rendre une image orchestrable (sécurité non-root, signaux PID 1).
Ce chapitre ne vise pas à maîtriser toutes les techniques, mais à comprendre les trajectoires possibles.
Exemples pratiques
Les exemples suivants sont accessibles dans le dépôt :
✅ : ebpro/notebook-containers-intro-sample-java-helloworld
Détails
- URL: https://github.com/ebpro/notebook-containers-intro-sample-java-helloworld
- Branch: develop
- Local Path:
/home/jovyan/work/github/ebpro/notebook-containers-intro-sample-java-helloworld - Latest Commit: 61b8847 - Merge remote-tracking branch ‘origin/ci/standardize-runs-on-ebpro-org-20260829’ into develop
- Timestamp: 2026-10-05T11:18:55Z
Clone Command:
git clone -b develop https://github.com/ebpro/notebook-containers-intro-sample-java-helloworldImage Java simple (Maven embarqué)
La méthode la plus directe consiste à utiliser une image Maven officielle. C’est l’approche “tout-en-un” où l’on compile et exécute au même endroit.
Dockerfile.10.maven
docker container run --label ebpro-render=true --rm javahello:mavenimagePicked up JAVA_TOOL_OPTIONS: -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0
11:19:52.893 INFO fr.univtln.bruno.demos.docker.App - Java Vendor: Eclipse Adoptium | Version: 25.0.4.1
11:19:52.900 INFO fr.univtln.bruno.demos.docker.App - Démarrage de l'application (Iterations: 10000)...
11:19:53.000 INFO fr.univtln.bruno.demos.docker.App - Traitement terminé. Éléments filtrés : 8931 | Temps : 99 ms
Fin du programme. (Éléments traités: 8931)
ANALYSE DE L'IMAGE : javahello:mavenimage • Sécurité : ✅ appuser • Surface : ⚠️ Shell présent • Structure : ❌ Polluants (Maven ou /src) • Runtime : ☕ Standard (JRE) • Java Opts : ❌ Manquante • Poids : 509MB
- Sécurité : L’image contient le code source, Maven et le JDK complet. C’est une surface d’attaque inutile.
- Poids : L’image dépasse souvent les 800MB pour une application simple.
Image Java optimisée (multi-stage build)
Le Multi-stage build est la stratégie de référence. On utilise une image “lourde” pour compiler, puis on ne transfère que le .jar et les dépendances (lib/*.jar) vers une image JRE légère pour l’exécution.
Dockerfile.20.stage
docker image build \
--tag javahello:mavenimagestage \
--file Dockerfile.20.stage .ANALYSE DE L'IMAGE : javahello:mavenimagestage • Sécurité : ✅ appuser • Surface : ⚠️ Shell présent • Structure : ✅ Artefact pur (Multi-stage OK) • Runtime : ☕ Standard (JRE) • Java Opts : ❌ Manquante • Poids : 304MB
Prêt pour l’orchestration (Production-Ready)
Pour être orchestrée (Kubernetes, Compose), une image doit respecter des règles d’hygiène :
- Non-Root : Vos Dockerfiles utilisent un
appuser. Cela empêche une faille applicative de compromettre l’hôte. - Signaux (Exec Form) : L’usage de
ENTRYPOINT ["/script.sh"]permet à la JVM de recevoir leSIGTERMpour un arrêt propre. - Mémoire : L’usage de
-XX:MaxRAMPercentagepermet à la JVM d’être “Container Aware”.
Affichage des images créées
docker image ls \
--filter "reference=javahello" \
--format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}\t{{.CreatedSince}}"REPOSITORY TAG IMAGE ID SIZE CREATED
javahello mavenimagestage 0e86ece34a65 319MB 7 seconds ago
javahello mavenimage a7d132aa3059 533MB About a minute ago
Accélérer les builds (Cache BuildKit)
Docker permet de monter des caches persistants pour le répertoire ~/.m2. Cela évite de retélécharger les dépendances à chaque modification du code source.
Dockerfile.30.cache
docker image build \
--tag javahello:dockercache \
--file Dockerfile.30.cache .ANALYSE DE L'IMAGE : javahello:dockercache • Sécurité : ✅ appuser • Surface : ⚠️ Shell présent • Structure : ✅ Artefact pur (Multi-stage OK) • Runtime : ☕ Standard (JRE) • Java Opts : ❌ Manquante • Poids : 304MB
L’instruction --mount=type=cache,target=/root/.m2 est une fonctionnalité BuildKit qui transforme radicalement l’expérience de CI/CD.
Construction personnalisée (SDKMAN) — Avancé
L’usage de SDKMAN permet un contrôle granulaire sur les versions exactes du JDK et des outils de build, indépendamment des images Maven officielles.
Dockerfile.40.manual
docker image build \
--tag javahello:manual \
--file Dockerfile.40.manual .ANALYSE DE L'IMAGE : javahello:manual • Sécurité : ✅ appuser • Surface : ⚠️ Shell présent • Structure : ✅ Artefact pur (Multi-stage OK) • Runtime : ☕ Standard (JRE) • Java Opts : ❌ Manquante • Poids : 304MB
Image minimale avec jlink (JPMS) — Expert
jlink permet de créer un runtime Java sur mesure contenant uniquement les modules nécessaires.
Nécessite une application modularisée (JPMS) et une analyse fine des dépendances.
Dockerfile.50.jlink
ANALYSE DE L'IMAGE : javahello:jlink • Sécurité : ✅ appuser • Surface : ⚠️ Shell présent • Structure : ✅ Artefact pur (Multi-stage OK) • Runtime : ☕ Standard (JRE) • Java Opts : ❌ Manquante • Poids : 121MB
jlink-alpine — Minimalité et compromis
jlink-alpine propose un runtime Java construit sur Alpine (musl) contenant uniquement les modules nécessaires. C’est une excellente option pédagogique pour montrer l’impact du runtime sur la taille d’image, mais elle comporte des limites pratiques.
Alpine utilise musl (libc alternative). Les composants natifs (JNI), certaines bibliothèques précompilées ou des outils attendus par glibc peuvent ne pas fonctionner. Testez systématiquement avant production.
Avantages:
- Image très compacte (gain de plusieurs dizaines à centaines de MB).
- Idéale pour démonstrations et environnements contrôlés.
Inconvénients:
- Compatibilité native réduite (JNI, agents natifs, certains drivers).
- Parfois plus difficile à déboguer (outils absents dans l’image).
Commandes utiles (exemples étudiants) :
Recommandation : pour un déploiement robuste, préférez jlink (Debian/distroless) sauf si vous maîtrisez l’ensemble des dépendances natives.
Exécutable natif avec GraalVM — Culture
GraalVM compile Java en binaire natif.
- Avantage : Démarrage instantané (< 100ms) et RAM dérisoire.
- Inconvénient : Build complexe et perte de certaines capacités dynamiques de la JVM.
Dockerfile.60.graalvm
ANALYSE DE L'IMAGE : javahello:graalvm • Sécurité : ✅ nonroot:nonroot • Surface : ✅ Minimaliste / Distroless • Structure : ✅ Artefact pur (Multi-stage OK) • Runtime : 🚀 Natif (GraalVM app) • Java Opts : ✅ N/A (Binaire natif) • Poids : 20MB
Comparaison des stratégies
| Image Tag | Build Cold | Size | Peak RAM | Surface d’Attaque |
|---|---|---|---|---|
| mavenimage | 28s | 928MB | 86.01 MiB | Maximale |
| mavenimagestage | 24s | 406MB | 71.97 MiB | Moyenne |
| jlink-alpine | 30s | 163MB | 55.83 MiB | Faible |
| graalvm | 57s | 46MB | 5.02 MiB | Minimale |
Conclusion
- Multi-stage build : Séparation build / runtime.
- Utilisateur non-root : Sécurité de l’hôte.
- Gestion des ressources : Paramétrage JVM pour les limites de conteneur.
- Logs : Toujours sur
stdout/stderr(laissé à la charge de l’orchestrateur).
Prochaine étape : orchestrer ces images avec Docker Compose.