A short introduction to Docker

Docker basics

Docker is a software container engine that helps you deploy applications in isolated environments without the overhead of virtual machines by utilizing operating-system-level virtualization.

You can define your custom images or use the many predefined ones from the central Docker HUB repository. There are official images for the most popular software as MySQL, Redis, Mongo, Apache, Nodejs and so on. From the images you can easily start containers (running image instances).

I'm going to introduce the world of Docker by example. We will Dockerize a really nice chat application consisting of a web application written in Agular JS, a backend in NodeJS and a Mongo database. These three components will be packaged into three separate images.

Creating images

We need to create three images for the three aforementioned components of the application. For the Mongo database we can use the official Mongo image from Docker HUB. You can simply start a container, Docker will automatically download the specified image.

docker run --name demo-mongo -d mongo:3.2  

The --name option sets the name of the container, -d tells docker that it is a detached instance and at last you specify the image to use, in this case we use mongo v3.2 from the Docker HUB repository.

For the backend and frontend we are going to have to specify our own images. The backend is written in NodeJS, there is an official Node image but I've decided to create my own. You can specify a new image using a simple text file called Dockerfile. There is great documentation on the commands you can use. For the backend the Dockerfile looks like this:

FROM ubuntu:14.04

RUN apt-get update && apt-get install -y nodejs-legacy npm

COPY ./angular-chat-backend-master /angular-chat-backend-master

WORKDIR /angular-chat-backend-master  
RUN npm install

ENV PORT=8080

EXPOSE 8080

CMD ["nodejs", "/angular-chat-backend-master/server.js"]  

This Dockerfile defines the following: we are using Ubuntu:14.04 as the base image, we update the packages and install Nodejs. After that we copy the backend source files to the image. We set the working directory to the root of the source, then install dependencies using npm. We can set environmental variables with the ENV command, in this case we set the port of the application to 8080. We also expose it to the host machine. With CMD we specify the command that should be run when the container starts.
The image for the frontend is much simpler. I have the Angular application with all of its dependencies in a folder. I just copy that into the htdocs folder of the official Apache httpd image.

FROM httpd:2.4

COPY ./angular-chat-master/ /usr/local/apache2/htdocs/  

There is one more step when the dockerfiles are ready, the images must be built. We can do that with the docker build command.

docker build -t demo-backend ./backend  
docker build -t demo-frontend ./frontend  

These two commands will build two images, one called demo-backend one called demo-frontend. the dockerfiles and the sources are in the backend and frontend folders responsively. During the build process you can see Docker downloading base images and running the commands specified in the dockerfiles. After it is done, you can check your built images with the following command:

docker images  

Thats it, we have the three images ready to deploy.

Deploying the app

Now it's finally time to start the application. We start with the mongo instance, it's already running if you have issued the first docker run command in the previous chapter. You can check running containers with:

docker ps  

If mongo is running ok, we can start the backend. The Node.js app has a dependency on the database, it has to know it's IP address for communication. For this kind of inter container communications we can use the docker link system. Newly started containers get IP adresses dynamically, the Docker link system gives us a method to resolve these dynamic IP addresses to hostnames inside containers. Docker does this by including the IPs and the corresponding hostnames to the containers /etc/hosts file.

docker run --name demo-backend-instance --link demo-mongo:demo-mongo -p 8080:8080 -d demo-backend  

With the above command I have started a container called demo-backend-instance and linked it with my mongo container. To be precise under the name demo-mongo the other container called demo-mongo will be available inside my new container. When I had set up the database configuration in my app, I have used the hostname demo-mongo, and now Docker will resolve it for me to the other container.
Time to start the frontend, the method is the same. The web application must communicate with the backend so I link them together.

docker run --name demo-frontend-instance --link demo-backend-instance:demo-backend-instance -p 80:80 -d demo-frontend  

And that's it, you better check out http://localhost to see the result. You have a web application up and running in less than 5 minutes, on any machine, without manually installing any dependencies, libraries or anything except the Docker engine.

The web app I have used for the demonstration was created by Anna Tomka. The built and Dockerized version of the app will be available soon on my GitHub.