Docker Compose Tutorial

28/12/2020

Docker’s popularity as a development tool is on the rise. Docker has breathed new life into the container movement. Developers like using it because it’s fast and easy-to-learn. It helps development teams share standard environments without worrying about wasting time and resources.

Developers can set up the desired environment in a Docker container, save the container as an image and share it easily with their development teams. The process works great for a single container. However, multi-container environments are harder to maintain. Docker Compose provides the solution.

With Docker Compose, developers can define a YAML file to set up the configuration for multiple services. Then they can start the multi-container services with a single command. It simplifies the process of working with multi-container applications.

Prerequisite

We are assuming, you have a basic understanding of Docker. Otherwise, look at How to Install and Use Docker on Ubuntu.  The examples use WordPress, MySQL, Flask, and Python. However, no prior knowledge of these tools is necessary.

Docker Compose Process: At a Glance

  1. Define Application Environment: Use Dockerfile to define the app environment to make it easily reproducible.
  2. Define Docker Compose Environment: Use docker-compose.yml to define the services in the application.
  3. Run Application: Use docker-compose up to run the multi-container application.

Example Docker Compose File

  version: '3'    services:     db:       image: mysql:5.7       volumes:         - db_data:/var/lib/mysql       restart: always       environment:         MYSQL_ROOT_PASSWORD: rootpassword123         MYSQL_DATABASE: wordpress         MYSQL_USER: wordpress_user         MYSQL_PASSWORD: wordpress_password       wordpress:       depends_on:         - db       image: wordpress:latest       ports:         - "8000:80"       restart: always       environment:         WORDPRESS_DB_HOST: db:3306         WORDPRESS_DB_USER: wordpress_user         WORDPRESS_DB_PASSWORD: wordpress_password  volumes:      db_data:  

If the above docker-compose.yml file is invoked with docker up, it will create a WordPress service that connects to a MySQL database service.

Docker Compose commands

You can use docker-compose –help to find the Docker Compose command

When to Use Docker Compose?

Currently, Docker is mainly used in development environments. Some of the popular uses of Docker Compose are:

1. Prototyping and Development

Application prototyping and development process are slowed down due to the lack of standard environments. Developers often have to waste time setting up the same environment multiple times. Also, reading guides to set up environment parameters is time-consuming.

Docker Compose simplifies the process. Once an environment is configured, development teams can share the Docker files across the organization. It can save an enormous amount of time wasted on configuration management issues.

2. Testing and Automating Processes

Continuous integration and continuous delivery (CI/CD) are becoming standard processes in today’s agile development environments. Automated testing is an important component of CI/CD.  Docker Compose helps define the automated testing process. All the complications of starting new services can be neatly put into docker configuration files. Testers can use these files to fire up temporary services, run text scripts and destroy the services after collecting the test results. It saves time because manually starting services is time-consuming and error-prone.

3. Future Production Deployment

Docker is mainly used in development environments. However, as Docker functionalities become more robust, Docker will be used for more production-level work. Docker Compose can be a valuable tool for single host deployments.

Exercise: A Simple Web Application

Let’s try our hand at a simple python based web application to try out Docker Compose. We will use the Flask web framework to create an application that communicates with an in-memory database Redis to keep track of how many times the web application has been visited.

The directory structure will look like this:

  simple_app  ├── content   │    ├── Dockerfile  │    └── code  │         ├── simple_app.py   │         └── requirements.txt   └──  docker-compose.yml   

The above directory structure is not necessary for a basic application. However, it shows how organizing information can be helpful for more efficient implementation of Docker Compose.

Step 1: Create Directory Structure and Files

Let’s create the directory structure and the necessary files:

  $ mkdir simple_app  $ mkdir simple_app/content  $ mkdir simple_app/content/code    $ touch simple_app/docker-compose.yml  $ touch simple_app/content/Dockerfile  $ touch simple_app/content/code/simple_app.py  $ touch simple_app/content/code/requirements.txt  

The touch command is just creating empty files. You can manually go into the folders and create the files.

Step 2: Web Application Code

The code folder contains the web application code. Put the following in simple_app.py file:

  from flask import Flask  from redis import Redis    app = Flask(__name__)  redis = Redis(host='redis', port=6379)    @app.route('/')  def hello():      count = redis.incr('hits')      return '<b>Welcome to Docker Compose Lessons!</b><br><br>You have visited this site {} times.n'.format(count)    if __name__ == "__main__":      app.run(host="0.0.0.0", debug=True)  

The above application creates a welcome page that displays the number of times the page has been visited. The visit counter is maintained in a Redis database. Redis uses port 6379 as its default listening port.  Next, fill out the requirements.txt file:

  flask  redis  

This will enable pip to install python dependencies on the web container. We will run pip as part of initializing our service.

Step 3:  Dockerfile

Fill the simple_app/content/Dockerfile with the following code:

  FROM python:3.6.3-jessie  ADD ./code /code  WORKDIR /code  RUN pip install -r requirements.txt  CMD ["python", "simple_app.py"]  

