Aller au contenu

Mettre en place un cluster MongoDB Replica Set à 3 nœuds avec Docker Compose

17 juillet 2024 · 8 min read · Read in English

Sommaire

Introduction

Dans cet article, je te guide à travers la mise en place d'un cluster MongoDB replica set à 3 nœuds avec Docker Compose. Ce guide suppose que tu es familier avec les concepts Docker de base — Docker Compose, volumes, réseaux, healthchecks — ainsi qu'avec les concepts MongoDB comme les replica sets. Si tu es prêt, on plonge dedans !

C'est quoi un replica set MongoDB ?

Restauration de backup réussie

Un replica set MongoDB est un groupe d'instances MongoDB qui hébergent le même jeu de données. Dans un replica set, un nœud est le nœud primary qui reçoit toutes les opérations d'écriture. Tous les autres nœuds, appelés secondary, appliquent les opérations du primary pour avoir le même jeu de données.

Les replica sets MongoDB offrent redondance et haute disponibilité, en gardant la base opérationnelle même si un ou plusieurs nœuds tombent. Ils répliquent les données sur plusieurs nœuds pour garantir intégrité et disponibilité.

Pourquoi utiliser un replica set MongoDB ?

1- Haute disponibilité : si le nœud primary tombe, un secondary peut être élu comme nouveau primary, ce qui garantit que la base reste disponible.

2- Redondance des données : les données sont répliquées sur plusieurs nœuds, garantissant leur intégrité et leur disponibilité.

3- Scalabilité en lecture : les nœuds secondary peuvent servir les opérations de lecture, distribuant la charge de lecture sur plusieurs nœuds.

4- Failover automatique : si le nœud primary tombe, un secondary est automatiquement élu comme nouveau primary.

Pour aller plus loin sur la réplication MongoDB, consulte la doc officielle sur les replica sets.

Mettre en place un cluster MongoDB Replica Set à 3 nœuds avec Docker Compose

Prérequis

Dans ce guide, on utilise Docker Compose pour mettre en place un cluster MongoDB replica set à 3 nœuds. Avant de commencer, assure-toi d'avoir Docker et Docker Compose installés sur ta machine. Si ce n'est pas le cas, télécharge-les depuis le site officiel Docker.

Tu auras peut-être aussi besoin d'une compréhension de base de MongoDB et des commandes mongo shell. Si tu débutes avec MongoDB, consulte la doc officielle sur les opérations CRUD MongoDB.

Étape 1 : créer un fichier Docker Compose

Commence par créer un fichier docker-compose.yml dans un nouveau dossier. Ce fichier définira les services pour les conteneurs MongoDB. On va créer :

  • trois services : mongo1, mongo2 et mongo3
  • un réseau appelé mongo-cluster
  • trois volumes : mongo1-data, mongo2-data et mongo3-data

C'est parti :

docker-compose.yml
networks:
  mongo-cluster:
    driver: bridge
volumes:
  mongo1_data:
  mongo1_config:
  mongo2_data:
  mongo2_config:
  mongo3_data:
  mongo3_config:
