Introduction
I'm going to
- extend the official Dockerfile for Tomcat
- build a new image
- launch a container from the modified image
- deploy and test a RESTful Web Service onto this container
Apache Tomcat
A docker search shows me the most popular (and official) Docker Tomcat container:
$ sudo docker search tomcat [sudo] password for craig: NAME DESCRIPTION STARS OFFICIAL AUTOMATED tomcat Apache Tomcat is an open source implementa... 103 [OK] tutum/tomcat Tomcat image - listens in port 8080. For t... 38 [OK] consol/tomcat-7.0 Tomcat 7.0.57, 8080, "admin/admin" 12 [OK] consol/tomcat-8.0 Tomcat 8.0.15, 8080, "admin/admin" 9 [OK] consol/tomcat-6.0 Tomcat 6.0.43, 8080, "admin/admin" 6 [OK] consol/tomcat-4.1 Tomcat 4.1.40, 8080, "admin/admin" 4 [OK] consol/tomcat-5.0 Tomcat 5.0.30, 8080, "admin/admin" 4 [OK] consol/tomcat-5.5 Tomcat 5.5.36, 8080, "admin/admin" 4 [OK] consol/tomcat-3.3 Tomcat 3.3.2, 8080, "admin/admin" 4 [OK] readytalk/tomcat-native Debian backed Tomcat + Tomcat Native Library 3 [OK] malderhout/tomcat Tomcat7 with OpenJDK7 on CentOS7 3 [OK] dordoka/tomcat Ubuntu 14.04, Oracle JDK 8 and Tomcat 8 ba... 3 [OK] meirwa/spring-boot-tomcat-mysql-app a sample spring-boot app using tomcat and ... 2 [OK] h2000/docker-tomcat-youtrack Dockerfile for youtrack to run under tomcat. 1 [OK] nicescale/tomcat Tomcat service for NiceScale. http://nices... 1 [OK] dmean/liferay-tomcat Debian + Liferay CE Tomcat 1 [OK] atomi/tomcat 0 [OK] mminke/apache-tomcat A Docker image which contains the Apache T... 0 [OK] ericogr/tomcat Tomcat 8.0.21, 8080, "docker/docker" 0 [OK] holmes/tomcat 0 [OK] paulkling/tomcat 0 [OK] dynamind/tomcat 0 [OK] fabric8/tomcat-8.0 runs Apache Tomcat 8.0 with jolokia enable... 0 [OK] learninglayers/tomcat 0 [OK] dmglab/tomcat CentOS 7 based tomcat installation 0 [OK]
The official site describes the supported tags:
I currently work with version 7, so I'll be using tomcat:7.
I'm new to Docker (at the time of this article) and hestitate to call out "best practices". What I'm abou to describe seems like a good practice, and I'll happily listen to any contrary opinion. For each Docker container I plan to launch, I prefer to create my own Dockerfile and extend the image. It's entirely possible that I won't ever extend the image, and will simply use it as-is. But building my own image from the target image seems like a logical way to use and extend the work of others in a consistent fashion.
In this case, I'm going to start by creating a simple Dockerfile with a single line:
FROM tomcat:7-jre7 MAINTAINER "Craig Trim <craigtrim@gmail.com>"
I'm going to build this image using this command:
$ sudo docker build -t craig/tomcat .
One advantage to this immediate extension is that I simplify my environment. Eventually, I'll be using containers for Eclipse, MySQL and other apps. I can give each a simplified namespace and image name. On a project, I might choose the project codename as the container namespace. I've also simplified the tag name. These are trivial changes, and it could be argued that the lack of precision obscures details that are important. I would suggest that on a large team with multiple developers this approach offers some advantages. A common namespace, with a simplified image name and tag, would make it eaiser to direct team members toward using official project images.
As an example, I offer this:
| tomcat:7-jre7 | ns/tomcat |
| mysql:5.6.23 | ns/mysql |
| fgrehm/eclipse:v4.4.1 | ns/eclipse |
ns corresponds to a namespace every team member would understand. Launching a container becomes a matter of remembering the project codename (namespace) and the app name. Nothing else.
Running Tomcat
This command will run Tomcat and expose the container's port 8080 on the host's port of 8080:
$ sudo docker run -p 8080:8080 craig/tomcat
If I wanted to launch additional containers from this same image, I could just change
$ sudo docker run -p 8081:8080 craig/tomcat
I can test the running instance on my host:
Extending the Dockerfile
I'm going to extend the Dockerfile to support a configuration that will permit automated deployments from Maven.
I need to add a settings.xml file and update the tomcat-users.xml file. A simplified form of each file is given
tomcat-users.xml:
<?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager-gui"/> <role rolename="manager-gui"/> <role rolename="manager-script"/> <user username="craig" password="password" roles="manager,manager-gui,manager-script" /> </tomcat-users>
settings.xml:
<?xml version="1.0" encoding="UTF-8"?> <settings> <servers> <server> <id>TomcatServer</id> <username>craig</username> <password>password</password> </server> </servers> </settings>
I place each of these files in the same directory as my Dockerfile.
The Dockerfile is updated to this:
FROM tomcat:7-jre7 MAINTAINER "Craig Trim <craigtrim@gmail.com>" ADD settings.xml /usr/local/tomcat/conf/ ADD tomcat-users.xml /usr/local/tomcat/conf/
The configuration files are added to the correct directories when the image is built. Any container launched from this image will now contain these files.
Rebuilding the Image
The image is rebuilt in the same manner we initially built it:
$ sudo docker build -t craig/tomcat . Sending build context to Docker daemon 5.632 kB Sending build context to Docker daemon Step 0 : FROM tomcat:7-jre7 ---> 77eb038c09d1 Step 1 : MAINTAINER "Craig Trim <craigtrim@gmail.com>" ---> Using cache ---> cadc51a3054c Step 2 : ADD settings.xml /usr/local/tomcat/conf/ ---> Using cache ---> 5009ba884f1f Step 3 : ADD tomcat-users.xml /usr/local/tomcat/conf/ ---> Using cache ---> 33917c541bb5 Successfully built 33917c541bb5
We can view the history of image:
$ sudo docker history craig/tomcat IMAGE CREATED CREATED BY SIZE 33917c541bb5 4 hours ago /bin/sh -c #(nop) ADD file:c1d08c42d5808537b4 1.761 kB 5009ba884f1f 4 hours ago /bin/sh -c #(nop) ADD file:5dd8f0f6d0cd64de3c 212 B cadc51a3054c 4 hours ago /bin/sh -c #(nop) MAINTAINER "Craig Trim <cra 0 B 77eb038c09d1 3 weeks ago /bin/sh -c #(nop) CMD [catalina.sh run] 0 B a96609fc8364 3 weeks ago /bin/sh -c #(nop) EXPOSE map[8080/tcp:{}] 0 B ca99125fbf51 3 weeks ago /bin/sh -c curl -SL "$TOMCAT_TGZ_URL" -o tomc 13.63 MB e7ca14a4280a 3 weeks ago /bin/sh -c #(nop) ENV TOMCAT_TGZ_URL=https:// 0 B eac866e259d8 3 weeks ago /bin/sh -c #(nop) ENV TOMCAT_VERSION=7.0.59 0 B d391d657b53a 3 weeks ago /bin/sh -c #(nop) ENV TOMCAT_MAJOR=7 0 B 7b323fd1e0d3 3 weeks ago /bin/sh -c gpg --keyserver pool.sks-keyserver 113.9 kB 4412b8a11fb6 3 weeks ago /bin/sh -c #(nop) WORKDIR /usr/local/tomcat 0 B b4ec9d590927 3 weeks ago /bin/sh -c mkdir -p "$CATALINA_HOME" 0 B 681b802059fe 3 weeks ago /bin/sh -c #(nop) ENV PATH=/usr/local/tomcat/ 0 B 11b245da4142 3 weeks ago /bin/sh -c #(nop) ENV CATALINA_HOME=/usr/loca 0 B 44faa7b2809f 3 weeks ago /bin/sh -c apt-get update && apt-get install 164.5 MB 42c3653e1b26 3 weeks ago /bin/sh -c #(nop) ENV JAVA_DEBIAN_VERSION=7u7 0 B 45ff981e92b4 3 weeks ago /bin/sh -c #(nop) ENV JAVA_VERSION=7u75 0 B 5e9b188bc82c 3 weeks ago /bin/sh -c apt-get update && apt-get install 676 kB 1073b544a1cb 3 weeks ago /bin/sh -c apt-get update && apt-get install 44.34 MB 50ec2d202fe8 3 weeks ago /bin/sh -c #(nop) CMD [/bin/bash] 0 B 3b3a4796eef1 3 weeks ago /bin/sh -c #(nop) ADD file:fb7c52fc8e65391715 122.8 MB 511136ea3c5a 21 months ago 0 B
The changes I have made are shown as occuring 4 hours ago. I can now launch a container from this modified image and proceed to test an automated deployment.
Deploying to Tomcat
This is properly the subject of another tutorial, but the best way to test our Tomcat installation is to deploy a WAR file.
I've created a simple JEE project using Maven with this structure:
$ tree . +-- pom.xml +-- src ¦ +-- main ¦ ¦ +-- java ¦ ¦ +-- com ¦ ¦ +-- trimc ¦ ¦ +-- blogger ¦ ¦ +-- samplewebapp ¦ ¦ +-- CorsFilter.java ¦ ¦ +-- TestREST.java ¦ +-- test ¦ +-- java ¦ +-- com ¦ +-- trimc ¦ +-- blogger ¦ +-- samplewebapp +-- target ¦ +-- classes ¦ ¦ +-- com ¦ ¦ +-- trimc ¦ ¦ +-- blogger ¦ ¦ +-- samplewebapp ¦ ¦ +-- CorsFilter.class ¦ ¦ +-- TestREST.class ¦ ¦ +-- TestREST$Result.class ¦ +-- generated-sources ¦ ¦ +-- annotations ¦ +-- m2e-wtp ¦ ¦ +-- web-resources ¦ ¦ +-- META-INF ¦ ¦ +-- MANIFEST.MF ¦ ¦ +-- maven ¦ ¦ +-- com.trimc.blogger.samplewebapp ¦ ¦ +-- trimc-samplewebapp ¦ ¦ +-- pom.properties ¦ ¦ +-- pom.xml ¦ +-- maven-archiver ¦ ¦ +-- pom.properties ¦ +-- maven-status ¦ ¦ +-- maven-compiler-plugin ¦ ¦ +-- compile ¦ ¦ ¦ +-- default-compile ¦ ¦ ¦ +-- createdFiles.lst ¦ ¦ ¦ +-- inputFiles.lst ¦ ¦ +-- testCompile ¦ ¦ +-- default-testCompile ¦ ¦ +-- inputFiles.lst ¦ +-- test-classes ¦ +-- trimc-samplewebapp-1.0.0 ¦ ¦ +-- META-INF ¦ ¦ ¦ +-- MANIFEST.MF ¦ ¦ +-- WEB-INF ¦ ¦ +-- classes ¦ ¦ ¦ +-- com ¦ ¦ ¦ +-- trimc ¦ ¦ ¦ +-- blogger ¦ ¦ ¦ +-- samplewebapp ¦ ¦ ¦ +-- App.class ¦ ¦ ¦ +-- CorsFilter.class ¦ ¦ ¦ +-- TestREST.class ¦ ¦ ¦ +-- TestREST$Result.class ¦ ¦ +-- lib ¦ ¦ ¦ +-- asm-3.1.jar ¦ ¦ ¦ +-- genson-1.2.jar ¦ ¦ ¦ +-- jersey-core-1.9.jar ¦ ¦ ¦ +-- jersey-server-1.9.jar ¦ ¦ +-- web.xml ¦ +-- trimc-samplewebapp-1.0.0.war +-- WebContent +-- META-INF ¦ +-- MANIFEST.MF +-- WEB-INF +-- lib +-- web.xml 48 directories, 26 files
This plugin (in pom.xml) specifies deployment information corresponding to both the exposed port and the username and password in the tomcat configuration files:
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <url>http://localhost:8080/manager/text</url> <server>TomcatServer</server> <path>/sample</path> <username>craig</username> <password>password</password> </configuration> </plugin>
Using Maven to deploy to Tomcat:
$ mvn tomcat7:deploy [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Test Runtime 1.0.0 [INFO] ------------------------------------------------------------------------ [INFO] *** SNIP *** [INFO] --- tomcat7-maven-plugin:2.2:deploy (default-cli) @ sandbox-web2 --- [INFO] Deploying war to http://localhost:8080/test Uploading: http://localhost:8080/manager/text/deploy?path=%2Ftest Uploaded: http://localhost:8080/manager/text/deploy?path=%2Ftest (1352 KB at 18512.6 KB/sec) [INFO] tomcatManager status code:200, ReasonPhrase:OK [INFO] OK - Deployed application at context path /test [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.495 s [INFO] Finished at: 2015-03-31T19:08:12-07:00 [INFO] Final Memory: 15M/506M [INFO] ------------------------------------------------------------------------
and the Tomcat log gives this:
Apr 01, 2015 2:08:12 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate INFO: Initiating Jersey application, version 'Jersey: 1.9 09/02/2011 11:17 AM' Apr 01, 2015 2:08:12 AM org.apache.catalina.startup.HostConfig deployWAR INFO: Deployment of web application archive /usr/local/tomcat/webapps/test.war has finished in 826 ms
and the all important echo output:
Conclusion
So what's the big deal?
Sure, we didn't have to install Tomcat; we launched a container that was built from an image described in a Dockerfile. That might come across as seeming more difficult than simply downloading Tomcat, unzipping it and running the startup script.
There's a few advantages that come to mind:
- Not every application is as easy to install as Tomcat
- Nearly every application requires additional configuration once installed
Using Docker in this capacity is similar to Vagrant/Puppet/Chef/Ansible/etc. We write a script that describes our environment and a build tool stands up the environment automatically. Docker has some clear advantages here -- it's much more lightweight than using a full-fleged VM. Using the entire VM stack for Tomcat may be overkill in many situations. Considering many developers work on laptops, it may not be possible to run multiple VMs successfully.
This brings up a third advantage of Docker: We can launch multiple containers from the same image. This is nowhere nearly as resource intensive as launching multiple VMs, and the startup time with Docker is nearly instantaneous.
References
- Web Application Links
- [GitHub] Sample REST Webapp
- Download the sample webapp used in this blog post.
- The WAR file is available here.
- [GitHub] Apache Tomcat Dockerfile
- [Blogger] Maven: a build automation tool
- [Blogger] An Introduction to JAX-RS (Java API for RESTful Web Services)
- [Blog] Bootstrapping WebSphere Liberty in Docker with confd
- An alternative to Apache Tomcat
- [Blog] Getting Started with Docker
