Spring Boot and Docker

Summary

My toy angular/rest/spring project, HIIT (high intensity interval training), was something I wanted to package inside a docker container that could run on any machine, regardless of the presence of a JVM (especially Java8). I decided to try out the maven targets for packaging in a docker image – the result was VERY easy to do.

Background

Docker is something I’ve been slow to warm to. Historically my argument against it is that it was a quick-and-dirty solution (and I felt dirtier than it has to be) vs. standard package management like rpm/deb files. However, as I’ve played with Docker more seriously and thought about the troubles it digs people out of I’ve changed my tune. Pros that docker brings include not worrying about the dependencies that make your app run (i.e. the jvm) and being able to allow parameters/shared volumes to run multiple containers side-by-side with nearly zero overhead. When comparing this to standard package management this just isn’t possible.

My friend Blake also said something to me that really resonated, he said think of a docker image as your application’s “executable”. This instantly clicked with me. An executable is something that can be adjusted with command-line switches and is fully self contained (let’s not get too pedantic about dynamic libraries). It’s can be run many times and the switches give it a different ‘profile’. Once I started thinking about Docker images this way, it made me want to do more with Docker since it gives so much flexibility AND given its immutable nature (i.e. an image can never be changed, it is frozen in time just as git freezes a repo in time at each commit) Docker allows multiple instances to be running quite easily and unaware of each other.

How To

The Spring Blogs given a good Docker tutorial, all I did was apply the tutorial to my app.

First

First I made sure docker was installed on my machine (Ubuntu 15.10). This was as simple as running apt-get

sudo apt-get install docker

Second

After docker was installed I proceeded to add details to my pom.xml. You can see my branch here where I have the Dockerfile and changes in the pom.xml. There’s some other noise with my update of Spring Boot in the same branch, but generally speaking the interesting changes are

Dockerfile

FROM java:8
VOLUME /tmp
ADD com.basilio.hiit-0.0.1-SNAPSHOT.jar hiit.jar
RUN bash -c 'touch /hiit.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-Dspring.profiles.active=hsqldb","-jar","/hiit.jar"]

pom.xml

..
<properties>
  <docker.image.prefix>jimbasilio</docker.image.prefix>
</properties>
..
<!-- Package as a docker image -->
<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>0.2.3</version>
    <configuration>
        <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
        <dockerDirectory>src/main/docker</dockerDirectory>
        <resources>
            <resource>
                <targetPath>/</targetPath>
                <directory>${project.build.directory}</directory>
                <include>${project.build.finalName}.jar</include>
            </resource>
        </resources>
    </configuration>
</plugin>

The Dockerfile grabs the java:8 docker image from the docker repo, creates a shared volume in /tmp (where tomcat will write to), ensures the jarfile has a fresh datestamp on a docker rebuild, and declares the entrypoint to run (which means what switches I want the docker “exe” to automatically use). You can see I wanted to run in the -Dspring.profiles.active=hsqldb profile but I could have also passed parameters into docker to set the active profile.

Third

Now when I issue the docker image build via maven

[jim@galago~/projects/HIIT (master)]$ mvn package docker:build

you can see the docker image being built. From here I can see my image and then run my image and start my container

[jim@galago~/projects/HIIT (master)]$ sudo docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
jimbasilio/com.basilio.hiit   latest              c3360feee95d        2 minutes ago       714.1 MB
java                          8                   d4849089125b        3 weeks ago         642 MB
[jim@galago~/projects/HIIT (master)]$ sudo docker run -p 8080:8080 jimbasilio/com.basilio.hiit
....loads of spring boot stuff printed to stdout to let you know what's going on

Now, point your browser to http://localhost:8080 you’ll see the hiit app (in all its skeleton glory) show up. Also, notice the console out messages being emitted from the docker container that’s being executed.

I can also run another container on port 8081 and it will happily startup

[jim@galago~/projects/HIIT (master)]$ sudo docker run -p 8081:8080 jimbasilio/com.basilio.hiit

Now point your browser to http://localhost:8081, you’ll see console message from hits to that port. The cool thing is the Spring Boot application has no idea it’s serving on port 8081, because it’s really bound to the docker container’s port 8080. At this point I could go more advanced and put a load balancer in front of both port 8080 and 8081 and optimally flood my CPUs (although realistically on 1 machine java will already do this quite happily by threaded connections through Tomcat).

Bottom Line

Docker is something that takes some getting used to. Thinking about the final Docker image as an ‘executable’ AND also an immutable (and comprised of) series of inner containers with each building on the previous immutable image helps things (at least it will help things once they start to click).

Being able to spin-up your ‘exe’ wherever you are (think dumb cloud box), not having to worry about what the host system has installed, and the very low overhead that containers have gives you one more tool in the toolbox to manage your application.

More examples of Docker usage are a maven docker image so you don’t have to worry about your CI builds (i.e. Jenkins) having maven installed. You can simply  have Jenkins start the maven docker container, share a volume with your sourcecode, compile/run tests, then the stdout is checked for success.

Docker is an endless thought experiment which really opens a lot of doors for creative (and reproducible) solutions.

Good luck and ENJOY!

Leave a Reply

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