Image for post
Image for post

Dockerising a Node.js and MongoDB App

Nida
Nida
Jul 29, 2017 · 7 min read

This post serves as a ‘getting started’ guide to dockerising an application.

I will run through some basic Docker terminology and concepts and then use a Node.js and MongoDB application that I previously built and demonstrate how to run this in a Docker container.

A quick overview of containers and why we would want to use them:

Containers such as Docker let us package up entire applications, including the application’s libraries, dependencies, environment and everything else needed by the application to run.

Image for post
Image for post
Source: Docker

So we can think of containers as portable, packaged bits of functionality.

Containers isolate the application from the infrastructure beneath so we can run our application on different platforms without having to worry about the underlying systems.

This allows consistency between environments, for example, a container can be easily moved from a development environment to a test environment and then to a production environment. Containers can also be easily scaled in response to increased user load and demand.

To create a Docker container, we will use a Docker image, and to build a Docker image, we will use a Dockerfile. An image is made up of a set of layers and each instruction in a Dockerfile adds a layer to the image.

These images can be stored in a registry for ease of discovery and sharing purposes. Examples of registries include the official Docker Hub registry, or the Amazon EC2 Container Registry or even an internal organisation registry.

An image is essentially a snapshot of a container and a container is a running instance of an image.

Image for post
Image for post
Source: Docker

Before we start, a quick introduction to the Node.js application itself, which I built this using this tutorial but the sample project is also available to fork from GitHub.

The application serves as a (very!) basic content management system.

Using the interface below, we can add new users…

Image for post
Image for post

…and upon hitting the Submit button, we can view the list of users added, all of which are persisted in MongoDB.

For testing purposes, I have added some dummy names as shown here.

Image for post
Image for post

Looking at the application structure, we can see the package.json file, which contains all the dependencies we need to run the application.

Image for post
Image for post

To run this application in a Docker container, we will write a Dockerfile using the official node image from the Docker Hub registry. We will then use Docker Compose, a tool for running multi-container applications, to spin up our containers and run our app.

Let’s create a Dockerfile in the project directory.

FROM node:latest
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
EXPOSE 3000
CMD [ “npm”, “start” ]

We are essentially using a bunch of instructions to build our own node image.

FROM lets us specify which base image from Docker Hub we want to build from. In our case, we are using the latest version of the official node image.

RUN lets us execute a command, which in our case is to create a new directory.

WORKDIR sets this newly created directory as the working directory for any COPY, RUN and CMD instructions that follow in the Dockerfile.

COPY is pretty straightforward and lets us copy files or a whole directory from a source to a destination. We are going to COPY the package.json file over to our working directory.

RUN lets us execute the npm install command which will download all the dependencies defined in package.json.

COPY lets us copy our entire local directory into our working directory to bundle our application source code.

EXPOSE exposes a port which the container will listen on.

And finally, CMD sets the default command to execute our container.

That’s the Node part done, now for MongoDB.

We could build our own mongo image but wherever possible, we should look to use official images.

This is beneficial as it saves us a fair amount of time and effort — we don’t have to spend time creating our own images or worry about the latest releases or applying updates. All of that is taken care of by the publisher of the image.

Additionally, there is usually information available on how to best use images in the Docker Hub repository and in the Dockerfile itself, simplifying the process even further.

Let’s take a look at the standard mongo image from Docker Hub. Most of the commands in this Dockerfile should be familiar as we used them in our own Dockerfile but there are a few new ones.

The one I’d like to point out is VOLUME /data/db /data/configdb.

Here, the creator of the image is identifying two mount points — one to persist data between restarts and the other to make it easy to update configuration files. The VOLUME instruction itself is not doing anything but it gives the user (us) a hint as to where we might mount persistent storage, which we will do below.

Let’s add a docker-compose.yml file to define the services in our application.

version: "2"
services:
app:
container_name: app
restart: always
build: .
ports:
- "3000:3000"
links:
- mongo
mongo:
container_name: mongo
image: mongo
volumes:
- ./data:/data/db
ports:
- "27017:27017"

Breaking this down, what we are doing here is

  • defining a service called app,
  • adding a container name for the app service as giving the container a memorable name makes it easier to work with and we can avoid randomly generated container names (Although in this case, container_name is also app, this is merely personal preference, the name of the service and container do not have to be the same.)
  • instructing Docker to restart the container automatically if it fails,
  • building the app image using the Dockerfile in the current directory and
  • mapping the host port to the container port.

We then add another service called mongo but this time instead of building our own mongo image, we simply pull down the standardmongo image from the Docker Hub registry.

For persistent storage, we mount the host directory /data (this is where the dummy data I added when I was running the app locally lives) to the container directory /data/db, which was identified as a potential mount point in the mongo Dockerfile we saw earlier.

Mounting volumes gives us persistent storage so when starting a new container, Docker Compose will use the volume of any previous containers and copy it to the new container, ensuring that no data is lost.

Finally, we link the app container to the mongo container so that the mongo service is reachable from the app service.

After adding the Dockerfile and docker-compose.yml to the project directory, the project structure now looks like this:

We can now navigate to the project directory, open up a terminal window and run docker-compose up which will spin up two containers and aggregate the logs of both containers.

Now if we point our browser to the application URL,

Image for post
Image for post

add a new user and hit the Submit button,

Image for post
Image for post

we should now be able to see the newly added user in addition to the users which were added before the application was containerised.

An interesting point to note is that if you stop your containers ctrl c and run docker-compose up again, you will notice that the build happens much faster this time.

Image for post
Image for post

This is because Docker caches the results of the first build of a Dockerfile and subsequently uses this cache the next time round, saving valuable time.

To summarise, in this post we have

  • reviewed useful Docker terminology and concepts
  • written a simple Dockerfile
  • used Docker Compose to define our services
  • and successfully dockerised our application.

For further reading, please refer to documentation on the official Docker website.

Statuscode

Keeping developers informed.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store