The above Dockerfile achieves the following:

  1. Creates an image from python:3.6.3-jessie. If it’s not available locally then it downloads it from Docker Hub.
  2. Copies elements in simple_app/content/code into /code on the container
  3. Set /code as the working directory on the container
  4. Uses pip to install the python dependencies
  5. Sets the default starting point for the container to run python simple_app.py.

Step 4: Docker Compose

Fill the simple_app/docker-compose.yml file with the following code:

  version: '3'  services:    web:      build: ./content      ports:       - "5000:5000"      volumes:       - ./content/code:/code    redis:      image: "redis:alpine"  

The docker-compose.yml file defines two containers: web and redis. It uses Docker Compose version 3 format.

For the web service:

  • Builds the web service using simple_app/content/Dockerfile
  • Forwards port 5000 from the web container to host’s port 5000. Port 5000 is the default port for Flask applications.
  • Volume simple_app/content/code is mounted as /code on the container. It means that if you change anything in the simple_app/content/code, it will be reflected in /code folder on the web container.

For the redis service:

  • Uses the redis:alpine image from Docker Hub to create the redis service.

Step 5: Running Applications using Docker Compose

The application is ready for deployment. From the simple_app folder, run the following command:

$ docker-compose up

The output should start like this:

  $ docker-compose up    Building web  Step 1/5 : FROM python:3.6.3-jessie  3.6.3-jessie: Pulling from library/python  85b1f47fba49: Downloading [===========>                                       ]  12.43MB/52.6MB  5409e9a7fa9e: Download complete  661393707836: Downloading [===============>                                   ]  13.71MB/43.23MB  1bb98c08d57e: Downloading [>                                                  ]  1.081MB/134.7MB  ...  

Once all the images are built and running, you should see the following:

  Status: Downloaded newer image for redis:alpine  Creating simpleapp_redis_1 ...   Creating simpleapp_web_1 ...   Creating simpleapp_redis_1  Creating simpleapp_web_1 ... done  Attaching to simpleapp_redis_1, simpleapp_web_1  redis_1  | 1:M 21 Oct 02:06:33.639 * Ready to accept connections  web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)  web_1    |  * Restarting with stat  web_1    |  * Debugger is active!  web_1    |  * Debugger PIN: 237-189-083  

You can test the application by going to http://localhost:5000:.  If you refresh the page a few times, it should reflect the number of visits.  You can check the status of services or containers running:

  $ docker ps    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES  22852e0ad98a        redis:alpine        "docker-entrypoint..."   5 minutes ago       Up 5 minutes        6379/tcp                 simpleapp_redis_1  d51739d0a3ac        simpleapp_web       "python simple_app.py"   5 minutes ago       Up 5 minutes        0.0.0.0:5000->5000/tcp   simpleapp_web_1  

If you start a bash shell in simpleapp_web_1 (your container name may differ), you will be logged into the working directory /code:

  $ docker exec -it simpleapp_web_1 bash    [email protected]:/code# ls  requirements.txt  simple_app.py  [email protected]:/code#   

The /code directory should reflect the content of simple_app/content/code inside it as seen above (simple_app.py and requirements.txt).

If you update your simple_app.py’s line from:

  return '<b>Welcome to Docker Compose Lessons!</b><br><br>You have visited this site {} times.n'.format(count)  

To:

return '<b>Welcome to Docker Compose Lessons!</b><br><br>Are you intrigued? <br><br>You have visited this site {} times.n'.format(count)

It should reflect on http://localhost:5000:

Step 6: Shutting Down the Services

You can stop the application using:

  $ docker-compose stop  Stopping simpleapp_redis_1 ... done  Stopping simpleapp_web_1 ... done  

The mounted volumes will persist. You can remove the containers entirely including the volumes using the following command.

  $ docker-compose down --volume    Removing simpleapp_redis_1 ... done  Removing simpleapp_web_1 ... done  Removing network simpleapp_default  

Congratulations! You have mastered the basics of Docker Compose.

Further Study

For further study, look at the following documentation:

References:

ONET IDC thành lập vào năm 2012, là công ty chuyên nghiệp tại Việt Nam trong lĩnh vực cung cấp dịch vụ Hosting, VPS, máy chủ vật lý, dịch vụ Firewall Anti DDoS, SSL… Với 10 năm xây dựng và phát triển, ứng dụng nhiều công nghệ hiện đại, ONET IDC đã giúp hàng ngàn khách hàng tin tưởng lựa chọn, mang lại sự ổn định tuyệt đối cho website của khách hàng để thúc đẩy việc kinh doanh đạt được hiệu quả và thành công.
Bài viết liên quan

Install Redis From Docker Hub

Overview In this post, we will see how we can install and use Redis server from  Docker Hub. Using Docker Hub is very...
28/12/2020

Deploying MySQL using Docker-Compose

MySQL is one of the most popular database management systems out there. Many applications use it for their backend needs....
29/12/2020

Managing Docker Volumes using Docker Compose

Purpose of Docker Volumes Docker containers are meant to be a drop-in replacement for applications. They are meant to be...
29/12/2020