Skip to content

TP10 : CI/CD, Observabilité et Multi-environnement

Objectif du TP

Construire un workflow complet pour une application Node.js : - CI/CD : Automatiser les tests et le build via Docker. - Multi-environnement : Gérer des configurations distinctes pour le développement et la production. - Observabilité : Rendre l'application capable d'exposer son état interne (logs et métriques).

Rendu attendu

  • Le fichier docker-compose.yaml gérant les multi-environnements.
  • Le fichier Dockerfile (ou plusieurs si nécessaire pour le CI).
  • Vos notes de carnet avec les réponses aux questions.

1. Pour débuter : L'application enrichie

Nous reprenons notre application Node.js du TP9, mais nous lui ajoutons quelques fonctionnalités pour suivre son usage.

app.js

const express = require('express');
const app = express();

const port = process.env.PORT || 3000;
const env = process.env.APP_ENV || "dev";

let counter = 0;
let errors = 0;

// Middleware de logging simple
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} - Env: ${env}`);
  next();
});

app.get('/', (req, res) => {
  counter++;
  res.json({
    message: "Bienvenue sur l'API",
    env: env,
    counter: counter
  });
});

app.get('/error', (req, res) => {
  errors++;
  res.status(500).json({ error: "Une erreur simulée s'est produite" });
});

// Endpoint de métriques pour l'observabilité
app.get('/metrics', (req, res) => {
  res.json({
    requests_total: counter + errors,
    errors_total: errors,
    uptime: process.uptime()
  });
});

app.listen(port, () => {
  console.log(`🚀 Serveur démarré sur le port ${port} en mode ${env}`);
});

2. CI/CD avec Docker

L'idée de la CI (Intégration Continue) est de valider chaque changement avant qu'il ne soit déployé. Si un test échoue, le pipeline doit s'arrêter immédiatement.

  1. Ajouter des tests : Faites générer un test très simple par une IA dans un dossier tests (ex: vérifier qu'une route répond 200) en utilisant un outil comme jest.
  2. Linting : Configurez un linter genre eslint pour vérifier la qualité du code.
  3. Service de test dans Docker : Créez un service test dans votre docker-compose.yaml. Ce service ne doit pas lancer l'app, mais uniquement les tests.

Le mécanisme d'échec : Docker (et les outils de CI) se basent sur l'exit code du processus. - exit 0 : Tout va bien, le pipeline continue. - exit 1 (ou autre) : Erreur détectée, le pipeline s'arrête.

Exercice à compléter :

# Dans votre docker-compose.yaml
services:
  test:
    build: .
    command: npm test  # <-- Si cette commande échoue, l'exit code sera... ?

Question : Pourquoi est-il préférable de lancer les tests dans Docker plutôt que sur votre machine directement lors d'un pipeline CI ?


3. Gestion Multi-environnement (App vs Infra)

Plutôt que d'avoir des fichiers indépendants, Docker permet de "surcharger" (override) une configuration. On sépare donc : - L'Application : Décrite dans le fichier de base. - L'Infrastructure : Décrite dans le fichier de production (overrides).

Fichier de base : docker-compose.yaml

Ce fichier contient la structure commune à tous vos environnements.

services:
  app:
    build: .
    environment:
      - APP_ENV=dev
    ports:
      - "3000:3000"

Fichier de production : docker-compose.prod.yaml

Ce fichier vient modifier ou ajouter des éléments à la base. Complétez les trous ci-dessous pour mapper l'application sur le port 80 et ajouter une politique de redémarrage automatique.

services:
  app:
    # À COMPLÉTER : Mapper le port 80 de l'hôte sur le 3000 du conteneur
    ports:
      - "___:3000"
    # À COMPLÉTER : Ajouter la politique pour redémarrer automatiquement en cas de crash
    restart: _________
    environment:
      - APP_ENV=production

Lancement en fusionnant les fichiers

Pour tester votre configuration de production :

docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d

Quel est l'avantage de cette méthode "d'override" plutôt que d'avoir deux fichiers Compose totalement indépendants si vous décidez de rajouter un nouveau réseau ou un nouveau volume demain ?


4. Observabilité & Prometheus

L'observabilité consiste à pouvoir comprendre l'état interne de votre système (logs et métriques) uniquement à partir des données qu'il expose.

Les Logs : Le fil d'Ariane

Les logs sont des événements textuels (ex: "Échec de connexion de l'utilisateur X"). - Pourquoi les structurer ? Si vos logs sont en JSON, une machine pourra les lire et créer des alertes automatiquement ("Alerte : 50 erreurs en 2 min !").

Les Métriques : Le tableau de bord

À l'inverse des logs, les métriques sont des chiffres montrant l'état actuel (ex: "42 utilisateurs connectés").

  1. Simulation d'usage : Lancez un petit script ou utilisez curl en boucle pour appeler la racine / et faire monter vos compteurs. Provoquez manuellement des erreurs via /error.
  2. Vérification : Consultez /metrics pour voir l'évolution.

Intégration Prometheus (Obligatoire via Override)

Nous allons maintenant ajouter Prometheus pour qu'il vienne récupérer vos métriques automatiquement. Mais attention : Prometheus ne doit être présent qu'en production !

Exercice à compléter : Ajoutez le service prometheus uniquement dans votre fichier docker-compose.prod.yaml.

# Dans docker-compose.prod.yaml
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  app:
    # ... vos configurations précédentes

N'oubliez pas de créer le fichier prometheus.yml pour dire à Prometheus de venir "scraper" l'application Express sur le port 3000.

Pourquoi est-il plus propre d'ajouter Prometheus dans le fichier prod.yaml plutôt que dans le fichier de base docker-compose.yaml ? Posez-vous la question du besoin de monitoring en phase de développement pur.


5. Conclusion

À la fin de ce TP, vous devez avoir un environnement capable de : - Rejeter un build si les tests ne passent pas. - Basculer facilement entre une configuration de test locale et une mise en ligne. - Surveiller la santé de votre application en temps réel sans avoir à fouiller manuellement les fichiers de logs.