Conteneurs — Application à Java

Construire des images Docker Java prêtes pour l’orchestration

Université de Toulon

LIS UMR CNRS 7020

2026-09-30

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).

Important

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/examples/github/ebpro/notebook-containers-intro-sample-java-helloworld
  • Latest Commit: a21605e - fix: native-image heap cap must use JAVA_TOOL_OPTIONS (real JVM var)
  • Timestamp: 2026-09-30T07:03:15Z

Clone Command:

git clone -b develop https://github.com/ebpro/notebook-containers-intro-sample-java-helloworld

Image 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 image build \
  --tag javahello:mavenimage \
  --file Dockerfile.10.maven .
docker container run --rm javahello:mavenimage
Picked up JAVA_TOOL_OPTIONS: -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0
07:04:08.564 INFO  fr.univtln.bruno.demos.docker.App - Java Vendor: Eclipse Adoptium | Version: 25.0.4.1
07:04:08.572 INFO  fr.univtln.bruno.demos.docker.App - Démarrage de l'application (Iterations: 10000)...
07:04:08.655 INFO  fr.univtln.bruno.demos.docker.App - Traitement terminé. Éléments filtrés : 8926 | Temps : 82 ms
Fin du programme. (Éléments traités: 8926)
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


Analyse critique

  • 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     : 298MB


Prêt pour l’orchestration (Production-Ready)

Pour être orchestrée (Kubernetes, Compose), une image doit respecter des règles d’hygiène :

  1. Non-Root : Vos Dockerfiles utilisent un appuser. Cela empêche une faille applicative de compromettre l’hôte.
  2. Signaux (Exec Form) : L’usage de ENTRYPOINT ["/script.sh"] permet à la JVM de recevoir le SIGTERM pour un arrêt propre.
  3. Mémoire : L’usage de -XX:MaxRAMPercentage permet à 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   c0415d4244c7   312MB     8 seconds ago
javahello    mavenimage        f21f79c68234   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     : 298MB


Note

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     : 298MB


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


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

Points clés pour la mise en production

  • 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.