services:
  mongo1:
    container_name: mongo1
    image: 'mongo:8.2-noble'
    hostname: mongo1
    command: ["--config", "/etc/mongod.conf"]
    volumes:
      - mongo1_data:/data/db
      - mongo1_config:/data/configdb
      - '$PWD/scripts/mongo/rs_keyfile:/etc/mongodb/keyfile:ro'
      - '$PWD/scripts/mongo/mongod.conf:/etc/mongod.conf:ro'
    networks:
      - mongo-cluster
    environment:
      MONGO_INITDB_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'
      MONGO_INITDB_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'
      MONGO_INITDB_DATABASE: '${DB_NAME:-test}'
    healthcheck:
      test: ["CMD", "mongosh", "--quiet", "--eval", "try { rs.status().ok } catch(e) { rs.initiate().ok }"]
      timeout: 5m
      interval: 10s
      retries: 5
      start_period: 12s
  mongo2:
    container_name: mongo2
    image: 'mongo:8.2-noble'
    hostname: mongo2
    volumes:
      - mongo2_data:/data/db
      - mongo2_config:/data/configdb
      - '$PWD/scripts/mongo/rs_keyfile:/etc/mongodb/keyfile:ro'
      - '$PWD/scripts/mongo/mongod.conf:/etc/mongod.conf:ro'
    networks:
      - mongo-cluster
    environment:
      MONGO_INITDB_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'
      MONGO_INITDB_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'
  mongo3:
    container_name: mongo3
    image: 'mongo:8.2-noble'
    hostname: mongo3
    volumes:
      - mongo3_data:/data/db
      - mongo3_config:/data/configdb
      - '$PWD/scripts/mongo/rs_keyfile:/etc/mongodb/keyfile:ro'
      - '$PWD/scripts/mongo/mongod.conf:/etc/mongod.conf:ro'
    networks:
      - mongo-cluster
    environment:
      MONGO_INITDB_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'
      MONGO_INITDB_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'
  mongo-init:
    container_name: mongo-init
    image: docker.io/mongo:8.2-noble
    restart: no
    entrypoint: ["/bin/bash", "/init.sh"]
    networks:
      - mongo-cluster
    depends_on:
      - mongo1
      - mongo2
      - mongo3
    volumes:
      - $PWD/scripts/mongo/init.js:/init.js
      - $PWD/scripts/mongo/init.sh:/init.sh
    environment:
      MONGO_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'
      MONGO_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'
      DB_USERNAME: '${DB_USERNAME:-myuser}'
      DB_PASSWORD: '${DB_PASSWORD:-mypassword}'

Décortiquons le fichier docker-compose.yml :

  • On définit un réseau mongo-cluster qui sera utilisé par tous les conteneurs MongoDB. Cela garantit que les conteneurs peuvent communiquer entre eux via les hostnames mongo1, mongo2 et mongo3.
  • On définit trois volumes : mongo1_data, mongo2_data et mongo3_data pour persister les données de chaque conteneur MongoDB.
  • On définit trois services : mongo1, mongo2 et mongo3. Chaque service lance un conteneur MongoDB avec l'image mongo:8.2-noble. On spécifie l'option --replSet pour définir le nom du replica set à rs0. On spécifie également l'option --bind_ip pour lier le conteneur aux hostnames mongo1, mongo2 et mongo3. On monte les volumes pour persister les données et le keyfile pour l'authentification. On expose les ports 27017, 27018 et 27019 pour les conteneurs MongoDB. On définit un healthcheck pour le service mongo1 pour vérifier le statut du replica set et l'initialiser s'il ne l'est pas encore. Après réussite du healthcheck, le replica set est initialisé avec le nœud primary mongo1 et les secondary mongo2 et mongo3.
  • On définit les variables d'environnement MONGO_INITDB_ROOT_USERNAME, MONGO_INITDB_ROOT_PASSWORD, MONGO_INITDB_DATABASE, DB_USERNAME et DB_PASSWORD pour chaque service pour configurer l'authentification MongoDB. On utilise l'interpolation pour garantir l'usage des valeurs par défaut si les variables d'environnement ne sont pas définies.

Étape 2 : créer un keyfile pour l'authentification

MongoDB utilise des keyfiles pour l'authentification interne entre les membres du replica set. Dans certains scénarios où aucun credentials n'est défini, on n'en a pas besoin. Mais pour les besoins de ce guide, il faut créer un keyfile et le monter dans les conteneurs MongoDB, puisqu'on utilise l'authentification.

Pour créer un keyfile, lance la commande suivante :

openssl rand -base64 756 > scripts/mongo/rs_keyfile && chmod 400 scripts/mongo/rs_keyfile

Cette commande génère une clé aléatoire de 756 octets et la sauvegarde dans le fichier scripts/mongo/rs_keyfile. On définit ensuite les permissions du fichier à 400 pour s'assurer que seul le propriétaire mongodb:mongodb peut lire et écrire le fichier. Tu peux en apprendre plus sur les keyfiles dans la doc MongoDB.

Note : le keyfile sert à l'authentification interne entre les membres du replica set. Il est important de garder le keyfile sécurisé et de ne pas l'exposer à des utilisateurs non autorisés.

Étape 3 : scripts d'initialisation

Initialisation MongoDB

Comme mentionné plus tôt, on va configurer une base de données initiale, un nom d'utilisateur et un mot de passe pour le replica set MongoDB. Pour ce faire, on utilise un script d'initialisation qui sera exécuté au démarrage du conteneur MongoDB. Crée maintenant un fichier init.js dans le dossier scripts/mongo avec le contenu suivant :

