Retour aux articles

Containerisation avancée d'applications Spring Boot et Angular avec Docker pour CI/CD et déploiement

Containerisation avancée d'applications Spring Boot et Angular avec Docker pour CI/CD et déploiement | Laty Gueye Samba - Développeur Full Stack Dakar Sénégal, Expert Java Spring Boot Angular
```html

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