Distribute your applications with Docker Images

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 do 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 my latest application, 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 had 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:

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:

Shell

1

RUN apt-getupdate&&apt-get-yinstall wget git

Install the JDK:

Shell

1

2

3

4

5

6

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&&\

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 DockerENV instruction.

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.

Shell

1

EXPOSE8080

Run the application

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

Shell

1

CMD git pull&&cdbatch&&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.

Complete Dockerfile

Shell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

FROM debian:latest

MAINTAINER Roberto Cortez<radcortez@yahoo.com>

RUN apt-getupdate&&apt-get-yinstall 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&&\

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

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

RUN cdopt&&\

git clonehttps://github.com/radcortez/wow-auctions.gitwow-auctions

WORKDIR/opt/wow-auctions/

RUN mvn clean install&&\

cdbatch&&\

mvn wildfly:start

EXPOSE8080

CMD git pull&&cdbatch&&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.

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:

Run the Image

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!

Related Articles

Comments ( 4 )

I’m not a big fan of pulling the latest code from git during container creation time, but I understand where you’re coming from.

Personally I would prefer to have different tags to clearly identify the versions of your application. I believe it’s a good practice to have your containers behave the same way every time you create them from the same image.

Since these are just sample applications, I’ve done it this way to simplify the distribution. I also didn’t want to mix in the concept of tags, because I think that tags needs a dedicated post to explain them.

Anyway, I do agree with you. If we were working with more serious applications, using tags is mandatory and the behaviour should be the same. I still want to refine the way I’m doing this, so maybe we can have a few conversations, since you have more experience on this area than me 🙂

If you run your own artifact server (Nexus, Artifactory, etc), you can also opt to have Docker pull your versioned, ready-built WAR, JAR, EAR, etc from there instead of building it from code with the Docker build.

Thank you for your comment. Indeed, its a very valid approach. I just didn’t use it, because I don’t have my projects published in an artifact server. Actually, it would be the proper way to do it, since with my way, you need to pre build, to ensure that Maven downloads its dependencies. Or you are going to wait an huge amount of time for the container to start each time.