Containerisation avancée d’applications Spring Boot et Angular avec Docker pour CI/CD et déploiement
La containerisation permet de standardiser l’exécution d’applications hétérogènes, notamment des backends Spring Boot et des frontends Angular. Une approche avancée avec Docker améliore la reproductibilité, accélère les pipelines CI/CD et simplifie le déploiement sur des environnements variés (serveurs physiques, VM, plateformes orchestrées, etc.).
Architecture recommandée : multi-conteneurs et séparation des responsabilités
Une pratique courante consiste à isoler le cycle de vie des composants : un conteneur pour le backend Spring Boot, un conteneur pour le frontend Angular, et un réseau applicatif commun. Cette séparation réduit le couplage, facilite les mises à jour indépendantes et clarifie la gestion des dépendances.
Principe : build en étapes et images légères
Les builds multi-stage Docker permettent de réduire la taille des images finales en ne conservant que les artefacts nécessaires au runtime. Cela améliore le temps de déploiement et réduit la surface d’attaque.
Containerisation du backend Spring Boot
Dockerfile multi-stage (exemple)
Le backend peut être containerisé en utilisant une étape de compilation avec Maven, puis une étape de runtime minimaliste avec une image JRE.
# backend/Dockerfile
# --- Stage 1: compilation ---
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn -B -DskipTests clean package
# --- Stage 2: runtime ---
FROM eclipse-temurin:17-jre
WORKDIR /opt/app
COPY --from=build /app/target/*.jar app.jar
# Optionnel : variables d'environnement standardisées
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /opt/app/app.jar"]
Configuration et secrets
Les configurations sensibles (URLs, tokens, mots de passe) doivent être externisées via des variables d’environnement, des fichiers montés (volumes) ou un gestionnaire de secrets. Les fichiers d’applications peuvent être structurés par profils Spring (par ex. application-prod.yml), puis sélectionnés à l’exécution.
Containerisation du frontend Angular
Dockerfile multi-stage pour Angular
Angular nécessite un build Node.js, suivi d’une étape de diffusion via un serveur statique (Nginx par exemple). Cette approche supprime Node du runtime final.
# frontend/Dockerfile
# --- Stage 1: build ---
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# Optionnel : configuration par environnement
RUN npm run build -- --configuration production
# --- Stage 2: serveur statique ---
FROM nginx:1.27-alpine
# Copie du build Angular dans nginx
COPY --from=build /app/dist/ /usr/share/nginx/html
# Optionnel : cache headers / SPA routing
# COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
Gestion du routage SPA
Pour les applications Angular basées sur le client-side routing, une configuration Nginx doit souvent rediriger toutes les routes vers index.html. Cela évite des erreurs 404 lors de l’accès direct à une URL d’une page.
Intégration réseau et variables d’environnement
Lorsqu’un frontend communique avec le backend, l’adresse du backend doit être injectée de manière cohérente.
Plusieurs stratégies existent :
configuration à la compilation (moins flexible), ou injection au démarrage via un fichier env.js
(plus adaptée aux déploiements multiples).
Exemple de modèle d’injection (stratégie “runtime config”)
Une solution consiste à exposer un script dans le conteneur Nginx, lu par l’application Angular pour configurer dynamiquement l’URL API.
// nginx/env.js (exemple)
window.__env__ = {
API_BASE_URL: "http://backend:8080"
};
L’application Angular peut ensuite référencer window.__env__.API_BASE_URL lors de la configuration
des services HTTP.
Orchestration de base avec Docker Compose
Compose pour développement et tests d’intégration
Docker Compose facilite la création d’un environnement cohérent local ou en CI. Il permet d’orchestrer : backend, frontend, et éventuellement une base de données ou un service d’infrastructure.
# docker-compose.yml
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile
environment:
- SPRING_PROFILES_ACTIVE=prod
ports:
- "8080:8080"
networks:
- appnet
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "80:80"
networks:
- appnet
networks:
appnet:
driver: bridge
Cette configuration illustre la logique d’un réseau applicatif commun, où le frontend peut appeler le backend
via son nom de service (backend).
CI/CD : bonnes pratiques de pipeline avec Docker
Étapes typiques de pipeline
Un pipeline CI/CD robuste inclut généralement : (1) tests unitaires et compilation, (2) build des images Docker, (3) scans de sécurité, (4) publication sur un registry, (5) déploiement via un environnement “staging”, puis “production”.
Tagging d’images et traçabilité
Pour garantir une traçabilité complète, les images doivent être taggées de manière déterministe : commit SHA, numéro de version, ou identifiant de build. Un tag “latest” peut rester disponible, mais ne doit pas remplacer la version immuable.
docker build -t registry.example.com/backend:<commit_sha> ./backend
docker build -t registry.example.com/frontend:<commit_sha> ./frontend
docker push registry.example.com/backend:<commit_sha>
docker push registry.example.com/frontend:<commit_sha>
Scans et politiques de sécurité
Les images Docker doivent être analysées (vulnérabilités OS, dépendances, secrets). Les pipelines peuvent inclure : SBOM (Software Bill of Materials), scanners SCA/DAST, et contrôles de conformité.
Déploiement : du serveur unique à l’échelle
Déploiement sur serveur avec Docker
Sur un serveur unique, le déploiement peut se faire via un pull des images versionnées, puis un restart orchestré (Compose ou commandes Docker). Les mises à jour doivent être conçues pour limiter les temps d’indisponibilité.
docker pull registry.example.com/backend:<commit_sha>
docker pull registry.example.com/frontend:<commit_sha>
docker tag registry.example.com/backend:<commit_sha> registry.example.com/backend:current
docker tag registry.example.com/frontend:<commit_sha> registry.example.com/frontend:current
docker compose up -d --remove-orphans
Déploiement “cloud-native” (conceptuellement)
Dans un environnement orchestré, les concepts restent identiques : images immuables, injection de configuration via secrets/configmaps, health checks, et stratégies de rollout/rollback. Les variables d’environnement et l’URL du backend doivent être alignées avec la topologie réseau.
Observabilité : logs, métriques et health checks
Pour un fonctionnement fiable, chaque conteneur doit exposer : des logs standardisés (stdout/stderr), des health checks (HTTP checks pour Spring Boot), et éventuellement des métriques (actuator Spring, instrumentation frontend).
Actuator Spring Boot
Le backend Spring Boot peut exposer des endpoints de santé et de métriques.
L’implémentation exacte dépend de la stratégie de monitoring, mais la notion de endpoints
/actuator/health et /actuator/metrics reste centrale.
# application-prod.yml (exemple)
management:
endpoints:
web:
exposure:
include: "health,info,metrics"
Performance et optimisation des images
La containerisation avancée ne s’arrête pas au build. Les gains passent aussi par : la réduction de taille d’image (multi-stage, suppression de cache), l’optimisation du couche “dependency” (COPY ciblé de fichiers de dépendances), et l’ajustement des paramètres runtime (limites mémoire, garbage collector, etc.).
Conseil : limiter les invalidations de cache Docker
Pour accélérer les builds, les Dockerfiles doivent copier d’abord les fichiers de dépendances
(pom.xml, package-lock.json ou package*.json), puis le code source.
Cela permet de réutiliser des couches déjà construites lorsque les dépendances n’ont pas changé.
Checklist de conformité CI/CD et déploiement
- Multi-stage builds pour réduire les images et supprimer les outils inutiles en runtime.
- Images versionnées par commit SHA pour une traçabilité complète.
- Scan sécurité (vulnérabilités et secrets) avant publication.
- Injection de configuration via variables d’environnement ou mécanismes de secrets.
- Health checks et logs sur stdout/stderr pour l’observabilité.
- Stratégies de déploiement compatibles rollback et réduction de downtime.
Conclusion
La combinaison de Docker multi-stage, d’une séparation claire frontend/backend, et d’un pipeline CI/CD maîtrisé apporte une industrialisation solide. Le résultat attendu : des déploiements plus rapides, un contrôle accru sur les versions, une meilleure sécurité, et une observabilité renforcée, tout en réduisant les écarts entre environnements.
Référence visuelle attendue : schémas Docker, flux CI/CD, et interaction front/back.
À 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