Docker: Using docker-compose to link a MongoDB server and client

Docker Compose gives us the ability to define and orchestrate multiple containers in order to construct a service.  In this article, we will use Docker Compose to create a MongoDB server and then another container that is used exclusively as a MongoDB client.

While it is entirely possible to manually create these containers, links, and set environment variables to the same affect; Docker Compose gives us a way to capture these relationships and standardize the process of deployment.

Prerequisites

If you haven’t installed Docker and Docker Compose, read my article here for full instructions.

Then install a git client so you can grab my source code from github.

$ sudo apt-get install git -y
$ git clone https://github.com/fabianlee/docker-mongodb.git
$ cd docker-mongodb

Build Docker image

The MongoDB server image is coming from the official repository, and will be downloaded when required.  But our MongoDB client will be custom built on top of the alpine base image.  The 4.2 version of the Mongo server was intentionally paired with Alpine 3.9 to use the 4.0.5 Mongo client from the Alpine community repository.

The Dockerfile for building this client is found in the subdirectory at my-mongoclient/Dockerfile, so in the docker-compose.yml we have specified  that directory as shown below.

 mongoclient:
  build: ./my-mongoclient
  image: fabianlee/my-mongoclient:3.9

Run the following command to build the image, and then get an image listing from docker and you should see a new available image named ‘fabianlee/my-mongoclient’.

$ sudo docker-compose build

$ sudo docker-compose images

 Container Repository Tag Image Id Size 
----------------------------------------------------------------
fabianlee/my-mongoclient 3.9 d74939567c02 125 MB
mongo 4.2.3-bionic 14c497d5c758 386 MB

Run Compose

To bring up all the services in docker-compose.yml, run the following command.

$ sudo docker-compose up -d

Creating network "docker-mongodb_mongo_net" with driver "bridge"
Creating my-mongoclient ... done
Creating my-mongodb ... done

Listing the running docker containers should show two running containers.

$ sudo docker-compose ps

 Name Command State Ports 
---------------------------------------------------------------------------------------------
my-mongoclient /bin/sleep 1d Up 
my-mongodb docker-entrypoint.sh mongod Up 0.0.0.0:27017->27017/tcp

Validate MongoDB server

Let’s validate the MongoDB server by tailing the logs.  Using docker-compose this can be done by simply using:

$ sudo docker-compose logs -f my-mongodb

This is an example of docker-compose being able to intelligently understand its deployment context because for comparison ‘docker logs’  will also allow us to tail the logs of a container, but it requires the container id.

$ docker logs -f $(docker ps -f ancestor=mongo | tail -n 1 | awk {'print $1'})

We will see more output once when attempt a client connection in the section below.

Validate MongoDB client

We have one host console tailing the MongoDB server logs, so we need to open up a new host console so we can test the client container.  After doing so and navigating back to the ‘docker-mongodb’ directory, issue the following command to start a shell inside the container.

$ sudo docker-compose exec my-mongoclient /bin/sh

Then run the command below to have the MongoDB client connect to server using the proper credentials which have been inserted into the environment.  We are able to use ‘targetmongo’ as the hostname because that is how we linked it in docker-compose.yml.

mongo $MONGO_SERVER/$MONGO_INITDB_DATABASE -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD

This connection will result in a log entry on the server that looks like ‘Successfully authenticated as principal admin on test’.  It will also show ‘unauthorized’ messages which are simply because the user is not a global administrator.

Test pulling data from the collection by issuing the following command at the client, and then ‘exit’.

> db.test.find().pretty()
{
"_id" : ObjectId("5b01a461e0aaab4d3c56eaea"),
"myfield" : "test1",
"anotherfield" : "TEST1"
}
{
"_id" : ObjectId("5b01a461e0aaab4d3c56eaeb"),
"myfield" : "test2",
"anotherfield" : "TEST2"
}
> exit

Typing ‘exit’ one more time will exit the “/bin/sh” shell of the container and get you back to the host.

 

REFERENCES

https://docs.docker.com/compose/overview/

https://docs.docker.com/compose/compose-file/

https://docs.docker.com/compose/reference/run/

https://pkgs.alpinelinux.org/package/edge/community/x86_64/mongodb (alpine package for mongodb)

https://stackoverflow.com/questions/34559557/how-to-enable-authentication-on-mongodb-through-docker (using javascript to create user in database)

http://ignaciosuay.com/how-to-connect-to-mongodb-3-0-using-spring-boot/ (connecting to Mongo 3)

https://www.programcreek.com/java-api-examples/?api=com.mongodb.MongoCredential (Connecting using latest Mongo 3)

https://blog.kevinchisholm.com/javascript/mongodb/getting-started-with-mongo-shell-scripting-basic-crud-operations/ (mongodb javascript)

https://jbouffard.fr/article/use-case-add-an-init-script-to-the-docker-official-mongo-image (init script for mongo container)

https://lakshminp.com/docker-mongodb (customized shell scripts for mongo docker startup)

https://programmaticponderings.com/2016/08/07/spring-music-revisited-java-spring-mongodb-web-app-with-docker-1-12/

 

NOTES

To run only specific container in docker-compose

sudo docker-compose run mongoclient

long docker commands that get easier with docker-compose

docker logs -f $(docker ps -f ancestor=mongo | tail -n 1 | awk {‘print $1’})

docker exec -it $(docker ps -f ancestor=my-mongoclient | tail -n 1 | awk {‘print $1’}) /bin/sh

delete containers and delete named volumes

sudo docker-compose down -v

get Mongo database version

db.version()