Mettre à jour ou migrer MongoDB doit être une opération ennuyeuse : plans prévisibles, vérifications reproductibles et filets de sécurité fiables. Ce guide propose un chemin pratique pour :

- Planifier une mise à niveau de version ou une migration de données avec un risque minimal

- Sauvegarder et répéter les restaurations

- Valider les changements avec du code et des requêtes

- Revenir proprement en cas d'échec

Vous verrez un flux de travail orienté production et un pilote local que vous pouvez exécuter avec Docker, Node.js et une API Express minimale.

## Vue d'ensemble du flux de travail

La méthode la plus sûre pour mettre à niveau consiste à diviser le travail en étapes claires et vérifiables. Traitez chaque étape comme une porte "pass/fail" et continuez seulement lorsque l'étape précédente est verte.

## 1) Définir le périmètre et le succès

- Cible : version(s) du serveur, versions des drivers, et tout changement de schéma ou d'index

- Contraintes : temps d'arrêt autorisé (idéalement zéro ou quasi nul pour les replica sets), fenêtre de maintenance, volume de données

- Succès : contrôles de santé mesurables, tests de fumée de l'application et seuils de performance

Exemple de critères de succès :

- Tous les nœuds sur la nouvelle version

- La compatibilité de fonctionnalité définie sur la nouvelle version

- Les tests de fumée de l'application passent (connexion, lecture, écriture, utilisation des index)

- Les erreurs et les requêtes lentes n'augmentent pas par rapport à la ligne de base (ex. latence p99 < 200 ms, taux d'erreur < 0,1 %)

## 2) Inventaire et compatibilité

Collectez ce que vous avez aujourd'hui :

- Topologie : standalone, replica set ou cluster sharded

- Versions : MongoDB, featureCompatibilityVersion, drivers

- Espace disque et marge de stockage

- Opérations critiques : grosses collections, collections capped, index TTL, index text et geo

Vérifications utiles dans mongosh :

# Version actuelle de featureCompatibility
 db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })

# Infos de build du serveur
 db.version()
 db.serverBuildInfo() 
 Préparation des drivers (exemple Node.js) :

- Utilisez une version de driver compatible avec le serveur cible (ex. driver Node.js 5.0+ pour MongoDB 7.0)

- Activez retryWrites quand c'est approprié

- Définissez des timeouts et des tailles de pool de connexion sensés

## 3) Sauvegardes et répétition de restauration

Prenez une sauvegarde cohérente et prouvez que vous pouvez la restaurer avant de toucher à la production.

# Sauvegarde logique (portable entre versions)
 mongodump --uri "mongodb://user:pass@host:27017/dbname" \
 --out ./backup-$(date +%Y%m%d-%H%M)

# Répétition de restauration dans une base jetable
 mongorestore --uri "mongodb://user:pass@staging:27017/rehearsal" \
 ./backup-YYYYMMDD-HHMM 
 Envisagez aussi les snapshots au niveau système de fichiers si votre plateforme le permet. Conservez au moins un snapshot vérifié par nœud.

## 4) Environnement de staging

Reproduisez les réglages de production au plus près :

- Même version majeure de MongoDB que la baseline de production

- Même mode d'authentification, paramètres TLS et index

- Volume de données représentatif (un sous-ensemble suffit)

Chargez des données de test avec mongorestore, ou laissez l'application les générer.

## 5) Préparation de l'application (Node.js + Express)

Préparez l'application pour les changements progressifs et les erreurs transitoires.

// app.js
const express = require('express');
const { MongoClient } = require('mongodb');

const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/appdb';

async function start() {
 const client = new MongoClient(MONGODB_URI, {
 maxPoolSize: 20,
 serverSelectionTimeoutMS: 5000,
 retryWrites: true
 });
 await client.connect();
 const db = client.db();

 // Vérification simple de disponibilité avec ping
 async function dbReady() {
 try {
 await db.command({ ping: 1 });
 return true;
 } catch (e) {
 return false;
 }
 }

 const app = express();
 app.get('/healthz', async (req, res) => {
 res.json({ ok: await dbReady() });
 });

 app.get('/smoke', async (req, res) => {
 const col = db.collection('items');
 await col.insertOne({ t: new Date(), v: Math.random() });
 const count = await col.countDocuments();
 res.json({ count });
 });

 const port = process.env.PORT || 3000;
 app.listen(port, () => console.log(`HTTP sur ${port}`));
}

