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