Dockerizing a Tomcat + PostgreSQL Java web application
Generally, a docker container is meant to hold exactly one application. For a typical Java web application (in this example we assume a Tomcat 8 servlet container and a Postgres 9.4 database), this will lead to two and a half containers:
- The
web
container for our web application (in the example we assume.WAR
packaging) deployed to Tomcat - The
db
container for our Postgres database - The
db-data
container. This one is necessary because Docker containers are volatile by default. Thus, data stored in a container is lost when they are restarted, so we need a volume to permanently store the database data.
Naturally, the applications within their respective containers need to communicate with each other. The current tool of choice to enable inter-container communication is Docker Compose.
Directory and file structure
The directory structure for our Docker files looks like this:
project
│ docker-compose.yml
│
└───db
│ Dockerfile
│
├───web
│ Dockerfile
│ application.war
The database container
We will begin with the Dockerfile of our database in ./db/Dockerfile
:
FROM postgres:9.4
MAINTAINER lpradel
ENV POSTGRES_USER admin
ENV POSTGRES_PASSWORD password
ENV POSTGRES_DB app_staging
In this example we are using the standard Postgres 9.4 image and configure credentials for a single user and DB.
The application container
The Dockerfile for our web
container in ./web/Dockerfile
looks like this:
FROM tomcat:8-jre8
MAINTAINER lpradel
RUN echo "export JAVA_OPTS=\"-Dapp.env=staging\"" > /usr/local/tomcat/bin/setenv.sh
COPY ./application.war /usr/local/tomcat/webapps/staging.war
CMD ["catalina.sh", "run"]
The image is based on the standard Tomcat 8 image. I have included an example of how to perform basic Tomcat configuration. Here, we pass some JAVA_OPTS to the setenv.sh shell script of Tomcat.
Finally, our application .WAR
is copied the to the Tomcat webapps
directory and deployed in the staging
context.
Docker Compose
The last step is orchestrating our containers in ./docker-compose.yml
:
app-web:
build: ./web
ports:
- "8081:8080"
links:
- app-db
app-db:
build: ./db
expose:
- "5432"
volumes_from:
- app-db-data
app-db-data:
image: cogniteev/echo
command: echo 'Data Container for PostgreSQL'
volumes:
- /var/lib/postgresql/data
We bind the default Tomcat port 8080 in the container to the 8081 port of our host system and expose the Postgres port 5432 to the web
container which is linked to it. Lastly, we reference the permanent data volume as a volume for our db
container.
Running the application
Execute the following command to start our containers:
$ docker-compose up -d
To stop the containers run
$ docker-compose stop
A list of all docker containers is available via
$ docker ps -a
To open a shell in one of the containers (for example to access log files), run the following command:
$ docker exec -it <container_id> /bin/bash
Finally, to redeploy only specific containers, use the following command:
$ docker-compose build <container_id>
$ docker-compose up --no-deps -d <container_id>