Discover the hidden gems of multi-container applications ( part 5 )
Tags: devops, devrel, docker, kubernetes
Published: 2023-05-08 16:35:45

## What is multi-container Docker applications ?

![https://media0.giphy.com/media/Ky53ckyw4Y6JnZeMXP/giphy.gif?cid=7941fdc6micms70mdi8ae5o6fc5ardny87wbbsajjl7v7czp&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media0.giphy.com/media/Ky53ckyw4Y6JnZeMXP/giphy.gif?cid=7941fdc6micms70mdi8ae5o6fc5ardny87wbbsajjl7v7czp&ep=v1_gifs_search&rid=giphy.gif&ct=g)

Multi-container Docker applications are composed of multiple containers that are designed to work together to provide the complete functionality of an application. The containers within a multi-container application are typically interconnected and work in concert to accomplish a specific task or set of tasks.

For example, a web application might consist of a front-end container that serves HTML pages and a back-end container that handles database queries. Both of these containers would be part of the same multi-container application and would work together to provide a fully functional web application.

### Where do we use multi-container applications?

Multi-container Docker applications are used in several scenarios, including:

1. **Microservices architectur**e: A microservices architecture breaks down complex applications into smaller, independent services that can be developed, deployed, and scaled separately. Each microservice can be run in its own container, and multiple containers can communicate with each other using Docker networking or other mechanisms.
2. **Development and testing environments**: Docker Compose allows developers to define the entire stack for their applications, including databases, message queues, and other services. This allows them to quickly spin up the environment on their local machine and test their code changes before deploying to production.
3. **High availability and scaling**: By running multiple containers for a single application, Docker Swarm or Kubernetes can manage the containers and provide load balancing, high availability, and scaling features. This helps to ensure that the application is always available and can handle varying levels of traffic.
4. **Separation of concerns**: Separating the application into multiple containers allows each container to have a specific role or responsibility. For example, one container may handle the application logic, while another container may handle the database. This separation of concerns can help with maintainability and scalability.

### Why should we use multi-container applications?

There are several benefits to using multi-container Docker applications:

1. **Scalability:** With multi-container Docker applications, you can easily scale individual parts of your application independently. For example, if one part of your application is experiencing high traffic, you can scale that part of the application without having to scale the entire application.
2. **Isolation**: By using multiple containers, you can isolate different parts of your application from each other. This can help to prevent issues in one part of the application from affecting other parts.
3. **Resource utilization:** By using multiple containers, you can optimize your resource utilization. For example, if one part of your application requires more CPU or memory than another part, you can allocate resources accordingly.
4. **Simplified deployment**: With multi-container Docker applications, you can deploy all parts of your application together in a single deployment unit, making it easier to manage and deploy your application.
5. **Improved maintainability**: With multiple containers, you can update and maintain different parts of your application separately, making it easier to keep your application up-to-date and secure.

Overall, multi-container Docker applications provide a flexible, scalable, and maintainable way to deploy complex applications.

## Docker Compose

![https://media4.giphy.com/media/QTxuZHy9wFzyM4Fjno/giphy.gif?cid=7941fdc6gv4tqbrzp18q5ruk51oyifm6yi9o47qcsv6nrgkh&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media4.giphy.com/media/QTxuZHy9wFzyM4Fjno/giphy.gif?cid=7941fdc6gv4tqbrzp18q5ruk51oyifm6yi9o47qcsv6nrgkh&ep=v1_gifs_search&rid=giphy.gif&ct=g)

Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you can define a set of related services in a YAML file, then start and stop them all together with a single command.

Here are some of the most commonly used Docker Compose commands:

- **`up`**: Create and start containers.
- **`down`**: Stop and remove containers, networks, images, and volumes.
- **`start`**: Start existing containers.
- **`stop`**: Stop running containers.
- **`restart`**: Restart running containers.
- **`pause`**: Pause running containers.
- **`unpause`**: Unpause paused containers.
- **`ps`**: List containers.
- **`logs`**: View output from containers.
- **`build`**: Build or rebuild services.
- **`config`**: Validate and view the Compose file.
- **`exec`**: Execute a command in a running container.
- **`pull`**: Pull service images.
- **`push`**: Push service images.
- **`rm`**: Remove stopped containers.
- **`run`**: Run a one-time command against a service.

In this series on Docker, we will discuss each command in great detail so that you can become proficient in using Docker. However, in this particular article, we will only be covering 5 commands.

<aside>
💡 I spent hours writing this article in a way that is easy to understand for those new to Docker. If you found it helpful, please like and save it for future reference.

</aside>

## `docker-compose up`

![https://media4.giphy.com/media/RbDKaczqWovIugyJmW/giphy.gif?cid=7941fdc6mx5t5ts83mwa370myy8ck6cn8jzd1qicips2o69p&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media4.giphy.com/media/RbDKaczqWovIugyJmW/giphy.gif?cid=7941fdc6mx5t5ts83mwa370myy8ck6cn8jzd1qicips2o69p&ep=v1_gifs_search&rid=giphy.gif&ct=g)

The **`docker-compose up`** command is used to build, create, start, and attach to containers for a set of services defined in a **`docker-compose.yml`** file.

Here is an example **`docker-compose.yml`** file : 

```yml
version: '3.9'  # specify the Compose file version

services:  # define the services/containers

  db:  # define the MySQL service
    image: mysql:latest  # specify the MySQL image to use
    restart: always  # restart the container automatically if it crashes
    environment:  # set environment variables
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: db_name
      MYSQL_USER: db_user
      MYSQL_PASSWORD: db_password
    ports:  # map container port to host port
      - "3306:3306"
    volumes:  # mount a host directory or named volume to the container
      - db_data:/var/lib/mysql

  app:  # define the Node.js/Express service
    build: ./app  # build the Docker image using the Dockerfile in the app directory
    restart: always  # restart the container automatically if it crashes
    environment:  # set environment variables
      NODE_ENV: development
      DB_HOST: db
      DB_USER: db_user
      DB_PASSWORD: db_password
      DB_NAME: db_name
    ports:  # map container port to host port
      - "8080:8080"
    volumes:  # mount a host directory or named volume to the container
      - ./app:/app

  client:  # define the React service
    build: ./client  # build the Docker image using the Dockerfile in the client directory
    restart: always  # restart the container automatically if it crashes
    ports:  # map container port to host port
      - "3000:3000"
    volumes:  # mount a host directory or named volume to the container
      - ./client:/client

volumes:  # define named volumes to be used in the services
  db_data:
```

To start these services, we can use the **`docker-compose up`** command from the same directory as the **`docker-compose.yml`** file:

```shell
$ docker-compose up
```

This will build and start the `db`, `client`, and `app`services. The containers will be started in the foreground, and we'll see the logs from all containers in the console.

<aside>
💡 When you run `docker-compose up`, a default network is created. For example, if your project is named `myapp`, a network called `myapp_default` is created. Each container for a service joins the network under the service’s name. For example, a container created using a service named web would join the network under the name web

</aside>

When running **`docker-compose up`**, there are a few options that can be used to modify the behavior of the command. Here are some of the most common options:

### **`--detach`**

The **`-d`** or **`--detach`** option is used with the **`docker-compose up`** command to run the containers in the background and print the container IDs.

By default, **`docker-compose up`** runs the containers in the foreground and logs their output to the terminal. However, with the **`-d`** option, the command returns immediately, and the containers continue to run in the background.

For example, to start the containers defined in the **`docker-compose.yml`** file in the background, you can use the following command:

```shell
docker-compose up -d
```

This will start the containers in the background and print their IDs.

### **`--build`**

The **`docker-compose up --build`** command is used to build the images for the services defined in the **`docker-compose.yml`** file before starting the containers. This command is useful when changes are made to the application code or Dockerfile of a service, and you want to rebuild the image to include those changes.

When you run **`docker-compose up --build`**, it will first build the images for any services that have a **`Dockerfile`** in their build context or use a custom build command specified in the **`build`** section of the **`docker-compose.yml`** file. Then it will start the containers for all services defined in the **`docker-compose.yml`** file.

Here is an example command to run **`docker-compose up --build`**:

```shell
docker-compose up --build
```

This will build the images for all services defined in the **`docker-compose.yml`** file before starting the containers.

### **`--force-recreate`**

The **`--force-recreate`** option for the **`docker-compose up`** command recreates all containers defined in the **`docker-compose.yml`** file, even if they are already running, and forces them to be built again. This option can be used to rebuild containers with updated configurations or to recover from a corrupted state.

By default, Docker Compose will try to reuse existing containers if possible, which can be useful for faster development and testing. However, in some cases, it may be necessary to rebuild and recreate all containers from scratch, such as when changing the Dockerfile or updating dependencies.

Here is an example of using **`--force-recreate`** with **`docker-compose up`**:

```shell
docker-compose up --force-recreate
```

This command will stop and remove all containers defined in the **`docker-compose.yml`** file, and then recreate them with the latest configurations and images. It can be useful when deploying changes to a production environment or when troubleshooting issues with containers.

<aside>
💡If a service is already running, the **`docker-compose up`** command will not start another instance of that service. Instead, it will leave the running instance as it is and continue to start up any other services that are defined in the **`docker-compose.yml`** file.

If you want to force a restart of the running service, you can use the **`docker-compose up --force-recreate`** command. This will stop the running service and start a new instance of it with the latest configuration defined in the **`docker-compose.yml`** file.

</aside>

If you want to recreate a specific service, you can use the **`docker-compose up --force-recreate <service>`** command. This will recreate only the specified service and not the others.

For example, if you have a **`web`** and a **`db`** service defined in your **`docker-compose.yml`** file, and you want to recreate only the **`db`** service, you can run the following command:

```shell
docker-compose up --force-recreate db
```

This will recreate the **`db`** service and leave the **`web`** service running as is.

### **`-no-recreate`**

If containers already exist, don't recreate them. This option is useful when you don't want to recreate the containers each time you run the command.

### **`-no-build`**

Don't build an image, even if it's outdated or missing. This option is useful when you don't want to build the image each time you run the command.

### **`--remove-orphans`**

The **`--remove-orphans`** option is used with the **`docker-compose up`** command to remove any containers that were created by the current project but are no longer defined in the Compose file. Orphaned containers can occur when a container is created but its service definition is later removed from the Compose file.

Here is an example of using the **`--remove-orphans`** option with **`docker-compose up`** command:

```shell
docker-compose up --remove-orphans
```

This will start all the services defined in the Compose file and remove any orphaned containers.

<aside>
💡 If a container is running that is not defined in the Compose file, it will not be affected by the **`--remove-orphans`** option. This option is only used to remove containers that were created as part of the Compose service definition, but are no longer defined in the Compose file.

</aside>

### **`-timeout`**

Specify a shutdown timeout in seconds. This option is used to specify how long Compose should wait for containers to shut down before killing them.

### **`--scale`**

The **`--scale`** option in **`docker-compose`** is used to scale a service up or down by increasing or decreasing the number of containers running for that service. It allows you to run multiple instances of the same service, with the same configuration, simultaneously.The syntax of using the **`--scale`** option is as follows:

```shell
docker-compose up --scale service_name=num_instances
```

Here, **`service_name`** is the name of the service that you want to scale and **`num_instances`** is the number of instances you want to run for that service.

For example, if you have a **`web`** service in your **`docker-compose.yml`** file and you want to run three instances of that service, you can use the following command:

```shell
docker-compose up --scale web=3
```

This will start three instances of the **`web`** service.

<aside>
💡 Note that the **`--scale`** option only works for services that are defined in the **`docker-compose.yml`** file. If a service is not defined in the file, the **`--scale`** option will have no effect.

</aside>

## **`docker-compose down`**

![https://media4.giphy.com/media/THuO7JQbmVdpEZnrMw/giphy.gif?cid=7941fdc6iwmbph6oq4u16m76g69zid0x33f2rm2e10oiqkf7&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media4.giphy.com/media/THuO7JQbmVdpEZnrMw/giphy.gif?cid=7941fdc6iwmbph6oq4u16m76g69zid0x33f2rm2e10oiqkf7&ep=v1_gifs_search&rid=giphy.gif&ct=g)

**`docker-compose down`** is a command used to stop and remove the containers, networks, and volumes that were created by **`docker-compose up`**.

By default, **`docker-compose down`** will stop and remove the containers declared in the **`docker-compose.yml`** file, and remove the network, but will not remove volumes. To remove the volumes as well, you can use the **`-v`** or **`--volumes`** option.

Here is the basic syntax for **`docker-compose down`**:

```shell
docker-compose down [options]
```

Here are some commonly used options:

- **`v, --volumes`**: Remove named volumes declared in the **`volumes`** section of the **`docker-compose.yml`** file
- **`-rmi <all|local>`**: Remove images. The **`all`** option removes all images used by any service defined in the **`docker-compose.yml`** file. The **`local`** option removes only images that don't have a custom tag or are not used by any other service.
- **`t, --timeout <timeout>`** : Specify a shutdown timeout (default is 10 seconds).
- **`-remove-orphans`** : Remove containers for services not defined in the **`docker-compose.yml`** file.

Here's an example command to stop and remove the containers and network defined in a **`docker-compose.yml`** file:

```shell
docker-compose down
```

If you also want to remove the volumes, you can add the **`-v`** option:

```shell
docker-compose down -v
```

The **`-rmi`** option is used to remove images created by **`docker-compose build`**. It accepts two values:

- **`all`**: Remove all images used by any service defined in the **`docker-compose.yml`** file.
- **`local`**: Remove only the images that are not associated with any container. This is the default option.

Here are some examples:

To remove all containers, networks, and volumes created by **`docker-compose up`** , and also remove all images used by the services:

```shell
docker-compose down --rmi all
```

To remove all containers, networks, and volumes created by **`docker-compose up`** , and only remove the images that are not associated with any container:

```shell
docker-compose down --rmi local
```

## `docker compose stop`

![https://media0.giphy.com/media/cKKXNlTYino7hWNXwl/giphy.gif?cid=7941fdc6pupw4p2l4v4ml5avyd2ih88tjmws27nnupouf3gj&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media0.giphy.com/media/cKKXNlTYino7hWNXwl/giphy.gif?cid=7941fdc6pupw4p2l4v4ml5avyd2ih88tjmws27nnupouf3gj&ep=v1_gifs_search&rid=giphy.gif&ct=g)

The **`docker-compose stop`** command stops the containers defined in the **`docker-compose.yml`** file. It sends a SIGTERM signal to the container processes, giving them a chance to stop gracefully. If the processes do not stop within the given timeout period (default is 10 seconds), then a SIGKILL signal is sent to forcefully terminate the processes.

The syntax of the command is:

```shell
docker-compose stop [options] [SERVICE...]
```

Where **`options`** can include:

- **`t, --timeout TIMEOUT`** : Specify a custom timeout value (in seconds) for stopping the containers.

And **`SERVICE...`** is an optional list of service names to stop. If no service name is provided, all services defined in the **`docker-compose.yml`** file will be stopped.

Here's an example of how to use the **`docker-compose stop`** command:

```shell
docker-compose stop
```

This will stop all the containers defined in the **`docker-compose.yml`** file.

```shell
docker-compose stop mysql
```

This will stop only the **`mysql`** service defined in the **`docker-compose.yml`** file.

<aside>
💡 If we stop a service that is a dependency for another service, the dependent service will also stop. For example, if we have two services, **`db`**and **`web`**, and **`web`** depends on **`db`**, if we stop the **`db`**service, the **`web`**service will also stop.

</aside>

## **`docker-compose start`**

![https://media0.giphy.com/media/ZWbeEcbeo0cKI/giphy.gif?cid=7941fdc6kq9gst2juusi2wui8fa5sazdmi0gkrsmf6tvb5xp&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media0.giphy.com/media/ZWbeEcbeo0cKI/giphy.gif?cid=7941fdc6kq9gst2juusi2wui8fa5sazdmi0gkrsmf6tvb5xp&ep=v1_gifs_search&rid=giphy.gif&ct=g)

The **`docker-compose start`** command is used to start containers that are already created but are currently stopped. It does not recreate containers or rebuild images. This command is useful when you have stopped a container using **`docker-compose stop`** and want to start it again.

Here's an example of using **`docker-compose start`** command:

Suppose you have a docker-compose.yml file that defines two services **`app`** and **`db`**. If you start the services using the following command:

```shell
docker-compose up -d
```

It will create and start the containers in detached mode. Now, if you want to stop the containers, you can use the following command:

```shell
docker-compose stop
```

This will stop the running containers. If you want to start the containers again, you can use the **`docker-compose start`** command:

```shell
docker-compose start
```

This will start the stopped containers.

You can start a specific service using the **`docker-compose start`** command followed by the service name. For example, if you have a service called **`web`** defined in your **`docker-compose.yml`**
file, you can start it using the following command:

```shell
docker-compose start web
```

This will start only the **`web`** service and any services that it depends on. If the service is already running, it will not be restarted.

## **`docker-compose restart`**

![https://media0.giphy.com/media/26G3J4n04974Q/giphy.gif?cid=7941fdc61u0ywlr2dnekmlsn32fn30zikpue0kyzs8td5abe&ep=v1_gifs_search&rid=giphy.gif&ct=g](https://media0.giphy.com/media/26G3J4n04974Q/giphy.gif?cid=7941fdc61u0ywlr2dnekmlsn32fn30zikpue0kyzs8td5abe&ep=v1_gifs_search&rid=giphy.gif&ct=g)

The **`docker-compose restart`** command is used to restart one or more services defined in the **`docker-compose.yml`** file.

Here is the basic syntax of the command:

```shell
docker-compose restart [options] [SERVICE...]
```

Some important options that can be used with this command are:

- **`-timeout TIMEOUT`** : This option sets the timeout for stopping the containers. The default value is 10 seconds.

Here is an example command that restarts the **`web`** service defined in the **`docker-compose.yml`** file:

```shell
docker-compose restart web
```

This command will stop and then start the **`web`** service.

In conclusion, Docker Compose is a powerful tool for managing multi-container applications. It allows you to define and run complex applications with ease, and provides a range of options for managing containers and services. In this article, we covered some of the most common Docker Compose commands and options, including **`docker-compose up`**, **`docker-compose down`**, **`docker-compose start`**, and **`docker-compose restart`**. We also looked at options for managing container builds, scaling services, and removing orphaned containers.

If you found this article helpful, please give it a thumbs up and save it for future reference. If you have any questions or comments, feel free to leave them below.Thank you for reading! I'll see you in the next article.