Since I started this blog, I had the need to develop a couple of sample applications to showcase some of the topics I have been covering. Usually, some kind of Java EE application that needs to be deployed in a Java EE container. Even by providing instructions on how to setup the environment, it can be tricky for a newcomer. A few of my readers don’t have a Java EE container available in their local machine. Some don’t even have Java Development Kit installed. If I could provide the entire environment set up for you and you only need to execute it somehow, wouldn’t that be great? I certainly think so! Instead of distributing only the application, also distribute the environment needed for the application to run. We can do that using Docker.

A couple of weeks ago, I wrote the post Get into Docker. Now, this post is going to continue to explore one of the most interesting Docker features: the Docker Images. This is the answer to provide my reader with a complete environment with everything ready to run.

Docker Image

A Docker Image is a read only template used to create the Docker containers. Each image is built with a series of layers composing your final image. If you need to distribute something using Ubuntu and Apache, you start with a base Ubuntu image and add Apache on top.

Create a Docker Image file

I’m going to use one of my latest applications, the World of Warcraft Auction House, to show how we can package it into a Docker Image and distribute it to others. The easiest way is to create a Dockerfile. This is a simple plain text file that contains a set of instructions that tells Docker how to build our image. The instructions that you can use are well defined and straightforward. Check the Dockerfile reference page for a list of possible instructions. Each instruction adds a new layer to your Docker Images. Usually the Dockerfile is named Dockerfile. Place it in a directory of your choice.

Base Image

Every Dockerfile needs to start with a FROM instruction. We need to start from somewhere, so this indicates the base image that we are going to use to build our environment. If you were building a Virtual Machine you also have to start from somewhere, and you have to start by picking up the Operating System that you are going to use. With the Dockerfile it’s no different. Let’s add the following to the Dockerfile:

FROM debian:latest

Our base image will be the latest version of Debian, available here: Docker Hub Debian Repo.

Add what you need

The idea here is to build an environment that checkouts the code, build and execute the World of Warcraft Auction House sample. Can you figure out what you need? The JDK of course, to compile and run and Maven to perform the build. But these are not enough. You also need the Git command line client to checkout the code. For this part you need to know a little bit about Unix shell scripting.

Since we need to install the JDK and Maven, we need to download them into our image from somewhere. You can use the wget command to do it. But wget is not available in our base Debian image so we need to install it first. To run shell commands we use the RUN instruction in the Dockerfile.

Install wget:

RUN apt-get update && apt-get -y install wget git

Install the JDK:

RUN wget --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u40-b25/jdk-8u40-linux-x64.tar.gz &&
    mkdir /opt/jdk &&
    tar -zxf jdk-8u40-linux-x64.tar.gz -C /opt/jdk &&
    update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_40/bin/java 100 &&
    update-alternatives --install /usr/bin/javac javac /opt/jdk/jdk1.8.0_40/bin/javac 100 &&
    rm -rf jdk-8u40-linux-x64.tar.gz

Install Maven:

RUN wget http://mirrors.fe.up.pt/pub/apache/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz &&
    tar -zxf apache-maven-3.2.5-bin.tar.gz -C /opt/ &&
    rm -rf apache-maven-3.2.5-bin.tar.gz

We also need to have Java and Maven accessible from anywhere in our image. As you would do to your local machine when setting the environment, you need to setJAVA_HOME and add Maven binary to the PATH. You can do this by using Docker ENV instruction.

ENV JAVA_HOME /opt/jdk/jdk1.8.0_40/

ENV PATH /opt/apache-maven-3.2.5/bin:$PATH

Add the application

Now that we have the required environment for the World of Warcraft Auction House, we just need to clone the code and build it:

RUN cd opt &&
    git clone https://github.com/radcortez/wow-auctions.git wow-auctions

WORKDIR /opt/wow-auctions/

RUN mvn clean install &&
    cd batch &&
    mvn wildfly:start

