-
Notifications
You must be signed in to change notification settings - Fork 0
Chapter 7: Docker Compose
Welcome back! In our previous chapters, especially Chapter 6: Docker Container Packaging, we learned how to package each of our microservices (Shopfront, Product Catalogue, and Stock Manager) into a neat, self-contained Docker Image using a Dockerfile
. We now have three individual, portable boxes, each ready to be run as a Docker Container.
This is great for running one service, but our application needs all three services running at the same time so they can talk to each other using their REST APIs and exchange DTOs.
The challenge now is:
- How do we easily start and stop all three containers together?
- How do we make sure they can find and communicate with each other using the service names we configured (like
productcatalogue
andstockmanager
in the Shopfront'sapplication.properties
)? - How do we manage their configurations and dependencies as a group?
Manually running docker run
commands for each service, setting up networks, and linking them would be complicated and hard to repeat consistently. This is where Docker Compose comes in.
Imagine you have three separate electrical appliances (our microservices) that need to be plugged into different sockets but also need to be connected to each other with cables.
Doing this manually involves:
- Finding a power outlet for each.
- Plugging each one in (running the container).
- Making sure the cables are connected correctly between the right appliances (setting up network communication).
- Turning them all on in the right order, or at least making sure they can find each other once they are on.
This is a hassle, especially if you have to set this up repeatedly or manage more appliances.
Docker Compose is a tool specifically designed to simplify the process of defining, running, and managing multi-container Docker applications. It lets you describe your entire application stack in a single configuration file.
Think of Docker Compose as the master switchboard or a blueprint and automated installer for your interconnected set of appliances (containers).
- You write a simple plan (a
docker-compose.yml
file) that lists all the appliances (services/containers) your application needs. - This plan includes details like which image to use for each, which ports they use, and which other services they need to talk to.
- With just one command, Docker Compose reads this plan and sets up and starts everything for you, including the network connections between them.
- With another single command, you can turn everything off and clean it up.
This makes running your complete microservice application locally for development or testing incredibly easy and repeatable.
The heart of Docker Compose is the docker-compose.yml
file (YAML format). This file defines the services, networks, and volumes for your multi-container application.
For our project, the docker-compose.yml
file defines our three microservices: shopfront
, productcatalogue
, and stockmanager
.
Let's look at a simplified version of the docker-compose.yml
file used in the project (there are slight variations like docker-compose-build.yml
for building locally vs. using pre-built images):
# Simplified example from docker-compose.yml or docker-compose-build.yml
version: '2' # Specifies the Compose file format version
services: # This section lists all the containers (services) in our application
shopfront: # Define the Shopfront service
# --- How to get the image ---
# image: danielbryantuk/djshopfront # Option 1: Use a pre-built image
build: shopfront # Option 2: Build the image from the Dockerfile in the 'shopfront' directory
# --- Network configuration ---
ports:
- "8010:8010" # Map port 8010 on your computer to port 8010 in the container
# --- Service discovery (how it finds others) ---
links:
- productcatalogue # Link to the productcatalogue service
- stockmanager # Link to the stockmanager service
productcatalogue: # Define the Product Catalogue service
# image: danielbryantuk/djproductcatalogue # Option 1
build: productcatalogue # Option 2: Build from Dockerfile in 'productcatalogue' directory
ports:
- "8020:8020" # Map port 8020 on your computer to port 8020 in the container
# No links needed here as it doesn't initiate calls to others
stockmanager: # Define the Stock Manager service
# image: danielbryantuk/djstockmanager # Option 1
build: stockmanager # Option 2: Build from Dockerfile in 'stockmanager' directory
ports:
- "8030:8030" # Map port 8030 on your computer to port 8030 in the container
# No links needed here
Let's break down the key parts relevant to our project:
-
version: '2'
: This line specifies which version of the Docker Compose file format is being used. Different versions support different features. -
services:
: This is the main section where you list all the containers that make up your application. Each item underservices
defines one container/service. -
shopfront:
,productcatalogue:
,stockmanager:
: These are the names we give to each service within the Compose file. These names are important because Docker Compose uses them for service discovery (how containers find each other). -
build: <directory>
ORimage: <image-name>
: For each service, you tell Compose where to get the Docker image.-
build: shopfront
: This tells Compose to build the image for this service using theDockerfile
located in the./shopfront
directory (relative to thedocker-compose.yml
file). This is great for local development because Compose will build the image from your latest code changes. This is what thedocker-compose-build.yml
file uses. -
image: danielbryantuk/djshopfront
: This tells Compose to pull a pre-built image with that name from a Docker registry (like Docker Hub). This is often used for deploying or running known versions. This is what thedocker-compose.yml
file uses.
-
-
ports:
: This section maps ports between the host machine (your computer) and the container. The format isHOST_PORT:CONTAINER_PORT
.-
"8010:8010"
forshopfront
: This means port 8010 on your computer will be forwarded to port 8010 inside the shopfront container. This is essential so you can access the Shopfront's web interface from your browser athttp://localhost:8010
. -
"8020:8020"
forproductcatalogue
: Maps host port 8020 to container port 8020. -
"8030:8030"
forstockmanager
: Maps host port 8030 to container port 8030. These mappings allow you (or other applications outside the Compose network) to access these services directly vialocalhost
if needed, e.g., for testing their APIs.
-
-
links:
: (Note: In newer Compose versions/formats, explicitlinks
are often not needed as service discovery is automatic, but it's present in the example file). This section defines dependencies.-
links: - productcatalogue - stockmanager
: This tells Docker Compose that theshopfront
service needsproductcatalogue
andstockmanager
. More importantly for us, Compose will configure the internal network so that when theshopfront
container tries to connect to the hostnameproductcatalogue
, it will be directed to theproductcatalogue
container, andstockmanager
will be directed to thestockmanager
container.
-
This links
behavior is crucial! Remember in Chapter 1, the Shopfront's application.properties
was configured like this:
# shopfront/src/main/resources/application.properties
# ... other properties ...
productCatalogueUri = http://productcatalogue:8020 # Uses 'productcatalogue' as the hostname
stockManagerUri = http://stockmanager:8030 # Uses 'stockmanager' as the hostname
Because Docker Compose sets up an internal network and uses the service names (productcatalogue
, stockmanager
) as hostnames that resolve to the correct containers, the Shopfront can simply use these names in its configuration URIs to find the other services! It doesn't need to know their internal IP addresses, which might change.
Once you have a docker-compose.yml
file (or docker-compose-build.yml
), running your entire application stack is incredibly simple using the docker-compose
command-line tool.
-
Navigate to the root directory of the project in your terminal (the directory containing the
docker-compose.yml
ordocker-compose-build.yml
file and the service subdirectories). -
To build the images (if you are using
build:
in the YAML, e.g.,docker-compose-build.yml
) and start the containers:docker-compose -f docker-compose-build.yml up --build
-
-f docker-compose-build.yml
: Tells Compose to use this specific file. (If your file is nameddocker-compose.yml
, you can omit-f docker-compose.yml
). -
up
: This command builds (if necessary) and starts all the services defined in the file. -
--build
: This flag ensures Compose builds the images from theDockerfile
s even if an image with the same name already exists. This is useful during development. - Output: You will see Docker downloading base images, building your service images, creating a network, and starting each container. You'll see logs from each service printed to your console.
# Example Output (simplified) Creating network djshopping_default Building shopfront Step 1/4 : FROM openjdk:8-jre ... (docker build output) ... Building productcatalogue Step 1/5 : FROM openjdk:8-jre ... (docker build output) ... Building stockmanager Step 1/4 : FROM openjdk:8-jre ... (docker build output) ... Creating djshopping_productcatalogue_1 ... done Creating djshopping_stockmanager_1 ... done Creating djshopping_shopfront_1 ... done Attaching to djshopping_productcatalogue_1, djshopping_stockmanager_1, djshopping_shopfront_1 ... (logs from your running applications) ...
Your application is now running! You should be able to access the Shopfront in your web browser at
http://localhost:8010
. The Shopfront container will be talking to the productcatalogue and stockmanager containers internally using the network Compose set up. -
-
To stop the running containers and remove the network and volumes created by
up
:docker-compose -f docker-compose-build.yml down
-
-f docker-compose-build.yml
: Again, specify the file if it's not the default name. -
down
: This command stops and removes the containers, network, and default volumes.
# Example Output Stopping djshopping_shopfront_1 ... done Stopping djshopping_stockmanager_1 ... done Stopping djshopping_productcatalogue_1 ... done Removing djshopping_shopfront_1 ... done Removing djshopping_stockmanager_1 ... done Removing djshopping_productcatalogue_1 ... done Removing network djshopping_default ... done
Your application is now shut down.
-
Let's refine our office/box analogy with Docker Compose:
sequenceDiagram
participant User as You
participant Compose as Docker Compose Tool
participant Daemon as Docker Daemon
participant Network as Compose Network
participant ContainerSF as Shopfront Container
participant ContainerPC as Product Catalogue Container
participant ContainerSM as Stock Manager Container
User->>Compose: Run 'docker-compose up'
Compose->>Daemon: Read docker-compose.yml
Compose->>Daemon: (If needed) Build Images
Compose->>Daemon: Create Network (e.g., 'djshopping_default')
Compose->>Daemon: Create & Start ContainerSF (using shopfront definition)
Compose->>Daemon: Create & Start ContainerPC (using productcatalogue definition)
Compose->>Daemon: Create & Start ContainerSM (using stockmanager definition)
Daemon-->>Network: Attach ContainerSF, ContainerPC, ContainerSM
Network-->>ContainerSF: 'productcatalogue' resolves to ContainerPC's IP
Network-->>ContainerSF: 'stockmanager' resolves to ContainerSM's IP
Note over ContainerSF: Now Shopfront can find others by name!
Daemon-->>User: Start logging output
You (the User) give the plan (docker-compose.yml
) to the automated setup crew (Docker Compose Tool). The crew talks to the main builder (Docker Daemon). The builder reads the plan, constructs the boxes (images), sets up a private internal office floor with phone lines (the Compose Network), puts each department's box (container) on that floor, and connects the phone lines so they can call each other using the department names ("Product Catalogue", "Stock Manager") instead of needing extension numbers (IP addresses). Then, they turn on the power, and you see everything working (the logs).
When you run docker-compose down
, you tell the crew to pack everything up and clear the floor.
Using Docker Compose for our local development and testing environment provides significant benefits:
-
Single Configuration File: Defines the entire application stack in one place (
docker-compose.yml
). -
Easy Lifecycle Management: Start (
up
), stop, and remove (down
) all services with single commands. -
Repeatable Environments: Ensures that everyone running
docker-compose up
gets the exact same multi-container environment. - Simplified Networking: Automatically sets up an internal network allowing containers to discover each other by service name.
-
Dependency Management: You can specify dependencies (like
depends_on
), thoughlinks
often implies this dependency for startup order.
This makes setting up and tearing down the complex microservice environment for local work much easier than managing individual containers manually.
In this chapter, we learned about Docker Compose and how it solves the problem of running and managing multi-container Docker applications. We saw that the central element is the docker-compose.yml
file, which defines all the services that make up our application stack (Shopfront, Product Catalogue, Stock Manager). The configuration file specifies how to get the images (build or pull), maps necessary network ports, and crucially, uses service names (productcatalogue
, stockmanager
) that Docker Compose's internal network makes discoverable by the other containers (like the Shopfront).
We also learned the simple commands (docker-compose up
, docker-compose down
) to bring our entire application stack online or offline with ease. This makes Docker Compose an invaluable tool for running our microservices locally for development and testing.
While Docker Compose is excellent for managing applications on a single machine (like your laptop or a single server), real-world production environments often involve clusters of machines and require more advanced features like automatic scaling, self-healing, and rolling updates. This is the domain of container orchestration platforms like Kubernetes.
In the next chapter, we will transition from managing our services locally with Docker Compose to preparing them for deployment in a distributed cluster environment using Kubernetes Configuration.
Next Chapter: Kubernetes Configuration
Doc by Reyas Khan. References: [1], [2]
TO Begin: Start here