Dockerizing a Spring Boot App

Photo by Ian Taylor on Unsplash

Dockerizing a Spring Boot App

Your first Dockerfile

Hey All. Hope everyone's doing fine.

Today we'll look into a very minimalistic and simple approach to convert a basic spring boot app to a dockerized one. And by any means, this is not a prod-ready configuration. Just a dev-ready one.

To start with, we get a Spring Boot application created from the official Spring initializr with the package manager of your choice (Gradle/Maven).

Once you have the application all set up and dependencies resolved, let's start working on the actual task of the article, which is Dockerizing the app.

To achieve this, let's begin with the creation of a file named Dockerfile. Of course, you can give any name you like, but that requires a bit more work to specify the file if we change the name. So, for now, let's call it Dockerfile. No extensions. No file type. Nothing. Just "Dockerfile". This you can create at the root of your project which should be right where you have your pom.xml/build.gradle.

Now let's start filling our newly created Dockerfile. Now, remember, Dockerfile is nothing but just a set of instructions to set up your project on an absolutely empty computer(just an analogy for making it easier to visualize). So how would you setup your project on a computer/hard disk that has absolutely nothing on it? Not even an OS.

Let's trace down the steps to achieve it.

  1. Install an OS of our choice.
  2. Get the set of software needed to execute instructions from our project which in our case is JDK & Gradle/Maven.
  3. Create a directory to save our project for the sake of tidiness.
  4. Create our project in that directory.
  5. Build the project.
  6. Execute the startup command.

There's a plethora of stuff you can achieve by containerizing/dockerizing your app but these are the most basic steps that we're going to cover in this article. Here's the Dockerfile that achieves all the above steps discussed

FROM gradle:jdk18

WORKDIR /app

COPY . .

RUN ["./gradlew", "clean", "build"]

ENTRYPOINT ["java","-jar","build/libs/dockerdemo-0.0.1-SNAPSHOT.jar"]

Now that we have our Dockerfile ready, what exactly does it mean? To answer that, let's see what each line in the file means.

FROM gradle:jdk18

This tells Docker to pull a particular image from the Docker hub which is the official repository to get prebuilt images or upload your own. But what is that syntax? All it's saying is "Build an image FROM the base image specified(gradle:jdk18). This means we're going to use an already created image that has Gradle installed over a flavor of Linux with a tag named "jdk18" which is kind of self-explanatory. Will have a separate post on Docker images and naming conventions. For now, this should suffice. You can read more about this image at Gradle Docker Hub Page.

WORKDIR /app

Once the docker image has an OS & base software ready, we talked about creating a directory where all our work is going to be saved. That's achieved by WORKDIR here. WORKDIR is basically like mkdir and cd combined.

COPY . .

Now, this is where the magic begins to happen. This copy instruction works exactly the same as every other copy instruction. Copies content from source to target. Now, both the source and the target are specified as dot here which means current directory. So what this instruction does is that it picks up the contents from current directory where Dockerfile is read from on your local system and copies them over to the WORKDIR inside our docker image.

RUN ["./gradlew", "clean", "build"]

After getting the code in place, we want to make sure our app is built fine. To do that, we execute the build command. In my case, I used gradle. For maven, we're going to have a different command but make sure it's in same format. RUN ["mvn", "clean", "install"]

ENTRYPOINT ["java","-jar","build/libs/dockerdemo-0.0.1-SNAPSHOT.jar"]

This, as the name suggests, is what initializes our application and starts running it INSIDE THE DOCKER CONTAINER. As of now, you can't access your application if you try to do the standard localhost:8080 thing or whatever port you've chosen for your app.

This explains the structure of our Dockerfile. But what do we do with it once we have created it?

The answer is you tell Docker to read this file and execute the instructions specified in it to create an image out of it. To achieve that, we use a simple command:

docker build .

This is going to build an image out of the code and store it on your system and return you a hash of the image. This hash you need to copy to get the image up and running which in turn is called a Container.

To run the container, a very simple command could be

docker run -it -p 8080:8080 <your_hash> sh

This command tells the Docker daemon to spin up the image with the hash provided and expose port 8080 from inside the container to the 8080 of our local system via that -p flag.

Now let's take a look at localhost:8080 and voila!! You should be able to find our service up and running there.

P.S. - This is just a very basic and unoptimized version of Dockerfile and shouldn't be used in actual development environments. This was intended just to give a taste of how simple it is to dockerize our apps.

That was all for today. Stay tuned for a better and optimized version of this Dockerfile and maybe a few new tricks to make our apps cloud-ready. ;)

Till then, stay safe. Take care. :)