Overview of MongoDB Sharded Clusters
This guide outlines the process of deploying a MongoDB 4.0 sharded cluster on CentOS 7, leveraging a Primary-Secondary-Arbiter (PSA) replica set configuration for high availability and data distribution. A sharded cluster enhances scalability by distributing data across multiple servers (shards), enabling horizontal scaling and handling larger datasets and higher throughput than a single replica set.
Cluster Architecture
The proposed sharded cluster setup consists of:
- Config Servers (Config SVR): A replica set storing the cluster's metadata, including chunk ranges and shard locations. Three instances ensure high availability of metadata.
- Shard Replica Sets (Shards): Individual replica sets (each with a primary, secondary, and arbiter) that store the actual data. For this deployment, we'll configure two shard replica sets (Shard1 and Shard2).
- Query Routers (Mongos): Client-facing processes that route read and write operations to the appropriate shards. Multiple
mongosinstances provide load balancing and fault tolerance.
Component Distribution Across Hosts
We will use five CentOS 7 hosts, generically named host1 through host5, with specific roles and port assignments:
| Hostname | Role(s) |
|---|---|
host1 |
Shard1 Arbiter (Port 27026), Shard2 Arbiter (Port 27027) |
host2 |
mongos (Port 20000), Config Server (Port 27000), Shard1 Secondary (Port 27001) |
host3 |
mongos (Port 20000), Config Server (Port 27000), Shard2 Secondary (Port 27001) |
host4 |
mongos (Port 20000), Config Server (Port 27000), Shard1 Primary (Port 27001) |
host5 |
mongos (Port 20000), Shard2 Primary (Port 27001) |
Port Assignments
| Component | Port |
|---|---|
mongos |
20000 |
| Config Server | 27000 |
| Shard Member | 27001 |
| Arbiter 1 | 27026 |
| Arbiter 2 | 27027 |
Initial System Configuration on All Hosts (host1-host5)
1. Configure MongoDB Yum Repository
Create the repository file /etc/yum.repos.d/mongodb-org-4.0.repo:
sudo tee /etc/yum.repos.d/mongodb-org-4.0.repo << EOF
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
EOF
2. Install MongoDB 4.0 Packages
sudo yum install -y mongodb-org-4.0.9 mongodb-org-server-4.0.9 mongodb-org-shell-4.0.9 mongodb-org-mongos-4.0.9 mongodb-org-tools-4.0.9
To prevent unintended upgrades, exclude MongoDB packages from future yum updates:
echo 'exclude=mongodb-org,mongodb-org-server,mongodb-org-shell,mongodb-org-mongos,mongodb-org-tools' | sudo tee -a /etc/yum.conf
3. Create Data and Log Directories, Disable SELinux
Create necessary directories and adjust permissions. The mongod user will own these directories:
sudo mkdir -p /var/lib/mongo-cluster/config
sudo mkdir -p /var/lib/mongo-cluster/mongos
sudo mkdir -p /var/lib/mongo-cluster/shards/shard1
sudo mkdir -p /var/lib/mongo-cluster/shards/shard2
sudo mkdir -p /var/lib/mongo-cluster/arbiters/arbiter1
sudo mkdir -p /var/lib/mongo-cluster/arbiters/arbiter2
sudo chown -R mongod:mongod /var/lib/mongo-cluster
Disable SELinux by editing /etc/sysconfig/selinux and setting SELINUX=disabled. Then enforce the change immediately:
sudo sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/sysconfig/selinux
sudo setenforce 0
4. Adjust File Open Limits
Increase the maximum number of open files for the mongod process:
# For current session
ulimit -n 1000000
# For persistent changes
sudo tee /etc/security/limits.d/99-mongodb.conf << EOF
* soft nofile 1000000
* hard nofile 1000000
* soft fsize unlimited
* hard fsize unlimited
* soft cpu unlimited
* hard cpu unlimited
* soft nproc 500000
* hard nproc 500000
EOF
5. Install NUMA Utilities and Disable Transparent Huge Pages
Install numactl:
sudo yum install -y numactl
Disable Transparent Huge Pages (THP) as recommended for MongoDB performance:
echo "never" | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo "never" | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
Also, disable zone reclaim mode:
sudo sysctl -w vm.zone_reclaim_mode=0
Configuring and Starting Cluster Components
1. Config Server Replica Set Setup (host2, host3, host4)
On each of host2, host3, and host4, create the config server configuration file /var/lib/mongo-cluster/config/mongodb.conf:
processManagement:
fork: true
net:
bindIp: 0.0.0.0
port: 27000
storage:
dbPath: /var/lib/mongo-cluster/config/data
replication:
replSetName: cfgReplSet
sharding:
clusterRole: configsvr
systemLog:
destination: file
path: "/var/lib/mongo-cluster/config/log/config.log"
logAppend: true
timeStampFormat: ctime
Create data and log directories for the config servers on these hosts:
sudo mkdir -p /var/lib/mongo-cluster/config/data
sudo mkdir -p /var/lib/mongo-cluster/config/log
sudo chown -R mongod:mongod /var/lib/mongo-cluster/config
Start the config server processes on host2, host3, and host4:
numactl --interleave=all mongod -f /var/lib/mongo-cluster/config/mongodb.conf
Initialize the config server replica set. Connect to any one of the config servers (e.g., host2):
mongo --port 27000
Then execute:
rs.initiate(
{
_id: "cfgReplSet",
configsvr: true,
members: [
{ _id : 0, host : "host2:27000" },
{ _id : 1, host : "host3:27000" },
{ _id : 2, host : "host4:27000" }
]
}
)
Verify the replica set status with rs.status().
2. Shard Replica Sets Setup (Shard1 and Shard2)
Shard Member Configuration
On host2 (Shard1 Secondary), host3 (Shard2 Secondary), host4 (Shard1 Primary), and host5 (Shard2 Primary), create the shard member configuration file /var/lib/mongo-cluster/shards/shard_member.conf. Note that replSetName will differ for Shard1 and Shard2.
For Shard1 Members (host2, host4): Use replSetName: shard1
processManagement:
fork: true
pidFilePath: /var/lib/mongo-cluster/shards/shard1/log/mongod.pid
net:
bindIp: 0.0.0.0
port: 27001
serviceExecutor: adaptive
storage:
dbPath: /var/lib/mongo-cluster/shards/shard1/data
journal:
enabled: true
commitIntervalMs: 200
replication:
replSetName: shard1
sharding:
clusterRole: shardsvr
systemLog:
destination: file
path: "/var/lib/mongo-cluster/shards/shard1/log/shard1.log"
logAppend: true
timeStampFormat: ctime
For Shard2 Members (host3, host5): Use replSetName: shard2
processManagement:
fork: true
pidFilePath: /var/lib/mongo-cluster/shards/shard2/log/mongod.pid
net:
bindIp: 0.0.0.0
port: 27001
serviceExecutor: adaptive
storage:
dbPath: /var/lib/mongo-cluster/shards/shard2/data
journal:
enabled: true
commitIntervalMs: 200
replication:
replSetName: shard2
sharding:
clusterRole: shardsvr
systemLog:
destination: file
path: "/var/lib/mongo-cluster/shards/shard2/log/shard2.log"
logAppend: true
timeStampFormat: ctime
Create data and log directories for shard members:
- On
host2andhost4:
sudo mkdir -p /var/lib/mongo-cluster/shards/shard1/data
sudo mkdir -p /var/lib/mongo-cluster/shards/shard1/log
sudo chown -R mongod:mongod /var/lib/mongo-cluster/shards/shard1
- On
host3andhost5:
sudo mkdir -p /var/lib/mongo-cluster/shards/shard2/data
sudo mkdir -p /var/lib/mongo-cluster/shards/shard2/log
sudo chown -R mongod:mongod /var/lib/mongo-cluster/shards/shard2
Arbiter Node Configuration (host1)
On host1, create two arbiter configuration files:
For Shard1 Arbiter: /var/lib/mongo-cluster/arbiters/arbiter1/arbiter.conf
processManagement:
fork: true
pidFilePath: /var/lib/mongo-cluster/arbiters/arbiter1/log/arbiter.pid
net:
bindIp: 0.0.0.0
port: 27026
serviceExecutor: adaptive
storage:
dbPath: /var/lib/mongo-cluster/arbiters/arbiter1/data
journal:
enabled: true
commitIntervalMs: 200
replication:
replSetName: shard1
sharding:
clusterRole: shardsvr
systemLog:
destination: file
path: "/var/lib/mongo-cluster/arbiters/arbiter1/log/arbiter.log"
logAppend: true
timeStampFormat: ctime
For Shard2 Arbiter: /var/lib/mongo-cluster/arbiters/arbiter2/arbiter.conf
processManagement:
fork: true
pidFilePath: /var/lib/mongo-cluster/arbiters/arbiter2/log/arbiter.pid
net:
bindIp: 0.0.0.0
port: 27027
serviceExecutor: adaptive
storage:
dbPath: /var/lib/mongo-cluster/arbiters/arbiter2/data
journal:
enabled: true
commitIntervalMs: 200
replication:
replSetName: shard2
sharding:
clusterRole: shardsvr
systemLog:
destination: file
path: "/var/lib/mongo-cluster/arbiters/arbiter2/log/arbiter.log"
logAppend: true
timeStampFormat: ctime
Create data and log directories for arbiters on host1:
sudo mkdir -p /var/lib/mongo-cluster/arbiters/arbiter1/data
sudo mkdir -p /var/lib/mongo-cluster/arbiters/arbiter1/log
sudo mkdir -p /var/lib/mongo-cluster/arbiters/arbiter2/data
sudo mkdir -p /var/lib/mongo-cluster/arbiters/arbiter2/log
sudo chown -R mongod:mongod /var/lib/mongo-cluster/arbiters
Start Shard Members and Arbiters
- Start shard members on
host2,host3,host4,host5(adjust config file path based on shard):
numactl --interleave=all mongod -f /var/lib/mongo-cluster/shards/shard1/shard_member.conf # For host2, host4
numactl --interleave=all mongod -f /var/lib/mongo-cluster/shards/shard2/shard_member.conf # For host3, host5
- Start arbiters on
host1:
numactl --interleave=all mongod -f /var/lib/mongo-cluster/arbiters/arbiter1/arbiter.conf
numactl --interleave=all mongod -f /var/lib/mongo-cluster/arbiters/arbiter2/arbiter.conf
Initialize Shard Replica Sets
Connect to a primary or secondary of each shard replica set (e.g., host4:27001 for Shard1, host5:27001 for Shard2) and initialize:
mongo --port 27001
For Shard1 (e.g., connected to host4:27001):
rs.initiate(
{
_id: "shard1",
members: [
{ _id: 0, host: "host4:27001", priority: 2 }, // Primary
{ _id: 1, host: "host2:27001", priority: 1 }, // Secondary
{ _id: 2, host: "host1:27026", arbiterOnly: true } // Arbiter
]
}
)
For Shard2 (e.g., connected to host5:27001):
rs.initiate(
{
_id: "shard2",
members: [
{ _id: 0, host: "host5:27001", priority: 2 }, // Primary
{ _id: 1, host: "host3:27001", priority: 1 }, // Secondary
{ _id: 2, host: "host1:27027", arbiterOnly: true } // Arbiter
]
}
)
Verify each replica set status with rs.status().
3. Mongos Query Router Setup (host2, host3, host4, host5)
On each of host2, host3, host4, and host5, create the mongos configuration file /var/lib/mongo-cluster/mongos/mongos.conf:
processManagement:
fork: true
pidFilePath: /var/lib/mongo-cluster/mongos/log/mongos.pid
net:
bindIp: 0.0.0.0
port: 20000
serviceExecutor: adaptive
sharding:
configDB: cfgReplSet/host2:27000,host3:27000,host4:27000
systemLog:
destination: file
path: "/var/lib/mongo-cluster/mongos/log/mongos.log"
logAppend: true
timeStampFormat: ctime
Create log directories for mongos instances on these hosts:
sudo mkdir -p /var/lib/mongo-cluster/mongos/log
sudo chown -R mongod:mongod /var/lib/mongo-cluster/mongos
Start the mongos processes on host2, host3, host4, and host5:
numactl --interleave=all mongos -f /var/lib/mongo-cluster/mongos/mongos.conf
4. Configure the Sharded Cluster
Connect to any mongos instance (e.g., host2:20000) to configure the cluster:
mongo --port 20000
Add the shard replica sets to the cluster:
sh.addShard("shard1/host4:27001,host2:27001,host1:27026")
sh.addShard("shard2/host5:27001,host3:27001,host1:27027")
Verify the cluster status:
sh.status()
Ensure the balancer is running:
sh.getBalancerState() // Check state
sh.startBalancer() // Start if not running
Securing the Cluster with Keyfile Authentication
Keyfile authentication is crucial for inter-component communication within the sharded cluster. Each mongod and mongos instance uses the keyfile as a shared password.
1. Create an Administrator User
Connect to a mongos instance and create an administrative user with root privileges:
mongo --port 20000
use admin
db.createUser(
{
user: "clusterAdmin",
pwd: "yourStrongPassword",
roles: [ { role: "root", db: "admin" } ]
}
)
2. Generate a Keyfile
On each of host1 through host5, generate a random keyfile and set appropriate permissions (read-only for the owner):
sudo openssl rand -base64 756 > /var/lib/mongo-cluster/mongodb-keyfile
sudo chmod 600 /var/lib/mongo-cluster/mongodb-keyfile
sudo chown mongod:mongod /var/lib/mongo-cluster/mongodb-keyfile
Ensure this keyfile is identical across all hosts that run mongod or mongos processes.
3. Update Configuration Files
Edit the configuration files for all Config Servers, Shard Members, Arbiters, and Mongos instances to enable authentication:
security:
authorization: enabled
keyFile: /var/lib/mongo-cluster/mongodb-keyfile
Add these lines under the security section of the respective .conf files (/var/lib/mongo-cluster/config/mongodb.conf, /var/lib/mongo-cluster/shards/shard1/shard_member.conf, /var/lib/mongo-cluster/shards/shard2/shard_member.conf, /var/lib/mongo-cluster/arbiters/arbiter1/arbiter.conf, /var/lib/mongo-cluster/arbiters/arbiter2/arbiter.conf, /var/lib/mongo-cluster/mongos/mongos.conf).
4. Restart the Cluster with Authentication
Stop the Cluster
First, stop the balancer via a mongos instance:
mongo --port 20000 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
sh.stopBalancer()
Then, shut down all mongos, then shard members (secondary first, then primary), and finally config servers:
# On each mongos host (host2, host3, host4, host5)
mongo --port 20000 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
use admin
db.shutdownServer()
exit
# On each shard member (host1, host2, host3, host4, host5), for secondary/arbiter first
# For shard members:
mongo --port 27001 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
db.getSiblingDB("admin").shutdownServer()
# For arbiters:
mongo --port 27026 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
db.getSiblingDB("admin").shutdownServer()
mongo --port 27027 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
db.getSiblingDB("admin").shutdownServer()
# On each config server (host2, host3, host4), secondary first
mongo --port 27000 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
db.getSiblingDB("admin").shutdownServer()
Start the Cluster (with authentication enabled)
Start the components in the following order, ensuring to use numactl:
- Config Servers: On
host2,host3,host4:
numactl --interleave=all mongod -f /var/lib/mongo-cluster/config/mongodb.conf
-
Shard Members & Arbiters:
- On
host2,host4: Shard1 members - On
host3,host5: Shard2 members - On
host1: Arbiters for Shard1 and Shard2
numactl --interleave=all mongod -f /var/lib/mongo-cluster/shards/shard1/shard_member.conf numactl --interleave=all mongod -f /var/lib/mongo-cluster/shards/shard2/shard_member.conf numactl --interleave=all mongod -f /var/lib/mongo-cluster/arbiters/arbiter1/arbiter.conf numactl --interleave=all mongod -f /var/lib/mongo-cluster/arbiters/arbiter2/arbiter.conf - On
-
Mongos Routers: On
host2,host3,host4,host5:
numactl --interleave=all mongos -f /var/lib/mongo-cluster/mongos/mongos.conf
- Re-enable Balancer: Connect to any
mongosinstance using the admin credentials:
mongo --port 20000 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
sh.startBalancer()
sh.getBalancerState() // Verify balancer status
5. Connection Test with Authentication
Test connections to various cluster components using the created admin user:
mongo --port 20000 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
mongo --port 27000 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
mongo --port 27001 --authenticationDatabase "admin" -u "clusterAdmin" -p "yourStrongPassword"
Data Flow in a Sharded Cluster
Query Data Flow
- Client Request: A client sends a query to a
mongosrouter. - Metadata Lookup: The
mongosqueries the Config Servers to determine which shards hold the relevant data, based on the query's shard key. - Query Routing:
mongosroutes the query to the appropriate shard(s). If the query involves data across multiple shards,mongosmight send concurrent queries. - Shard Execution: Each target
mongodon the shard processes its portion of the query. This involves parsing the query, checking indexes, and retrieving data. - Index Optimization: If an index covers the query, the
mongodcan rapidly locate data without scanning entire collections. Data might be in memory (cache) or on disk. - Data Retrieval: If data is on disk, the storage engine loads it into memory.
- Filtering and Processing: Each
mongodfilters, sorts, and aggregates its local data according to the query criteria. - Result Consolidation: Each
mongodsends its partial results back to themongosrouter. - Final Result Assembly: The
mongosaggregates the results from all shards, performs any final sorting or merging, and returns the complete result set to the client.
Insert Data Flow
- Client Request: A client sends an insert operation with document data to a
mongosrouter. - Shard Key Determination:
mongosextracts the shard key value from the document to be inserted. - Chunk Identification:
mongosconsults the Config Servers to determine which chunk range the shard key falls into and, consequently, which shard owns that chunk. - Target Shard Selection: Based on the shard key and chunk distribution,
mongosselects the appropriate shard to handle the insert. - Data Insertion:
mongosforwards the insert request to the primary member of the target shard's replica set. - Local Write Operation: The primary
mongodon the target shard performs the data insertion locally. If the insertion causes a chunk to grow beyond its maximum size, a chunk split operation might be triggered asynchronously by the balancer. - Replication: The primary replicates the write operation to its secondary members.
- Acknowledgement: Once the write is confirmed (according to the write concern), the target shard's primary sends an acknowledgment back to the
mongos. - Client Confirmation:
mongosforwards the success or failure of the insert operation back to the client.