Aller au contenu
DevOps 6 min de lecture

Docker pour Java : conteneuriser une application Spring Boot

Dockerfile multi-étapes, optimisation de l'image, docker-compose pour le développement local et bonnes pratiques de sécurité pour conteneuriser une application Spring Boot.

DockerJavaSpring BootDevOpsConteneur

Conteneuriser une application Spring Boot semble simple au premier abord. Mais entre l’image de base correcte, le Dockerfile optimisé, la gestion de la mémoire JVM et la configuration pour les différents environnements, il y a plusieurs pièges à éviter. Ce guide couvre les bonnes pratiques.

Pourquoi Docker pour Java ?

Sans Docker, déployer une app Java nécessite d’installer la bonne version de JDK sur chaque serveur, configurer les variables d’environnement, gérer les dépendances système. Avec Docker, l’application et son environnement sont empaquetés ensemble : un seul artefact déployable partout.

Dockerfile multi-étapes

Le multi-stage build est la pratique standard pour Java. L’étape de build compile avec le JDK complet. L’image finale n’embarque que le JRE (plus léger, meilleure sécurité).

# ─────────────────────────────────────────────
# Étape 1 : Build avec Maven + JDK
# ─────────────────────────────────────────────
FROM maven:3.9-eclipse-temurin-21 AS build

WORKDIR /build

# Copier pom.xml en premier pour profiter du cache Docker
COPY pom.xml .
RUN mvn dependency:go-offline -B -q

# Copier les sources et compiler
COPY src ./src
RUN mvn package -DskipTests -B -q

# ─────────────────────────────────────────────
# Étape 2 : Image runtime minimale avec JRE
# ─────────────────────────────────────────────
FROM eclipse-temurin:21-jre-alpine AS runtime

# Utilisateur non-root (sécurité)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# Copier uniquement le jar depuis l'étape build
COPY --from=build /build/target/*.jar app.jar

# Métadonnées
LABEL org.opencontainers.image.title="mon-api"
LABEL org.opencontainers.image.version="1.0.0"

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1

USER appuser
EXPOSE 8080

ENTRYPOINT ["java", \
  "-XX:MaxRAMPercentage=75.0", \
  "-XX:+UseContainerSupport", \
  "-Djava.security.egd=file:/dev/./urandom", \
  "-jar", "app.jar"]

Points clés :

-XX:UseContainerSupport : active la détection des limites mémoire du conteneur par la JVM. Sans cela, la JVM lit la RAM totale de l’hôte et peut allouer beaucoup trop.

-XX:MaxRAMPercentage=75.0 : alloue 75% de la RAM disponible dans le conteneur au heap Java.

-Djava.security.egd=file:/dev/./urandom : évite les blocages liés à l’entropie sur certains environnements Linux dans un conteneur.

Taille de l’image

# Sans multi-stage (JDK complet + sources)
$ docker images mon-api:fat
REPOSITORY   TAG   SIZE
mon-api      fat   678MB

# Avec multi-stage (JRE Alpine)
$ docker images mon-api:slim
REPOSITORY   TAG   SIZE
mon-api      slim  183MB

L’image Alpine réduit encore la taille, mais peut poser des problèmes avec certaines dépendances glibc. Si vous rencontrez des erreurs natives, utilisez eclipse-temurin:21-jre (Debian slim) à la place d’Alpine.

docker-compose pour le développement local

En développement, vous avez besoin de l’application + la base de données + éventuellement un broker de messages. docker-compose orchestre tout ça localement.

# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/monapp
      SPRING_DATASOURCE_USERNAME: monapp
      SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD:-devpassword}
      SPRING_PROFILES_ACTIVE: dev
    depends_on:
      db:
        condition: service_healthy
    volumes:
      # Hot reload en dev (si vous utilisez spring-boot-devtools)
      - ./target:/app/target:ro

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: monapp
      POSTGRES_USER: monapp
      POSTGRES_PASSWORD: ${DB_PASSWORD:-devpassword}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U monapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"  # Exposer pour accès depuis l'IDE

volumes:
  postgres_data:
# Démarrer l'environnement complet
docker-compose up -d

# Voir les logs de l'app
docker-compose logs -f app

# Reconstruire après modification du Dockerfile
docker-compose up -d --build app

Profiles Spring par environnement

Utilisez les profils Spring pour adapter la configuration sans modifier l’image Docker.

# application-dev.yml
spring:
  jpa:
    show-sql: true
  devtools:
    restart:
      enabled: true
logging:
  level:
    com.monapp: DEBUG
# application-prod.yml
server:
  tomcat:
    max-threads: 200
spring:
  jpa:
    show-sql: false
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics

Le profil est activé via la variable d’environnement SPRING_PROFILES_ACTIVE.

Sécurité de l’image

Utilisateur non-root : l’app ne doit jamais tourner en root dans le conteneur. Le adduser dans le Dockerfile et USER appuser l’assurent.

Pas de secrets dans l’image : ne copiez jamais .env, application-prod.yml ou des certificats dans l’image Docker. Injectez les secrets via des variables d’environnement ou des secrets Docker/Kubernetes.

Scan de vulnérabilités : intégrez Trivy ou Snyk dans votre pipeline pour scanner l’image avant déploiement.

# Scan avec Trivy
trivy image mon-api:1.0.0 --severity HIGH,CRITICAL

Mises à jour de l’image de base : surveillez les CVE sur eclipse-temurin. Reconstruisez régulièrement pour embarquer les correctifs de sécurité.

Conclusion

Un Dockerfile Spring Boot bien construit produit une image légère, sécurisée et prévisible entre les environnements. Le multi-stage build, la gestion correcte de la mémoire JVM et l’utilisateur non-root sont les trois fondamentaux à ne pas négliger. Le reste s’adapte selon les contraintes du projet.

Amine MEGDICHE

Amine MEGDICHE

Développeur AEM & Java Full Stack — Freelance depuis 2013

Vous avez un projet sur ces sujets ?

Envoyez-moi un message pour qualifier votre besoin, vos contraintes techniques et le bon format d'intervention.

Cadrer mon besoin