Today, what I want to cover is setting up a MongoDB replica set on Docker. What will make this post a little bit different than most of the documentation for setting up MongoDB on Docker is that I will try to emulate a setup that is closer to what you will find in a production environment. Once we're complete with setting this up you will have a MongoDB Replica Set that has authentication enabled and uses a key as well. In my production environments, I would use certificates, however for development purposes, the key file will certainly suffice.
At the end of this post, if you happen to be following along, you should have a similar setup as per the below diagram.
+-Docker-Host---------------------------------+
| |
| +----+--+Docker-MongoDB-Network+---+----+ |
| | | | |
| +----v------+ +----v------+ +------v----+ |
| | MongoDB | | MongoDB | | MongoDB | |
| +-----^-----+ +-----^-----+ +-----^-----+ |
| | | | |
+------37017---------37018---------37019------+
PRIMARY SECONDARY SECONDARY
Pre-requisites
- Docker version 1.12.1 >
- Docker Mongo Image
Assumptions
While this is more around MongoDB and Docker, I will try to not make any assumptions and start from the top. You should, however, have Docker already installed, but if you don't, here is the one-liner from their site.
curl -sSL https://get.docker.com/ | sh
Setup
Now that you have Docker installed, let's start by pulling down the latest MongoDB Docker Image by simply running the following command.
docker pull mongo
Next, let's verify we have the image pulled down and available to us for use.
docker images | grep mongo
You should see the following output, however, the time CREATED time will be different depending on when you execute the command to view the image.
REPOSITORY TAG IMAGE ID CREATED SIZE
mongo latest 83b0cb5c5a67 1 minute ago 326.7 MB
Creating our Network
Creating a network for our containers to live on is more like what you may find in a production Mongo deployment. Typically, you will have a database network that is segregated from front-end traffic. This can be done a variety of ways but in this instance, we will go with a simple bridge network. In future posts, I will show how to utilize other network types.
To create our new network for our containers to live on, we will issue the following command:
docker network create mongodb-rs-local
Next, we will check that our network was created.
docker network ls -fname=mongodb-rs-local
# Output
NETWORK ID NAME DRIVER SCOPE
f91c765e4686 mongodb-rs-local bridge local
We can also check with brctl. You can see under the interfaces header that we have no containers bound to this network. I use brctl here as I find the output very clean for a quick at-a-glance look. However, Docker also provides us a method of viewing this information in much more detail, so I will show both methods below.
brctl show
# Output
bridge name bridge id STP enabled interfaces
br-f91c765e4686 8000.02424e7f33d7 no
docker network inspect mongodb-rs-local
# Output
[
{
"Name": "mongodb-rs-local",
"Id": "f91c765e46860b3e5bfe078419f829aea29062773e5bd7c1325dd818813236a8",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1/16"
}
]
},
"Internal": false,
"Containers": {
},
"Options": {},
"Labels": {}
}
]
Creating our Keyfile
Now that we have our network created and ready for containers to be connected to, we will create the key file which our MongoDB replica set will use for internal authentication. To set this up is a trivial process but the permissions are really what is important here.
We need to know what the UID/GID are of the MongoDB user running inside the container. So we will run a quick command against a throwaway container to get the UID/GID of the MongoDB user.
docker run -t mongo grep mongodb /etc/passwd
# Output
mongodb:x:999:999::/home/mongodb:/bin/sh
Take note of the UID/GID which is 999, we will need to give the key this ownership so that MongoDB can access the key for internal authentication. We will also need to set the key to RO(ReadOnly)400 for the owner. The path to the key is important as we need to mount the key. Alternatively, you can mount the entire directory. In our example, we will be mounting the key file only.
Create the Key
openssl rand -base64 755 > <path-to-keyfile>
chmod 400 <path-to-keyfile>
chown 999:999 <path-to-keyfile>
Start our Containers
Our key file is created and ready, our network is created and ready. Let's start up the containers. First, let's examine the command we will use to start each container so you have a better idea
The below command we will use to start our containers. Keep in mind that the command is the same for all three containers except the port and the name, which we will increment accordingly.
docker run
-p 37017:27017
-v <full-path-to-keyfile-src-on-host-on-host>:<full-path-to-keyfile-dst-on-container>
--name mongo-local-001
--net mongodb-rs-local
-d
mongo --auth --replSet rslocal01 --keyFile <full-path-to-keyfile-dst-on-container>
- docker run - Runs a command in a new container
- -p 37017:27017 - Publish a container's port(s) to the host. In this case host:37017 -> container:27017
- -v full-path-to-keyfile-src-on-host:full-path-to-keyfile-dst-on-container - Bind mount a volume. In this case we're binding the keyfile.
- --name mongo-local-001 - Assign a name to the container
- --net mongodb-rs-local - Connect a container to a network. In this case the network we previously created.
- -d - Run container in background and print container ID
- mongo --auth --replSet rslocal01 --keyFile full-path-to-keyfile-dst-on-container - The starting command: mongo with auth, a replica set and keyfile
Starting the Containers
Notice the port and name has been incremented as stated.
docker run -p 37017:27017 -v <full-path-to-keyfile-src-on-host>:<full-path-to-keyfile-dst-on-container> --name mongo-local-001 --net mongodb-rs-local -d mongo --auth --replSet rslocal01 --keyFile <full-path-to-keyfile-dst>
docker run -p 37018:27017 -v <full-path-to-keyfile-src-on-host>:<full-path-to-keyfile-dst-on-container> --name mongo-local-002 --net mongodb-rs-local -d mongo --auth --replSet rslocal01 --keyFile <full-path-to-keyfile-dst>
docker run -p 37019:27017 -v <full-path-to-keyfile-src-on-host>:<full-path-to-keyfile-dst-on-container> --name mongo-local-003 --net mongodb-rs-local -d mongo --auth --replSet rslocal01 --keyFile <full-path-to-keyfile-dst>
Verify the Containers
This step is not needed, however I like to perform this step as a checks and balances measure to make sure everything is as it should be. So first, let's check to see if the containers are started.
docker ps
# Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1b3f8ecc1fd3 mongo "/entrypoint.sh --aut" 2 minutes ago Up 2 minutes 0.0.0.0:37019->27017/tcp mongo-local-003
506d20720dda mongo "/entrypoint.sh --aut" 2 minutes ago Up 2 minutes 0.0.0.0:37018->27017/tcp mongo-local-002
57409f00d332 mongo "/entrypoint.sh --aut" 2 minutes ago Up 2 minutes 0.0.0.0:37017->27017/tcp mongo-local-001
We can also verify they are bound to our network
brctl show br-f91c765e4686
bridge name bridge id STP enabled interfaces
br-f91c765e4686 8000.02424e7f33d7 no veth2130dea
veth4ee24f4
vethf0a3285
Alternatively, you can use the docker network inspect
docker network inspect mongo-rs-local
[
{
"Name": "mongo-rs-local",
"Id": "f91c765e46860b3e5bfe078419f829aea29062773e5bd7c1325dd818813236a8",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1/16"
}
]
},
"Internal": false,
"Containers": {
"1b3f8ecc1fd3dc278d2c6d042ab2b31026f248ab77fc754007ef805def0e9a72": {
"Name": "mongo-local-003",
"EndpointID": "1722b055adb4d87b08f5e0673a96f3e8e970aaf728e0d8905515d9a164717042",
"MacAddress": "02:42:ac:12:00:04",
"IPv4Address": "172.18.0.4/16",
"IPv6Address": ""
},
"506d20720dda532fe0336028842fa96a5441c1380b55b817ce16078d494dce05": {
"Name": "mongo-local-002",
"EndpointID": "e91baad1d629b9533c0e76fefaa9ff91b97e29a11c10cb1720f95ae075412494",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
},
"57409f00d332840f9158ce69fea1577ee4f5ddcdcb6a074539f28c1b4642fa65": {
"Name": "mongo-local-001",
"EndpointID": "a8ccdda4d2a5ed252a2cfab201c49a6bc7f861130db6d724123c73a0e6c5f276",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
Excellent, the containers are started and are connected to the network we created. They are now ready for configuration.
Configuring our Containers
We have auth enabled, but before we create a user, we must first configure our replica set.
Connect to Mongo
You can connect to any of the containers to perform the following, however, I am choosing to connect to the first instance. Here, I am using docker exec, however, you could also use mongo shell directly if you have that installed as we have ports exposed.
docker exec -it mongo-local-001 mongo
MongoDB shell version: 3.2.8
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
>
Initiate the Replica Set
Now, we need to initiate the replica set. Doing so is quite simple for our standard replica set. There are more options and I will cover some in other posts, but for now the following configuration can be passed to rs.initiate();
> rs.initiate({
"_id" : "rslocal01",
"members" : [
{
"_id" : 0,
"host" : "mongo-local-001:27017"
},
{
"_id" : 1,
"host" : "mongo-local-002:27017"
},
{
"_id" : 2,
"host" : "mongo-local-003:27017"
}
]
});
Add an Admin User
Finally, we can add a user to authenticate with. Here I will simply create a root user, however, you can create any user you need to at this point.
> use admin;
> db.createUser({ user: "root", pwd: "password", roles: ["root"] });
> db.auth("root", "password");
I hope this proves to be useful to some of you out there.
Until next time. Keep it simple. - ShellFu
