Development notes

Thoughts, notes and ideas about development

How to run Spring Boot and MongoDB in Docker using Docker Compose

2017-12-26 9 min read Development Alexey Bogdanov

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 Dockerfiles. 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:

spring-boot-demo application

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 the PROJECT_HOME folder (/opt/app)
  • mark /opt/app folder as working for CMD
  • 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 definition
    • springboot: - the name of our spring-boot service
      • build: . - 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 user Dockerfile , e.g. form docker folder this row will look like build ./docker
      • restart: always - restarting policy. More details can be found here
      • container_name: springboot - specifies a custom container name, rather than a generated default name.
      • ports:8182:8080 - exposes ports, ie maps the host 8182 port to the container’s 8080 port. By default, spring boot runs web application on port 8080. 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 any RUN, CMD, ENTRYPOINT, COPY and ADD commands.
      • depends_on: mongo - our springboot container will depend on mongo container, and will be started after the mongo.
    • mongo: the name of our mongo 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 in Dockerfile and recreate image in case when Dockerfile was changed.
  • run docker-compose up -d command. It will recreate and restart only our spring-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.

comments powered by Disqus