Get to know what is docker compose and explore all about Docker Compose Volumes to learn how to manage multiple containers on a single host with examples:
Docker is an open-source platform used to build, test, and deploy applications. Docker helps to package the software into units called containers which contain everything that software needs like the code, OS libraries, runtimes, etc. Docker can deploy applications to any environment.
Docker Image is a read-only template that contains instructions to create a Docker container. You can use Docker images for yourself or publicly share them with other users using Docker. Dockerfile is a basic building block to create images.
Table of Contents:
- Getting Started With Docker Compose
- Docker Compose Use Case and Benefits
- Installation of Docker-Compose on Ubuntu
- First Docker-Compose Files and Commands
- Docker-Compose Build
- Docker Compose Volumes
- Networking in Docker Compose
- Docker Compose Environment Variables
- Docker Compose and Angular Example with NGINX Image
- Docker Compose and MySQL Using phpMyAdmin Example
- JSON File With Docker Compose
- Frequently Asked Questions
- Conclusion
Getting Started With Docker Compose
Docker Container is a run-time image instance. Docker images are only templates and cannot be started or run. A container is a running image. Think of containers as an object and images as a class.
DockerHub is a cloud-based repository that helps to create, store, and deploy images. It also provides access to thousands of open-source public images. It is also called an open-source container registry.
Docker Compose: While Docker is used to manage individual containers, Docker Compose is used to manage and run multi-container applications. Usually, an application may have many containers comprising a single service (Container Configuration), it can be tedious and manage these containers individually.
With Docker Compose, you can speed up and centrally manage the deployment of many containers. If an application needs to be microservice-based, then Docker Compose is the tool wherein each service is managed in separate containers.
So rather than managing multiple services in one large container, Docker Compose helps to split the services into individual containers that can be managed. The configuration for managing all the containers is defined in one single YAML file docker-compose.yml and starts all the containers using one single command.
In this article, we will look at the usage of Docker Compose and examples of managing multiple containers on a single host.
Docker Compose Use Case and Benefits
Docker Compose, as we have seen in the introduction section, is a tool that helps in running multiple services simultaneously. All the services in compose are defined in a YAML file, wherein we can spin them up and bring them down with one command.
To illustrate, let’s consider any e-commerce site today. As a user, you normally do multiple activities like login/logout of your account, browsing the product catalog, adding to the cart, checkout, etc. In the backend, there would be different databases for these different services.
So, each of these here can be considered as a microservice. Each of these services runs in a different container and you should be able to connect them. Docker Compose helps to connect these different containers into a single service.
Benefits:
- Deployment on a single host: Run the application on a single piece of hardware
- Simple configuration: Using YAML files
- Secured internal communication: Through the creation of networks that are shared by all services
- Reduce time: To perform tasks
- Highly secured: As all containers are isolated, reducing the threat
- Efficient CI/CD support
Installation of Docker-Compose on Ubuntu
Pre-Requisites: Docker compose needs a docker engine to be installed so the docker engine will have to be installed on your OS. In this case, it would be Ubuntu.
Further Reading => Podman – Best Alternative to Docker
Follow the steps as shown below to install Docker Compose v2.18.1 which is as of now the latest version.
#1) Download Docker Compose
sudo curl -L https://github.com/docker/compose/releases/download/v2.18.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
#2) Apply Permission to the Binary
sudo chmod +x /usr/local/bin/docker-compose
Note: Every compose command starts with docker-compose
#3) Test the Installation
docker-compose –version
You can download and install the latest version from Releases · docker/compose (github.com).
#4) Usage of Docker Compose
Once you’ve downloaded and installed Docker Compose using the steps mentioned above, you can start using it as follows:
- Create a Dockerfile to define the application environment so it can be reused.
- Define the services for your application in the docker-compose.yml file.
- Use the command docker-compose up to start all services.
- Use the command docker-compose down to stop all services.
Use the ‘docker-compose <Command> –help ‘ to get more information on the arguments.
Also Read =>> Use the MySQL Docker container with example
First Docker-Compose Files and Commands
Let’s start by creating our first Docker Compose YAML file. The YAML files are normally stored in a GitHub repository.
version: '3.8'
services:
web:
image: httpd
ports:
- 8080:80
database:
image: redis
Let’s understand this step by step. Also, note in the above YAML file indentation is very important.
- version: This specifies the version of Docker Compose. At this point in writing the document, the current version is 3.8. All the versions of the Docker Compose that support the specific docker releases can be found @ Compose file version 3 reference | Docker Documentation
- services: This section specifies the multiple containers that will be created. In the above example there are 2 services, web and database
- ports: To map the container port to the host machine. Ensure ports are opened in the security group of your EC2 VM
- image: If there is no Dockerfile, we can specify to run the service from a pre-built image
#1) Check the validity of the docker-compose.yml file
Run the command
docker-compose config
#2) Start the services (up Command)
We will need to use the below command to start or restart all the services as defined in the docker-compose.yml file. The command will help to create the containers specified in the docker-compose.yml file. To run the command, you should be in the directory where the docker-compose.yml file is present.
docker-compose up -d
The -d option will run the containers in the background. ‘docker ps’ command will show the newly created containers.
Since httpd image is running in a container, we can check using the EC2 VM public IP.
docker-compose ps
This command will list all containers as defined in the docker-compose.yml file. They will either be running or stopped. In the above example, if your application requires httpd server and redis database, then both the containers can be run as a single service instead of starting them separately.
#3) Shutdown the Services
The below command will stop and clean up the containers and networks.
docker-compose down
#4) Stop the Services
The below command will only stop the running containers from the specified services but will not remove the containers
docker-compose stop
#5) Start the Services
The below command will restart the containers of the specified services which were stopped previously
docker-compose start
#6) Build the Images
The below command will build the images or even rebuild the images as mentioned in the docker-compose.yml file. Once the image is built, you can then create containers for each service mentioned in the compose file using the docker-compose up command
#7) Create and run the containers
The below command will create the containers from the images built for the services as mentioned in the docker compose file. It will run on-off tasks and requires a service name that needs to be started.
docker-compose run database
docker-compose run web
The above command is similar to the ‘docker run’ command.
Docker-Compose Build
In this section, we will look at an example of Docker Compose Build. The Docker Compose provides a sub-command to build the images, which is ‘docker-compose build’. The command will go through all the services and build the images which are in the ‘build’ section.
Example #1: Create a Dockerfile in a webapp directory and add the content as shown.
vi webapp/Dockerfile
FROM httpd
COPY index.html /var/www/html
docker-compose.yml file in the current directory
version: "3.8"
services:
web:
build: ./webapp
ports:
- "9000:80"
redis:
image: redis
build section above refers to the location of Dockerfile.
Build the webapp image: Build the image using the ‘docker-compose build’ command. For the web app, it will use the webapp/Dockerfile
docker-compose build
Start the containers: Start the containers using the ‘docker-compose up -d’ command
docker-compose up -d
Update content in the HTML file
echo “Bangalore, Karnataka” >> webapp/index.html
Rebuild the web container and restart the containers.
docker-compose build
docker-compose up -d
If you see the output, the web container alone has been recreated and started as there was a new build for the image. The database container is unchanged.
Example #2: In this section, we will look at creating two docker containers. One will be the web container and the other will be the database container. The below-mentioned service uses an image built from Dockerfile which will deploy a Java Maven WAR file to Tomcat.
Dockerfile
FROM tomcat:latest
MAINTAINER Niranjan V
ADD SampleWebApp.war /usr/local/tomcat/webapps/
EXPOSE 8080
CMD [“catalina.sh”, “run”]
docker-compose.yml file
version: '3.8'
services:
web:
build: .
ports:
- 9000:8080
database:
image: redis
build: specifies the path to Dockerfile which represents the current directory in which the docker-compose.yml file is present.
List Images: The below command will list images built using the docker-compose.yml file
docker-compose images
Let’s start the containers now
docker-compose up -d
Running ‘docker-compose ps ‘ will now show both the containers running as per the services defined in the ‘docker-compose.yml’ file
The application can be tested by using the URL http://<EC2VM-IP>:9000/SampleWebApp
Docker Compose Volumes
As we know that in Docker data is stored inside the container. Once the container is removed, data will also be lost. So, volumes in docker help to preserve the data even if containers are removed. The volumes can also be shared across different containers.
If you want to save your data locally on your host machine or want to share data from your Docker host to the Docker container e.g., conf files or any other source code, then this type of volume is called a Bind mount.
If you want Docker to manage the data and the mount points, then this type of volume is called Volume mount.
Docker compose allows you to configure both types of mount.
E.g., Bind mount volume
volumes:
./website /usr/local/apache2/htdocs
In the above the absolute path (./website sub-folder in the current folder) is mounted in to the container.
E.g. Volume mount
services
db:
image: mysql
volumes:
- My_SQL_Data:/var/lib/mysql
volumes:
My_SQL_Data
If a source is present before the (COLON): and it’s not an absolute path, then Docker Compose assumes you’re referring to a named volume. This volume needs to be declared in the top-level volume’s key declaration. Docker manages this volume.
The top-level volumes key always declares volumes, they never are bind mount. Bind mounts don’t have a name and they cannot be named. In docker you normally use the ‘docker volume create’ command.
Example #1: Bind Mount – Docker Compose Example
In the example let’s mount the websites folder to /usr/local/apache2/htdocs folder in the container.
docker-compose.yml
version: "3.8"
services:
web:
image: httpd
ports:
- 9000:80
volumes:
- ./websites:/usr/local/apache2/htdocs
Start the services and check inside the container
docker-compose up -d
docker exec -it <Container-Name> bash
Example #2: Bind mount with MySQL data
In this example, we will use the mysql image with bind mount where the DB data will persist even if the container is removed.
docker-compose.yml file
version: '3.8'
services:
DB:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=alpha123
- MYSQL_USER=niranjan
- MYSQL_PASSWORD=niranjan
volumes:
- ./db_vol:/var/lib/mysql
ports:
- 3306:3306
Start the services, log in to MySQL, and create a database
docker-compose up -d
docker exec -it <Container-Name> bash
Login to MySQL using the below command:
mysql -u root -p
Create a database inside the MySQL container
Shutdown the services
Exit the container and shut the services
docker-compose down
The container is removed but the data is persisted. Let’s now start the services again and check in the NEW container created.
docker-compose up -d
Get inside the container and login to mysql
The database created earlier ‘niranjandb’ is listed in this new container as well.
Networking in Docker Compose
Networks are configured for complete isolation of containers. So, applications that work together can be secure. Once Docker is installed, it configures 3 types of networks: None, Host, and Bridge. The none and host network cannot be removed. They are part of the network stack and have no external interfaces.
Suggested Read =>> Jenkins Integration with Docker, Docker-Compose, and Docker Swarm
Bridge network can be configured and this type of network creates its IP Subnet and gateway. All of the containers belonging to this network are part of the subnet and communication between the containers part of this network happens through IP address. This is also a private network.
Create Bridge Network in Docker
Let’s create a new docker custom bridge network and check connectivity between containers of the same bridge.
docker network create –driver bridge ctr-bridge1
Run 2 containers as part of the same bridge network ctr-bridge1.
docker run -it –network ctr-bridge1 –name ctr-c1 busybox
docker run -it –network ctr-bridge1 –name ctr-c2 busybox
Attach terminal’s std input, output to running container 2
docker attach ctr-c2
ping ctr-c1
Since both containers are part of the same subnet in the bridge network created, I can ping container 1 from within container 2.
Let’s now create the same in Docker Compose.
docker-compose.yml file
You can specify our custom networks with the network key as shown below in the docker-compose.yml file. Each service can specify which network to connect to.
version: "3.8"
services:
app:
image: httpd
networks:
- ctr-bridge1
db:
image: redis
networks:
- ctr-bridge1
networks:
ctr-bridge1:
name: ctr-bridge1
Both services are part of the same bridge network.
Let’s start the services.
docker-compose up -d
Look at the subnet details of the bridge and containers.
docker inspect dcnetexample-app-1
Look at the IPV4 address which is part of the same subnet
Docker Compose Environment Variables
In this section, we will look at how to use environment variables to define configuration values which will help to be organized.
Environments should not be used to pass sensitive information such as passwords into the containers.
We will look at how environment variables can be set through the compose file and CLI.
Create a .env file
The .env file would be useful if there are multiple environment variables to be stored. Create the .env and the docker-compose.yml file in the same directory.
Validate the docker-compose file
docker-compose config
If the .env file is not in the same directory as with docker-compose.yml file then you can specify the –file or –env-file option in the CLI.
docker-compose –env-file ./envfile/.env up
docker-compose –env-file ./envfile/.env config
Use environment attribute
Use env_file attribute
web:
env_file:
– variables.env
Docker Compose and Angular Example with NGINX Image
In this section, we will look at creating and running an Angular example on NGNIX web server using Docker Compose.
Step #1: As a pre-requisite, we will need to install nodejs, npm, and angular cli. Use the commands as shown below to install the same.
sudo apt install nodejs
sudo apt install npm
sudo npm install -g @angular/cli@latest
Step #2: Create a new angular application
ng new angular-demo-app
Dockerfile
FROM node:18.16.0 as build
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/package.json
COPY . /usr/src/app
RUN npm install -g @angular/cli
RUN npm install --force
RUN ng build
FROM nginx
COPY --from=build /usr/src/app/dist/angular-demo-app/. /usr/share/nginx/html
docker-compose.yml file
version: "3.8"
services:
client:
build: .
ports:
- 9000:80
Start the services:
docker-compose up -d
Test the application by entering the URL http://<EC2VM-IP>:9000
Also Read =>> Docker Selenium Tutorial
Docker Compose and MySQL Using phpMyAdmin Example
In this section we will look to launch mysql container along with phpMyAdmin container which is a web interface for managing databases. I am extending the docker-compose.yml file created in the previous section to add the phpMyAdmin service.
docker-compose.yml file
version: '3.8'
services:
DB:
image: mysql
environment:
- MYSQL_ROOT_PASSWORD=alpha123
- MYSQL_DATABASE=niranjandb
- MYSQL_USER=niranjan
- MYSQL_PASSWORD=niranjan
volumes:
- ./db_vol:/var/lib/mysql
ports:
- 3306:3306
phpmyadmin:
depends_on:
- DB
image: phpmyadmin/phpmyadmin
ports:
- 9000:80
environment:
PMA_HOST: DB
MYSQL_ROOT_PASSWORD: alpha123
Start the Services:
docker-compose up -d
Launch phpMyAdmin in web browser:
Launch using the URL http://<EC2VM-IP>:9000 and login as root
Once you log in you can then create databases, tables, columns, etc.
JSON File With Docker Compose
JSON file can be used with Docker Compose and is valid. However, the YAML file is a superset of JSON format and is preferred as it is more readable. Use the JSON file with the following command in Docker Compose.
Examples of JSON format with Docker Compose can be found @ Lab #24: Use JSON instead of YAML compose file in Docker? | dockerlabs (collabnix.com)
docker-compose -f docker-compose.json up -d
Frequently Asked Questions
1. What is Docker Compose?
Docker Compose is a tool for defining and running multi-container applications of Docker. It uses a YAML file to configure the services of the applications.
2. What are 3 main steps of Docker Compose?
The 3 main steps are as below
Define the apps environment in Dockerfile
Define the services of your application in docker-compose.yml file
Run docker-compose up to start and run the entire application
3. What is the difference between Up, Run, and Start?
These 3 commands in docker compose have different purposes:
docker-compose up: This command builds, start/restart all services and attaches a container for each service defined in the YAML file.
docker-compose run: This command will create the containers from the images built for the services as mentioned in the docker compose file. It will run on-off tasks and requires a service name which needs to be started.
docker-compose start: This command is used to restart the containers that were created previously but was stopped.
4. What is a docker compose file used for?
A docker compose file defines the services, networks, and volumes needed for your application
5. Can I use a JSON file instead of YAML?
Yes JSON files can be used
6. Can I control the startup order?
By using the “depends_on” command in docker compose file, it is possible to change the startup order of the containers and give priority to the containers that are needed to start.
Conclusion
In this article, we have seen how Docker Compose can be used to run your entire application or multiple containers on a single host which is possible using a single docker-compose.yml file. It helps to orchestrate multiple containers that work together.
Docker Compose reduces time to perform tasks and security wise all the containers are isolated from each other.
To move on to the next level of Docker knowledge to run multiple containers on multiple hosts and manage them in a cluster you will use Docker Swarm.