Docker has revolutionized application development with easy-to-use containers that can run on any machine and provide seamlessly consistent behavior in each environment. Docker Compose allows defining the multiple containers in a YAML file that the application depends on. These containers can be databases, message queues, or even network services.
Spring Boot supports a dockerized environment in two ways:
- Utilize the optional support for Docker Compose
- Utilize Testcontainers to bootstrap containers just as with tests
This article discusses how to start all the docker containers when the application starts; and shut down all containers when the application stops during development and local testing as well.
1. Docker Compose Support in Spring Boot 3
Since version 3.1, Spring boot provides a new module ‘spring-boot-docker-compose‘ that directly integrates with Docker Compose.
As a prequisite, the ‘docker compose‘ or ‘docker-compose CLI‘ application must be present on the path. Spring boot uses the commands that are executed using this application.
1.1. Dependency
The Docker Compose support can be enabled by including the ‘spring-boot-docker-componse‘ dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>
For Gradle, add this dependency:
dependencies {
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
}
When this module is included as a dependency Spring Boot, automatically, will do the following:
- Search for a
compose.yml
and other common compose filenames in your application directory - Call
docker compose up
with the discoveredcompose.yml
. - Create service connection beans for each supported container
- Call
docker compose stop
when the application is shutdown.
1.2. Docker Compose File
Next, create a docker-compose.yaml file with the following content:
services:
redis:
image: 'redis'
ports:
- '6379'
1.3. Test the Application
Next, we can use the RedisTemplate similar to a normal application and we will be able to connect to the Docker containerized Redis instance.
Now, start the application and notice the logs. The application downloads the docker images, starts the container and then the application is fully started.
INFO 25644 --- [ main] com.howtodoinjava.demo.App : Starting App using Java 17.0.1 with PID 25644 (C:\Users\lokes\IdeaProjects\Spring-Boot-Examples\spring-boot-docker-compose\target\classes started by lokesh in C:\Users\lokes\IdeaProjects\Spring-Boot-Examples)
2023-05-30T13:08:00.218+05:30 INFO 25644 --- [ main] com.howtodoinjava.demo.App : No active profile set, falling back to 1 default profile: "default"
2023-05-30T13:08:00.321+05:30 INFO 25644 --- [ main] .s.b.d.c.l.DockerComposeLifecycleManager : Using Docker Compose file 'C:\Users\lokes\IdeaProjects\Spring-Boot-Examples\spring-boot-docker-compose\docker-compose.yaml'
2023-05-30T13:08:01.742+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : redis Pulling
2023-05-30T13:08:06.034+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : f03b40093957 Pulling fs layer
2023-05-30T13:08:06.034+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : 8db26c5e8435 Pulling fs layer
...
...
...
2023-05-30T13:08:12.639+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : 635073d8ccd5 Pull complete
2023-05-30T13:08:12.704+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : redis Pulled
2023-05-30T13:08:12.718+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Network spring-boot-docker-compose_default Creating
2023-05-30T13:08:12.785+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Network spring-boot-docker-compose_default Created
2023-05-30T13:08:12.785+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Container spring-boot-docker-compose-redis-1 Creating
2023-05-30T13:08:13.618+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Container spring-boot-docker-compose-redis-1 Created
2023-05-30T13:08:13.624+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Container spring-boot-docker-compose-redis-1 Starting
2023-05-30T13:08:14.149+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Container spring-boot-docker-compose-redis-1 Started
2023-05-30T13:08:14.149+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Container spring-boot-docker-compose-redis-1 Waiting
2023-05-30T13:08:14.668+05:30 INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli : Container spring-boot-docker-compose-redis-1 Healthy
2023-05-30T13:08:16.091+05:30 INFO 25644 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-05-30T13:08:16.100+05:30 INFO 25644 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-05-30T13:08:16.101+05:30 INFO 25644 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-05-30T13:08:16.194+05:30 INFO 25644 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-05-30T13:08:16.195+05:30 INFO 25644 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1100 ms
2023-05-30T13:08:16.623+05:30 INFO 25644 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-05-30T13:08:16.633+05:30 INFO 25644 --- [ main] com.howtodoinjava.demo.App : Started App in 16.819
2. How does Docker Compose Support Work?
During the startup, when this module detects a ‘docker-compose.yml‘ or ‘compose.yml‘ in the root of your application folder, it uses that to start the containers before starting the application. The new module provides the following features:
- Checks for a docker-compose file in the project’s root directory. The file is searched with names:
- compose.yaml
- compose.yml
- docker-compose.yaml
- docker-compose.yml
- When the application starts, containers/services defined in the compose file are started using the command “
docker compose up
“. - The service connection beans are created for each supported container.
- When the application stops, containers/services defined in the compose file are shut down using the command “
docker compose down
“.
docker compose up # when the application starts
docker compose down # when the application shuts down
Internally, it will use the ‘ConnectionProvider‘ abstraction and @ServiceConnection support to determine which containers are started and will make the metadata available to Spring Boot.
By default, the Docker Compose support is disabled when running tests, in favor of using Testcontainers. To enable it, set
spring.docker.compose.skip.in-tests
tofalse
.
3. Configuring the Docker Compose File
3.1. File Name and Location of Compose File
By default, the compose file is looked into the project’s root. To use a custom file with a different name or location, set the spring.docker.compose.file property. The file path can be exact or relative to the root.
spring.docker.compose.file = ./docker/compose.yaml
If the default or specified compose file is not found, java.lang.IllegalStateException is thrown:
java.lang.IllegalStateException: No Docker Compose file found in directory
'C:\Users\lokesh\IdeaProjects\Spring-Boot-Examples\.'
3.2. Active Profile-specific Compose File
We can also create spring profile-specific compose files. Spring boot will execute the compose file based on the active profile only.
- docker–compose-local.yaml # local development profile
- docker–compose-dev.yaml # dev profile
- docker–compose-test.yaml # test profile
We can activate a specific profile using the following property:
spring.docker.compose.profiles.active = dev
4. Docker Compose Supported Containers
The Docker Compose support in Spring Boot uses the names of the Docker images to determine what to configure. See the following table for the names and supported technologies.
Technology | Container Image Name(s) Matched |
---|---|
Cassandra | cassandra |
ElasticSearch | elasticsearch |
JDBC and R2DBC | gvenzl/oracle-xe, mariadb, mssql/server, MySQL, postgres |
MongoDB | mongo |
RabbitMQ | rabbitmq |
Redis | redis |
Zipkin | openzipkin/zipkin |
Suppose we want to use a customized container, for instance with a preconfigured and filled PostgreSQL database or a preconfigured and secured message broker. In that case, we can do some additional configuration in the Docker Compose file.
For example, in the following configuration, Spring Boot Docker Compose support will read the ‘org.springframework.boot.service-connection label‘ and the value it has will be used as the name of the container:
services:
redis:
image: 'mycompany/mycustomredis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.service-connection: mycustomredis
5. Customizing the Docker Support
The module supports a few more customizations using the properties:
5.1. Waiting for Containers to Start
Docker containers may take time to install/start. By default, Spring boot waits for all the services and containers to start. Note that a container is considered ready when a TCP/IP connection can be established to its mapped port.
If we want to turn off this behavior for a particular container(s), we can use the org.springframework.boot.readiness-check.tcp.disable property as follows:
services:
redis:
image: 'redis'
ports:
- '6379'
labels:
org.springframework.boot.readiness-check.tcp.disable: true
5.2. Connection Timeouts
It is also possible to configure different timeouts using the respective properties.
spring.docker.compose.readiness.tcp.read-timeout=200ms #Timeout for reads
spring.docker.compose.readiness.tcp.connect-timeout=200ms #Timeout for connections
spring.docker.compose.readiness.timeout=2m #Timeout of the readiness checks
spring.docker.compose.readiness.wait=always #Wait strategy to use
5.3. Managing the Container Lifecycle
The following property controls the Docker Compose lifecycle:
spring.docker.compose.lifecycle-management = start-and-stop
It can have either of the three values:
- none – Do not start or stop Docker Compose
- start-only – Start Docker Compose when the application starts and leave it running
- start-and-stop – Start Docker Compose when the application starts and stop it when the JVM exits
6. Conclusion
In this Spring boot docker-compose tutorial, we learned to use the inbuilt docker-compose module in Spring boot. We learned to customize the compose file and its lifecycle with an example.
Happy Learning !!
Comments