Docker 101: Docker Compose

A beginner’s introduction to Docker & why it’s awesome?

Dev, Sec & Ops
Published in
8 min readFeb 1, 2020

--

Docker 101, is a new series that I am beginning to give you guys a closer look at one of the most buzzing word of the DevSecOps world. This new technology have been revolutionising the way deployment takes place while easing the work for the people at the DevOps department.

I have been working very closely with the DevOps team at the organisation I currently work at. For a guy who comes from a pen-testing background, listening to the word “Docker” day in and day out became frustrating to a point where I decided to just leave security all together for some time and get involved in the DevOps part of things.

For the past few days, I have been binge-watching Docker tutorials, understanding why is it required, what are its benefits and how it saves time and resources. After putting in the work, I have realised that dockers are here to stay for a long time and how beneficial it is for everyone from development to deployment and management.

Last week I published my fourth article, Docker 101: Volume and Bind Mounting. You can go back to those article and have a look at them so to be better able to understand the following article.

Let’s recap!

In my previous article about volume and bind mounting I mentioned how to overcome one of the biggest flaws with docker that is that they are stateless and does not store data persistently in any matter or form. Using volume and bind mounting this is one of the problems that we solved straight away.

The difference between volume mounting and bind mounting is that in volume mounting the data is persistently stored inside the docker volume whereas in the bind mount the data is stored in the host’s system directly. In both the cases the data can be very easily accessed by the docker while setting them up.

The reason for writing this article is that whenever we are setting up a cluster of docker containers we still execute every single docker command one after the another which is extremely cumbersome and we needed a smart way to do that rather than typing them over and over again. Don’t worry there is a solution to this problem as well known as docker compose, so let’s dive in.

Docker Compose

Docker compose is a YAML file which is used to feed in the commands for creation of multiple docker at the same time for a bigger project. Let’s have a look at how docker run commands need to be executed one after the another to set up a project.

$ docker run aditya12anand/webapp
$ docker run mongodb
$ docker run redis:alpine
$ docker run ansible

All these docker run commands need to be executed time and time again whenever trying to set up a project and we very well know that in real life scenario things can get way more complex than this. Hence, we introduced the concept of docker compose that helps us in situation like these. Let’s have a look at what a sample docker compose yaml file looks like.

Docker Compose - yaml file

Once we have the services mentioned that we need to be execute all we need to do is run the following command.

$ docker-compose up

This command helps us with setting up the all the docker containers and docker take cares of it all on its own without having to interrupt at all. It all happens on the same docker host. Let’s have a look at an example to better understand things.

Voting Application

The above image gives us a bit of a realistic image on how real life application might look like and how the docker containers might need to communicate with each other. Let’s see how would we set it up if we can use only the docker run commands.

$ docker run -d --name=redis redis
$ docker run -d --name=db postgres:9.4
$ docker run -d --name=vote -p 5000:80 --link redis:redis voting-app
$ docker run -d --name=result -p 5001:80 --link db:db result-app
$ docker run -d --name=worker --link db:db --link redis:redis worker

Now, to type out all these commands back to back, every time you want to make a change can be a bit hectic and that is what the whole idea of docker compose really is. Let’s see what these commands would look like as a docker compose file.

The docker compose file on the side helps us set up the exact environment that we have created by typing in the above commands one after the another. At first glance this might seem a bit complicated and maybe entering a few commands feels the easier way of doing things.

The main advantage of docker compose is visible when we have to setup the entire cluster of docker over and over again and typing in the same commands become extremely tiresome.

Well, we have discussed enough about docker-compose’s abilities and advantages, let’s discuss the compose file itself.

First of all we mention the name of our docker container, then we mention the image file that it would be using

image: <image-name>

Once, that is done we then mention any special requirements or description for those docker containers, like

ports: 
- <host-port>:<docker-port>

This is used to map the docker ports with the ports on the host machine so that the user using the host machine can interact with the docker and its outputs.

links:
- <container-name>

This is used to create a link between different docker containers that we have spawned so that they can work with each other as a cluster.

Now that we have seen and understood the docker-compose.yml file we took one thing for granted that every docker image is already present in the docker registry but that won’t be the case. Not every time we will have docker-images ready to be executed right away as we download them, most the time we will need to configure them and set them up.

The new docker-compose.yml file would look something like this.

The change that takes place is in the build command.

build: ./<image-location>

In this way we can build the image from our host system’s directory and combine it with the ready to be used images and directly use those from the docker registry.

Well now that we have a proper look at docker-compose.yml file. Let us discuss regarding its different versions.

Versions of Docker Compose

Docker compose files have evolved over time and hence with time the methodology to write docker-compose.yml file have changed.

Different version of Docker Compose

Docker compose have three different versions, the above image of docker-compose.yml files are just a way to represent the changes that have taken place in all the versions and compare them.

Version 1

  • This is the most well known method of writing a docker-compose file
  • It’s disadvantage was that it could only be used to set up a bridged network
  • It can’t run docker images in a sequence as it just runs everything simultaneously and hence if one docker image depends on the other for certain things the whole thing crashes.

Version 2

  • In this version we developed a new branch, know as services.
  • It became better with networking as it established a whole new bridged network and the docker images were then attached to that.
  • It also removed links and replaced it with depends_on, so in this way if a docker depends on other docker images, then first those docker images are run and only then the depended images are executed.

Version 3

  • This is the most advanced and current version of docker-compose
  • It also supports docker swarm, which is a whole different topic.

Networking in Docker Compose

The later versions of the docker compose mainly focused their developments around networking, so let’s dive in and see what are the benefits with an example of our voting app.

Architecture of Docker cluster

Here we are trying to create two different network, i.e. front-end and back-end. We want to set it up in such a way that everything communicates with each other as it is supposed to but via different networks.

So let’s dive in and have a look at the docker-compose.yml file,

  • Note: It is not complete as it focuses only on the networking part of things
versions: 2services:    
redis:
image: redis
networks:
- back-end
db:
image: postgres:9.4
networks:
- back-end
vote:
image: voting-app
ports:
- 5000:80
depends_on:
- redis
networks:
- front-end
- back-end
result:
image: result-app
ports:
- 5000:80
depends_on:
- db
networks:
- front-end
- back-end
worker:
image: worker
depends_on:
- redis
- db
networks:
- back-end
networks:
front-end:
back-end:

The tag “network” enables us to create two different networks, named front-end and back-end

networks:
- <network-name-1>:
- <network-name-2>:

In this way we can very easily establish as many networks as we wish to establish and run machines with dependencies on other machines without a problem using the “Version 2” of docker-compose.yml files.

Conclusion

Docker Compose files are extremely useful as they help us setup entire cluster of dockers effortlessly. It is a .yaml file that has all the configurations set in place so that we can simply run the “docker-compose up” command to carry out the entire task. Docker compose has evolved over three different versions with their certain pros and cons and has also enhanced its networking capabilities drastically with now being able to support docker swarm.

I have more articles related to Docker that I will be publishing in this upcoming month. Stay tuned for those.

If you enjoyed it please do clap & let’s collaborate. Get, Set, Hack!

Website : aditya12anand.com | Donate : paypal.me/aditya12anand
Telegram : https://t.me/aditya12anand
Twitter : twitter.com/aditya12anand
LinkedIn : linkedin.com/in/aditya12anand/
E-mail : aditya12anand@protonmail.com

Credits

To present you with this content I had to go through a lot of video content and lab environments.

  1. Docker for Beginners — KodeKloud
  2. Docker for Beginners — Lab Environment
  3. Docker Tutorial for Beginners — Edureka

Follow us on Dev, Sec & Ops to read related articles.

--

--