DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Securing Your Software Supply Chain with JFrog and Azure
Register Today

Trending

  • Microservices With Apache Camel and Quarkus
  • Writing a Vector Database in a Week in Rust
  • RBAC With API Gateway and Open Policy Agent (OPA)
  • Comparing Cloud Hosting vs. Self Hosting

Trending

  • Microservices With Apache Camel and Quarkus
  • Writing a Vector Database in a Week in Rust
  • RBAC With API Gateway and Open Policy Agent (OPA)
  • Comparing Cloud Hosting vs. Self Hosting
  1. DZone
  2. Coding
  3. Frameworks
  4. Running Multiple Spring Boot Services With Docker Compose

Running Multiple Spring Boot Services With Docker Compose

In this post, we'll look at how Docker Compose makes it easier to configure and run multiple containers in your local environment.

Brian Hannaway user avatar by
Brian Hannaway
·
Sep. 14, 20 · Tutorial
Like (1)
Save
Tweet
Share
17.51K Views

Join the DZone community and get the full member experience.

Join For Free

in this post, we'll look at how docker compose makes it easier to configure and run multiple containers in your local environment.

why docker compose?

first up, you don't need docker compose to run multiple containers. you can do this just fine by manually starting and stopping the containers yourself, as shown previously in this post . however, as the number of containers in your application grows, it becomes more cumbersome to manage each container manually.

docker compose simplifies things by allowing you to configure a multi-container application in a single yaml file. you can start and stop all containers in the application with a single command.

sample app code

i've created a sample app for this post which you can pull from github . it contains the following

  • 2 spring boot applications
    • bank account service — exposes a rest api for creating and reading bank simple account details
    • config service — exposes a rest api with application configuration for the bank account service
  • 2 dockerfiles — to define the container images for the above services
  • a docker compose file defining the multi-container application

aside from the docker side of things, i won't go into any detail on the boot services. if you want more info you can check out this previous post .

bank account service dockerfile

we'll begin defining a docker image for the bank account service.

dockerfile




x
15


1
# maintainer brian hannaway
2

           
3
from openjdk:8-jre-alpine
4

           
5
workdir /app
6

           
7
# add wait script to the image - script pulled from https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
8
copy /scripts/wait /app/
9
run chmod +x /app
10

           
11
run apk --no-cache add curl
12

           
13
copy /target/bank-account-service-0.0.1-snapshot.jar /app/
14

           
15
cmd ./wait && java -jar bank-account-service-0.0.1-snapshot.jar



from openjdk:8-jre-alpine tells docker to use the openjdk base image.

workdir /app tells docker to create a new working directory in the image called. all further commands will run from this directory.

copy /scripts/wait /app/ tells docker to copy the wait script from the scripts directory on the host to the /app directory in the image. i'll explain the purpose of the wait script in detail later.

run chmod +x /app makes the contents of the /app directory executable

copy /target/bank-account-service-0.0.1-snapshot.jar /app/ copies the service jar from the target directory on the host to the /app directory in the image

cmd ./wait && java -jar bank-account-service-0.0.1-snapshot.jar runs the wait script, followed by the bank account service. the service won't run until the wait script has finished.

config service dockerfile

next, we'll define the config service docker image. its a slightly simpler version of the image we created for the bank account service above. we'll simply create a working directory, copy in the service jar, and run it.

dockerfile




xxxxxxxxxx
1


1
from openjdk:8-jre-alpine
2

           
3
maintainer brian hannaway
4

           
5
workdir /app
6

           
7
copy /target/config-server-0.0.1-snapshot.jar /app/
8

           
9
entrypoint ["java", "-jar", "config-server-0.0.1-snapshot.jar"]



defining the docker compose file

now that we've defined dockerfiles for the bank account and config services, the next step to create a docker-compose file that describes how we'll use these images to run containers.

dockerfile




xxxxxxxxxx
1
23


1
version: "3"
2

           
3
services:
4
   config-service:
5
      image: config-service
6
      container_name: config-service
7
      networks:
8
         - micro-service-network
9
      ports:
10
         - 8888:8888
11
         
12
   bank-service:
13
      image: bank-service
14
      container_name: bank-service
15
      networks:
16
         - micro-service-network
17
      ports:
18
         - 8080:8080
19
      environment:
20
         wait_hosts: config-service:8888
21

           
22
networks:
23
    micro-service-network:



version: "3" tells docker that we're using version 3 of the docker-compose file format. at the time of writing version 3 is the latest and recommended version. the docker-compose format version you use will be dictated by the version of docker you're running. i'm running docker version 19.03.12 which means that i should be using version 3. if you want to check what version of docker-compose is compatible with your docker version, check out this compatibility matrix .

services definition

the services section defines the containers that make up your application. each service definition contains all the configuration required to start a container from an image. the information in each service definition is what you'd typically supply on the command line, running a container manually.

dockerfile




xxxxxxxxxx
1


1
config-service:
2
   image: config-service
3
   container_name: config-service
