If you are a beginner to Docker Compose, but have some knowledge of Docker, this article is for you. You’ll get to learn about:
- What is Docker Compose?
- Popular Comparisons
- Docker Compose vs Kubernetes
- Docker Compose vs Docker Swarm
- Installing Docker Compose
- The Docker-Compose.yml File
- Docker-Compose Commands
Before diving into the juicy parts of this article, a little background on the tech should be awesome.
Containerization has become a key part of software infrastructure, and this applies to large, medium or small-scale projects. While containers are not new, Docker has made them popular. With containers, dependency issues become a thing of the past. Containers also play a huge role in making the micro-services architecture very effective. Software applications are made of smaller services, so it is easy to have these services in containers, and they communicate.
The issue with doing this, is that there will be so many containers running. Such that managing them becomes complex. This creates a need for a tool helps run multiple containers, which Docker Compose does. At the end of the article, you’ll understand basic Docker Compose concepts and be able to use it too.
What is Docker Compose?
Without all the complexity, Docker Compose is a tool that lets you manage multiple Docker containers. Remember micro-services? The concept of splitting a web application into different services? Well, those services will run in individual containers which need to be managed.
Imagine a web application has some of these services:
- Sign up
- Sign in
- Reset password
- History
- Chart
Following a microservice-like architecture, these services will be split and run in separate containers. Docker Compose makes it easy to manage all these containers, instead of managing them individually. It is important to note that Docker Compose doesn’t explicitly build Docker images. The job of building images is done by Docker through the Dockerfile.
Popular Comparisons
It is common to have many solutions to a problem. Docker Compose solves this problem of managing multiple containers. As a result, there are often comparisons with other solutions. You should note that most of these comparisons are the wrong ones. While they are often not valid, it is best you learn about them as it helps you understand Docker Compose better.
The two comparisons to be discussed are:
- Docker Compose vs Kubernetes
- Docker Compose vs Docker Swarm
Docker Compose vs Kubernetes
Kubernetes is often compared to Docker Compose. But, the similarities in both tools are minute, with large dissimilarities. These technologies are not at the same level or scale. Hence, comparing both tools is outrightly wrong.
Kubernetes popularly known as k8s is an open-source tool that can be used to automate containers (not restricted to Docker). With k8s, you can deploy and administer containers, ensuring they scale at different loads. Kubernetes ensures that containers are fault-tolerant and work optimally by causing them to self-heal, which you won’t get from Docker Compose.
Kubernetes is a more powerful tool. It is more suitable for administering containers for large-scale applications in production.
Docker Compose vs Docker Swarm
Docker Compose is also often compared to Docker Swarm, and it is as wrong as the Kubernetes comparison. Instead, Docker Swarm should be the one being compared to Kubernetes.
Docker Swarm is an open-source tool that lets you perform container orchestration just as you would Kubernetes. Both have their pros and cons, but that is not the topic of discussion. You’ll do fine knowing that both are similar and neither is an alternative to Docker Compose.
Installing Docker Compose
Docker Compose is an official Docker tool, but it doesn’t come with the Docker installation. So, you need to install it as a separate package. The installation process of Docker Compose for Windows and Mac is available on the official site.
To install Docker Compose on Ubuntu, you can use the following command:
To install Docker Compose on other Linux distros, you can use curl. Simply run the following commands:
https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname
-s`-`uname -m` -o /usr/local/bin/docker-compose
Then:
The first command downloads the latest version of Docker Compose to the directory dedicated for packages. The second one sets the file permissions, making it executable.
The Docker-Compose.yml File
It won’t be awfully wrong to say that a Docker Compose file is to Docker Compose, what a Dockerfile is to Docker. Inside the Docker Compose file, lies all the instructions that Docker Compose follows when managing the containers. Here, you define the services which end up being containers. You also define the networks and volumes that the services depend on.
The Docker Compose file uses the YAML syntax, and you have to save as docker-compose.yml. You can have services for the backend, frontend, database and message queues in a web app. These services will need specific dependencies. Dependencies such as the networks, ports, storage for optimal operation. Everything needed for the entire application will be defined in the Docker Compose file.
You need a basic understanding of the YAML syntax to write your compose file. If you aren’t familiar with that, it should take less than an hour to grasp. There’ll be a lot of key-value pairings or directives in your file. The top-level ones are:
- Version
- Services
- Network
- Volumes
However, only the version and services will be discussed, as you can define the other two in the services directive.
Version
When writing your file, you’ll define the version first. As at the time of writing, Docker Compose only has versions 1, 2 and 3. It’s not surprising that it is the recommended version to use as it has certain differences from the older versions.
You can specify the version to use for Docker Compose in the file as seen below:
- Version: “3”
- Version: “2.4”
- Version: “1.0”
Services
The service key is arguably the most important key in a Docker Compose file. Here, you specify the containers you want to create. There are a lot of options and tons of combinations for configuring containers in this section of the file. These are some options you can define under the services key:
- Image
- Container_name
- Restart
- Depends_on
- Environment
- Ports
- Volumes
- Networks
- Entrypoint
In the rest of this section, you’ll learn how each of these options affect the containers.
Image
This option defines what image as service uses. It uses the same convention as you use when pulling an image from Dockerhub in a Dockerfile. Here’s an example:
However, there is no restriction to using Dockerhub files alone. You can also build images from your machine through your Docker Compose file, using a Dockerfile. You can use the “build”, “context” and “dockerfile” directives to do this.
Here’s an example:
context: .
dockerfile: Dockerfile
“Context” should contain the path to the directory with the Dockerfile. Then “dockerfile” contains the name of the Dockerfile to be used. It is conventional to always name your Dockerfiles as “Dockerfile”, but this gives an opportunity to use something different. You should note that this is not the only way to use an image through a Dockerfile.
Container_name
Docker assigns random names to containers. But you may desire to have customized names for the containers. With the “container_name” key, you can give specific names to containers, instead of Dockers randomly generated names.
Here’s an example:
However, there’s one thing you should be careful about: do not give the same name to multiple services. Container names have to be unique; doing so will cause the services to fail.
Restart
Software infrastructure is doomed to fail. With the knowledge of this, it is easier to plan towards recovering from this failure. There are many reasons for a container to fail, so the restart key tells the container to wake or not. You have the following options, no, always, on-failure and unless-stopped. These options imply that a container will never restart, will always restart, only restart on failure or only when stopped.
Here’s an example:
Depends_on
Services run in isolation. But practically, services can’t do much in isolation. There needs to be a dependency on other services. For example, the backend service of a web app will depend on databases, caching services, etc. At the “depends_on” key, you can add the dependencies.
Here’s an example:
- db
Doing this means that Docker Compose will start those services before the current one. However, it doesn’t ensure that those services are ready for use. The only guarantee is that the containers will start.
Environment
Applications depend on certain variables. For security and ease of use, you extract them from the code and set them up as environment variables. Examples of such variables are API keys, passwords, and so on. These are common in web applications. Note that this key only works if there is no “build” directive in that service. Hence, create the image beforehand.
Look at this:
API-KEY: 'the-api-key'
CONFIG: 'development'
SESSION_SECRET: 'the-secret'
If you intend to use the “build” directive regardless, you’ll need to define the environment variables in an “args” directive. The “args” directive is a sub-directive of “build”.
Here’s an example:
context: .
args:
api-key: 'the-api-key'
config: 'development'
session_secret: 'the-secret'
Ports
No container works in isolation despite running separately from the others. To provide a link to communicate with the “outside world”, you need to map ports. You map the Docker container’s port to the actual host port. From Docker, you may have come across the “-p” argument that is used to map ports. The ports directive works similar to the “-p” argument.
- "5000:8000"
Volumes
Docker containers have no means of storing data persistently, so they lose data when they restart. With volumes, you can work around this. Volumes makes it possible to create a persistent data storage. It does this by mounting a directory from the docker host into the docker container’s directory. You can also setup volumes as top level services.
Here’s an example:
- host-dir:/test/directory
There are many options available when configuring volumes, you can check them out.
Networks
Networks can also be created in services. With the networks key, you can setup the networking for individual services. Here, you can setup the driver the network uses, if it allows IPv6, etc. You can setup networks like services too, just like volumes.
Here’s an example:
- default
There are many options when configuring networks, you can check them out.
Entrypoint
When you start a container, you often must run certain commands. For example, if the service is a web application, you must start the server. The entrypoint key lets you do this. Entrypoint works like ENTRYPOINT in Dockerfile. The only difference in this case is that whatever you define here overrides the ENTRYPOINT configurations in the Dockerfile.entrypoint: flask run
Here’s an example:
Docker Compose Commands
After creating a Docker-Compose file, you need to run certain commands to get Compose to work. In this section, you’ll learn about some major Docker Compose commands. They are:
- Docker-compose up
- Docker-compose down
- Docker-compose start
- Docker-compose stop
- Docker-compose pause
- Docker-compose unpause
- Docker-compose ps
Docker-compose up
This Docker-compose command helps builds the image, then creates and starts Docker containers. The containers are from the services specified in the compose file. If the containers are already running and you run docker-compose up, it recreates the container. The command is:
Docker-compose start
This Docker-compose command starts Docker containers, but it doesn’t build images or create containers. So it only starts containers if they have been created before.
Docker-compose stop
You’ll often need to stop the containers after creating and starting them up. Here’s where the Docker-compose stop command comes in handy. This command basically stops the running services, but the setup containers and networks remain intact.
The command is:
Docker-compose down
The Docker-compose down command also stops Docker containers like the stop command does. But it goes the extra mile. Docker-compose down, doesn’t just stop the containers, it also removes them. The networks, volumes and actual Docker images can also be removed if you use certain arguments. The command is:
If you intend to remove volumes, you specify by adding –volumes. For example:
If you intend to remove images, you specify by adding –rmi all or –rmi local. For example:
docker-compose down --rmi local
Where all causes Docker Compose to remove all images, and local causes Docker Compose to remove only images without a custom tag set by the ‘image’ field.
Docker-compose pause
There are scenarios where you have to suspend a container, without killing or deleting it. You can achieve this with the Docker-compose pause command. It pauses the activities of that container, so you can resume them when you want to. The command is:
Docker-compose unpause
The docker-compose unpause is the opposite of the docker-compose pause command. You can use it to resume suspended processes as a result of using Docker-compose pause. The command is:
Docker-compose ps
Docker-compose ps lists all the containers created from the services in the Docker-Compose file. It is similar to docker ps which lists all containers running on the docker host. However, docker-compose ps is specific to the containers from the Docker Compose file. The command is:
Bringing It All Together
Now that you have seen some of the key concepts behind a Docker Compose file, let’s bring it all together. Below is a sample Docker-Compose file for a Python Django web application. You’ll see a breakdown of every line in this file and see what they do.
services:
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
The short story is that with this Docker-Compose file, a PostgreSQL database is created and a django server is started.
The long story is:
- This file uses the version 3 of Docker-Compose.
- It creates two services. The db and web services.
- The db service uses the official docker postgres image.
- The web service builds its own image from the current directory. Since it does not define the context and Dockerfile keys, Dockerfile is expected to be named “Dockerfile” by convention.
- The command that will run after the container starts is defined.
- The volume and ports are defined. Both use the convention of host:container mapping.
- For volume, the current directory “.” is mapped to “/code” directory inside the container. This helps data in the container become persistent, so it is not lost every time the container starts.
- For port, the host’s port 8000 is mapped to the container’s port 8000. Note that the web app runs on the port 8000. Hence, the web app can be accessed on the host through that port.
- Finally, the web service depends on the db service. Hence, the web service will only start when the db container has started.
- More on the Dockerfile for the Django application and Docker Compose file can be gotten from the documentation.
Conclusion
You do not need to be an expert with Docker to use Docker Compose. As a beginner not intending to master this tool, it is fine to learn what you need alone. In this article, you’ve learnt the basics of Docker Compose. Now, you understand why Docker Compose is needed, the wrong comparisons, how to setup a Docker Compose config file and the commands too. It’s exciting knowing these things, but the real joy comes from putting them to practice. It’s time to get to work.