How to create and configure MongoDB replica set with docker-compose
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).
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..