We also want to expose a port, so you can access the application. You should use the listening http port of the application server. In this case, it’s 8080. You can do this in Docker with the EXPOSE instruction:

I had to use a little trick here. I don’t want to download and install the application server, so I’m using the embedded Wildfly version of the Maven plugin. Now, as I told you before, each instruction of the Dockerfile adds a new layer to the image. In here I’m forcing a start and stop of the server, just for Maven to download the required dependencies and have them available in the image. If I didn’t do this, whenever I wanted to run the image, I would have to download all the server dependencies and the startup of the image would take considerably longer.

EXPOSE 8080

Run the application

The final instruction should be a CMD to set the command to be executed when running the image:

CMD git pull && cd batch && mvn wildfly:run

In this case we want to make sure we are using the latest code, so we do a git pull and then run the embedded Wildfly server. The deploy configuration has been already set up in Wildfly Maven plugin.

FROM debian:latest
MAINTAINER Roberto Cortez <radcortez@yahoo.com>

RUN apt-get update && apt-get -y install wget git

RUN wget --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u40-b25/jdk-8u40-linux-x64.tar.gz &&
    mkdir /opt/jdk &&
    tar -zxf jdk-8u40-linux-x64.tar.gz -C /opt/jdk &&
    update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_40/bin/java 100 &&
    update-alternatives --install /usr/bin/javac javac /opt/jdk/jdk1.8.0_40/bin/javac 100 &&
    rm -rf jdk-8u40-linux-x64.tar.gz

ENV JAVA_HOME /opt/jdk/jdk1.8.0_40/

RUN wget http://mirrors.fe.up.pt/pub/apache/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz &&
    tar -zxf apache-maven-3.2.5-bin.tar.gz -C /opt/ &&
    rm -rf apache-maven-3.2.5-bin.tar.gz

ENV PATH /opt/apache-maven-3.2.5/bin:$PATH

RUN cd opt &&
    git clone https://github.com/radcortez/wow-auctions.git wow-auctions

WORKDIR /opt/wow-auctions/

RUN mvn clean install &&
    cd batch &&
    mvn wildfly:start

EXPOSE 8080

CMD git pull && cd batch && mvn wildfly:run

Build the Dockerfile

To be able to distribute your image, you need to build your Dockerfile. What this is going to do, is to read every instruction, execute it and add a layer to your Docker Images. You only need to do this once, unless you change your Dockerfile. The CMD instruction is not executed in the build, since it’s only used when you are actually running the image and executing the container.

To build the Dockerfile, I use the following command in the directory containing your Dockerfile:

docker build -t radcortez/wow-auctions .

The -t radcortez/wow-auctions is to tag and name the image I’m building. You should use the format user/name. You should use the same user name that you register with Docker Hub.

Pushing the Image

Docker Hub is a Docker Image repository. It’s the same concept of Maven repositories for Java libraries. Download or upload images and you are good to go. The Docker Hub already contains a huge number of images ready to use, from simple Unix distributions, to full blown application servers.

We can now pick the image we build locally and upload it to Docker Hub. This will allow anyone to download and use this image. We can do it like this:

docker push radcortez/wow-auctions

Depending on the image size, this can take a few minutes.

Run the Image

Finally to run the image and the container we execute:

docker run -it --name wow-auctions -p 8080:8080 radcortez/wow-auctions

Since I’ve built the image locally first, this will run the CMDradcortez/wow-auctions. Just by using the above command, the image is going to be downloaded and executed in your environment.

Conclusion

With Docker, is possible to distribute your own applications and have the required environment for the application to run properly created by you. It’s not exactly trivial, since you need some knowledge of Unix, but it’s shouldn’t be a problem.

My main motivation to use Docker here, was to simplify the distribution of my sample applications. It’s not unusual to receive a few reader emails asking for help to set up their environment. Sure, in this way you now have to install Docker too, but that’s the only thing you need. The rest, just leave it to me now!