How to create and configure MongoDB replica set with docker-compose

·

4 min read

MongoDB is an open source NoSQL database, popular for ‘cloud native’ applications that don’t require strict consistency guarantees. As mongoDB stores data in documents (opposite to a RDBMS schema consisting of tables, rows, and relations) it scales well and allows easier sharding of the large collections across multiple machines/clusters. Production deployment of mongoDB is usually done as a replica set to provide redundancy and fault tolerance. The standard replica set deployment for a production system is a three-member replica set.

In this blog post we will create a setup for a 3-member mongoDB replica set with regular backups using docker-compose.

Prerequisites

Docker 19.03.0+ installed and docker-compose that supports 3.9 version

MongoDB replica set members

  • Primary - accepting writes from the application
  • Secondary - replicate operations from the primary to maintain an identical data set. Secondaries may have additional configurations for special usage profiles (like backups, non-voting member, arbiter).

image.png

Note: The value of the member's priority setting determines the member's priority in elections. The higher the number, the higher the priority (please see below mongo_setup.sh script).

MongoDB replica set in docker-compose.yml consists of:

  • mongodb-primary (accepting writes from the application)
  • mongodb-secondary-1 (secondary)
  • mongodb-secondary-2 (secondary, used for backups)
  • mongodb-setup (temporary docker container responsible for initiation of the replica set)

Bash scripts used to setup mongodb replica and backups:

  • mongo_setup.sh - initialise mongodb replica set (used in mongodb-setup container)
  • mongo_backup_scheduler.sh - using cron schedules mongo backups (used in mongodb-secondary-2 container)
  • mongo_backup.sh - runs mongodump command (used in mongo_backup_scheduler.sh)

For each member inside the docker container a mongod instance started with the following settings: --port 27017 --bind_ip_all --replSet mongo-replica , where --replSet defines replica set name. mongodb-setup container is started for a short period of time to initialise mongoDB replica set. Alternatively this can be achieved by leveraging docker container healthcheck but I’d rather use healthcheck as it designed to be used.

docker-compose.yml to setup mongoDB replica set and backups:

version: "3.9"
services:

  mongodb-primary:
    image: mongo:5.0
    container_name: mongodb1
    hostname: mongodb1
    command: --bind_ip_all --replSet mongo-replica
    networks:
      mongodb_network:
    volumes:
      - mongodb_data_1:/data/modelDb
    expose:
      - 27017

  mongodb-secondary-1:
    image: mongo:5.0
    container_name: mongodb2
    hostname: mongodb2
    command: --bind_ip_all --replSet mongo-replica
    networks:
      mongodb_network:
    volumes:
      - mongodb_data_2:/data/modelDb
    expose:
      - 27017

  mongodb-secondary-2:
    image: mongo:5.0
    container_name: mongodb3
    hostname: mongodb3
    command: --bind_ip_all --replSet mongo-replica
    networks:
      mongodb_network:
    environment:
      DB: modelDb
    volumes:
      - mongodb_data_3:/data/modelDb
      - ./scripts:/scripts
    expose:
      - 27017

  mongosetup:
    image: mongo:5.0
    depends_on:
      - mongodb-primary
      - mongodb-secondary-1
      - mongodb-secondary-2
    restart: "no"
    networks:
      mongodb_network:
    volumes:
      - ./scripts:/scripts
    environment:
      DB: modelDb
    entrypoint: [ "bash", "-c", "sh ./scripts/mongo_setup.sh"]

volumes:
  mongodb_data_1:
  mongodb_data_2:
  mongodb_data_3:

networks:
  mongodb_network:

mongo_setup.sh script runs in mongosetup container as entrypoint to initiate replica set:

#!/bin/bash
sleep 10

echo SETUP.sh time now: `date +"%T" `
mongo --host mongodb1:27017 <<EOF
 var cfg = {
   "_id": "mongo-replica",
   "version": 1,
   "members": [
     {
       "_id": 0,
       "host": "mongodb1:27017",
       "priority": 2
     },
     {
       "_id": 1,
       "host": "mongodb2:27017",
       "priority": 1
     },
     {
       "_id": 2,
       "host": "mongodb3:27017",
       "priority": 0
     }
   ]
 };
 rs.initiate(cfg, { force: true });
 rs.reconfig(cfg, { force: true });
 rs.secondaryOk();
 db.getMongo().setReadPref('nearest');
 db.getMongo().setSecondaryOk();
EOF

MongoDB backups

When deploying MongoDB in production, it is important to have a strategy for capturing and restoring backups in the case of data loss events.

Without paid subscription to Enterprise Advanced, mongoDB backup options are:

  • Backup with Filesystem Snapshots
  • Backup with cp or rsync
  • Backup with mongodump

In this blog post mongodump is used for backups. This approach is efficient tools for backing up and restoring small MongoDB deployments, but are not ideal for capturing backups of larger systems. Backups set up to happen at 1 am each day using cron command and scheduled on mongodb-secondary-2 docker container.

mongo_backup_scheduler.sh run in mongodb-secondary-2 container (please see docker-compose.yml) to schedule daily backups:

#!/bin/bash

apt-get update
apt-get install nano
apt-get install cron

# Start cron service
service cron start

mkdir -p /data/db/backups
touch /data/db/backups/logs
touch /var/spool/cron/crontabs/root

# Everyday at 1 a.m.
CRON_COMMAND="00 01 * * * /bin/bash /scripts/mongo_backup.sh > /data/db/backups/logs"

crontab -l | grep -F -q "$CRON_COMMAND" && echo 'Backup has already been scheduled.' || echo 'Scheduling new backup cron job for mongodb.' && crontab -l | { cat; echo "$CRON_COMMAND"; } | crontab -

echo "Joining mongodb replica set: mongo-replica"
mongod --port 27017 --bind_ip_all --replSet mongo-replica

mongo_backup.sh used in mongo_backup_scheduler.sh and runs mongodump command:

#!/bin/bash

set -e

BACKUP_NAME=$(date +%y%m%d_%H%M%S).gz

date
echo "Backing up MongoDB database"

echo "Dumping MongoDB modelDb database to compressed archive: $BACKUP_NAME"
mongodump --port 27019 --db modelDb --archive=/data/db/backups/$BACKUP_NAME --gzip >> /data/db/backups/logs

echo 'Backup completed successfully!'

Run

From root folder run:

docker-compose up

Source code

Can be found on Github: github.com/gwenu/blog-howto/tree/master/how..