DRY principle with docker-compose
An oft-repeated and sensible principle in software engineering is DRY, or “don’t repeat yourself”. Here we will apply this principle to Docker compose files.
This post is part of the “Spring Boot Primer” series.
Intended audience
This post assumes a basic level of familiarity with Docker compose files. If you are not already familiar, there is good documentation available.
Dependencies
To build the source, you will need JDK 8+, and a Docker installation.
What are we aiming for?
Following the DRY principle, we would like to use Docker compose for both development and production environments, with as little duplication as possible.
Docker compose will support this with its ability to compose, or layer, multiple compose files together. In the application we are building, which is a simple Spring Boot based REST API, we have the following service dependencies:
- Database (MariaDB)
- Cache (Redis)
- Administration console (Adminer)
Development
In development we want to run the Spring Boot app normally, and have it be able to connect to the database and cache inside the Docker network.
Production
In production we want to run the entire stack inside Docker, we don’t want to expose the database or cache, but we do want to expose the Spring Boot App.
Composing them together
This logically leads us to a set of three compose files, which will be pulled together in two different combinations, as below:
Common services
The database, cache and admin tools will be common between both production and
development, so we create a shared docker-compose.yml
as follows:
1 |
|
The object at line 4 defines our MariaDB service. We are asking for a database and user to be created (lines 7 and 8), and asking for UTF8 support to be enabled by default (lines 10 and 11).
Development
The development file needs to expose the ports for the database and cache so that a locally run version of the application can access them. It also wires a development-specific volume to the database service.
1 |
|
Running
To run up the development environment:
docker stack deploy --prune -c docker-compose.yml -c docker-compose-development.yml spring-rest-example
Then start the Spring Boot application in your IDE, or with the command:
./mvnw spring-boot:run
Production
The production file needs to start the Spring Boot application as a service, and expose it outside of the Docker network. It also needs to connect a volume to the database, that won’t get mixed up with the development version.
We would also like to use different passwords for development and production.
1 |
|
Running
To run up the production environment, we first need to build the image:
docker build -t petewoods/spring-rest-example .
Then we can run docker stack deploy
:
(. production.env && docker stack deploy --prune -c docker-compose.yml -c docker-compose-production.yml spring-rest-example)