Docker tutorial – Build Docker image for your Java application

From the last post,we have understood what is container & why do we use containers in general. Just to recap here are some of the key points

  • Containers are an abstraction at the app layer that packages code and dependencies together. Multiple containers can run on the same machine and share the OS kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are typically tens of MBs in size), and start almost instantly.
  • A container image is a lightweight, stand-alone, executable package of a piece of software that includes everything needed to run it: code, runtime, system tools, system libraries, settings.
  • Containers run apps natively on the host machine’s kernel. They have better performance characteristics than virtual machines that only get virtual access to host resources through a hypervisor. Containers can get native access, each one running in a discrete process, taking no more memory than any other executable.

In this post, we are going to take look at how to build Docker image for Java application (typically the steps are same for any type of application).

Step #1. Setup Docker

  1. From the docker site,install the latest version of the docker for your platform.Docker is available in two editions: Community Edition (CE) and Enterprise Edition (EE). Docker Community Edition (CE) is ideal for developers and small teams looking to get started with Docker and experimenting with container-based apps. Docker Enterprise Edition (EE) is designed for enterprise development and IT teams who build, ship, and run business critical applications in production at scale.
  2. Once the installation of docker is over,check the installation by running following command docker run hello-world:

    Docker
    Image- docker hello world
  3. Run docker –version to check the version of the docker you’re running.

    Docker
    Image- check docker version

OK, now we have got the docker setup,next step is to define the docker container.

Step #2. Create Dockerfile for our container

  1. Dockerfile will define what goes on in the environment inside the container. Access to resources like networking interfaces and disk drives is virtualized inside this environment, which is isolated from the rest of the system, so you have to map ports to the outside world, and be specific about what files you want to “copy in” to that environment. So that you can expect that the build of your app defined in this Dockerfile will behave exactly the same wherever it runs.
  2. Common Dockerfile instructions start with RUN, ENV, FROM, MAINTAINER, ADD, and CMD, among others.
    • FROM – Specifies the base image that the Dockerfile will use to build a new image. As an example, we are going to use phusion/baseimage as our base image (this is minimal Ubuntu-based image)
    • MAINTAINER – Specifies the Dockerfile Author Name and his/her email.
    • RUN – Runs any UNIX command to build the image.
    • ENV – Sets the environment variables. For this post, JAVA_HOME is the variable that is set.
    • CMD – Provides the facility to run commands at the start of container. This can be overridden upon executing the docker run command.
    • ADD – This instruction copies the new files, directories into the Docker container file system at specified destination.
    • EXPOSE – This instruction exposes specified port to the host machine.
  3. To begin with create a new folder and then create a file in it named “Dockerfile” with the following content.
    # Dockerfile
    FROM  phusion/baseimage:0.9.17
    MAINTAINER  Author Name [email protected]
    
  4. Once we have the baseimage set,next step is to update the base image with Ubuntu repository by running apt-get -y update command
    RUN echo "deb http://archive.ubuntu.com/ubuntu trusty main universe" > /etc/apt/sources.list
    RUN apt-get -y update
    
  5.  If you want to use python-software-properties, this enables to use add-apt-repository for use later in the process.
    RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -q python-software-properties software-properties-common
    
  6. Install Oracle Java 8
    ENV JAVA_VER 8
    ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
    
    RUN echo 'deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' >> /etc/apt/sources.list && \
        echo 'deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' >> /etc/apt/sources.list && \
        apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C2518248EEA14886 && \
        apt-get update && \
        echo oracle-java${JAVA_VER}-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections && \
        apt-get install -y --force-yes --no-install-recommends oracle-java${JAVA_VER}-installer oracle-java${JAVA_VER}-set-default && \
        apt-get clean && \
        rm -rf /var/cache/oracle-jdk${JAVA_VER}-installer
    
    
  7. Set Oracle Java as the default Java
    RUN update-java-alternatives -s java-8-oracle
    RUN echo "export JAVA_HOME=/usr/lib/jvm/java-8-oracle" >> ~/.bashrc
    
  8. Clean Up when finished
    RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
    
  9. Finally add command for docker’s init system
    CMD ["/sbin/my_init"]
    
  10. Docker file would now be looking like the one below
    # Dockerfile
    
    FROM  phusion/baseimage:0.9.17
    
    MAINTAINER  Author Name <[email protected]>
    
    RUN echo "deb http://archive.ubuntu.com/ubuntu trusty main universe" > /etc/apt/sources.list
    
    RUN apt-get -y update
    
    RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -q python-software-properties software-properties-common
    
    ENV JAVA_VER 8
    ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
    
    RUN echo 'deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' >> /etc/apt/sources.list && \
        echo 'deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' >> /etc/apt/sources.list && \
        apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C2518248EEA14886 && \
        apt-get update && \
        echo oracle-java${JAVA_VER}-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections && \
        apt-get install -y --force-yes --no-install-recommends oracle-java${JAVA_VER}-installer oracle-java${JAVA_VER}-set-default && \
        apt-get clean && \
        rm -rf /var/cache/oracle-jdk${JAVA_VER}-installer
    
    RUN update-java-alternatives -s java-8-oracle
    
    RUN echo "export JAVA_HOME=/usr/lib/jvm/java-8-oracle" >> ~/.bashrc
    
    RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
    
    CMD ["/sbin/my_init"]
    

Step #3. Build Docker Image

Now that we have completed Dockerfile, next step is to build Docker image by docker build command 

docker build -t demo/oracle-java:8 .

Here -t specifies the name of the image. The name demo/oracle-java, and the 8 after the colon, specify the image tag. The tag 8 is used because we are using Java version 8.

