An Intro to Docker Compose
What is Docker Compose?
Docker Compose is a tool that allows you to configure, build and run multiple containers. It's incredibly useful for building and deploying apps that use multiple services, each with their own container.
For example, let's say you have an app that uses 3 services:
- site
: An ecommerce site that lets users browse and shop for items.
- database
: A database that stores user, product, and order information.
- notifier
: A script that monitors your database and sends email notifications to users when their orders have shipped.
Your app directory structure would look like this:
app
├── database
│ └── Dockerfile
│ └── ...
├── notifier
│ └── Dockerfile
│ └── ...
└── site
└── Dockerfile
└── ...
Normally, you'd have to start 3 containers yourself, either by running docker run
for each image or by writing a custom script to launch them all. But with Docker Compose, you can launch all 3 containers with a single file AND configure them to boot.
How do I use Docker Compose?
Basic Overview
Let's expand on our example above. To use Docker Compose
with our app, we add a docker-compose.yml
file to the top level of our project directory:
app
├── docker-compose.yml
├── database
│ └── Dockerfile
│ └── ...
├── notifier
│ └── Dockerfile
│ └── ...
└── site
└── Dockerfile
└── ...
The contents of our docker docker-compose.yml
file would be:
# line below tells docker-compose which version of the docker-compose file format to use
version: "3.8"
services:
database:
build: ./database
notifier
build: ./notifier
site:
build: ./site
The lines above define each service and tell Docker compose where to find their build files. To build the images for services, we'd run the command docker-compose build
from the top level of the project directory. And to run them, we'd use the command docker-compose up
.
Configuration Options
The example docker-compose.yml
file above is very basic. But Docker compose allows you to configure your services in all sorts of ways. For example: we could add a few lines to tell docker-compose
that the site
and notifier
services depend on the database
service, and that the database
service needs to build and run successfully before the others:
version: "3.8"
services:
site:
build: ./app
depends_on:
- database
notifier
build: ./notifier
depends_on:
- database
db:
build: ./database
Note that each of our services still has their own Dockerfile. This is useful for long, service specific configurations, when a service might have a long and/or complex Dockerfile. However, you could include all of the configuration options for a service in your docker-compose.yml
file if you wanted.
For example - let's assume the database
service has 2 files: database/Dockerfile
and database/init.sql
.
app
├── docker-compose.yml
├── database
│ └── Dockerfile
│ └── init.sql
├── notifier
│ └── Dockerfile
│ └── ...
└── site
└── Dockerfile
└── ...
Let's also assume the contents of database/Dockerfile
are as follows:
FROM postgres:13
COPY ./init.sql /docker-entrypoint-initdb.d/init.sql
This simply tells Docker which image to use for database
, and copies a single file (init.sql
) to the container on build. Note that /docker-entrypoint-initdb.d
is a special directory on postgres
images, and that .sql
files in this directory are run when postgres starts.
We could remove our database/Dockerfile
entirely by changing our docker-compose.yml
as below:
version: "3.8"
services:
site:
build: ./app
depends_on:
- database
notifier
build: ./notifier
depends_on:
- database
database:
image: "postgres:13"
volumes:
"./init.sql:/docker-entrypoint-initdb.d/init.sql"
This achieves the same effect - Docker Compose now knows which image to use for database
, and will copy our init.sql
file to the appropriate place in the container (volumes
is slightly different than COPY
, but I won't get into that here).
Closing Remarks
Hope that's enough to get you started using Docker compose on your own! If you want to learn more, I highly recommend reading the Docker compose docs.