scripts/mongo/init.js
db = db.getSiblingDB('admin');

try {
  var rsConf = {
    _id: 'okydookReplSet',
    members: [
      { _id: 0, host: 'mongo1:27017', priority: 2 },
      { _id: 1, host: 'mongo2:27017' },
      { _id: 2, host: 'mongo3:27017' }
    ]
  };
  
  // Check if replica set is already initialized
  var isInitialized = false;
  try {
    var status = rs.status();
    if (status.ok === 1) {
      isInitialized = true;
      print("ReplicaSet already initiated");
    }
  } catch(e) {
    // rs.status() throws error if not initialized yet
    print("ReplicaSet not yet initiated, proceeding with initiation...");
  }
  
  if (!isInitialized) {
    rs.initiate(rsConf);
    print("ReplicaSet initiated successfully");
    
    // Wait for replica set to be ready
    sleep(20000);
  }
} catch(e) {
  print("Failed to initiate replica set: " + e.message);
  throw e;
}

Décortiquons le fichier init.js :

  • On se connecte à la base spécifiée par la variable MONGO_INITDB_DATABASE.
  • On crée un utilisateur spécifié par les variables DB_USERNAME et DB_PASSWORD avec le rôle readWrite sur la base.
  • On définit le write concern à majority avec un timeout de 5000 millisecondes. Cela garantit que l'opération d'écriture est acquittée par la majorité des membres du replica set.

Ce script s'exécute au démarrage du conteneur MongoDB, créant la base et l'utilisateur initiaux pour le replica set. Note que la partie scriptée n'est pas obligatoire et que tu pourrais utiliser des valeurs en dur. Cependant, selon la section III du 12-factor app, il vaut mieux utiliser les variables d'environnement pour stocker les secrets ou tout ce qui peut varier entre les déploiements.

Process de connexion au replicaset

#!/bin/bash

echo "Waiting for MongoDB to be ready..."
sleep 15

# Test connection
until mongosh --host mongo1:27017 -u "${MONGO_ROOT_USERNAME}" -p "${MONGO_ROOT_PASSWORD}" --authenticationDatabase admin --eval "db.adminCommand('ping')" > /dev/null 2>&1; do
    echo "Waiting for MongoDB connection..."
    sleep 5
done

echo "MongoDB is ready, initializing replica set..."

mongosh --host mongo1:27017 \
    -u "${MONGO_ROOT_USERNAME}" \
    -p "${MONGO_ROOT_PASSWORD}" \
    --authenticationDatabase admin \
    --eval "
        var MONGO_DB_USER='${MONGO_DB_USER}';
        var MONGO_DB_PASSWORD='${MONGO_DB_PASSWORD}';
    " \
    --file /init.js

if [ $? -eq 0 ]; then
    echo "Initialization completed successfully"
else
    echo "Initialization failed"
    exit 1
fi

Étape 4 : démarrer le cluster MongoDB Replica Set

Maintenant que tout est en place, on peut démarrer le cluster MongoDB replica set avec Docker Compose en exécutant la commande suivante :

docker-compose up -d

Note : le flag -d lance les conteneurs en mode détaché, ce qui signifie qu'ils tournent en arrière-plan.

Étape 5 : vérifier le replica set MongoDB

Pour vérifier que le replica set MongoDB tourne correctement, tu peux utiliser le shell mongo pour te connecter au nœud primary et vérifier le statut du replica set. Lance la commande suivante pour te connecter au primary :

docker exec -it mongo_rs0 mongosh -u admin -p veryStringPassword --authenticationDatabase admin

Note : remplace veryStringPassword par le mot de passe que tu as défini pour la variable MONGO_INITDB_ROOT_PASSWORD. Si tu n'en as pas défini, utilise veryStringPassword.

Une fois connecté, lance la commande suivante pour vérifier le statut du replica set :

rs.status()

Tu devrais voir le statut du replica set avec le nœud primary mongo1 et les secondary mongo2 et mongo3. La sortie devrait ressembler à :