Docker build
Image – Docker build command running

 

Docker build complete
Image – Docker build complete

Step #4. Test Docker Image

  1. To test the image, we have built using docker build command, create simple java program to print “Hello World”:
    public class Main
    {     public static void main(String[] args) {
    
            System.out.println("Hello, World");
        }
    }
  2. Execute the following commands to compile the Java file from the current project directory.
    docker run --rm -v $PWD:/app -w /app demo/oracle-java:8 javac Main.java
  3. Execute the compiled Main.class file.
 docker run --rm -v $PWD:/app -w /app demo/oracle-java:8 java Main

Now you should get Hello, World displayed on your terminal.

Step #5. Build Docker container for Maven/Gradle based Java application

In the real world scenario, Java apps would need other applications like Maven/Gradle or Spring MVC on top of the base image. Following is an example of how to add more applications on top of the base image.

  1. Now lets consider,we are going to use gradle as build tool,lets start building the container image from the base.
    FROM demo/oracle-java:8
    
    RUN wget -q https://services.gradle.org/distributions/gradle-3.3-bin.zip \
        && unzip gradle-3.3-bin.zip -d /opt \
        && rm gradle-3.3-bin.zip
    
    ENV GRADLE_HOME /opt/gradle-3.3
    ENV PATH $PATH:/opt/gradle-3.3/bin
    
    

    If you notice, base image is demo/oracle-jdk:8 image we have built this image in our previous example.There is also official openjdk 8 available on the docker repository.

  2. Build the docker image from the above Dockerfile using this command
    $ docker build -t demo/gradle:3.3-jdk-8 .
  3. Now let’s test the gradle image using following command
    $ docker run --rm -v "$PWD":/usr/src/project -w /usr/src/project gradle gradle init --type=java-library

This command should start the gradle daemon & create the directory structure shown below

Starting a Gradle Daemon (subsequent builds will be faster)
:wrapper
:init
Gradle Directory Structure
Image – Gradle Directory Structure

Step #6. Build Docker container for Spring MVC based Java application

For Spring MVC based applications,we would require Tomcat or any other web server.To create new image,we can use the base image as gradle image and install Tomcat on it to run the web application.

  1. Create Dockerfile like the one below
    # Dockerfile
    FROM demo/maven:3.3-jdk-8
    MAINTAINER Author <[email protected]>
    RUN apt-get update && \
        apt-get install -yq --no-install-recommends wget pwgen ca-certificates && \
        apt-get clean && \
        rm -rf /var/lib/apt/lists/*
    ENV TOMCAT_MAJOR_VERSION 8
    ENV TOMCAT_MINOR_VERSION 8.0.11
    ENV CATALINA_HOME /tomcat
    
    
  2. Add commands to install Tomcat
    RUN wget -q https://archive.apache.org/dist/tomcat/tomcat-${TOMCAT_MAJOR_VERSION}/v${TOMCAT_MINOR_VERSION}/bin/apache-tomcat-${TOMCAT_MINOR_VERSION}.tar.gz && \
    	wget -qO- https://archive.apache.org/dist/tomcat/tomcat-${TOMCAT_MAJOR_VERSION}/v${TOMCAT_MINOR_VERSION}/bin/apache-tomcat-${TOMCAT_MINOR_VERSION}.tar.gz.md5 | md5sum -c - && \
    	tar zxf apache-tomcat-*.tar.gz && \
     	rm apache-tomcat-*.tar.gz && \
     	mv apache-tomcat* tomcat
    
    ADD create_tomcat_admin_user.sh /create_tomcat_admin_user.sh
    RUN mkdir /etc/service/tomcat
    ADD run.sh /etc/service/tomcat/run
    RUN chmod +x /*.sh
    RUN chmod +x /etc/service/tomcat/run
    
    EXPOSE 8080
    
    
  3. Finally add command to use baseimage-docker’s init system
    CMD ["/sbin/my_init"]
  4. Create the file to check if Tomcat admin user is created this file would be used in the next step.
    !/bin/bash
    if [ -f /.tomcat_admin_created ]; then
        echo "Tomcat 'admin' user already created"
        exit 0
    fi
    
  5. Add the following script to generate a password.
    PASS=${TOMCAT_PASS:-$(pwgen -s 12 1)}
    _word=$( [ ${TOMCAT_PASS} ] && echo "preset" || echo "random" )
    
    echo "=> Creating an admin user with a ${_word} password in Tomcat"
    sed -i -r 's/<\/tomcat-users>//' ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo "" >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo "=> Done!"
    touch /.tomcat_admin_created
    
    echo "========================================================================"
    echo "You can now configure to this Tomcat server using:"
    echo ""
    echo "    admin:${PASS}"
    echo ""
    echo "========================================================================"
    This file creates the Tomcat admin user. Add one more file in the same directory named as run.sh with following content. This will call the create users file and then reload the Tomcat server.
    !/bin/bash
    
    if [ ! -f /.tomcat_admin_created ]; then
        /create_tomcat_admin_user.sh
    fi
    
    exec ${CATALINA_HOME}/bin/catalina.sh run
    
  6. Build the docker image using following command.
    $ docker build -t demo/spring:maven-3.3-jdk-8 .

Congrats! You’ve successfully built container for your Java application.There is much more to the Docker platform than what was covered here, but now you would have got a good idea of the basics of building containers for an application.

Like this post? Don’t forget to share it!

Additional Resources:

Summary
Docker tutorial - Build Docker image for your Java application
Article Name
Docker tutorial - Build Docker image for your Java application
Description
In this post, we are going to take look at how to build Docker image for Java application
Author
Publisher Name
upnxtblog