start().catch((e) => {
 console.error('Fatal:', e);
 process.exit(1);
}); 
 Gardez la configuration dans des variables d'environnement. Entraînez-vous à une courte interruption en redémarrant les primaires en staging et observez la récupération de l'application.

## 6) Plan d'exécution

Choisissez le bon flux selon votre topologie.

### Nœud unique

- Arrêter les écritures de l'application (mode maintenance ou arrêt du service)

- Sauvegarde : snapshot + mongodump

- Arrêter mongod

- Installer ou basculer vers le nouveau binaire ou image conteneur

- Démarrer mongod et surveiller les logs pour un démarrage propre

- Exécuter vérifications de base et tests de fumée

- Si tout va bien, réactiver les écritures

### Replica set (rolling, quasi zéro downtime)

- S'assurer des sauvegardes et snapshots récents pour tous les nœuds

- Mettre à jour les secondaires un par un :

- Arrêter un secondaire

- Installer ou basculer vers le nouveau binaire/image

- Le redémarrer et attendre qu'il devienne SECONDARY et rattrapé

- Faire démissionner le primaire depuis mongosh :

rs.stepDown(60) 

- Mettre à jour l'ancien primaire avec les mêmes étapes

- Quand tous les nœuds tournent sur la nouvelle version, définir la featureCompatibility sur la nouvelle version depuis le primaire :

 db.adminCommand({ setFeatureCompatibilityVersion: "X.Y" }) 

- Lancer tests de fumée et validations plus poussées

 Notes :

- Maintenez l'application connectée avec retryWrites et timeouts sensés

- Attendez de brefs basculements ; le driver doit se reconnecter automatiquement

### Cluster sharded (niveau haut)

- Mettre à jour d'abord les serveurs de configuration, puis les routeurs mongos, puis les replica sets des shards

- Traiter chaque shard comme son propre upgrade rolling

## 7) Checklist de validation

Exécutez des vérifications rapides immédiatement, puis des vérifications plus profondes dans la même fenêtre.

Immédiates :

- Connexion et ping

- Lecture et écriture d'un petit document

- Confirmer la santé du replica set : rôles PRIMARY et SECONDARY attendus

rs.status() 

- Confirmer la version attendue :

 db.version()
 db.serverBuildInfo().version 
 Plus profondes :

- Comparer requêtes lentes et taux d'erreurs vs baseline (ex. db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 }).limit(5) si le profiling est activé)

- Valider l'existence et l'utilisation des index critiques par vos chemins chauds

// Exemple de création d'index pendant la migration
 const col = db.getSiblingDB('appdb').items;
 col.createIndex({ userId: 1, createdAt: -1 }, { name: 'ix_user_createdAt' }); 

- Exécuter les tests de fumée spécifiques à l'application (connexion, création de commande, etc.)

## 8) Plan de rollback

 Ayez un chemin clair et répété vers l'état précédent.

- Pour un nœud unique : arrêter les écritures, arrêter mongod, restaurer depuis snapshot ou mongorestore vers un nouveau répertoire de données, démarrer l'ancien binaire/image, valider

- Pour les replica sets : revenir nœud par nœud vers l'ancien binaire/image, restaurer les données seulement si le nœud ne démarre pas proprement (restaurer son répertoire de données depuis snapshot avant redémarrage)

- Si vous avez changé la featureCompatibility, évitez d'utiliser les nouvelles fonctionnalités jusqu'à la fin de la fenêtre de validation, pour garder le rollback simple

- Gardez l'application capable de se connecter à la version précédente du cluster sans modification de code

Liste de fumée pour le rollback :

- L'application se connecte et passe /smoke

- La forme des données et les index clés correspondent aux attentes

- L'élection primaire et la réplication sont saines

## 9) Monitoring et sécurité

Monitoring :

- Suivre connexions, latences d'opérations, lag de réplication, CPU/mémoire

- Surveiller les logs pour avertissements et requêtes lentes

Sécurité :

- Exiger l'authentification et des rôles au moindre privilège pour l'utilisateur de l'application

- Stocker les chaînes de connexion dans des secrets, pas dans le code

- Utiliser TLS là où c'est approprié et tester la rotation de certificats en staging

## Plan de pilote local

Gardez le premier pilote petit, rapide et facile à déboguer. L'objectif est d'exercer les étapes critiques de bout en bout sur votre portable.

Périmètre :

- MongoDB mono-nœud

- Jeu de données minuscule

- Mise à niveau vers une nouvelle version

- Validation avec une application Node.js Express

- Entraînement au rollback

