Dropwizard Kotlin in a Docker container

Docker is a fantastic way to run micro-services like Dropwizard in a cloud environment. Because the service should be stateless, you can spin up multiple instances of them easily, allowing increased resilience (if one instance becomes unresponsive), simplified no outage deployments (can easily spin up new versions of the software) and much more.

Docker also runs instance in their own isolated container, allowing you to put multiple containers on a single host for greater density and in turn cheaper infrastructure costs and less maintenance. Finally, as a container includes all the underlying software (operating system, JRE etc), it is a more reliable way to ensure that if the code works on your machine, it will work when it’s running on a build agent, deployed out to production and so on as all layers of the application stack will be identical.

The first step is to install Docker on your machine, the best way to do that is to follow the instructions on the Docker website for Mac and Windows. You will also need an account which while is a little frustrating is free and quite painless. Once you have both the software installed an account, you should be able to run docker login and sign in successfully.

Back in our dropwizard-kotlin-app, in the root folder create a new file with the name Dockerfile, note there is no file extension and starts with a capital D. What this is a serious of instructions that Docker uses to build our Dropwizard Kotlin app image. You can see a full list of commands on their website, but thankfully for us we will only use four of them.

The first line is arguable one of the most import, the FROM command instructs Docker what base image you want to build on-top-of, you can think of it as an AMI if you are familiar with AWS. For example if you want to build a container with WordPress installed, you could use FROM wordpress:latest and it would download an image with the required operating system, PHP and WordPress binaries already installed.

As our Dropwizard Kotlin App runs on the Java Runtime environment, all we need is to add FROM openjdk:8-jre but I prefer to use the 8-jre-slim alternative as its a much smaller file size and is quicker to build and push. I am using Java 8 here, but you are free to use any newer version you require and it should (hopefully) work.

The next two lines should be fairly self-explanatory, we will need to copy the jar file of our application onto the image, as well as the configuration file. We are going to copy them across to the /data folder on the image, remembering this container is running separately from your machine, so you don’t need to worry or care if your machine has a /data folder.

ADD target/dropwizard-kotlin-app.jar /data/dropwizard-kotlin-app.jar
ADD src/main/resources/app-config.yml /data/app-config.yml

Now that we have an operating system with a JRE installed, our application executable jar and configuration YAML file on the container, all that is left is to execute it. If you remember from Part 4 of the series, you can do that by running the java -jar <jar file> server <yaml file> command.

CMD java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -jar /data/dropwizard-kotlin-app.jar server /data/app-config.yml

To the eagle-eyed viewers will notice two additional parameters are being passed in, UnlockExperimentalVMOptions and UseCGroupMemoryLimitForHeap. If you want to understand these, you need to learn about how the JRE allocates the heap size for your application. It determines how much to allocate based on the amount of memory available on the machine (25% of the total), but Java doesn’t know you are running in a container! In a docker container you are almost always limited to a subset of the total memory of the host, so if the host has 8gb, it will try and allocate 2gb, but your container might only have 1gb available and will crash. These parameters are a new feature that have been added to the JRE specifically to address this issue.

The final command is not so much a command but a hint to the user regarding what port your application runs on. EXPOSE 8080 tells anyone using this image that you will need to open this port to access your app. Putting it all together, you should have something like the below now.

FROM openjdk:8-jre-slim

ADD target/dropwizard-kotlin-app.jar /data/dropwizard-kotlin-app.jar

ADD src/main/resources/app-config.yml /data/app-config.yml

CMD java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap \
       -jar /data/dropwizard-kotlin-app.jar server /data/app-config.yml

EXPOSE 8080

Once the file is ready to go, you should run a mvn deploy first to ensure the latest executable jar is ready to go, then run docker build -t dropwizard-kotlin-app . to build the image. This is saying three things, first that you want to build the image, secondly you want to tag this image with the name dropwizard-kotlin-app and finally the . indicates that you want to build in the current folder, where the Dockerfile you just created resides.

That should hopefully complete successfully (remembering you needed to have logged in first), so now all that is left is to run it. Again another Docker command is needed docker run -p 8080:8080 -it --rm dropwizard-kotlin-app:latest which tells docker to run the latest version of the dropwizard-kotlin-app image and also to expose port 8080 on the container to port 8080 on your host. The it component tells it to run in an interactive session so you can kill it when you are finished (with control-C) and the –rm tells it to remove the container once you have stopped it. You should be able to visit http://localhost:8080/echo and see the EchoResource that you have created.

Remembering these commands can be a pain, so we are going to create a file named run.sh in the root folder of our application with the below:

#!/usr/bin/env bash
############################
# Usage: run.sh [-d] [-j]
# -d: Run with docker
# -j: Run jar directly
############################

APP_NAME=dropwizard-kotlin-app

echo "Building ${APP_NAME}.jar"
mvn deploy

if [[ "$1" == "-d" ]]; then
 echo "Building ${APP_NAME} docker image"
 docker build -t ${APP_NAME} .
 echo "Running docker image and exposing port 8080"
 docker run -p 8080:8080 -it --rm ${APP_NAME}:latest
else
 echo "Executing jar"
 java -jar target/${APP_NAME}.jar server src/main/resources/app-config.yml
fi

Hopefully it should all make sense, but breaking it down it does two things. Firstly builds the executable jar by running Maven deploy command, then based on passing in a -d argument when you run this script, will either build and run the docker image, or execute the jar directly like we did in step four. So you should be able to run ./run.sh -d or ./run.sh -j to build and run your app after making changes. If you get any errors, make sure the file is executable, you can make it executable by running chmod +x run.sh.

You can view all the source code as well on GitHub now, so if you get stuck you can check there for a working example.

About the Author

Mannan

Mannan is a software engineering enthusiast and has been madly coding since 2002. When he isn't coding he loves to travel, so you will find both of these topics on this blog.

Leave a Reply

Your email address will not be published. Required fields are marked *