Beginner's Guide to Docker (Part 4) - Docker Compose
Tags: devops, beginners, docker, webdev
Published: 2022-01-22 17:27:43

{% link https://dev.to/paru429/beginner-s-guide-to-docker-and-docker-cli-commands-1p75 %}

{% link https://dev.to/paru429/beginner-s-guide-to-docker-part-2-manage-data-in-docker-1co9 %}

{% link https://dev.to/paru429/beginner-s-guide-to-docker-part-3-communication-network-1p1n %}

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](https://github.com/paru429/todo-app)

The file structure
![Todo app file structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1e9o82umarrwsm8vfb4e.png)

## 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](https://docs.docker.com/compose/compose-file/)

**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
```

```
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
```

## Manual commands

**Creating network for mongo and node to share**
```
docker network create todo-app
```

**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
```

**Frontend**

```
docker build -t todo-react .
```
```
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
```

**Backend**

```
docker build -t todo-react .
```
```
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
```

## 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:
```

**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!