Implementing a MongoDB container for our application
We can explore using pre-built containers from Docker Hub by implementing a MongoDB container. We'll use this container later as part of a demo application that is made up of several containers that work together.
We will use the official Docker image for MongoDB, found on the Docker Hub website at https://hub.docker.com/_/mongo. We will create a .sh script to start running our image within Docker so that the startup process is easy and repeatable.
We learned in Chapter 2, Using VirtualBox and Docker Containers for Development, that we can expose a container's network ports to the host. That means we can run this MongoDB container image in Docker and access the running MongoDB server within that container by accessing the MongoDB port on the host.
In the GitHub repository (https://github.com/PacktPublishing/Docker-for-Developers) for this book, there is a chapter3/ directory, which is a companion for this chapter. Within that directory is a shell script, start-mongodb.sh. This script is a bit more elaborate than the simple ones we used in the previous chapter. We're going to use environment variables to configure MongoDB, and we're going to use a directory on the host for MongoDB's data files—this makes backing up the data as easy as copying those files to back-up media:
#!/bin/bash
SERVICE=mongodb # name of the service
# You can set these in this script (uncomment and edit the lines) or set them in your .zshrc/.bashrc/etc.
# Change this to an EXISTING directory on the HOST where the mongodb database files will be created #!/bin/bash
SERVICE=mongodb # name of the service
# Change this to an EXISTING directory on the HOST where the mongodb database files will be created and maintained.
#MONGO_DATADIR="$HOME/data"
# Stop any running MongoDB container, remove previous container, pull newer version
docker stop $SERVICE
docker rm $SERVICE
docker pull mongo:3.4
# Now we run it!
docker run …
You do need a Dockerfile to create a container image. However, if you are using a pre-made container image from Docker Hub that is standalone, such as MongoDB, you won't need one. The developers at MongoDB use Dockerfiles to generate the images before uploading them to Docker Hub.
In fact, you can see from the Supported tags section of the MongoDB page in Docker Hub that they produce and support quite a few images, including different versions—some for Windows OS, some for Linux, and so on. The MongoDB developers must have quite a few Dockerfiles—one for each image!
We must provide one environment variable to start-mongodb.sh: MONGO_DATADIR, which is an existing directory on your workstation where you want MongoDB in the container to store its data files. There are a few ways to set this variable:
- You can add export MONGODB_DATADIR=/path/to/data/dir to your shell startup file (.zshrc, .bashrc, and so on).
- You can do the export (environment variable) operation by hand in the shell before running the script.
- You can set the value of the environment variable when using the command line to run the start-mongodb.sh script: # MONGODB_DATADIR=~/data ./start-mongodb.sh.
- You can uncomment the line that sets MONGO_DATADIR in the start-mongodb.sh script file and edit it to set it to your desired data directory each time you run the script.
The last line in the start-mongodb.sh script is a single command line. The backslash (\) character at the end of the line signifies that the line is being continued or joined with the next line. This command is the one that starts the container. As you can imagine, if you had to type in this long command every time to start your MongoDB container, it would be painful. The .sh script makes it rather painless:
docker run \
--name $SERVICE \
-d \
--restart always \
-e TITLE=$SERVICE \
-p 27017:27017 \
-v "$MONGO_DATADIR":/data/db \
mongo:3.4
Let's take a look at the different parts of the preceding command:
- The docker run command names the mongodb running container.
- The -d switch runs the container in detached mode. The container will automatically start when your workstation is rebooted.
- The -e switch allows you to pass environment variables to the container; in this case, we pass the TITLE=mongodb environment variable. You can have multiple -e switches if you want to pass more than one variable.
- The -p switch exposes port 27017 in the container to port 27017 on the host. You can remap an exposed port in the container to a different port number on the host. You would do this if you have a MongoDB server already running in a container or on your host. However, Docker provides us the flexibility to always run MongoDB within a container, so we'll never have to install it on our host.
We might want to install MongoDB client programs on the host so that we can access MongoDB using the MongoDB REPL/shell. Once port 27017 is exposed on the host, any program can access the MongoDB database, using it as if it were running on the host.
- The -v switch maps a directory on the host to the directory in the container where MongoDB will manage its database and other files.
- We choose to download and run mongo:3.4 (tag/version 3.4) from Docker Hub.
Note
The docker run command only downloads the container from Docker Hub if it doesn't exist on your workstation yet or if the container image on Docker Hub is newer.
You can run any container you find on Docker Hub in the same way!
Let's run the script by using the following commands:
# mkdir -p ~/mongodb
# MONGO_DATADIR=~/mongodb ./start-mongodb.sh
The following output contains a few warnings about not being able to stop an already-running container named mongodb (this is expected):
# mkdir -p ~/mongodb && MONGO_DATADIR=~/mongodb ./start-mongodb.sh stopping mongodb Error response from daemon: No such container: mongodb removing old mongodb Error: No such container: mongodb pulling mongodb 3.4: Pulling from library/mongo 976a760c94fc: Pull complete c58992f3c37b: Pull complete 0ca0e5e7f12e: Pull complete …
3757d63ce2b9: Pull complete Digest: sha256:4c7003e140fc7dce5f12817d510b5a9bd265f2 c3bbd6f81d50a60cc11f6395d9 Status: Downloaded newer image for mongo:3.4 docker.io/library/mongo:3.4 e3854f6931e1aa4b64557d5a54e652653123f84a 544fedf39a5cf68d2ee9d0af # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e3854f6931e1 mongo:3.4 "docker-entrypoint.s…" 5 seconds ago Up 3 seconds 0.0.0.0:27017->27017/tcp mongodb #
Docker pulled the proper MongoDB image and ran it in the background in the Docker engine. You can observe the following:
- The MongoDB image consists of several layers that were downloaded (Pull complete).
- There was already an existing (but older) image on the workstation (Downloaded newer image…).
- The container is running via the docker ps command.
If the container encounters errors, it may exit and print diagnostic messages in the output. You can run a shell in the container to perform forensic diagnosis.
Running a shell within a container
Generally, you would run a shell within the container so that you can discover more about the container's environment. For example, you may have a bug in your Dockerfile—such as forgetting to copy a file into the container. You can run a shell in the container and list directories and you will see that the file is missing.
In the case of the MongoDB container, you might want to run the MongoDB client commands from within the container. The Docker Hub page for the MongoDB container says we can run the client commands by simply attaching to the running container (https://hub.docker.com/_/mongo). The command from the MongoDB Docker Hub page is as follows:
docker exec -it mongodb bash
The different parts of this command are as follows:
- docker exec runs a command in a running container (https://docs.docker.com/engine/reference/commandline/exec/).
- The -it switches specify that Docker is to run the container interactively—this means it gets input from the keyboard and sends output to the Terminal window.
Within the container, we can list directories using the ls command:
# docker exec -it mongodb bash root@e3854f6931e1:/# ls bin data docker-entrypoint-initdb.d etc js-yaml.js lib64 mnt proc run srv tmp var boot dev entrypoint.sh home lib media opt root sbin sys usr
We can see that the Docker containers are running using the ps command within the container:
root@e3854f6931e1:/# ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND mongodb 1 0.7 0.0 954676 62028 ? Ssl 22:37 0:02 mongod root 40 2.8 0.0 18240 3248 pts/0 Ss 22:41 0:00 bash root 51 0.0 0.0 34420 2848 pts/0 R+ 22:41 0:00 ps -aux root@e3854f6931e1:/#
We can run the command-line MongoDB tools inside the container. We did not have to install these on our workstation! Here, we run the MongoDB command and then run the show collections and show databases commands within the Mongo REPL:
root@e3854f6931e1:/# mongo MongoDB shell version v3.4.23 connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 3.4.23 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 Server has startup warnings:2019-12-13T22:37:12.342+0000 I CONTROL [initandlisten]2019-12-13T22:37:12.342+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.2019-12-13T22:37:12.342+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.2019-12-13T22:37:12.342+0000 I CONTROL [initandlisten]> show collections > show databases admin 0.000GB local 0.000GB >root@e3854f6931e1:/# exit
We're all set to go—MongoDB is running and we were able to use the REPL. The show collections command returned no collections because we haven't created any. The show databases command shows that MongoDB has, by default, two databases: admin and local.
The docker logs command shows us the stdout and stderr output of the container:
# docker logs mongodb 2019-12-13T22:37:09.161+0000 I CONTROL [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=e3854f6931e1 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] db version v3.4.23 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] git version: 324017ede1dbb1c9554dd2dceb15f8da3c59d0e8 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.0.2g 1 Mar 2016 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] allocator: tcmalloc 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] modules: none 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] build environment:2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] distmod: ubuntu1604 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] distarch: x86_64 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] target_arch: x86_64 2019-12-13T22:37:09.162+0000 I CONTROL [initandlisten] options: {}2019-12-13T22:37:09.165+0000 I STORAGE [initandlisten] wiredtiger_open config: create,cache_size=31491M,session_max=20000,eviction=(threads_min=4,threads_m ax=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(w ait=60,log_size=2GB),statistics_log=(wait=0),verbose=(recovery_progress),2019-12-13T22:37:14.335+0000 I INDEX [initandlisten] building index using bulk method; build may temporarily use up to 500 megabytes of RAM 2019-12-13T22:37:14.342+0000 I INDEX [initandlisten] build index done. scanned 0 total records. 0 secs 2019-12-13T22:37:14.344+0000 I COMMAND [initandlisten] setting featureCompatibilityVersion to 3.4 (
…
You will likely use the docker logs command to see the debugging output from your containers.
What we see in our preceding logs is that MongoDB seems to be running just fine within the container. There are no error messages printed.
You can have the docker logs command follow the log file using the -f command-line switch. When the command is in follow mode, any new lines written to the log as the application is running will be appended to the display on the screen.
Up to point, we have explored using Docker to run a complex server application (MongoDB) without having to install MongoDB on our workstation. Using Docker, we have access to MongoDB.
We can start MongoDB using our .sh script, and we can also stop it—we can do this at will so that we don't have to always have MongoDB running in the background.
Now that we know how to run a Docker container, let's have a look at how to work with multiple containers that work together.