Docker allows you to create containers from your applications, for more information see: https://www.docker.io/. Docker can be used for many things, one of the options is to replace virtual machines with Docker containers. This article will explain how Docker can be used to setup Tomcat and deploy a Java application.
Of course there are many tools available to automate the installation of servers and applications. Virtual machines can be used together with tools like Vagrant, Chef, Puppet and many more. But most of them have their own DSL and some are not for free. That makes it harder to use them as developers and operations are unfamiliar with the tools and DSL’s. Docker has some specific commands, but for the installation of applications like the JDK or Tomcat standard OS commands can be used like apt-get, wget etcetera. Also Docker containers require less resources and startup faster than virtual images as this article will show.
Docker can be used to setup Java application servers for instance test and production machines (although for production it is better to wait until release 1). It is also possible to create a build environment with Jenkins, Nexus, Git, Sonar etc. One container should be used for every application to separate concerns.
The advantages of Docker are quite useful to overcome some of the technical challenges of continuous delivery. Versioning of Docker configuration files is quite easy. Another advantage is that environments can be setup from scratch for every deployment because Docker containers are quickly created and started.
It is quite easy to install Docker. Instructions can be found on https://docs.docker.io/en/latest/installation. For this article Ubuntu 13.10 was chosen and the commands to install Docker are shown below.
sudo apt-get update sudo apt-get install linux-image-extra-`uname -r` sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" sudo apt-get update sudo apt-get install lxc-docker sudo docker run -i -t ubuntu /bin/bash
The last step will start a Docker container based on Ubuntu and start bash.
Adding your user to the Docker group
Root access is necessary to work with Docker, but you can also add your user to the docker group. After adding yourself to that group you no longer have to use the ‘sudo’ command.
sudo groupadd docker sudo gpasswd -a your_username docker sudo service docker restart
It is necessary to logout and login, restart Ubuntu or enter the command ‘newgrp docker’ before this works correctly.
Create a Docker container with Java and Tomcat
Create a file called ‘Dockerfile’ and add the content below. This is the configuration to install Java and Tomcat on a Ubuntu Saucy (13.10) Docker container that is provided by Docker. There are a lot of official and user supplied container configurations available at https://index.docker.io/. These containers can be used as the basis for your own container.
FROM ubuntu:saucy # Update Ubuntu RUN apt-get update && apt-get -y upgrade # Add oracle java 7 repository RUN apt-get -y install software-properties-common RUN add-apt-repository ppa:webupd8team/java RUN apt-get -y update # Accept the Oracle Java license RUN echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections # Install Oracle Java RUN apt-get -y install oracle-java7-installer # Install tomcat RUN apt-get -y install tomcat7 RUN echo "JAVA_HOME=/usr/lib/jvm/java-7-oracle" >> /etc/default/tomcat7 EXPOSE 8080 # Download Slashdot homepage RUN mkdir /var/lib/tomcat7/webapps/slashdot RUN wget http://www.slashdot.org -P /var/lib/tomcat7/webapps/slashdot # Start Tomcat, after starting Tomcat the container will stop. So use a 'trick' to keep it running. CMD service tomcat7 start && tail -f /var/lib/tomcat7/logs/catalina.out
Build the container using: ‘docker build –t tomcat7 .’. Do not forget to use the ‘.’ at the end. Instead of ‘tomcat7’ you can pick another name if you want. Now start the container using ‘docker run -p 8080:8080 -d tomcat7’. This command makes sure that port 8080 of the container is forwarded to your local port 8080. After a few seconds the Slashdot page should be available at http://localhost:8080/slashdot/.
Container startup speed
A small test was executed to see how fast the container actually started. This test starts the Docker container and uses ‘wget’ to retrieve a page from within the container.
The commands are placed in a file called ‘dockerSpeed’, the content of the file is shown below:
docker run -p 8080:8080 -d tomcat7 wget localhost:8080/slashdot
The ‘time’ command can be used to see how fast the commands above are executed. Executing ‘time sh dockerSpeed’ gave the following result:
real 0m4.367s user 0m0.011s sys 0m0.008s
A few of these tests showed that the execution times were always between 4 and 5 seconds. That’s really quick if you compare that to starting virtual machines.
Docker creates a container for every command in the Dockerfile. If you have 12 commands in the Dockerfile, then Docker will create 12 containers. This allows the user to select an older container, for instance if you only need a container with Java, you could use that container instead of the one including Tomcat. Luckily only the difference between the containers is stored, which saves quite some diskspace.
Where Docker really wins from virtual machines is if you use multiple containers with the same base. For instance if you have 8 containers each using Ubuntu and Java. You could make a base container with Ubuntu and Java and create containers based on that base image. That way Ubuntu and Java are stored only once in a container, this results in fewer diskspace being used. With virtual machines, you would need that diskspace 8 times as it is not possible to create a common base.
This results in another good point of Docker. Separation of concerns is quite important and can be achieved easily, every application can be deployed in a separate container. So for a buildserver, you could make separate containers for Nexus, Jenkins, Git etcetera.
There are a few options to view the containers. The docker ‘ps’ command shows all active containers. Adding ‘-a’ to the ‘ps’ command also shows the stopped containers. Adding ‘-s’ to the ‘ps’ command shows the size of the containers. The output below from the ‘tomcat7’ container that was created in this article shows that most containers are quite small. Only the containers where bigger things are added like the JDK result in larger containers.
$ docker ps -a -s CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE 3d9bd89b5ace tomcat7:latest /bin/sh -c service t 10 seconds ago Up 10 seconds 0.0.0.0:8080->8080/tcp grave_hawking 51.64 kB fc116050d899 1ed13b7f9eb1 /bin/sh -c #(nop) CM 15 minutes ago Exit 0 drunk_babbage 0 B b788f8373ae7 5eb0489aed66 /bin/sh -c wget http 15 minutes ago Exit 0 sleepy_nobel 4.613 kB f465ff7c6cdf a8febaa9ed55 /bin/sh -c wget http 15 minutes ago Exit 0 hungry_lumiere 121.3 kB d4dee2fd7c2f 1938691cd911 /bin/sh -c mkdir /va 15 minutes ago Exit 0 dreamy_euclid 7 B 56f8bff7cfb9 d2e740750084 /bin/sh -c #(nop) EX 15 minutes ago Exit 0 goofy_turing 0 B 1f8639f1841f 52d8cf48f2f3 /bin/sh -c echo "JAV 15 minutes ago Exit 0 grave_bohr 2.074 kB 4cffeada2f59 8811557c9b1b /bin/sh -c apt-get - 15 minutes ago Exit 0 determined_ptolemy 11 MB e6a24a05efb3 7d4e8d307140 /bin/sh -c apt-get - 16 minutes ago Exit 0 nostalgic_wozniak 450 MB 090b2652f31e 9f35a5c15127 /bin/sh -c echo "ora 16 minutes ago Exit 0 condescending_brattain 2.764 MB 743d10527376 cce7c0072447 /bin/sh -c apt-get - 16 minutes ago Exit 0 sleepy_ritchie 179 kB 9da7cf4f4ca7 cea346235f02 /bin/sh -c add-apt-r 16 minutes ago Exit 0 jovial_engelbart 863 B d8c359bdd163 0dcc4e24ae2f /bin/sh -c apt-get - 17 minutes ago Exit 0 jolly_albattani 33.06 MB 7b184e8b6ece ubuntu:13.10 /bin/sh -c apt-get u 17 minutes ago Exit 0 trusting_shockley 25.18 MB
Another option is to view the images tree from Docker, the output for the ‘tomcat7’ container is shown below.
$ docker images -tree ??511136ea3c5a Virtual Size: 0 B ??1c7f181e78b9 Virtual Size: 0 B ??9f676bd305a4 Virtual Size: 178 MB Tags: ubuntu:13.10, ubuntu:saucy ??0dcc4e24ae2f Virtual Size: 215.6 MB ??cea346235f02 Virtual Size: 249 MB ??cce7c0072447 Virtual Size: 249 MB ??9f35a5c15127 Virtual Size: 249.2 MB ??7d4e8d307140 Virtual Size: 251.9 MB ??8811557c9b1b Virtual Size: 702.3 MB ??52d8cf48f2f3 Virtual Size: 713.7 MB ??d2e740750084 Virtual Size: 713.7 MB ??1938691cd911 Virtual Size: 713.7 MB ??a8febaa9ed55 Virtual Size: 713.7 MB ??5eb0489aed66 Virtual Size: 713.8 MB ??1ed13b7f9eb1 Virtual Size: 713.8 MB ??f846774ccd37 Virtual Size: 713.8 MB Tags: tomcat7:latest
Deploy a Java application in Tomcat
The example above used a simple HTML file from Slashdot. Of course that’s not enough, we want to deploy Java applications! That can be done quite easily, just add the following line to the Dockerfile just below the ‘wget’ command for Slashdot.
RUN wget http://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war -P /var/lib/tomcat7/webapps
Build a Docker container based on the new Dockerfile and run the container. Browsing to http://localhost:8080/sample/ will show the sample Tomcat web application.
Some useful Docker commands
Although Docker basics are fairly simple it still has a lot of options and useful commands.
Testing a bit with Docker will result in quite some containers. Stopping and starting them one by one is not really convenient. The following commands can be used to stop and remove all containers
docker stop $(docker ps -a -q) docker rm $(docker ps -a -q)
Sometimes you create a container using a Dockerfile but then you realize the container is not working properly. With the command below you can login to the container and see what is going wrong.
docker run -i -t tomcat7 /bin/bash