Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Dockerizing Spring Boot Applications

DZone's Guide to

Dockerizing Spring Boot Applications

Docker seems to be everywhere you look. Here's a look at migrating Spring Boot apps to Docker!

· Integration Zone
Free Resource

Learn how API management supports better integration in Achieving Enterprise Agility with Microservices and API Management, brought to you in partnership with 3scale

Docker here, Docker there, I see Dockers everywhere.

In this post, I will describe the process of migrating a Spring Boot application to Docker. We will start with modifying a build file, then we will create a Dockerfile so it can be run locally. Finally, we will publish our image in DockerHub.

Introduction

A few months ago I started a new personal project called JVM Bloggers to help Polish programmers to spread the news about their new blog posts. Initially, this Spring Boot application was hosted on my local machine, but I have migrated it to a free account on Heroku.

And for the first weeks, I was satisfied. The application didn’t have to be online 24/7 so sleeping for 8 hours per day (limitation of free Heroku account) was not a big problem, and the 500 MB memory cap didn’t limit me either. But as JVM Bloggers grew I started to encounter strange issues with memory usage: the application started to consume 500-550MB and it was very difficult to find a source of this behaviour. I even left the application running on my local machine with a profiler attached but still didn’t find anything suspicious.

Memory footprint problems became irritating as I had to monitor the application and restart it every 1-2 days and due to Heroku's nature, I could not simply ssh to the server and debug or attach a profiler to a running process. It became clear that if I was going to add more features to JVM Bloggers I had to migrate it to something more flexible – a Linux machine with Docker.

Docker

Nowadays Docker is a very popular topic among many developers, especially in projects migrating to microservices architecture. But the most interesting feature from my problem’s perspective is the ability to create a standardized image of my application and run it in a different environment without worrying about differences. One image can be deployed either locally or on almost any Linux machine, so with Docker, I would be able to test my app locally and deploy it on AWS or somewhere else without much hassle.

Migration

First we have to add some new dependencies and tasks to our build.gradle script:

buildscript {
    // ...
    dependencies {
        // ...
        classpath('se.transmode.gradle:gradle-docker:1.2')
    }
}

// ...

group = 'tdziurko' // this will be my login at DockerHub (more about it later in this post)

task buildDocker(type: Docker, dependsOn: build) {      // this task will build our Docker image
    push = true
    applicationName = jar.baseName
    dockerfile = file('src/main/docker/Dockerfile')
    doFirst {
        copy {
            from jar
            into stageDir
        }
    }
}

In line 16, we have specified the location of our Dockerfile so now it is time to create one there.

Creating Dockerfile

Dockerfile is a configuration file that specifies how to create our Docker image so that we can deploy later on.

FROM java:8
MAINTAINER email@example.com
VOLUME /tmp
EXPOSE 8080

ENV USER_NAME blogger
ENV APP_HOME /home/$USER_NAME/app

RUN useradd -ms /bin/bash $USER_NAME
RUN mkdir $APP_HOME

ADD jvm-bloggers-0.5.0.jar $APP_HOME/jvm-bloggers.jar
RUN chown $USER_NAME $APP_HOME/jvm-bloggers.jar

USER $USER_NAME
WORKDIR $APP_HOME
RUN bash -c 'touch jvm-bloggers.jar'

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","jvm-bloggers.jar"]

Let me explain this file step by step:

  • FROM java:8: our base image, it’s something like the extend keyword in Java. Here we want to base the image with Java 8 installed
  • VOLUME /tmp: mounted directory where our application could write something to disk
  • EXPOSE 8080: port number our app will be started, and it will be available from outside the Docker container
  • ENV USER_NAME blogger: helper variable with a username we will use to run our application
  • ENV APP_HOME /home/$USER_NAME/app: next helper variable with directory where our app will be located
  • RUN useradd -ms /bin/bash $USER_NAME: create user with a name defined in $USER_NAME
  • RUN mkdir $APP_HOME: create app directory
  • ADD jvm-bloggers-0.5.0.jar $APP_HOME/jvm-bloggers.jar: add fat-jar to our image and put it in $APP_HOME
  • RUN chown $USER_NAME $APP_HOME/jvm-bloggers.jar: all previous commands were executed as ROOT so we need to change the owner of our jar file to $USER_NAME. Generally using ROOT in Docker is considered a wrong approach because of security.
  • USER $USER_NAME; WORKDIR $APP_HOME: change user and working directory to ones we want to use to run our application
  • RUN bash -c ‘touch jvm-bloggers.jar’: touch our file so it has modification time
  • ENTRYPOINT [“java”,“-Djava.security.egd=file:/dev/./urandom”,“-jar”,“jvm-bloggers.jar”]: execute our fat-jar (urandom is for Tomcat source of entropy)

Running Docker Image Locally

Now we have everything ready to build and launch our Docker image.

./gradlew clean build buildDocker

Response:

:buildDocker FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':buildDocker'.
> Docker execution failed
  Command line [docker push tdziurko/jvm-bloggers:latest] returned:
  unauthorized: access to the requested resource is not authorized

Task buildDocker failed because we don’t have an account on DockerHub or we are not logged there. but if you check local images...

docker images

...you will see that our image with tag latest is ready to use:

REPOSITORY                  TAG                             IMAGE ID            CREATED             VIRTUAL SIZE
tdziurko/jvm-bloggers       latest                          a09f884c5aa9        11 minutes ago      785.6 MB

So we can run it in a Docker container with the following command:

docker run -p 8080:8080 --add-host=database:<your_local_db_host> -e jasypt.encryptor.password="<secretPassword>" -e spring.profiles.active="dev" tdziurko/jvm-bloggers:latest

Again, some fragments need more detailed explanation:

  • —add-host=database:<your_local_db_host>: adds address to /etc/hosts in the container, database var should be the address of our database, e.g. 192.168.0.101
  • -e jasypt.encryptor.password=“ ” : Jasypt password to decrypt some values from *.properties files, -e adds it as env variable to the container
  • -e spring.profiles.active=“dev”: Spring Boot profile. dev is one used to run the application locally

After a few seconds, you should see that our application started and it is running in the Docker container.

Publishing to DockerHub

Having a Docker image available locally is OK as long as you plan to play with it only on your own computer, but because we are aiming for AWS deployment we need to publish our image to DockerHub so it is accessible from any Linux machine.

First need to create an account there (tdziurko in my case) and log in using Docker client so we could publish it.

$ docker login
Username: tdziurko
Password:
Email: tomek@example.com
WARNING: login credentials saved in /Users/tomek/.docker/config.json
Login Succeeded

Now we can rebuild our application to push the image to DockerHub:

./gradlew clean build buildDocker

After a few minutes we will see a message BUILD SUCCESSFUL and if you visit your DockerHub public profile page you should see something similar:

which means that our Docker image with the application is there waiting for deployment to an AWS EC2 machine. But this is material for another blog post :)

Summary

In a few steps, I have described how to add Docker capabilities to your Java project, how to configure your image and publish it in DockerHub. The whole process is not very complicated and lets us prepare the application for deployment on every Linux machine or server supporting Docker.

Unleash the power of your APIs with future-proof API management - Create your account and start your free trial today, brought to you in partnership with 3scale.

Topics:
spring boot ,docker

Published at DZone with permission of Tomasz Dziurko, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}