Docker for spring boot application: Practical approach

We started dockerizing our applications ( since we have been breaking our monolith into microservices ) and have seen that docker is the best way to support a containerized and automated continuous integration with microservices.

In this post, I am going to show the details on how to have a spring boot application created as a docker container and run using docker.
Following are the points we are going to cover

  • Configuring a spring boot application with spotify-maven-docker plugin
  • Dockerfile specification for the application
  • Generation of the docker image
  • Pushing docker image to docker-hub repository
  • Connecting to a running docker container
  • Connecting to a mysql database in host
  • Managing logs in docker container.

Let’s get started and see each one in details on the way.

Configuring a spring boot application

The first and obvious step is to have a spring boot application that needs to be made into a docker image.

I would head to http://start.spring.io to generate a spring boot project with the required dependencies if you don’t have the project already. You can create a sample project from there and open it in maven ( I am currently using Maven and the tutorial would be following the same. There are several other documentations online which handles Gradle as well ).

POM file changes

Open the pom file of the boot application and add the following plugin to the plugins section

<properties>  
   <docker.image.prefix>dockerid</docker.image.prefix>
</properties>
<plugins>
<plugin>
   <groupId>com.spotify</groupId>
   <artifactId>docker-maven-plugin</artifactId>
   <version>0.4.11</version>
   <configuration>
      <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
      <serverId>docker-hub</serverId>
      <dockerDirectory>src/main/docker</dockerDirectory>
      <resources>
         <resource>
            <targetPath>/</targetPath>
            <directory>${project.build.directory}</directory>
            <include>${project.build.finalName}.jar</include>
         </resource>
      </resources>
   </configuration>
</plugin>
</plugins>

If you already have a <properties> and <plugins> section, just add the individual values to the respective sections.

The docker.image.prefix value is the value of the dockerid that you have. If you don’t have one, please go ahead to https://hub.docker.com/ and create one.

Also create a repository in the docker hub with the name as the artifactId.
Eg : if the docker id is ‘mydockerid’ and the artifactId is ‘sample-app’, the docker image name would be
mydockerid/sample-app

Here, we are setting

  1. The image name to the prefix/project artifact id
    Eg: If the docker id is ‘mydockerid’ and the artifactId is ‘sample-app’, the docker image name would be
    mydockerid/sample-app
  2. The targetPath and the build directory.

Dockerfile specification for the application

For the docker plugin to work, we need to provide a Dockerfile in the class path. Create a file ‘Dockerfile‘ in the /src/main/docker folder. Please note that the ‘f’ in the Dockerfile is small and cannot be put as in camel case.

Put the following content in the file.

FROM frolvlad/alpine-oraclejdk8:slim
VOLUME /tmp
#### START OF TIMEZONE RELATED COMMANDS ####
RUN apk update
RUN apk add tzdata
ENV TZ="Asia/Kolkata"
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
#### END OF TIMEZONE RELATED COMMANDS ####
ADD in-payment-0.0.1-SNAPSHOT.jar sample-app.jar
RUN sh -c 'touch /sample-app.jar'
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sample-app.jar" ]

We are doing the following things here

  1. Get the os image with  oracle jdk8 from the docker hub ( We are using the docker image frolvlad/alpine-oraclejdk8:slim which is based on Alpine linux and has very small footprint )
  2. Mount a volume /tmp ( This is where the tomcat base directory will be placed inside the container ).
  3. Set the timezone to Asia/Kolkata region ( You may change it to something of your on. But make sure that its something existing in /usr/share/timezone )
  4. Add the application jar file in the target folder as sample-app.jar file in the container
  5. Touch the jar file so that there will be a modification time ( optional )
  6. Set the java_opts environment variable to empty
  7. Set the entry point for the execution of the container

The TIMEZONE RELATED COMMANDS are to set the timezone of the Docker container. You can remove those if you would like to run the docker container in a UTC timezone. Or you may change the timezone specified in the script to anything that match your region.

Generation of the docker image

This is the point where we are going to generate the docker image for the spring application. But before that, we need to make sure that docker is installed in the system.

Setting up docker

Run the following in the prompt
docker ps

This should list the following output indicating an empty list of docker containers currently running in the system.

selection_263

NOTE 1:
If docker is not installed, please follow the guides from different sources depending on the operating system you are using.
Ubuntu 16.04 installation instructions are available in digital ocean documentation

NOTE 2:
If you are getting any permission denied error for the docker ps command and is only working when you put sudo, we need to do the following

$ sudo usermod -aG docker $(whoami)

This will add the current user to the docker group and to take this in effect, you need to logout and login back. You may repalce $(whoami)  with any username that should be allowed to run docker.

Maven configuration ( For private repository )

If you are using a private repository, then you need to specify the username and password for the repository in the maven global settings.xml file. This is required for successfully pushing the docker image to the repository.

First, find out the maven installation directory. There is a settings.xml file in the conf directory of the maven installation directory or you can copy and paste the settings.xml file to $USER_HOME/.m2/settings.xml file location for user specific configuration.

In either case, open the settings.xml file and put the following content between the <servers> </servers> tags

 <server>
 <id>docker-hub</id>
 <username>dockerid</username>
 <password>your-password</password>
 <configuration>
 <email>your-email</email>
 </configuration>
 </server>

Docker !!

