Set up Apache httpd and Tomcat with docker-compose

Download (2)Docker has become so widely accepted that I wanted to give it a serious try. Many times I am faced with the situation to define a typical web infrastructure to host multiple web applications. A typical minimal configuration consists of an Apache http Server and multiple Tomcat instances hosting different applications.

Webfrastructure_Logical

After I had played around with Docker images and containers I digged into docker-compose to define and link multiple docker containers.

Build your docker images

Initially you would have a look at some public docker repository to see whether appropriate container definitions are available for your purpose.
I haven’t been successful finding the right ones yet so I just created my own docker files based on already existing definitions.
Linking the good old Apache with Tomcat is quite straightforward via the use of the mod_jk module and the AJP connector in Tomcat.
So it would be helpful to have an Apache Web Server container with mod_jk already installed.

Dockerfile for httpd

FROM ubuntu

ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update \
	&& apt-get install -y --no-install-recommends apache2 \
					 		libapache2-mod-jk
VOLUME ["/var/log/apache2"]
EXPOSE 80 443

ENTRYPOINT ["apache2ctl", "-D", "FOREGROUND"]

Dockerfile for Tomcat

For a Tomcat container I extend an already existing definition and additionally expose its AJP port 8009.

FROM tomcat
EXPOSE 8009

After building the images they would become available in my local repository.
>docker images

REPOSITORY  TAG      IMAGE ID       CREATED      VIRTUAL SIZE
mg          tomcat   ff21e2bb0767   2 weeks ago  350 MB
mg          httpd    eb2ff0c48096   2 weeks ago  220.1 MB
tomcat      latest   52ae4ebca944   2 weeks ago  350 MB
ubuntu      latest   e9aeff8e9dd1   2 weeks ago  187.9 MB
....

Define dockercompose.yml

All containers have to be defined in a YAML file docker-compose.yml which looks as follows.

portalapp:
   volumes:
    - $PWD/tomcat/portal-webapps:/usr/local/tomcat/webapps
   expose:
    - "8009"
   image: mg:tomcat
   container_name: "portal"
searchapp:
   volumes:
    - $PWD/tomcat/search-webapps:/usr/local/tomcat/webapps
   expose:
    - "8009"
   image: mg:tomcat
   container_name: "search"
businessapp:
   volumes:
    - $PWD/tomcat/businessapp-webapps:/usr/local/tomcat/webapps
   expose:
    - "8009"
   image: mg:tomcat
   container_name: "business"
http:
   volumes:
    - $PWD/httpd/conf/jk_mod/jk.conf:/etc/apache2/mods-available/jk.conf
    - $PWD/logs:/var/log/apache2
    - $PWD/httpd/conf/jk_mod/workers.properties:/etc/libapache2-mod-jk/workers.properties
    - $PWD/httpd/conf/apache2/000-default.conf:/etc/apache2/sites-available/000-default.conf
   ports:
     - "80:80"
   image: mg:httpd
   container_name: "http-proxy"

3 different Tomcat based containers – portalapp, searchapp, businessapp – and an Apache Webserver – http – hosting individual applications. All Tomcat containers expose their 8009 port so AJP connectivity is possible. Via data volumes directive we provide mount points so externally provided web applications may be deployed to the Tomcat instances.

The Apache Webserver container makes its http-port 80 available to the public via its ports directive. Quite a few mount points are defined to configure mod_jk and the webserver itself.

The above configuration is based on the following directory structure

/webfrastructure
    /tomcat
     /conf
     /portal-webapps
         /portal
     /search-webapps
         /search
     /business-webapps
        /business
   /httpd
     /conf
       /apache2
       /jk_mod

mod_jk configuration

We need to tell Apache via mod_jk how to connect to the individual tomcat instances.
For this purpose we provide necessary configuration via worker.properties and jk.conf.

worker.properties

In this configuration file we define the necessary workers, one for each tomcat instance we need to connect to. If you have a look at the host definitions you will recognize that we use the container names defined in the docker-compose.yml file appended with the network name ‘webfrastructure’.

#
#------ worker list ------------------------------------------
#---------------------------------------------------------------------
#
#
# The workers that your plugins should create and work with
#
worker.list=ajp13_worker,search_worker,business_worker

#
#------ ajp13_worker WORKER DEFINITION ------------------------------
#---------------------------------------------------------------------
#

#
# Defining a worker named ajp13_worker and of type ajp13
# Note that the name and the type do not have to match.
#
worker.ajp13_worker.port=8009
worker.ajp13_worker.host=portal.webfrastructure
worker.ajp13_worker.type=ajp13
#
...
#
#------ search_worker WORKER DEFINITION ------------------------------
#---------------------------------------------------------------------
#

worker.search_worker.port=8009
worker.search_worker.host=search.webfrastructure
worker.search_worker.type=ajp13

...
#
#------ business_worker WORKER DEFINITION ------------------------------
#---------------------------------------------------------------------
#

worker.business_worker.port=8009
worker.business_worker.host=business.webfrastructure
worker.business_worker.type=ajp13
...
#
#------ DEFAULT LOAD BALANCER WORKER DEFINITION ----------------------
#---------------------------------------------------------------------
#

#
# The loadbalancer (type lb) workers perform wighted round-robin
# load balancing with sticky sessions.
# Note:
#  ----> If a worker dies, the load balancer will check its state
#        once in a while. Until then all work is redirected to peer
#        workers.
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=ajp13_worker,search_worker,business_worker

jk.conf

In this configuration file we mount the URIs of our applications to the corresponding workers so each URI is served by its corresponding tomcat instance.

    ...
    JkMount /portal|/* ajp13_worker
    JkMount /search|/* search_worker
    JkMount /business|/* business_worker
    ...

Run docker-compose

Finally we are able to run our docker containers in its isolated network.
> docker-compose --x-networking up -d

and end up as depicted as follows.

WebFrastructure-Networking

Now we are able to access our applications from the given URLs

http://localhost/search
http://localhost/portal
http://localhost/business

If you want to learn more about Docker Compose, just head over to the official documentation at https://docs.docker.com/compose/.