Friday, February 17, 2017

Replication Set in MongoDB – HowTo

Following write up describe how one can set up Replica Set in MongoDB (3x version). It is tested on CenOS-6x version. The same setup should work in any *nix based system. 

The IPs cited here are for example only.

A.  Participating Servers


1.   primary-ins   ========    172.16.20.3
2.   secondary-ins ========    172.16.20.4
3.   arbiter       ========     172.16.20.5

# cat /etc/centos-release
CentOS release 6.8 (Final)
    
     [On Openstack cloud platform]

B.   Host resolution (on all three servers)

     /etc/hosts
    
     172.16.20.3          primary-ins    
     172.16.20.4          secondary-ins  
     172.16.20.5          arbiter  

C.        Installed packages (On all three)

    
mongodb-org-shell-3.4.2-1.el6.x86_64
     mongodb-org-tools-3.4.2-1.el6.x86_64
     mongodb-org-server-3.4.2-1.el6.x86_64
     mongodb-org-mongos-3.4.2-1.el6.x86_64
     mongodb-org-3.4.2-1.el6.x86_64 
    

D.  Firewall considerations

* MongoDB listen on all interfaces instead of Loopback on all
  three hosts.
       [tcp 0  0 0.0.0.0:27017 0.0.0.0:*  LISTEN  22913/mongod]
    

     1. IPTABLES:

        Allowed entire subnet in Iptables INPUT chain for port 27017         (in all three hosts)
          
         # iptables -I INPUT -p tcp -s 172.16.20.0/24 --dport 27017 -j ACCEPT
         # service iptables save
         iptables: Saving firewall rules to /etc/sysconfig/iptables:[  OK  ]
         # service iptables restart
         iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
         iptables: Flushing firewall rules:                         [  OK  ]
         iptables: Unloading modules:                               [  OK  ]
         iptables: Applying firewall rules:                         [  OK  ]
          
         # iptables -L | grep 27017
         ACCEPT     tcp  --  172.16.20.0/24   anywhere   tcp dpt:27017
              2.         SeLinux
          
           SeLinux changed from Enforcing to Permissive:
           # getenforce
            Enforcing
             
           #setenforce permissive

E.  Replication Setup (all three) /etc/mongod.conf

     replication:

       ## Replica Set name, consistent across all the systems
         replSetName: myRepSet

       ## Operation Log file size
         oplogSizeMB: 64

F.  Restart Mongo service (on all three, any order)

# service mongod restart 

G.  Initiate replica set

Connect to any Mongo Server and run rs.initiate() to initiate the new replica set (preferably connect to the one that would act as Primary):
    
# mongo
      
       > rs.initiate()
       {
"info2" : "no configuration specified. Using a default configuration for the set",
                     "me" : "primary-ins:27017",
                     "ok" : 1
       }
       myRepSet:OTHER>
    
    
     Note:
Once the initialization is done, the prompt would change to;
     myRepSet:PRIMARY>
    

H.  Add replica set members


Now, while on the Primary Server's prompt, add the other members (Secondary and the Arbiter) as follows;

     i.     Add seondary:
              myRepSet:PRIMARY> rs.add("secondary-ins:27017")
              { "ok" : 1 }

       ii.    Add arbiter
              myRepSet:PRIMARY> rs.addArb("arbiter:27017")
              { "ok" : 1 }

I.  Connect to other two Mongo servers and check the prompt(s):

     myRepSet:SECONDARY>
     myRepSet:ARBITER>

J.  Allow read from secondary members

Execute rs.slaveOk(). This is to allow you to read from secondary members (including the Arbiter);
    
myRepSet:SECONDARY> rs.slaveOk()
myRepSet:ARBITER> rs.slaveOk()

K.  Test

    Create a DB and insert a document to a collection on the primary
and check if it that has been replicated to the secondary;
    
     i.   Create DB and insert document on Primary Mongo server;
          
        myRepSet:PRIMARY> use test_replica
        switched to db test_replica
             
        myRepSet:PRIMARY> db.movie.insert({"name":"Jungle Book"})
        WriteResult({ "nInserted" : 1 })

     ii.  On the secondary Mongo server;
          
           myRepSet:SECONDARY> show dbs
           admin         0.000GB
           local         0.000GB
           test_replica  0.000GB
             
           myRepSet:SECONDARY> use test_replica
           switched to db test_replica
           myRepSet:SECONDARY> show collections
           movie
             
           myRepSet:SECONDARY> db.movie.find({})
           { "_id" : ObjectId("58963c37bede8f63243f5e52"), "name" : "Jungle Book" }
          
    
     iii. On Arbiter (Remember Arbiter does not take part in Data
replication, its only job is to elect member during a Failover)
          
myRepSet:ARBITER> show dbs
          admin  0.000GB
          local  0.000GB
               

L.   Check the configuration


myRepSet:PRIMARY> rs.conf()
{
        "_id" : "myRepSet",
        "version" : 3,
        "protocolVersion" : NumberLong(1),
        "members" : [
                {
                        "_id" : 0,
                        "host" : "primary-ins:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 1,
                        "host" : "secondary-ins:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "arbiter:27017",
                        "arbiterOnly" : true,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                }
        ],
        "settings" : {
                "chainingAllowed" : true,
                "heartbeatIntervalMillis" : 2000,
                "heartbeatTimeoutSecs" : 10,
                "electionTimeoutMillis" : 10000,
                "catchUpTimeoutMillis" : 2000,
                "getLastErrorModes" : {

                },
                "getLastErrorDefaults" : {
                        "w" : 1,
                        "wtimeout" : 0
                },
                "replicaSetId" : ObjectId("589632c875b52533db9c7540")
        }

}