4
   networks:
5
      - micro-service-network
6
   expose:
7
      - "8888"



config service

the config-service section defines all the configuration docker needs to run the config-service container

image tells compose which image to use to run the container.

container_name is the name given to the container when it starts. if we don't specify a name, compose will derive one based on the name of the compose file and the image name. for example, if i omit the name attribute for the config-service and run docker-compose up , i can see that the containers derived name is boot-microservices-docker-compose_config-service_1 .

code snippet

generally, it's a good idea to give your containers a meaningful name. you'll see later that we need to reference config-service from the bank-service . we'll do this using the name specified in container_name .

networks defines the networks that the config-service container will join when it starts. in this instance it will join micro-service-network , which we'll define later.

expose lists the ports that are exposed on the container. the ports are exposed on either the default network or any network the container is attached to. the ports are not exposed to the host machine. to do this you'll need to use the ports attribute and supply the appropriate mappings.

bank service

the bank-service definition is very similar to what we've already defined, with the image , container_name , networks and expose attributes being similar to those defined for the config-service

dockerfile




xxxxxxxxxx
1


1
   image: bank-service
2
   container_name: bank-service
3
   networks:
4
      - micro-service-network
5
   expose:
6
      - "8080"
7
   environment:
8
      wait_hosts: config-service:8888



the environment attribute is used to specify a list of environment variables for the container. in the bank-service we specify the environment variable wait_hosts and give it the value config-service:8888 . in short, this is required to control container start up order and ensure that the config-service is up and running before the bank-service starts. i'll explain this in detail later.

networks definition

the networks section allows you define a network for your services. for our application we defined a network called micro-service-network . when you run docker-compose up , each container that starts will be added to the micro-services-network and will be visible to every other container in the application. containers can reference one another via their hostname, which is the same as the service name. so in our sample application, the banks-service can access the config-service as config-service:8888.

if we don't explicitly define a network , docker will create one by default and add all services in the compose file to it.

running the application

running the docker-compose up command will

  • create a bridge network called micro-service-network
  • start a container using the config-service image. the container will expose port 8888 on micro-service-network and will be accessible to other containers via host name config-service .
  • start a container using the bank-service image. the container will expose port 8080 on micro-service-network and will be accessible to other containers via host name bank-service .

it takes approximately 20 seconds for both the bank-service and config-service to start. if you run docker container ls you should see the two containers that have just been created.

docker container

service dependencies and startup order

it's common to have dependencies between containers, such that a container a requires container b to be running before container a can start. compose allows you to handle this scenario to a certain degree, by defining the startup order using the depends_on attribute. for example, the compose file below defines a web service and a db service, where web is dependent on db .

dockerfile




xxxxxxxxxx
1


1
version: '3'
2
services:
3
  web:
4
    image: mywebapp
5
    depends_on:
6
      - db
7
  db:
8
    image: postgres



in the above example, compose will start the containers in dependency order, so db will be started before web . although depends_on sets the order in which containers are started, it does not guarantee that postgres, inside the db container is fully operational before the web container starts.

we have a similar problem in our sample application because bank-service tries to call config-service on startup. if config-service isn't fully stood up and available to take requests on port 8888, bank-service will fail. using the depends_on attribute to start the config-service first, won't guarantee that the config-service is fully operational before bank-service calls it.

introducing docker-compose-wait

docker-compose-wait is a great command line utility that solves the problem described above. when defining the bank-service earlier we made docker-compose-wait available to the image by copying the wait script into the app directory.

dockerfile




xxxxxxxxxx
1


1
# add wait script to the image - script pulled from https://github.com/ufoscout/docker-compose-wait/releases/download/2.7.3/wait /wait
2
copy /scripts/wait /app/



we then told docker to run the wait script along with the jar when starting the container.

cmd ./wait && java -jar bank-account-service-0.0.1-snapshot.jar

when we defined bank-service in the docker-compose file, we included a wait_hosts environment variable that referenced config-service on port 8888. when we run docker-compose up , the wait script pings config-service on port 8888. it will not allow bank-service container to start until config-service is up and running on port 8888.

we can see this in action in the log snippets below. the wait script checks if config-service is available on port 8888, initially reporting that it isn't available.

config-service

eventually, config-service bootstraps and is up and running on port 8888. the wait script then reports host config-service:8888 is now available and the bank-service container is started.

wrapping up

in this post, we looked at how docker-compose makes it easy to manage multiple containers in a simple single-node environment. this is particularly useful for development environments and automated test environments. if you want to manage multiple containers in a multi-node environment, then docker swarm is a better bet. we'll look at swarm in another post soon.

Docker (software) Spring Framework Spring Boot application Host (Unix) Directory Network

Published at DZone with permission of Brian Hannaway, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • Microservices With Apache Camel and Quarkus
  • Writing a Vector Database in a Week in Rust
  • RBAC With API Gateway and Open Policy Agent (OPA)
  • Comparing Cloud Hosting vs. Self Hosting

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: