Please refer to my previous Docker tutorial if you have no idea what Docker is.
In the previous tutorial we explored the basics of Docker by running a simple PHP website in a container that has a built-in web server. But what if we want to set up a database?
Well we can simply install a database engine (eg MySQL) to our image and recreate the container, right?
Technically, you can do that, but it defeats the purpose of Docker. Docker is different to virtual machines and it is designed to be highly scalable and isolable.
The Docker way to do things is to create a new image + containers for the database! Furthermore, we can even extract the web server from our PHP container onto its own, and end up having 3 containers with different services on them, eg PHP, Apache and MySQL. This concept of splitting components in a system into smaller chunks is known as microservices.
Now at this point, you may ask:
- So containers are like tiny virtual machines, if we have multiple containers, how do they talk to each other?
- If we are going to create all the containers using the CLI, isn’t that tedious? There are a lot of typings to do, is there a better way?
To answer these questions, we present: Docker Compose. Docker compose helps to organise our images and containers in one YAML config file.
Instead of typing this:
docker run -v /super/long/path/to/volume/folder:/var/www/html php-appdocker run -p 3306:3306 mysqldocker run -p 80:80 nginx
We can just type:
docker-compose up -d php-app nginx mysql
mysql are services defined in your
docker-compose.yml file, which is located in your project folder. The
-d flag is just telling docker to run those containers as daemon, ie running in the background.
Let’s take a look at some of the common options in docker-compose.yaml.
Note: we can place a .env file on the same directory as
docker-compose.yml and access the environmental variable using
Here is my directory structure:
The Docker Compose file
docker-compose.yml always start with a
version key. This tell docker compose to read this file in the specified version syntax. Each version has its own options and syntax, here is the reference.
All the services created by docker are isolated by default. That means they can’t talk to each other unless they are living under the same network. The
networks key here defines a list of networks that will be used by the services defined in this
docker-compose.yml . To place a service under a network, we just need to assign the network name under the
networks option in the service (refer to example below).
Once we place both services under the same network, they can refer to each other using their service name as the IP address. Eg.
php-app can talk to
mysql:3306 instead of something like
mysql must expose the port
This is where we define our services.
Building the services
Once we are done with the docker compose file, run the following to build the docker images for the services.
# in the directory containing the docker-compose.ymldocker-compose build php-app
This will build our
php-app service. We don’t need to build
mysql since we don’t have a
Dockerfile and it is based on a pre-built docker image.
Running and stopping the services
# starting the services
docker-compose up -d php-app mysql#stopping the services
# To stop php-app only
docker-compose stop php-app
Executing one off command in containers
# this will run the 'ls' command in php-app
docker-compose exec php-app ls
List all containers
We created 2 services in
docker-compose.yml, and they are able to communicate with each other via the
backend network. Unlike the traditional way to deploy app where all the components of an app locate under 1 environment, docker allows us to extract the services out into smaller, more-manageable containers, otherwise known as microservices.
With the microservice architecture, its modularity allows us to quickly experiment with technology and scale up/down app components as needed without affecting the other part of the app. For example, if our site’s traffic goes up, we can simply add a caching service to our service stack or increase the number of containers that is serving our site.
The microservice architecture may have minimal impact on smaller apps, but medium or large enterprise apps would appreciate it more than anyone else.