How to run Spring Boot and MongoDB in Docker using Docker Compose
Into the previous post How to run Spring Boot and MongoDB in Docker container I described how to start Spring Boot web application and MongoDB
in docker
using Dockerfile
s. Described approach works, but has some drawbacks: we need to remember all commands and repeat them from time to time. Into this tutorial I’ll describe how to build locally a Docker image with Java application and run it together with MongoDB in Docker containers using Docker Compose.
Just a short remainder of what is our demo application. It is a very simple Spring Boot web application which saves users into Mongo DB
. A new added user is displayed in the Users
table at the bottom of the page:
Requirements
To run this tutorial we need the following:
- Any Linux distribution/Mac OS/Windows
- Docker
- Docker-compose
- Java JDK
Prepare Java (Spring Boot) application
Clone the following demo application or use any existing java application. Into this tutorial I will use springboot-mongo-demo
application.
Building spring boot application
Go to the cloned repository and build java project:
cd springboot-mongo-demo && ./gradlew clean build
Make sure build/libs/springboot-mongo-demo.jar
file is created. We’ll need it a little bit later.
Prepare Docker
To run our containers with Docker Compose we need some docker preparations, like docker
and docker-compose
installation (if not installed yet) and docker-compose.yml
and Dockerfile
for our Java application.
Install Docker and Docker Compose
How to install Docker
can be found here: How to install Docker on Ubuntu 16.04
How to install Docker Compose
can be found here: Install Docker Compose
Create/Edit Dockerfile
for Java application
Into the project’s root folder create/edit Dockerfile
with the following content:
FROM openjdk:8-alpine
# Required for starting application up.
RUN apk update && apk add bash
RUN mkdir -p /opt/app
ENV PROJECT_HOME /opt/app
COPY build/libs/springboot-mongo-demo.jar $PROJECT_HOME/springboot-mongo-demo.jar
WORKDIR $PROJECT_HOME
CMD ["java", "-Dspring.data.mongodb.uri=mongodb://springboot-mongo:27017/springmongo-demo","-Djava.security.egd=file:/dev/./urandom","-jar","./springboot-mongo-demo.jar"]
which ’tells’ Docker to do the following:
- build image for our Java application from
openjdk alpine
image - update alpine and install
bash
- create
/opt/app
for our java application - copy compiled (previously)
jar
file into thePROJECT_HOME
folder (/opt/app
) - mark
/opt/app
folder as working forCMD
- specify a command which will be run when our container is being started
The following CMD
argument
-Dspring.data.mongodb.uri=mongodb://springboot-mongo:27017/springmongo-demo
specifies the URI which will be used for connection to MongoDB
. More interesting here is the springboot-mongo:27017
. It contains the name (springboot-mongo
) of our docker container with Mongo DB.
Some words about Alpine
images. Docker-Alpine
image is a super small Docker image based on Alpine Linux. The image is only 5 MB. More information can be found in its Github repository.
Create/Edit docker-compose.yml file
Docker Compose file (docker-compose.yml
or docker-compose.yaml
) is a YAML file defining services
, networks
and volumes
. More details can be found into the docker compose-file official documentation.
Into the root folder of our demo application create/edit docker-compose.yml
with the following content:
version: '3.1'
services:
springboot:
build: .
# image: registry.gitlab.com/idgst/springboot-mongo-demo:latest
restart: always
container_name: springboot
ports:
- 8182:8080
working_dir: /opt/app
depends_on:
- mongo
mongo:
image: mongo
container_name: springboot-mongo
# ports: # for demo/debug purpose only
# - 27018:27017
volumes:
- $HOME/data/springboot-mongo-data:/data/db
- $HOME/data/springboot-mongo-bkp:/data/bkp
restart: always
Let’s take a look closer at this file.
version: '3.1'
- describes which syntax version should be used. (for more details please see official docs)services:
- service definitionspringboot:
- the name of ourspring-boot
servicebuild: .
- Dot is required here and means to build our own image from the current folder.Dockerfile
should be placed in the project’s root folder. (In order to userDockerfile
, e.g. formdocker
folder this row will look likebuild ./docker
restart: always
- restarting policy. More details can be found herecontainer_name: springboot
- specifies a custom container name, rather than a generated default name.ports:8182:8080
- exposes ports, ie maps the host8182
port to the container’s8080
port. By default, spring boot runs web application on port8080
. We can specify any free port on our computer/laptop/Virtual Machine were docker runs and map it to the container’s port.working_dir: /opt/app
- sets the working directory for anyRUN
,CMD
,ENTRYPOINT
,COPY
andADD
commands.depends_on: mongo
- ourspringboot
container will depend onmongo
container, and will be started after themongo
.
mongo:
the name of ourmongo
service.volumes:
- mount host paths or named volumes, specified as sub-options to a service. It means that our host/local folder$HOME/data/springboot-mongo-data
will be mounted as/data/db
inside our running container(s).
Running our application in docker
Now we’re ready to start our application using Docker Compose
command like
docker-compose up
The output will look like
Building springboot
Step 1/7 : FROM openjdk:8-alpine
---> a2a00e606b82
Step 2/7 : RUN apk update && apk add bash
---> Using cache
---> 09b204c859e1
Step 3/7 : RUN mkdir -p /opt/app
---> Using cache
---> e06d17001873
Step 4/7 : ENV PROJECT_HOME /opt/app
---> Using cache
---> 09e2d9fa816a
Step 5/7 : COPY build/libs/springboot-mongo-demo.jar $PROJECT_HOME/springboot-mongo-demo.jar
---> 20c9d43e526c
Step 6/7 : WORKDIR $PROJECT_HOME
---> 4edd1ec9a9cf
Removing intermediate container 91586410821f
Step 7/7 : CMD java -Dspring.data.mongodb.uri=mongodb://springboot-mongo:27017/idgst -Djava.security.egd=file:/dev/./urandom -jar ./springboot-mongo-demo.jar
---> Running in 335f848db06d
---> 388a1e4b05f0
Removing intermediate container 335f848db06d
Successfully built 388a1e4b05f0
Successfully tagged springbootmongodemo_springboot:latest
WARNING: Image for service springboot was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating springboot-mongo ...
Creating springboot-mongo ... done
Creating springboot ...
Creating springboot ... done
Attaching to springboot-mongo, springboot
....
Because we specified build: .
in our docker-compose.yml
file, It will build springboot
container first and then will start springboot
after mongo
container.
The docker-compose up
command aggregates the output of each container (essentially running docker-compose logs -f). In case when the command exits (Ctrl + C
is pressed), all containers are stopped.
To start the containers in the background and leave them running, we need to use -d
key in command:
docker-compose up -d
For more details please see the official documenation.
Our running web application should be available in browser by this URL : http://localhost:8182. Now we can create some users.
Verify records in MongoDB
Let’s verify that our users are actually saved in MongoDB. We can do it into two ways:
- attach
bash
to the running container with mongo - temporary expose the port of container with mongo
Let’s take a look at how to attach bash
to the running container approach. We’ll talk about exposing ports a little bit later in this post.
Attach bash to the running Mongo container
To attach bash
to the running mongo container and verify Mongo records we can do into the following way:
docker exec -i -t springboot-mongo /bin/bash
the console/terminal will be changed to something like: root@ff55937c3772:/#
. It means we’re inside the docker container.
After that we can run the following commands:
root@ff55937c3772:/# mongo
root@ff55937c3772:/# use springmongo-demo
root@ff55937c3772:/# db.users.find()
The ouput will contain a user created over UI:
{ "_id" : ObjectId("5a3b69b2da6ad50001acc5c2"), "_class" : "info.idgst.demo.User", "firstName" : "sdfsdfsd", "lastName" : "sdfsdf", "email" : "[email protected]" }
The ObjectId
, firstName
, lastName
and email
will differ from specified here.
Docker compose tips and tricks
Here I’ll provide some information which can be useful for working with Docker
and Docker Compose
.
Verify local springboot image
To verify image with our Java application was created locally we can run the following command:
docker images
The output will look like:
REPOSITORY TAG IMAGE ID CREATED SIZE
springbootmongodemo_springboot latest f0be6cbeaa67 3 minutes ago 124MB
How to update containers
This part of the article contains information about how to update our running docker containers with our Java application and MongoDB.
Making some changes in docker-compose file
In case when we update/add/remove any property in docker-compose.yml
file the docker-compose up -d
will recreate and will restart our running containers. The output will look like:
Recreating springboot-mongo ...
Recreating springboot-mongo ... done
Recreating springboot ...
Recreating springboot ... done
Please note: in case if Dockerfile
was updated, the image with Java application won’t be recreated and container won’t be restarted by docker-compose up -d
. How to update the image with Java application described bellow.
By now, let’s take a look at concreate example by exposing ports for MongoDB container.
Expose port for MongoDB
We can just modify the docker-compose.yml
file and uncomment the following lines:
# ports: # for demo/debug purpose only
# - 27018:27017
As a result, docker-compose.yml
will look into the following way:
version: '3.1'
services:
springboot:
build: .
# image: registry.gitlab.com/idgst/springboot-mongo-demo:latest
restart: always
container_name: springboot
ports:
- 8182:8080
working_dir: /opt/app
depends_on:
- mongo
mongo:
image: mongo
container_name: springboot-mongo
ports: # for demo/debug purpose only
- 27018:27017
volumes:
- $HOME/data/springboot-mongo-data:/data/db
- $HOME/data/springboot-mongo-bkp:/data/bkp
restart: always
Here we map the host’s 27018
port to the container’s 27017
port. After saving changes in docker-compose.yml
we need to recreate and restart our running containers by the following command:
docker-compose up -d
After containers are restarted we’ll be able to connect to Mongo DB using any GUI application (e.g Robo 3T) by specifying 27018
port in connection properties.
Update external images
When Docker pulls any image first time, the image and running containers from this image leave without changes. Docker images often get some updates like hot/security fixes, new versions, etc. To update any external
image (by external I mean any image which will be pulled from any Docker registry
) we need use pull
command like this:
docker-compose pull
It will verify updates for any used image in docker-compose.yml
file and download it. The output will look like:
Pulling mongo (mongo:latest)...
latest: Pulling from library/mongo
c4bb02b17bb4: Pull complete
3f58e3bb3be4: Pull complete
a229fb575a6e: Pull complete
8f5ddc533743: Pull complete
5e9d2af6e206: Pull complete
3b6c28c0235b: Pull complete
56df4a1a7aca: Pull complete
2693a8f3c155: Pull complete
a579e0ac9ece: Pull complete
158f54c96e9a: Pull complete
Digest: sha256:d16539343d6b47ac150a9fae8e1278253e5f00a4c1d9d3f4a3858bd90d5f3097
Status: Downloaded newer image for mongo:latest
Our springboot
container won’t be updated bacause we built it locally from Dockerfile
.
Update local images
To update our local springboot
image we need do the following:
- run
docker-compose build
command. It will verify changes inDockerfile
and recreate image in case whenDockerfile
was changed. - run
docker-compose up -d
command. It will recreate and restart only ourspring-boot
container:
springboot-mongo is up-to-date
Recreating springboot ...
Recreating springboot ... done
As you can see container with mongo was not recreated and restarted.
Into the next part I will describe how can we build any docker image locally, push it to Gitlab’s Docker registry and use it in our docker-compose.yml
file.