Conteneurs — Application à Java

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

Lecture
Containers
Docker
Optimisation, sécurité et bonnes pratiques pour la conteneurisation Java
Auteur
Affiliations

Université de Toulon

LIS UMR CNRS 7020

Date de publication

2026-10-05

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/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-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 --label ebpro-render=true --rm javahello:mavenimage
Picked 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


ImportantAnalyse 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     : 304MB


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


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


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

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

Réutilisation