MongoDB Replica Set on Docker

31 / Aug / 2015 by Neeraj Gupta 5 comments

In the previous blog, we discussed about how to setup MongoDB replica set on AWS EC2. In this blog post, we will be discussing about setting up MongoDB replica set of three Docker containers running on a single host. After going through this blog, you will be able to setup MongoDB replica set in a few minutes and it will be easy to maintain and configure. We will create custom Docker image with pre-installed MongoDB using base Ubuntu image and Dockerfile. Dockerfile is a text file that contains all the commands required by the user to bundle custom image and by using Docker build you can create an automated build that executes command line instruction mentioned in the Dockerfile in succession.

Use Case: We are currently using MongoDB in most of our projects, and we often get requirement from the development team to set-up new MongoDB replica sets for testing. Using this script we can now start new replica sets in minutes.

Pre-requisites: Before starting this blog, it is required to have basic understanding of Docker and its basic features. If you want to go through basics of Docker click here

mongodb_replicaset_docker_setup_1

To make deployment process easier I have written custom shell script to deploy complete replica set in few minutes. So here is the overview of complete process:

  • Create custom Docker image that will comprise of Ubuntu base image and MongodDB version 2.0.3
  • Deploy three containers using custom Docker image (created in the previous step)
  • Initiate replica set on the master container and enable slave mode on the other two containers.
Note: This setup can be used in development and testing environment, it is not recommended for production environment.

Create custom Docker image:-

    • Create Dockerfile and add below mentioned content to it.

[js]mkdir /mongodb
touch /mongodb/Dockerfile # add below mentioned content
docker build -t neerjaj2/mongodb /mongodb/. [/js]

    • To use my Docker hub image use:-

[js]docker search neerjaj2/mongodb
docker pull neerjaj2/mongodb[/js]

Dockerfile:-

[js]FROM ubuntu:latest
RUN apt-key adv –keyserver hkp://keyserver.ubuntu.com:80 –recv 7F0CEB10
RUN echo ‘deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen’ | tee /etc/apt/sources.list.d/10gen.list
RUN apt-get update
RUN apt-get install -y mongodb-10gen=2.2.3
RUN mkdir -p /data/db
EXPOSE 27017
ENTRYPOINT ["usr/bin/mongod"] [/js]

Script to start MongoDB containers:-

  1. Spinning up three Docker containers with specific host names, image name and custom parameters like replica set name, pre-allocation and file size.
  2. Once containers are up script will extract containers IP and hostname in a new file getip.txt and it is then copied to all the containers.
  3. After that updateHost.sh script (generated in the main script) will be copied to all the containers and executed. The purpose of updateHost.sh is to update /etc/hosts file with the latest container IP’s and hostnames. (This is an alternative to –add-host in Docker command )
  4. At last the script will run rs.status(), rs.initiate() and rs.add() command on the first node  (mongo1.ttnd.com) and rs.slaveOk() on remaining two nodes (mongo2.ttnd.com and mongo3.ttnd.com). As a result first node is configured as primary and remaining two as secondary nodes. Find details in the screenshot below:-

[js]#!/bin/bash
# Define Hostnames
h1="mongo1.ttnd.com"
h2="mongo2.ttnd.com"
h3="mongo3.ttnd.com"

# Start containers with specific hostnames and replica set name
docker run -P –name ttnd1 –hostname="$h1" -d neerjaj2/mongodb –replSet ttnd –noprealloc –smallfiles
docker run -P –name ttnd2 –hostname="$h2" -d neerjaj2/mongodb –replSet ttnd –noprealloc –smallfiles
docker run -P –name ttnd3 –hostname="$h3" -d neerjaj2/mongodb –replSet ttnd –noprealloc –smallfiles

# Commands to extract IP addresses of containers
echo $(docker inspect –format ‘{{ .NetworkSettings.IPAddress }}’ ttnd1) "$h1" > getip.txt
echo $(docker inspect –format ‘{{ .NetworkSettings.IPAddress }}’ ttnd2) "$h2" >> getip.txt
echo $(docker inspect –format ‘{{ .NetworkSettings.IPAddress }}’ ttnd3) "$h3" >> getip.txt

# Commands to cp getip.txt to containers
docker cp getip.txt ttnd1:/etc
docker cp getip.txt ttnd2:/etc
docker cp getip.txt ttnd3:/etc