## 1) Docker Compose pour un nœud unique

# docker-compose.yml
version: '3.8'
services:
 mongo:
 image: mongo:6.0
 container_name: mongo-old
 ports:
 - '27017:27017'
 volumes:
 - ./data:/data/db
 environment:
 MONGO_INITDB_ROOT_USERNAME: root
 MONGO_INITDB_ROOT_PASSWORD: example 
 Démarrez :

docker compose up -d 
 Initialisez un utilisateur pour l'application (mongosh dans le conteneur ou depuis l'hôte) :

mongosh "mongodb://root:example@localhost:27017/admin" --eval 'db.getSiblingDB("appdb\)).createUser({ user: "app", pwd: "appsecret", roles: [ { role: "readWrite", db: "appdb" } ] })' 

## 2) Données de seed

 export MONGODB_URI="mongodb://app:appsecret@localhost:27017/appdb"
mongosh "$MONGODB_URI" --eval 'db.items.insertMany([{name:"alpha"},{name:"beta"}])' 

## 3) Lancer l'application Express

 Créez app.js à partir de l'extrait précédent et lancez :

export MONGODB_URI="mongodb://app:appsecret@localhost:27017/appdb"
node app.js 
 Vérifiez health et smoke :

curl http://localhost:3000/healthz
curl http://localhost:3000/smoke 

## 4) Sauvegarde

 mongodump --uri "$MONGODB_URI" --out ./backup-preupgrade 
 Optionnellement, snapshot du répertoire ./data.

## 5) Mise à niveau locale

Arrêtez l'ancien conteneur et démarrez un nouveau avec le même répertoire de données :

docker compose down 
 Remplacez le tag d'image par une version plus récente dans docker-compose.yml, par exemple :

image: mongo:7.0 
 Démarrez :

docker compose up -d 
 Surveillez les logs jusqu'à un démarrage propre :

docker logs -f mongo-old 
 Exécutez la validation :

mongosh "$MONGODB_URI" --eval 'db.version()'

curl http://localhost:3000/healthz
curl http://localhost:3000/smoke 
 Optionnellement, définissez la featureCompatibility si nécessaire :

mongosh "mongodb://root:example@localhost:27017/admin" --eval 'db.adminCommand({ setFeatureCompatibilityVersion: "X.Y" })' 

## 6) Exemple simple de migration

 Créez un index et backfill un champ :

mongosh "$MONGODB_URI" --eval '
 db.items.createIndex({ name: 1 }, { name: "ix_name" });
 db.items.updateMany({ migrated: { $exists: false } }, { $set: { migrated: true } });
' 
 Vérifiez :

mongosh "$MONGODB_URI" --eval 'db.items.getIndexes()' 

## 7) Exercice de rollback

 Arrêtez le nouveau conteneur, restaurez le dump dans un nouveau répertoire de données, et lancez l'ancienne image.

# Arrêter et supprimer les conteneurs
docker compose down

# Déplacer les données actuelles et recréer un répertoire propre
mv ./data ./data-new && mkdir ./data

# Restaurer la sauvegarde dans un mongod temporaire
mongod --dbpath ./data --bind_ip 127.0.0.1 --port 27018 --fork --logpath ./mongod.log
mongorestore --uri "mongodb://localhost:27018/appdb" ./backup-preupgrade/appdb

# Arrêter le mongod temporaire
mongosh "mongodb://localhost:27018/admin" --eval 'db.shutdownServer()'

# Remettre l'image à mongo:6.0 dans docker-compose.yml et démarrer
docker compose up -d

# Re-vérifier les tests de fumée de l'application
curl http://localhost:3000/healthz
curl http://localhost:3000/smoke 
 Résultat : vous pouvez mettre à niveau, valider et revenir en arrière localement en quelques minutes.

## Conclusion

Vous disposez maintenant d'un chemin pratique pour mettre à niveau MongoDB avec un risque faible :

- Séparer planification, sauvegardes, staging, exécution, validation et rollback

- Utiliser une approche rolling pour les replica sets afin de minimiser l'indisponibilité

- Valider avec des contrôles de santé ciblés, tests de fumée Node.js et vérification d'index

- Conserver un rollback testé grâce aux snapshots et mongodump

- Intégrer monitoring et sécurité au moindre privilège dès le départ

Prochaines étapes :

- Étendre le pilote local en un replica set de staging et répéter les exercices

- Automatiser les tests de fumée et vérifications dans vos scripts de déploiement

- Documenter vos commandes exactes de bascule et de rollback pour la production