DEV Community

Parvathi
Parvathi

Posted on

Beginner's Guide to Docker (Part 4) - Docker Compose

We have learned above topics by taking todo-app as an example, which involves frontend(react), backend(node) and DB(mongo). So far we did everything manually, building images, creating the containers, attaching storage to the container, creating network for container communication, etc... this seems to be a lot of work...Right??? What could be the solution? Yes, Docker Compose!!!

Reference

Todo app git repo

The file structure
Todo app file structure

Docker Compose

Docker Compose is a tool that was developed to help define and share multi-container applications. With Compose, we can create a YAML file to define the services and with a single command, can spin everything up or tear it all down.

The big advantage of using Compose is you can define your application stack in a file, keep it at the root of your project repo (it’s now version controlled), and easily enable someone else to contribute to your project. Someone would only need to clone your repo and start the compose app. In fact, you might see quite a few projects on GitHub/GitLab doing exactly this now.

If you installed Docker Desktop/Toolbox for either Windows or Mac, you already have Docker Compose! In Linux, we need to install manually.

Writing a compose file

At the root of the app project, create a file named docker-compose.yml.

In the compose file, we’ll start off by defining the schema version. It’s best to use the latest supported version. Next, we’ll define the list of services (or containers) we want to run as part of our application. Compose file reference

Service

Let's see how we can move one service to docker compose file.

docker run
    --name todo-node
    -p 3030:3030
    --rm
    --network todo-app
    -e MONGO_HOST=mongodb
    -v /Users/xyz/Documents/dockerExamples/todo-app/todo-backend:/app
    -v /app/modules
    todo-node
Enter fullscreen mode Exit fullscreen mode
version: '3.8'
services:
  backend:
    build: ./todo-backend 
    ports:
      - '3030:3030' #host:container
    volumes:
      - ./todo-backend:/app 
      - /app/node_modules
    env_file:
      - ./env/node.env
    depends_on:
      - mongodb
Enter fullscreen mode Exit fullscreen mode

Manual commands

Creating network for mongo and node to share

docker network create todo-app
Enter fullscreen mode Exit fullscreen mode

Creating mongo container

docker run
    --name mongodb
    --rm
    -d
    --network todo-app
    -v data:/data/db
    -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=password
    mongo
Enter fullscreen mode Exit fullscreen mode

Frontend

docker build -t todo-react .
Enter fullscreen mode Exit fullscreen mode
docker run
    --name todo-react
    --rm
    -p 3000:3000
    -v /Users/xyz/Documents/dockerExamples/todo-app/todo-frontend/src:/app/src
    -v /app/node_modules
    todo-react
Enter fullscreen mode Exit fullscreen mode

Backend

docker build -t todo-react .
Enter fullscreen mode Exit fullscreen mode
docker run
    --name todo-node
    -p 3030:3030
    --rm
    --network todo-app
    -e MONGO_HOST=mongodb
    -v /Users/xyz/Documents/dockerExamples/todo-app/todo-backend:/app
    -v /app/modules
    todo-node
Enter fullscreen mode Exit fullscreen mode

Docker compose file

This is how the docker compose file will look like for the above commands

version: '3.8'
services:
  mongodb: #name of the service
    image: 'mongo' #Use existing image
    volumes:
      - data:/data/db
    restart: always
    # environment:
    #   MONGO_INITDB_ROOT_USERNAME: root
    #   MONGO_INITDB_ROOT_PASSWORD: password
    env_file:
      - ./env/mongo.env
    ports:
      - '27017:27017'
    container_name: mongodb # optional
    # networks:
      # - todo-network
  backend:
    build: ./todo-backend #if dockerfile path is straightforward we can use this
    # build:
    #   context: ./todo-backend
    #   dockererfile: Dockerfile - docker file name inside the path
    #   args:
    #     some-args: 1
    ports:
      - '3030:3030' #host:container
    volumes:
      - ./todo-backend:/app #bind mount - live source code change for development purpose
      - /app/node_modules
    env_file:
      - ./env/node.env
    depends_on:
      - mongodb
  frontend:
    build: ./todo-frontend
    ports:
      - '3000:3000'
    volumes:
      - /app/node_modules
      - ./todo-frontend:/app
    stdin_open: true #to run in interactive mode
    tty: true #to run in interactive mode
    depends_on:
      - mongodb
      - backend
volumes: #specify all named volumes that are used
  data:
Enter fullscreen mode Exit fullscreen mode

Note - We don't have to explicitly create a network while working with docker compose. By default, it automatically creates a network specifically for the application stack.

Run the application stack

Now that we have our docker-compose.yml file, we can start it up!

Start up the application stack using the docker-compose up command. We’ll add the -d flag to run everything in the background.

docker-compose up -d

We shall look at logs using

docker-compose logs -f

You’ll see the logs from each of the services interleaved into a single stream. This is incredibly useful when you want to watch for timing-related issues. The -f flag “follows” the log, so will give you live output as it’s generated.

If you want to view the logs for a specific service, you can add the service name to the end of the logs command

docker-compose logs -f frontend

Hola, Our application is up and running!!!

In order to bring down our application stack

docker-compose down

By default, named volumes in your compose file are NOT removed when running docker-compose down. If you want to remove the volumes, you will need to add the --volumes flag.

Hope, this gave you a basic understanding about docker compose.

Thanks for reading!

Top comments (0)