MongoDB Sharded Cluster with Replica Set in Docker
This blog guides you through deploying a sharded cluster using Docker — ideal for local testing, learning, and adding a dash of fun to your database exploration journey. Let’s dive in!
Cluster Components:
The components in a sharded cluster are:
- Shards (with replica sets)
- Config Server (replica set)
- Mongos
In total, we’ll have 11 docker containers:
- 2 shards each of them configured as a replica set with 3 instances i.e. one primary and two secondary.
- 1 config server configured as a replica set with 3 instances i.e. one primary and two secondary.
- 2 mongos instances.
Brief introduction to all the components
Replica set
- A group of 2 or more MongoDB instances that maintain identical data sets to provide high availability.
- It has 1 primary node for write operation and 1 or more secondary node that replicate data written on primary and allow read operation.
- If the primary node fails, one of the secondaries is automatically elected to primary, ensuring high availability and fault tolerance.
Shard
- When sharding is enabled data is broken down and distributed (not replicated) across multiple servers known as shards.
- This enhances the horizontal scalability, i.e. you can add more shards to the cluster when required.
- Each shard manages a portion of the dataset, allowing for parallel processing and improved performance.
Config Server
- It stores metadata and configuration settings for sharded clusters.
- It keeps track of the sharding key ranges and provide information about the distribution of data across the shards to MongoDB router (mongos) to route queries efficiently to the appropriate shards.
- Its a single point of failure and to avoid that it’s generally deployed as a replica set.
Mongos
- It routes client requests to the appropriate shard in a sharded MongoDB cluster.
- It acts as an interface between the application and the sharded environment, ensuring that read and write operations are seamlessly and correctly distributed among the shards.
- Its also a single point of failure and to avoid we generally deploy 2 instances of mongos for a sharded cluster.
Prerequisites
- Docker must be installed and running.
- Internet connectivity for downloading container images.
- mongosh command line tool to connect to mongo instances
Lets Deploy
Step 1: Create a docker network
docker network create mongodb-network
In case you already have a network of this name please check if you’re not using it, if not then, try deleting and recreating:
docker network ls # shows present networks
docker network rm mongodb-network # delete network "mongodb-network"
Or you may create a network with another name but make sure to modify the commands mentioned ahead accordingly.
Why create a docker network?
Its not necessary but recommended as it provides better isolation and a more consistent deployment experience.
Step 2: Setup Sharded Clusters with Replica Sets
Shard 1 (mongo-shard1)
docker run -d -p 27101:27017 --name mongo-shard1-1 --network mongodb-network mongo:5 mongod --shardsvr --replSet mongo-shard1-rs --port 27017 --bind_ip localhost,mongo-shard1-1
docker run -d -p 27102:27017 --name mongo-shard1-2 --network mongodb-network mongo:5 mongod --shardsvr --replSet mongo-shard1-rs --port 27017 --bind_ip localhost,mongo-shard1-2
docker run -d -p 27103:27017 --name mongo-shard1-3 --network mongodb-network mongo:5 mongod --shardsvr --replSet mongo-shard1-rs --port 27017 --bind_ip localhost,mongo-shard1-3
Shard 2 (mongo-shard2)
docker run -d -p 27201:27017 --name mongo-shard2-1 --network mongodb-network mongo:5 mongod --shardsvr --replSet mongo-shard2-rs --port 27017 --bind_ip localhost,mongo-shard2-1
docker run -d -p 27202:27017 --name mongo-shard2-2 --network mongodb-network mongo:5 mongod --shardsvr --replSet mongo-shard2-rs --port 27017 --bind_ip localhost,mongo-shard2-2
docker run -d -p 27203:27017 --name mongo-shard2-3 --network mongodb-network mongo:5 mongod --shardsvr --replSet mongo-shard2-rs --port 27017 --bind_ip localhost,mongo-shard2-3
Check connectivity via:
docker exec -it mongo-shard1-1 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-shard1-2 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-shard1-3 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-shard2-1 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-shard2-2 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-shard2-3 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
You should see “ok: 1” in the output. Or you can even get the shell via:
mongosh mongodb://localhost:27101
mongosh mongodb://localhost:27102
mongosh mongodb://localhost:27103
mongosh mongodb://localhost:27201
mongosh mongodb://localhost:27202
mongosh mongodb://localhost:27203
Note: The mongo image we’re using automatically creates a docker volume for database persistence. You can get the volume via:
docker inspect mongo-shard1-1 | grep -A 20 "Mounts"
Step 3: Initialize the Replica Sets
Replica Set 1 (mongo-shard1-rs)
docker exec -it mongo-shard1-1 mongosh --eval "rs.initiate({
_id: \"mongo-shard1-rs\",
members: [
{_id: 0, host: \"mongo-shard1-1\"},
{_id: 1, host: \"mongo-shard1-2\"},
{_id: 2, host: \"mongo-shard1-3\"}
]
})"
Replica Set 2 (mongo-shard2-rs)
docker exec -it mongo-shard2-1 mongosh --eval "rs.initiate({
_id: \"mongo-shard2-rs\",
members: [
{_id: 0, host: \"mongo-shard2-1\"},
{_id: 1, host: \"mongo-shard2-2\"},
{_id: 2, host: \"mongo-shard2-3\"}
]
})"
You can check status of the replica sets via rs.status() command:
docker exec -it mongo-shard1-1 mongosh --eval "rs.status()"
docker exec -it mongo-shard2-1 mongosh --eval "rs.status()"
Step 4: Setup Config server Replica Set
Config Server (mongo-config-server)
docker run -dit --name mongo-config-server-1 --net mongodb-network -p 27001:27017 mongo:5 --configsvr --replSet mongo-config-server-rs --port 27017 --bind_ip localhost,mongo-config-server-1
docker run -dit --name mongo-config-server-2 --net mongodb-network -p 27002:27017 mongo:5 --configsvr --replSet mongo-config-server-rs --port 27017 --bind_ip localhost,mongo-config-server-2
docker run -dit --name mongo-config-server-3 --net mongodb-network -p 27003:27017 mongo:5 --configsvr --replSet mongo-config-server-rs --port 27017 --bind_ip localhost,mongo-config-server-3
Check connectivity via:
docker exec -it mongo-config-server-1 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-config-server-2 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongo-config-server-3 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
You should see “ok: 1” in the output. Or get the shell via:
mongosh mongodb://localhost:27001
mongosh mongodb://localhost:27002
mongosh mongodb://localhost:27003
Step 5: Initialize Config server Replica Set
Replica Set (mongo-config-server-rs)
docker exec -it mongo-config-server-1 mongosh --port 27017 --eval "rs.initiate({
_id: \"mongo-config-server-rs\",
members: [
{_id: 0, host: \"mongo-config-server-1\"},
{_id: 1, host: \"mongo-config-server-2\"},
{_id: 2, host: \"mongo-config-server-3\"}
]
})"
Check status of the replica sets via rs.status() command:
docker exec -it mongo-config-server-1 mongosh --port 27017 --eval "rs.status()"
Step 6: Setup mongos
mongos-router-1
docker run -dit --name mongos-router-1 --net mongodb-network -p 27100:27017 mongo:5 mongos --configdb mongo-config-server-rs/mongo-config-server-1:27017,mongo-config-server-2:27017,mongo-config-server-3:27017 --port 27017 --bind_ip localhost,mongos-router-1
mongos-router-2
docker run -dit --name mongos-router-2 --net mongodb-network -p 27200:27017 mongo:5 mongos --configdb mongo-config-server-rs/mongo-config-server-1:27017,mongo-config-server-2:27017,mongo-config-server-3:27017 --port 27017 --bind_ip localhost,mongos-router-2
Check connectivity via:
docker exec -it mongos-router-1 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
docker exec -it mongos-router-2 mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" | grep ok
You should see “ok: 1” in the output. Or get the shell via:
mongosh mongodb://localhost:27100
mongosh mongodb://localhost:27200
Step 7: Connect Shards with mongos
Connecting shards to mongos-router-1
docker exec -it mongos-router-1 mongosh --port 27017 --eval "sh.addShard(\"mongo-shard1-rs/mongo-shard1-1:27017,mongo-shard1-2:27017,mongo-shard1-3:27017\")"
docker exec -it mongos-router-1 mongosh --port 27017 --eval "sh.addShard(\"mongo-shard2-rs/mongo-shard2-1:27017,mongo-shard2-2:27017,mongo-shard2-3:27017\")"
Connecting shards to mongos-router-2
docker exec -it mongos-router-2 mongosh --port 27017 --eval "sh.addShard(\"mongo-shard1-rs/mongo-shard1-1:27017,mongo-shard1-2:27017,mongo-shard1-3:27017\")"
docker exec -it mongos-router-2 mongosh --port 27017 --eval "sh.addShard(\"mongo-shard2-rs/mongo-shard2-1:27017,mongo-shard2-2:27017,mongo-shard2-3:27017\")"
Connecting to your cluster
Connection String:
mongodb://localhost:27100,localhost:27200
You can connect via mongosh command:
mongosh mongodb://localhost:27100,localhost:27200
Since we have 2 mongos i.e. mongos-router-1 at localhost:27100 and mongos-router-2 at localhost:27200, so after connecting to the mongos shell even if one of them fails you’ll seamlessly be connected to the other mongos without any downtime.
To create or use an existing database:
use <database>
Note: Since we’re using MongoDB 5.0 we also need to run:
sh.enableSharding("<database-name>")
and to shard collection:
sh.shardCollection("<database-name>.<collection-name>", { <shard-key>: "hashed", ...})
And there you have it — a MongoDB Sharded Cluster running on your local machine!
Cleanup
For cleanup use:
Warning: Using -v will also remove volumes associated with the containers!
docker rm -vf mongos-router-1 mongos-router-2 mongo-config-server-1 mongo-config-server-2 mongo-config-server-3 mongo-shard1-1 mongo-shard1-2 mongo-shard1-3 mongo-shard2-1 mongo-shard2-2 mongo-shard2-3
docker network rm mongodb-network
There are many more configurable parameters in MongoDB, as detailed in the official documentation. And there are a ton of other things you can do in docker like:
- Mount a config file via docker volumes instead of commands to configure the components.
- Adjust logging and even configure dedicated volumes for logs.
- etcetera.
I hope this guide was helpful. If so, please give it a like and consider sharing it with your fellow tech enthusiasts.
Thank you!
Until next time!