members: [
    {
      _id: 0,
      name: 'mongo1:27017',
      health: 1,
      state: 1,
      stateStr: 'PRIMARY',
      uptime: 478,
      electionTime: Timestamp({ t: 1721354075, i: 1 }),
      electionDate: ISODate('2024-07-19T01:54:35.000Z'),
        ...
    },
    {
      _id: 1,
      name: 'mongo2:27018',
      health: 1,
      state: 2,
      stateStr: 'SECONDARY',
      uptime: 471,
      syncSourceHost: 'mongo1:27017',
        ...
    },
    {
      _id: 2,
      name: 'mongo3:27019',
      health: 1,
      state: 2,
      stateStr: 'SECONDARY',
      uptime: 471,
      syncSourceHost: 'mongo1:27017',
        ...
    }
  ],
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1721354536, i: 1 }),
    signature: {
      hash: Binary.createFromBase64('8KKPt/SmHkhGZDH299+yq6j5i04=', 0),
      keyId: Long('7393159456961331205')
    }
  },

Note : c'est une sortie tronquée, et elle peut varier selon la version de MongoDB utilisée.

Bonus : ajouter Mongo-Express pour une administration web

Ajoutons un outil d'administration web pour MongoDB en utilisant mongo-express. Pour ajouter mongo-express au fichier Docker Compose, ajoute la définition de service suivante :

docker-compose.yml
mongo-express:
  container_name: mongo-express
  image: 'mongo-express:latest'
  ports:
    - '8081:8081'
  networks:
    - mongo-cluster
  environment:
    ME_CONFIG_BASICAUTH: false
    ME_CONFIG_MONGODB_ENABLE_ADMIN: false
    ME_CONFIG_MONGODB_ADMINUSERNAME: '${MONGO_ADMIN_USER:-admin}'
    ME_CONFIG_MONGODB_ADMINPASSWORD: '${MONGO_ADMIN_PASSWD:-veryStrongPassword}'
    ME_CONFIG_MONGODB_URL: >-
      mongodb://${DB_USERNAME:-myuser}:${DB_PASSWORD:-veryStringPassword}@mongo1:27017,mongo2:27018,mongo3:27019/${DB_NAME}?replicaSet=rs0
  depends_on:
    mongo1:
      condition: service_healthy

Note : le service mongo-express dépend du service mongo1 healthy. Cela garantit que le replica set est en marche avant de démarrer le service mongo-express.

Tu peux accéder à mongo-express à http://localhost:8081 dans ton navigateur, et avoir quelque chose comme :

Connexion réussie à mongo-express

Note : pour connecter ton application au replica set MongoDB, tu peux utiliser la chaîne de connexion mongodb://${DB_USERNAME}:${DB_PASSWORD}@mongo1:27017,mongo2:27018,mongo3:27019/${DB_NAME}?replicaSet=rs0. Pense aussi à remplacer DB_USERNAME, DB_PASSWORD et DB_NAME par les credentials de ton application en définissant les variables d'environnement dans le fichier .env.

Si tout est bien configuré, tu devrais avoir quelque chose comme ceci dans ton Docker Desktop :

Replicaset créé avec succès

Et voilà, tu as un cluster MongoDB replica set avec un outil d'administration web !

Conclusion

Dans cet article, je t'ai guidé à travers la mise en place d'un cluster MongoDB replica set à 3 nœuds avec Docker Compose. On a couvert les bases des replica sets MongoDB, les prérequis pour mettre en place un replica set, et les étapes pour créer un fichier Docker Compose qui le configure. Je t'ai aussi montré comment vérifier le replica set et ajouter un outil d'administration web avec mongo-express. J'espère que ce guide t'aidera à démarrer avec les replica sets MongoDB et Docker Compose.

Dans un futur article, je parlerai de quelque chose de plus important : comment sauvegarder et restaurer une base MongoDB dans un environnement conteneurisé. Tu as peut-être entendu que les conteneurs Docker sont éphémères, donc il est important d'avoir une stratégie de backup en place pour éviter la perte de données. Reste à l'écoute !

Si tu as des questions ou des retours, n'hésite pas à me contacter. Bon code !

Références

S'abonner aux prochains articles

Recevez les nouveaux articles par e-mail. Pas de spam, désinscription à tout moment.

Propulsé par Buttondown.

Related posts

© 2026 < Denis AKPAGNONITE /> | N1BBzerLZXT