Docker is a platform for developers and sysadmins to develop, deploy, and run applications with containers. As we explained in the previous post, Docker containers wrap up software and its dependencies into a standardized unit for software development that includes everything it needs to run: code, runtime, system tools, and libraries.
In this post, we are going to step through a basic tutorial on getting a web application running on Tomcat Docker Container.
The Tomcat server is the most widely used open source implementation of the Java Servlet, JavaServer Pages, Java Expression Language, and Java WebSocket technologies. Apache Tomcat software powers numerous large-scale, mission-critical web applications across a diverse range of industries and organizations.
The sample TomCat application we will be using for this exercise is included in a git repository so that you can run through this tutorial easily.
In Tomcat, we have to move the war file to the CATALINA_BASE/webapps directory. Tomcat will then install it automatically and deploy the application for you.
We are going to use Tomcat 8.0 for this exercise.
By the end of this tutorial, you should be able to:
- Understand the basic concepts of Docker containers
- Run containers using Docker images
- Get Tomcat server running on a container
- Deploy web application on the Tomcat server
- Build your own Docker images using Dockerfile
- Mapping ports from container on to the host machine
We are going to use Docker Community Edition (CE) as it is ideal to get started with Docker and experimenting.
Download and install Docker from https://docs.docker.com/install/
Choose the appropriate installation method depending on the OS you are using.
Once the installation is complete, open a command prompt terminal and type the command as below. Output similar to below verifies that your installation went ok.
|$ docker run hello-world
Unable to find image ‘hello-world:latest’ locally
Hello from Docker!
As explained further down in the output generated, many steps were carried out in that one command.
To generate this message, Docker took the following steps:
- The Docker client contacted the Docker daemon.
- The Docker daemon pulled the “hello-world” image from the Docker Hub.
- The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.
- The Docker daemon streamed that output to the Docker client, which sent it to your terminal.
Don’t worry if the above is a mouthful, here is all you need to know for now.
Docker Engine is a client-server application with these major components:
- The Docker daemon is a service that runs on your host operating system. When you type any command, it is interpreted by the demon and it takes necessary actions.
- A REST API to talk to the daemon and instruct it what to do.
- A command line interface (CLI) client (the docker command).
Additionally, Docker Hub is the place where open Docker images are stored. You can pull images, make changes and push them back into this repository – sounds much like GitHub isn’t it?
Image source: docker.com
All the code and the sample application needed for this tutorial is available in:
Clone it to your local computer
|$ git clonehttps://github.com/softwareyoga/docker-tomcat-tutorial.git
Dockerfile README.md sample.war
First, we search docker hub for an official image of Tomcat. So, what is an image?
To use a programming metaphor, if an image is a class, then a container is an instance of a class. Images are created with the build command, and they’ll produce a container when started with run
|$ docker search tomcat|
One of the results you should see is that of an official image.
|Tomcat Apache Tomcat is an open source implementati… [OK]|
Alternately, you could search directly on DockerHub website. Tomcat image information is available in https://hub.docker.com/r/_/tomcat/
Checking the documentation for the Tomcat page, we see that Tomcat built on the alpine version of Linux is smaller in size and ideal for experimentation.
Once we have identified the image to use, we can write a Dockerfile specifying the base image to be used, the webapp to be used, etc.
It can build images by reading the instructions from a Dockerfile. Dockerfile contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.
Our simple Dockerfile is as follows:
|$ cat Dockerfile
ADD sample.war /usr/local/tomcat/webapps/
CMD [“catalina.sh”, “run”]
The FROM instruction initializes a new build stage and sets the Base Image for subsequent instructions.
The LABEL instruction sets the Author field of the generated images. You could use any key-value pair in labels.
The ADD instruction copies new files, directories or remote file URLs from <src> and adds them to the filesystem of the image at the path <dest>.
In our case, we are adding the sample webapp and placing it in the folder /usr/local/tomcat/webapps/ on the container. That is because according to the Tomcat documentation, the War should be placed under CATALINA_BASE/webapps. It will be automatically expanded and deployed. From the Tomcat image documentation, we know that the default path CATALINA_BASE corresponds to /usr/local/tomcat on the container.
The EXPOSE instruction informs Docker that the container listens on the specified network ports at runtime.
The CMD instruction specifies what to run when the container (not the image) is run. In our case, TomCat server is started by running the shell script that starts the web container. There can only be one CMD instruction in a Dockerfile.
Don’t confuse RUN with CMD. RUN actually runs a command at build time.
The build is run by the Docker daemon, not by the CLI. It downloads any images that are necessary and also executes the commands specified in the Dockerfile.
In the below command, the Dockerfile we created earlier is used (Docker daemon looks for Dockerfile specified by the current directory using a dot) and the newly built image is tagged mywebapp.
|$ docker build -t mywebapp .
Sending build context to Docker daemon 60.42kB
Step 1/5 : FROM tomcat:8.0-alpine
8.0-alpine: Pulling from library/tomcat
4fe2ade4980c: Pull complete
6fc58a8d4ae4: Pull complete
7d9bd64c803b: Pull complete
a22aedc5ac11: Pull complete
5bde63ae3587: Pull complete
69cb0c9b940a: Pull complete
Status: Downloaded newer image for tomcat:8.0-alpine
Step 2/5 : LABEL maintainer=”email@example.com”
—> Running in 65215fcf5e93
Removing intermediate container 65215fcf5e93
Step 3/5 : ADD sample.war /usr/local/tomcat/webapps/
Step 4/5 : EXPOSE 8080
—> Running in 724588a8c1f3
Removing intermediate container 724588a8c1f3
Step 5/5 : CMD [“catalina.sh”, “run”]
—> Running in dd5ed463f256
Removing intermediate container dd5ed463f256
Successfully built ab09b4cafc66
Successfully tagged mywebapp:latest
If this is the first time you are running this, it might take a few minutes to complete as the Tomcat image (and any of its dependencies) has to be downloaded.
You can now verify that the mywebapp image is built and ready to be used. Note that at this point, you have only built the image, there is no running container.
|$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
mywebapp latest ab09b4cafc66 About a minute ago 147MB
tomcat 8.0-alpine 624fb61775c3 6 weeks ago 147MB
To actually run the container using the image, follow the steps below.
Run the Container
The CLI has a command called run which will start a container based on a Docker Image. The structure is docker run <options> <image-name>.
As we mentioned before, the EXPOSE instruction in the Dockerfile doesn’t actually publish the port. To so that when running the container, use the -p flag on docker run to publish and map one or more ports.
So for mapping the container port 8080 for the mywebapp image to port 80 on the host machine, we execute:
|$ docker run -p 80:8080 mywebapp
30-Oct-2018 22:17:48.498 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/8.0.53
30-Oct-2018 22:17:48.503 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
30-Oct-2018 22:17:48.505 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /usr/lib/jvm/java-1.7-openjdk/jre
30-Oct-2018 22:17:48.506 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
30-Oct-2018 22:17:48.506 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
// LOGS cut for brevity
30-Oct-2018 22:17:48.691 INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 926 ms
30-Oct-2018 22:17:50.212 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1520 ms
Open http://localhost:80/ in a browser to see the sample web application running.
Additional useful Docker commands
By default, it will run a command in the foreground. To run in the background, the option -d needs to be specified. Doing so will output the container ID that could be used for other commands as below.
|$docker run -d -p 80:8080 mywebapp
The command docker inspect <container-id> provides more details about a running container, such as IP address.
The command docker logs <container-id> will display messages the container has written to standard error or standard out.
The command docker ps lists all the running containers.
Congratulations, you have just run your first Docker container!
Of course, there is more to Docker than this simple tutorial. In real life situations, you would want the sample application built using Maven or gradle and included in the image. Your Dockerfile would also have many more instructions such as defining environment variables, copying files into docker volumes etc.
However, this was a starting point to see and experience first-hand how to run a web application on Docker. For further reading, I recommend Docker documentation which is quite comprehensive.