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.
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.
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:
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.
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
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 set
JAVA_HOME and add Maven binary to the
PATH. You can do this by using Docker
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
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.
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 <email@example.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.
docker build -t radcortez/wow-auctions .
-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.
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!