# Commands to create new file updateHost.sh (to update /etc/hosts file in all the three docker containers)
echo "#!/bin/bash
cat /etc/hosts > /etc/hosts1
sed -i ‘/ttnd.com/d’ /etc/hosts1
cat /etc/getip.txt >> /etc/hosts1
cat /etc/hosts1 > /etc/hosts" > updateHost.sh

# Change permission of updateHost.sh and cp files to docker container
chmod +x updateHost.sh
docker cp updateHost.sh ttnd1:/etc
docker cp updateHost.sh ttnd2:/etc
docker cp updateHost.sh ttnd3:/etc
docker exec -it ttnd1 chmod +x /etc/updateHost.sh
docker exec -it ttnd2 chmod +x /etc/updateHost.sh
docker exec -it ttnd3 chmod +x /etc/updateHost.sh

# Execute scripts on all the three containers
docker exec -it ttnd1 /etc/updateHost.sh
docker exec -it ttnd2 /etc/updateHost.sh
docker exec -it ttnd3 /etc/updateHost.sh
# Start MongoDB Replica Set with Primary
docker exec -it ttnd1 mongo –eval "rs.status()"
docker exec -it ttnd1 mongo –eval "db"
docker exec -it ttnd1 mongo –eval "rs.initiate()"
sleep 120
docker exec -it ttnd1 mongo –eval "rs.add(\"$h2:27017\")"
docker exec -it ttnd1 mongo –eval "rs.add(\"$h3:27017\")"
docker exec -it ttnd2 mongo –eval "rs.slaveOk()"
docker exec -it ttnd3 mongo –eval "rs.slaveOk()" [/js]

mongodb_check_replicaset_status_2

After executing the script or running commands manually, you can list active containers by using command “docker ps”.

[js]docker ps[/js]

Once we have complete MongoDB replica set running let us add some data on the primary node and test replication on secondary nodes. So, I have added five records using commands:-

[js]db.people.save() # to insert data in the people collection
db.people.find() # to list down all the records in the people collection[/js]

add_data_to_primary_mongodb_node_3

Let us now switch to the secondary MongoDB container using below-mentioned commands and test, read and write request on the secondary node. In this example, I am trying to insert new user Ram2 in people collection, but I am unable to do so because it is not a master node. Secondly, we can still read complete database using “find()” command.

[js]docker exec -it ttnd2 /bin/bash [/js]

add_data_to_secondary_mongodb_node_4

Great, everything is working as expected, let us now test failover. We can do this by either terminating or stopping primary MongoDB container and check the status of remaining two MongoDB containers. In the current example let us stop primary MongoDB container by using command:-

[js]docker stop ttnd1[/js]

After running above command check status of MongoDB replica set by running rs.status( ) command in any of the two available nodes as shown below (ttnd2 or ttnd3).

[js]docker exec -it ttnd2 /bin/bash
mongo
rs.status().members[/js]

check_mongodb_replicaset_failover_5

From the screenshot above, you can see that original primary node (mongo1.ttnd.com) is in unavailable state and third node (mongo3.ttnd.com) has become the primary node.

FOUND THIS USEFUL? SHARE IT

comments (5)

  1. ATrivedi

    Hi Neeraj,
    I am getting below error, kindly help me.

    root@ip-172-31-23-20:/mongodb# docker exec -it ttnd{1,2,3} chmod +x /etc/updateHost.sh
    rpc error: code = 2 desc = oci runtime error: exec failed: exec: “ttnd2”: executable file not found in $PATH

    Reply
    1. Neeraj Gupta

      Hi Akhilesh,

      Thanks for the feedback, I have updated blog with separate commands to change the permission of /etc/updateHost.sh in all the containers.

      Reply
  2. dencypriya

    Hiii…
    I have read this MongoDB Blog…It was very helpful to learn about the information provided…Thank for sharing the such a nice post…

    Reply
  3. Dinesh

    Very nice article Neeraj !!!
    One question though, once the primary db container is stopped how to get it connected to replica set again?
    I am not able to get that working.

    Reply
    1. Neeraj Gupta

      Hi Dinesh,

      Thanks for writing back, to keep all the containers running you can either use –restart=”always” in the docker run command or you will need to manually start that container.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *