Conteneuriser une application Full Stack Java Spring Boot et Angular avec Docker Compose
Cet article présente une approche professionnelle pour conteneuriser une application Full Stack composée de Java Spring Boot et Angular à l’aide de Docker et Docker Compose. L’objectif est de fournir une exécution cohérente en environnement local, avec un workflow reproductible et une séparation claire entre le backend et le frontend.
Architecture cible
L’architecture typique repose sur deux services :
- backend : API Spring Boot exposée via un port (ex. 8080)
- frontend : application Angular servie depuis un conteneur (souvent via Nginx)
- réseau Docker : connecte les services entre eux sans exposer inutilement des ports
Prérequis
Avant de démarrer, l’environnement doit disposer de :
- Docker
- Docker Compose
- Java + outil de build (Maven ou Gradle)
- Node.js + Angular CLI
Préparer le backend Spring Boot
1) Dockerfile pour Spring Boot
Le backend est compilé puis empaqueté. Un image multi-étapes permet de réduire la taille finale.
<!-- backend/Dockerfile -->
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn -q -DskipTests dependency:go-offline
COPY src ./src
RUN mvn -q -DskipTests package
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app/app.jar"]
2) Configuration d’accès depuis le frontend
Le frontend doit pointer vers l’URL du backend. En conteneurs, l’accès se fait généralement via le nom du service Docker (ex. http://backend:8080), et non via localhost.
Côté backend, une configuration CORS peut être nécessaire pour autoriser les requêtes HTTP venant du frontend. Une stratégie courante consiste à activer CORS dans la configuration Spring.
3) Exemple de CORS (Spring Boot)
<!-- backend/src/main/java/.../CorsConfig.java -->
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200", "http://frontend")
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
.allowedHeaders("*");
}
};
}
}
Préparer le frontend Angular
1) Dockerfile pour Angular
L’application Angular est construite, puis servie avec Nginx. Cela évite de lancer un serveur Node en production.
<!-- frontend/Dockerfile -->
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build -- --configuration production
FROM nginx:1.27-alpine
COPY --from=build /app/dist/ /usr/share/nginx/html
EXPOSE 80
CMD ["nginx","-g","daemon off;"]
2) Gestion de l’URL du backend
Une bonne pratique consiste à éviter les valeurs codées en dur dans Angular. Pour un usage simple en local, la cible du backend peut être injectée via une variable d’environnement lors de la construction.
Par exemple, dans Angular, l’API base URL peut être déclarée dans un fichier d’environnement. En environnement conteneurisé, la valeur peut viser backend.
Exemple : environment.ts
// frontend/src/environments/environment.ts
export const environment = {
apiBaseUrl: 'http://backend:8080'
};
Selon le niveau d’exigence, un fichier runtime-config.js chargé par Nginx peut aussi être utilisé pour ajuster l’URL sans reconstruire l’image.
Créer un fichier docker-compose.yml
Structure de répertoires recommandée
project-root/
backend/
Dockerfile
pom.xml
src/
frontend/
Dockerfile
package.json
src/
docker-compose.yml
docker-compose.yml
Docker Compose coordonne les deux conteneurs, définit les ports exposés et établit un réseau commun. Les conteneurs internes communiquent via les noms de service.
version: "3.9"
services:
backend:
build:
context: ./backend
container_name: fullstack-backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
networks:
- app-network
frontend:
build:
context: ./frontend
container_name: fullstack-frontend
ports:
- "4200:80"
depends_on:
- backend
networks:
- app-network
networks:
app-network:
driver: bridge
Construire et démarrer l’application
La commande suivante lance le build et la mise en service des deux services.
docker compose up --build
Une fois les conteneurs opérationnels :
- Le frontend est accessible via http://localhost:4200
- Le backend est accessible via http://localhost:8080
Optimisations et bonnes pratiques
Réduction de taille et sécurité
L’usage de multi-stage builds pour Spring Boot et Angular limite la taille des images finales. Pour un niveau de sécurité supérieur, des images “distroless” ou des utilisateurs non-root peuvent être ajoutés.
Gestion des variables et profils
Les profils Spring (ex. dev, prod) peuvent être pilotés via des variables d’environnement. Angular peut être aligné via des environnements de build.
Respect du chemin API
Les endpoints doivent être cohérents entre backend et frontend (ex. préfixe /api). Un petit décalage dans la configuration peut provoquer des erreurs CORS ou des “404” côté UI.
Dépannage courant
Erreur CORS
Si les appels depuis le navigateur sont bloqués, la configuration CORS Spring doit autoriser l’origine du frontend et les méthodes utilisées.
Le frontend cherche localhost
En conteneur, localhost pointe vers le conteneur lui-même. La base URL côté Angular doit cibler backend (nom de service Docker) plutôt que localhost.
Image Angular “stale”
Si le frontend ne reflète pas les changements, reconstruire les images avec :
docker compose up --build --force-recreate
Conclusion
La combinaison de Spring Boot, Angular, Docker et Docker Compose permet de mettre en place une chaîne d’exécution fiable. La séparation backend/frontend, l’utilisation de réseaux Docker et l’injection correcte de l’URL API assurent une intégration fluide. Cette approche sert de base solide pour passer ensuite à des environnements plus avancés (CI/CD, Kubernetes, images signées, variables runtime).
À propos de l'auteur
Laty Gueye Samba est développeur Full Stack basé à Dakar, Sénégal. Spécialiste des écosystèmes Java / Spring Boot et Angular.
Contact : latygueyesamba@gmail.com | Dakar, Sénégal