Docker – Compose
Docker is an open-source platform that makes development, shipping and deployment of application easy. It packages all the dependencies of an application in a so called container and runs it as an isolated environment. To know more about docker, read Introduction to docker.
In a typical scenario there will be multiple services needed to support an application, like database, load balancing, etc. In this article we will see how Docker Compose helps us in setting up multiple services.
We will also see an example to how to install and use Docker Compose.
Let’s start with briefly explaining some basic concepts.
Docker Container
A docker container is a light weight linux based system that packages all the libraries and dependencies of an application, prebuilt and ready to be executed. It is an isolated running image which makes the application feel like the whole system is dedicated to it.
Many large organizations are moving towards containers from VMs as they are light and simple to use and maintain.
But when it comes to using containers for real world applications, usually one container is not sufficient. For example, Lets assume Netflix uses a microservices architecture. Then it needs services for authentication, Login, Database, Payment etc and for each of these services we want to run a separate container.
It is preferred for a container to have only a single purpose.
Now, imagine writing separate dockerfiles, managing configuration and networks for each container. This is where Docker compose comes in the picture and makes our lives easy.
Docker Compose
To better understand what is a docker compose, we should start with why.
Why Docker Compose?
As discussed earlier, a real world application has a separate container for each of its services. And we know that each container needs to have a Dockerfile. It means we will have to write may be hundreds of dockerfiles and then manage everything about the containers individually, That’s cumbersome.
Hence we use docker compose, which is a tool that helps in the definition and running of multi-container applications. With docker compose we use a single YAML file for every configuration and just single commands to start and stop all the services.
If you are using a custom image then you will need to define its configurations in a separate Dockerfile in contrast to using a prebuilt image from Docker Hub, which you can define with the docker-compose.yaml file.
These are the features that docker compose support:
- All the services are isolated running on the single host.
- Containers are recreated only when there is some change.
- The volume data is not reset when creating new containers, volumes are preserved.
- Movement of variables and composition within environments.
- It creates a virtual network for easy interaction within the environments.
Now, let’s see how we can use docker compose, using a simple project.
A Simple Project
In this project we will create a straightforward Restfull API that will return a list of fruits. We will use flask for this purpose. And a PHP application will request this service and show it in the browser. Both the services will run in their own containers.
- First, Create a separate directory for our complete project. Use the following command.
mkdir dockerComposeProject
- Move inside the directory.
cd dockerComposeProject
The API
we will create a custom image that will use python to serve our Restful API defined below. Then the service will be further configured using a Dockerfile.
- Then create a subdirectory for the service we will name it product. and move into the same.
mkdir product cd product
api.py
Python3
from flask import Flask from flask_restful import Resource, Api # create a flask object app = Flask(__name__) api = Api(app) # creating a class for Fruits that will hold # the accessors class Fruits(Resource): def get( self ): # returns a dictionary with fruits return { 'fruits' : [ 'Mango' , 'Pomegranate' , 'Orange' , 'Litchi' ] } # adds the resources at the root route api.add_resource(Fruits, '/' ) # if this file is being executed then run the service if __name__ = = '__main__' : # run the service app.run(host = '0.0.0.0' , port = 80 , debug = True ) |
- Create a Dockerfile to define the container in which the above API will run.
The Dockerfile:
FROM python:3-onbuild COPY . /usr/src/app CMD ["python", "api.py"]
Explanation:
FROM accepts an image name and a version that the docker will download from docker hub. Copy command is used to copy the contents of current working directory to the location from where the server expects the code to be. And the CMD command accepts a list of commands that will be executed once the container is started, which will finally start the service.
A separate container for the website
Let’s create a simple website using PHP that will use our API.
- Move to the parent directory and create another subdirectory for the website.
cd .. mkdir website cd website
index.php
PHP
<!DOCTYPE html> <html lang= "en" > <head> <title>Fruit Service</title> </head> <body> <h1>Welcome to India's Fruit Shop</h1> <ul> <?php $obj = json_decode( $json ); $fruits = $obj ->fruits; foreach ( $fruits as $fruit ){ echo "<li> $fruit </li>" ; } ?> </ul> </body> </html> |
- Now create a compose file where we will define and configure the two services, API and the website.
- Move out of the website subdirectory using the following code.
cd ..
And then create the following file.
docker-compose.yaml
version: "3" services: fruit-service: build: ./product volumes: - ./product:/usr/src/app ports: - 5001:80 website: image: php:apache volumes: - ./website:/var/www/html ports: - 5000:80 depends_on: - fruit-service
Explanation:
The first line is optional where we specify the version of the docker-compose tool. Next services defines a list of services that our application is going to use. The first service is fruit service which is our API and the second one is our website. The fruit-service has a property build which contains the dockerfile that is to be built and created an image. Volumes defines storage mapping between host and the container so that we can make live changes. Finally port property exposes the containers port 80 through host’s 5001.
The website service does not uses a custom image but we download the PHP image from Docker hub and then map the websites folder that contains our index.php to /var/www/html (PHP expects the code to be at this location). Ports expose the containers port. Finally the depends_on specifies all the services on which the current service depends on.
- The folder structure after creating all the required files and directory will be as follows:
- To start the application, enter the following command.
docker-compose up
Now all the services will start and our website will be ready to be used at localhost:5000.
- Open your browser and enter localhost:5000.
Output:
- To stop the application, either press CTRL + C or
docker-compose stop
Conclusion:
In this article, we learned about Docker Compose, why and when to use it. And demonstrated its use through a simple project.
Please Login to comment...