Docker-Compose Management
Tags: linux, docker, beginners
Published: 2023-03-07 04:52:43

## Introduction

Docker Compose is a tool for defining and running multi-container Docker applications. It allows you to define the services that make up your application and how they interact with each other. Docker Compose uses a **YAML** file to configure the application services, networks, and volumes.

Here are some of the key elements we can find in a Docker Compose YAML file:

- **version:** Specifies the version of the Docker Compose file format being used.
- **services:** Defines the various services that make up the application. Each service is given a name and specifies its image, ports, environment variables, and any other necessary configuration options.
- **networks:** Specifies the networks that the application's services will use to communicate with each other.
- **volumes:** Defines the volumes that will be mounted to the containers in the application.
configs: Specifies configuration files that will be injected into the containers as Docker Configs.
- **secrets:** Specifies secrets that will be injected into the containers as Docker Secrets.
- **deploy:** Specifies options for deploying the application as a stack, including replicas, update policies, and placement constraints.

> 💡 To read more regarding docker compose, you can look at the official documentation over [here](https://docs.docker.com/compose/) 


![compose!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7r2e68nujcivbr4umrf.png)

---

### Some of the important terminologies

✨ Infrastructure as Code (IaC)

- Technology for deploying and managing IT system infrastructure in the form of software, rather than by people.
- As the software is written in source code, management quality can be improved.
- Decreased work time for infrastructure managers due to parallel processing of multiple systems.
- Cost savings due to efficient processing possible by reducing work time.
- Lower error rate because only defined operations are performed by the software.

✨ Container Scaling Out (Scale Out)

- Scale out refers to increasing the number of servers operating the service in response to increasing loads to distribute the load.
- Horizontal scaling of containers: one of the core technologies of microservices, it prevents unnecessary system expansion by horizontally scaling only the containers responsible for a specific service.
- Vertical scaling (Scale Up): Concept of expanding the server vertically by increasing insufficient resources such as CPU and RAM.
- Traditional monolithic scaling method / Hardware is added because the entire system server cannot be added (there are limitations).

✨ Service Dependency and Discovery

- Services running on each container in a specific project have interdependent relationships (e.g., WEB-DB-Kafka, etc.).
- In a cloud environment, each service runs on an instance, and these instance information (IP, Port, etc.) has a characteristic that can easily change depending on the situation.
- Services with interdependencies are highly sensitive to such changes, and to quickly reflect the changed information, service discovery must be configured.

---

## Docker Compose Wordpress

For starters, I will be creating an emtpy directory

```bash
mkdir /Compose
cd /Compose
```

Creating a new `.yml` file

```bash
vi docker-compose.yml

# Compose File Format Version

version: '3.7'    
services:
  wordpress_db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress
    networks:
      - wordpress_net
    volumes:
      - wordpress_data:/var/lib/mysql

  wordpress:
    depends_on:
      - wordpress_db
    image: wordpress:latest
    restart: always
    ports:
      - "80:80"
    environment:
      WORDPRESS_DB_HOST: wordpress_db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress
      WORDPRESS_DB_NAME: wordpress
    networks:
      - wordpress_net
    volumes:
      - wordpress_web:/var/www/html

volumes:
  wordpress_data: {}
  wordpress_web: {}
  
networks:
  wordpress_net: {}
```

Checking the docker-compose version

```bash
docker-compose -v
Docker Compose version v2.2.3
```

If we use the following,

```bash
docker-compose config
```

It will show us the current configuration for the docker compose file in the current directory

Now composing the YAML file

```bash
docker-compose up -d

 ⠿ Network compose_wordpress_net     Created                                        0.4s
 ⠿ Volume "compose_wordpress_data"   Created                                        0.0s
 ⠿ Volume "compose_wordpress_web"    Created                                        0.0s
 ⠿ Container compose-wordpress_db-1  Created                                        0.7s
 ⠿ Container compose-wordpress-1     Created                                        0.0s
```
👉 The `-d` option declares the container to run in the background


After the compose build is done, we can check the docker volume and network are created as per the compose file

```bash
docker volume ls
local               compose_wordpress_data
local               compose_wordpress_web

docker network ls
755a8af21193        compose_wordpress_net   bridge              local
```

We can also check compose status as well

```bash
docker-compose ls
docker-compose ps
```
Now if we open the website using the IP address of our host system


![wordpress](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3t8kwbs9v1su04c5bhoy.png)


> 💡 When deleting composed containers, we can use `docker-compose down` however this won't delete the docker volume. We will need to include the `-v` option to delete the docker volume as well

---

## Docker Compose building Nginx

I will now demonstrate using docker compose to build a Dockerfile

Creating a new directory

```bash
mkdir /Compose/build
cd /Compose/build
```

Creating an `index.html`

```bash
echo "Hello My Nginx" > index.html
```

Creating the Dockerfile

```bash
vi Dockerfile

FROM nginx:latest
LABEL maintainer "Author <Author@localhost.com>“
ADD index.html /usr/share/nginx/html
```

Creating the docker-compose YAML file

```bash
vi docker-compose.yml

version: '3.7'
services:
  web:
    image: myweb/nginx:v1
    build: .
    restart: always
```

Now using docker compose to build our Dockerfile

```bash
docker-compose -p myweb up -d --build

[+] Running 2/2
 ⠿ Network myweb_default  Created                                                   0.1s
 ⠿ Container myweb-web-1  Started                                                   0.2s
```

👉 Here the "-p" option specifies a project name, and the "--build" option skips the image search and pull, performing only the build operation. If the "--build" option is omitted, the pull operation is automatically performed, but using it is recommended during build operations to avoid errors. 

We can confirm the process

```bash
docker-compose -p myweb ps -a
NAME                COMMAND                  SERVICE             STATUS              PORTS
myweb-web-1         "/docker-entrypoint.…"   web                 running             80/tcp
```

Another thing we can try is,

```bash
docker-compose -p myweb up -d --scale web=3

[+] Running 3/3
 ⠿ Container myweb-web-3  Started                                                   0.9s
 ⠿ Container myweb-web-2  Started                                                   0.9s
 ⠿ Container myweb-web-1  Started                                                   0.9s
```

Checking the compose process again
```bash
docker-compose -p myweb ps

NAME                COMMAND                  SERVICE             STATUS              PORTS
myweb-web-1         "/docker-entrypoint.…"   web                 running             80/tcp
myweb-web-2         "/docker-entrypoint.…"   web                 running             80/tcp
myweb-web-3         "/docker-entrypoint.…"   web                 running             80/tcp
```

👉 With the "--scale" option, it's possible to horizontally scale specific service containers, explicitly declaring a scale of 1 reduces the containers to 1. However, this option cannot be used when ports are specified, as it's not possible to expose multiple containers to the outside with the same port number. To serve multiple instances of the same web container, a proxy server must be used

---

## Docker Compose HAproxy with Nginx LB


![Proxy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y03m0j35a18nxl3fqomo.png)




Starting with an empty directory

```bash
mkdir /Compose/prod
cd /Compose/prod
```

Creating an `index.html`

```bash
echo "Hello My Nginx" > index.html
```

Creating a Dockerfile for Nginx and HAproxy

```bash
vi Dockerfile_nginx

FROM nginx:latest
LABEL maintainer "Author <Author@localhost.com>"
ADD index.html /usr/share/nginx/html
WORKDIR /usr/share/nginx/html

vi Dockerfile_haproxy

FROM haproxy:2.3
LABEL maintainer "Author <Author@localhost.com>"
ADD haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
```

Configuring the `haproxy.cfg` file

```bash
vi haproxy.cfg
global
        log /dev/log  local0
        log /dev/log  local1 notice
        chroot /var/lib/haproxy
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

defaults
        log global
        mode http
        option httplog
        option dontlognull
        option dontlog-normal
        option http-server-close
        maxconn 3000
        timeout connect 10s
        timeout http-request 10s
        timeout http-keep-alive 10s
        timeout client 1m
        timeout server 1m
        timeout queue 1m

listen stats
        bind *:9000
        stats enable
        stats realm Haproxy Stats Page
        stats uri /
        stats auth admin:haproxy1

frontend proxy
        bind *:80
        default_backend WEB_SRV_list

backend WEB_SRV_list
        balance roundrobin
        option httpchk HEAD /
        server prod-web-1 prod-web-1:80 check inter 3000 fall 5 rise 3
        server prod-web-2 prod-web-2:80 check inter 3000 fall 5 rise 3
        server prod-web-3 prod-web-3:80 check inter 3000 fall 5 rise 3
```

👉 At the backend section of the configuration file, the web server's status is checked using the HEAD method every 3 seconds. If the status check fails 5 times in a row, the web server is removed from the load balancer group. If the server becomes available again and passes 3 consecutive status checks, it is added back to the load balancer group.

Finally creating the docker compose YAML file

```bash
vi docker-compose.yml

version: '3.7'
services:
  proxy:
    depends_on:
      - web
    image: prod/haproxy:v1
    build:
      context: ./
      dockerfile: ./Dockerfile_haproxy

    restart: always
    ports:
      - "80:80"
      - "9000:9000"
    networks:
      - myweb_net

  web:
    image: prod/nginx:v1
    build:
      context: ./
      dockerfile: ./Dockerfile_nginx
    restart: always
    deploy:
      mode: replicated
      replicas: 3
    networks:
      - myweb_net

networks:
  myweb_net: {}
```

Using docker compose to build our Dockerfiles

```bash
docker-compose up -d --build

Successfully built 5deb0e3b5821
Successfully tagged prod/haproxy:v1
[+] Running 5/5
 :: Network prod_myweb_net  Created                                                                  0.2s
 :: Container prod-web-3    Started                                                                  0.7s
 :: Container prod-web-1    Started                                                                  0.7s
 :: Container prod-web-2    Started                                                                  0.6s
 :: Container prod-proxy-1  Started                                                                  1.5s
```

Once the job is done, we can check the processes

```bash
docker-compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
prod-proxy-1        "docker-entrypoint.s…"   proxy               running             0.0.0.0:80->80/tcp, 0.0.0.0:9000->9000/tcp
prod-web-1          "/docker-entrypoint.…"   web                 running             80/tcp
prod-web-2          "/docker-entrypoint.…"   web                 running             80/tcp
prod-web-3          "/docker-entrypoint.…"   web                 running             80/tcp
```

To test if the load balancing if it is working between these 3 web pages, we can include a different entry in each of the web server

```bash
docker exec -it prod-web-1 /bin/bash
root@c6f1c535ce6c:/usr/share/nginx/html# echo "prod-web-1 Server Main Page" >> index.html
root@c6f1c535ce6c:/usr/share/nginx/html# cat index.html
Hello My Nginx
prod-web-1 Server Main Page

docker exec -it prod-web-2 /bin/bash
root@e86233007a76:/usr/share/nginx/html# echo "prod-web-2 Server Main Page" >> index.html
root@e86233007a76:/usr/share/nginx/html# cat index.html
Hello My Nginx
prod-web-2 Server Main Page

docker exec -it prod-web-3 /bin/bash
root@11aa89e08619:/usr/share/nginx/html# echo "prod-web-3 Server Main Page" >> index.html
root@11aa89e08619:/usr/share/nginx/html# cat index.html
Hello My Nginx
prod-web-3 Server Main Page
```

Testing our setup



![1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gasqzxzyfplkwp5izw8j.png)



![2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkew4c9ecq7hxsaoc5qb.png)



![3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e17w1my193n72tjd15sk.png)


👉 We can confirm that round robin load balancing is working


---

## Conclusion

In conclusion, Docker Compose is a powerful tool that simplifies the process of managing and deploying complex multi-container applications. Through hands-on experience building an Nginx server with HAProxy and a WordPress application, we have seen how Docker Compose streamlines the management of container orchestration, network configuration, and service scaling.