Docker compose
Tags: docker, devops, database
Published: 2020-11-30 21:17:00

## Context
We usually need to run multiple docker containers when we deploy a web-app/website back-end project. These containers may also have inter-dependencies between each other, making it a really tedious work for me. I used to configure and bring up these containers manually by using a series of bash files. To do so, I need to remember the order of containers by heart, remember their launch file locations, and type manually multiple command lines in terminal. Fortunately there is the super tool `Docker Compose` who saves my ass. 


## Introduction

It's a tool that defines a list of containers to run with their configs and operations. In a sense, it is a little similar to how **Dockefile** works for defining a container. 

As far as I understand, **Dockerfile** defines a list of instructions for bringing up a container, while **compose** defines a list for bringing up a **containers group**.

>**Compose** is a tool for defining and running multi-container Docker applications. With **Compose**, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. Compose works in all environments: production, staging, development, testing, as well as CI workflows. 

Generally we need three steps for deploying with Compose:

1. Define a **Dockerfile** or several Dockerfiles for one or several services of the project.

2. Define all the services of the project in *docker-compose.yml* so they shall be run together in an isolated environment.

3. Run `docker-compose up` and **Compose** starts and runs the entire app.

## Installation

Installation of Docker Compose is quite simple. One can see (this page)[https://docs.docker.com/compose/install/] the official instructions for all the platforms. For Linux, I use the following cmd lines for installing it:

```bash
$ sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
```

## Try an example
I tried the simple example presented in the official doc.

In my *flask_app* folder, I firstly added one `docker-compose.yml` file which defined the following content:

```yml
version: "3.8"
services:
  web:
    build: .
    ports:
      - "8666:5000"
  redis:
    image: "redis:alpine"
```
Clear as it shows, there are two services that shall be started up
: `web` and `redis`. And `web` service requires a port mapping as `8666:5000` and it's build file is located in current folder.

Now I defined a simple flask app which is a Python file `app.py` and fill in the following code :

```python
import time
import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

```

To run this app, I need a Python-env service which can execute Python script and install dependency modules `flask` and `redis`. Thus we define a Dockerfile and a requirement.txt in *flask_app* folder.

File *Dockerfile* content:
```bash
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
``` 

File *requirement.txt* contains:
``` 
flask
redis
``` 
Till now, I have all the files for starting this web app. To summarize, `web` service is a python app which will be built from *Dockerfile* and binds container's 5000 port to host machine's 8666 port; `redis` is a standard Redis container started from a pulled official image with default port setting.

Now launch it (I have already installed **docker-compose** beforehand for my Huawei Cloud server):
```bash
sudo docker-compose up
```
After some printed information about downloading and preparing, I see the following info regarding launching of the services: 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/g97hrs7mefle1qpx44h7.PNG)

And `docker ps` gives two more containers:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/225kt6p7xx1frrkpv8i7.PNG)

One can notice that the container name is formated as `{folderName}_{serviceName}_1`.

Now test it. In my browser, I type *my_serverIp:8666* and it shows the page shown below. Each time I refresh it, the counter increases by one. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2t5ly7sk8lartqqh4x7w.png)


Reference:
https://docs.docker.com/compose/