We need to run the following commands in the project folder  to generate the docker image.

$ mvn package
$ mvn docker:build

This would create a docker image with name yourdockerid/artifactId

You can run the docker image in your local system immediately using the following command

$ docker run -p 8080:8080 -t yourdockerid/artifactId

This should start the docker container and you can see the spring boot logs showing up in the terminal and finally indicating the successful startup.

You can access http://localhost:8080/ in the browser to access the application. To get the list of the running docker containers, you can run the following command

$ docker ps

This would list the containers with the details like the id, name etc.

You can stop a container using the following command

$ docker stop

Pushing docker image to docker-hub repository

Now that we have the docker image, we are able to run it locally. But we would like to have this distributed so that anyone with access can pull the image and fire up the container. For that, we need to push the image to the repository we have created.

Run the following maven command

$ mvn docker:push

This will push the latest image to the repository. Please make sure that you have put the details of the repository login information in the maven settings.xml file as specified in the section above.

At this point, you can browse your docker hub and see that the latest push is visible there.

Pulling the docker image and running

Now that the docker image is pushed to the repository, you can have it pulled from any system and run there. If you want to run in a different system do the following:

$ docker login

This will ask for your docker id and password. Provide the details ( the same as the one you provided for the repository). You may also add collaborators and have them pull from your private repository.
Once the login is provided, you can use the docker run command as below

$ docker run -p 8080:8080 -t yourdockerid/artifactId

This would download the docker image to your local system and then run the image as a container.

Connecting to a running docker container

Once you have a container running, we would want to connect to the container using shell and execute commands. We can connect using the following command

$ docker exec -it  bash

If this command is giving errors for bash, you may try the following

$ docker exec -it  /bin/sh

You can get the container id by running the ‘docker ps’ command in the command line which lists all the running docker containers in the system.

This will provide you with a shell ( bash or sh ) and you can execute the commands to list the files and check the network interfaces.

Connecting to a mysql database in host

One of the most common task you would want to do with any application is to connect to the database server. If you have the database accessible in the host system, you can make it available to the docker by having the docker run the networking in host mode.

$ docker run --net=host -p 8080:8080 -t yourdockerid/artifactId

For more information in the docker networking concepts, please take a look at the following links

Please note that making the –net=host will open all the ports in the docker container open to the host system.

Managing logs in docker container.

Logs are a very important aspect of any application and they are the only source of information once deployed in production. We need to have some mechanism to make sure that the logs are available to analyzing.

Docker containers are stateless. Once they are stopped, the entire information is lost ( whether its application data stored in the container or the logs generated). Hence it’s important that we implement a mechanism to retain the logs. There are a couple of ways to do this.

  1. Have the container ship the logs to a remote server ( using Filebeat or similar tools.
  2. Put the files in the host location

We are going to look at the option where  the logs generated are kept in a location in the host. The idea is to have a location in the host mounted to the locations where the container would be storing the applications logs.

Let’s say the application is configured to have the logs generated to /var/app/logs inside the container. We will have the container started with this location mapped to a local location in the host. This will allow the logs to be appended and retained even when the container is stopped or destroyed.

For this to work, the docker container is run with a different syntax.

$ docker run -d -v ~/logs/sample-app:/var/app/logs --net=host yourdockerid/artifactId

This does the following

  1. The -d switch will make the docker container run in the background.
  2. The -v switch says docker to have the /var/app/logs to mount to ~/logs/sample-app of the host system.
  3. The networking is set to host mode.

Final thoughts

I believe that docker based containers are going to be the de-facto standard for containerized application deployment and it’s very imperative to start exploring and converting your applications to a container based deployment architecture ( preferably docker) pretty soon.

We have not yet started putting docker in production, but is using them in test environments and believe that there is much to be learned before we can go ahead with docker in production.  I will keep you all posted with the useful discoveries and information as it comes to me.

Do let me know your queries and thoughts in the comments.

References

regards
S

You may also like...

5 Responses

  1. qtzar says:

    Your Dockerfile adds a couple of extra layers that are making the Docker image bigger than needed. I would look at concatenating the RUN commands for the apt update and install and also add a command to remove the apt indexes as a single line so that your timezone files are all done in a single layer.

    You can also remove the RUN command that does the touch on the added file. It is not needed and doubles the size of the jar added to the docker image as the ADD will create a layer and then then the touch will create a new layer of the same size of the add.

    Reducing the number of layers and the overall image size will make your docker hosts much happier.

    • Hi,
      Thank you so much for the suggestion 🙂
      This is a valuable information. As I said in the post, I am still learning on docker and this seems to be something I should have been careful about.
      It would be great if you could provide the same Dockerfile with the said optimizations. I can update it immediately and comment about this in the post. Also we can update it in our test systems as this is a common format we are using currently.
      Thanks

  1. March 3, 2017

    […] application: The practical way. {$excerpt:n} submitted by /u/microideation [link] [comments] Source: […]

  2. March 6, 2017

    […] We started dockerizing our applications ( since we have been breaking our monolith into microservices ) and have seen that docker is the best way to support a containerized and automated continuous… – Read full story at Hacker News […]

  3. March 6, 2017

    […] have started playing around with docker containers lately and was looking for a solution for container orchestration. I stumbled upon Kubernetes and started […]

Leave a Reply

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