Tech, Docker, DevOps, DockerCompose, Getting Started

Getting started with Docker Compose

Building on the previous article about getting started with Docker, today we'll take a deep dive in how Docker handles orchestration with Docker compose. This being the second article (originally published on my blog here) of the DevOps Series.

Why Docker Compose

Docker Compose is a tool for running multi-container Dockerized applications.

Given a YAML configuration file which describes all your services and a single command, you can create and start your application, drastically improving the flow compared to building and running each container individually as we learned in the previous article.

To prove the claim above, let's take a real example. Say we want to build a nodeJs application which connects to a database and we want to have the ability to scale it.

FROM node:11

LABEL maintainer="Darius Cupsa <[email protected]>

RUN mkidr -p /usr/src/app
WORKDIR /usr/src/app

EXPOSE 80

COPY package.json /usr/src/app/package.json
COPY package-lock.json /usr/src/app/package-lock.json

RUN npm install

COPY . /usr/src/app

CMD ["npm", "run", "start"] 

Considering the Dockerfile above, to accomplish the requirements we set we'd have to go through a couple of steps, as follows:

  • Create a bridge network for the containers should connect and communicate through.
  • Start a reverse proxy that would route and balance the traffic inside the network.
  • Create a storage volume for the database.
  • Start a database container.
  • Build the actual application.
  • Start the application.
docker network create -d bridge demo-network
docker run -d --name proxy \
           -p 80:80 -p 8080:8080 \
           -v /var/run/docker.sock:/var/run/docker.sock \
           -network demo-network \
           traefik:latest \
           --web --docker --docker.domain=demo-app.dkr
docker volume create demo-app-db
docker run -d --name mysql \
           -p 3306:3306 \
           -e MYSQL_ROOT_PASSWORD=secret \
           -v demo-app-db:/var/lib/mysql \
           --network demo-network \
           mysql:5
docker build -t demo-app:latest .
docker run -d --name demo-app_1 \
           --network demo-network \
           -e MYSQL_HOST=mysql \
           -e MYSQL_USER=root  \
           -e MYSQL_PASSWORD=secret \
           -e PORT=80 \
           -l "traefik.port=80" \
           -l "traefik.backend=app" \
           -l "traefik.frontend.rule=Host:demo-app.dkr;PathPrefixStrip:/" \
           -l "traefik.network=demo-network" \
           demo-app:latest

Now if for whatever reason we need to scale this up, we would just need to run the last command but with a different name, traefik should know once the container is ready to route traffic towards it.

If at a quick glance it looks way more complicated than it should be, you would be correct in assuming all this can be accomplished with just a configuration file and one simple command as we shall see below.

Considering that all commands will be run from inside the nodeJs app folder, the docker-compose.yml file should look something like this:

version: '3.7'

services:
  proxy:
    image: traefik:latest
    ports:
      - 80:80
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - demo-network
    command: --web --docker --docker.domain=demo-app.dkr
  
  mysql:
    image: mysql:5
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - demo-app-db:/var/lib/mysql
    networks:
      - demo-network
  
  app:
    build:
      context: .
    image: demo-app:latest
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      PORT: 80
    labels:
      - "traefik.port=80"
      - "traefik.backend=app"
      - "traefik.frontend.rule=Host:demo-app.dkr;PathPrefixStrip:/"
      - "traefik.network=demo-network"
    networks:
      - demo-network
  
volumes:
  demo-app-db:
    driver: local

networks:
  demo-network:
    driver: bridge

If we take a deeper look we see that every argument given to the commands we used above is in a way or another reflected in the configuration file.

As for the almighty one-line command we use to run, this is just this:

docker-compose up -d

This will read the configuration file, create the demo-network network, create the demo-app-db persistent volume and it will start booting up containers and if we ever need to scale this, docker-compose has a handy way of doing that by just telling it how many containers of one type do you want to run.

docker-compose scale app=3

Thank you for sticking up with me up until this point and I hope something out of this article was helpful to you.

Author image

by Darius Cupsa

Fullstack developer @Around25 , studied System Engineering building robots as a hobby​​.
  • Cluj-Napoca, Romania

Have an app idea? It’s in good hands with us.

Contact us
Contact us