Building out Tomcat and MySQL with Docker
Published: 2018-09-04 12:10:22

---
title: Building out Tomcat and MySQL with Docker
published: true
tags: 
canonical_url: http://oldmrcrankypants.blogspot.com/2018/09/building-out-tomcat-and-mysql-with.html
---

(Pardon the formatting, still getting used to the Markdown)

We have a Test Queuing and Archive server that runs on Tomcat, with the data stored in MySQL.  This currently runs on vm's in our lab, but as part of the process to move this to AWS I moved the manual setup for these to Docker images.  This gives us a few advantages:

1.  A long, multi-day wiki page install is distilled down into one docker image that builds in 5 minutes
2. We get a repeatable environment usable whenever we need to deploy, without worrying about underlying changes of configuration files
3. Being able to build a local Development version of the environment that can be brought up and used in minutes, rather than move to the Development vm and run some install scripts for an environment we barely use.  Updates to this system are rare.

Because I love automation I added some local shell scripts to check for running Docker images, containers, and added cleanup for previous builds.  In testing I was building these over and over and found I had at one point 5 GB of previous build images that I don't need.  The scripts also handle stopping the Containers so I don't have collisions in case I forget to check if the Containers are running, sometimes I don't check my laptop after the weekend to see what I might have left running.  I know, that's a bad habit, but this way I don't need to worry.

  

For my Local Development environment I know what IP I am going to start my Containers on, so I added an entry in my HOST file to be able to use a named instance to the Container when it comes up.  I like that better than worrying about trying to connect to an IP address.

  

As part of adding this into Jenkins I added parameters to the shell scripts to build for the environments I need, a Local Development environment needs MySQL the Production environment has an RDS DB running, so all I need for that is Tomcat.  This way all the configuration is set in the Dockerfile and I just build for the environment I want.  
  
> **#!/bin/bash**  
> # Build script for Local Tomcat usable on local environments
> BUILD='LOCAL'
> 
> show\_help()
> 
> {
> 
> cat \<\< EOF 
>     usage:
>         $0 -b local     for local Thunder and MySQL Docker  (DEFAULT) 
>         $0 -b prod      for remote Thunder Docker image to connect to AWS RDS
> 
> EOF 
> }   
> 
> while getopts "hb:" opt; do 
>     case $opt in 
>         h) 
>             show\_help 
>             exit 0 
>         ;;
>         b) 
>             BUILD=$OPTARG 
>         ;; 
>     esac 
> done   
> 
> # WAR file and web server files 
> _echo_ "We should always want to do a clean then a new build" 
> _wget_ -O www/scripts/jquery.js code.jquery.com/jquery-1.11.1.js 
> _cp_ www/index.html.template.templ www/index.html 
> _./gradlew_ clean build 
> **if [-e** build/libs/my.war **]then** 
> **   ** _echo_ "my.war was built correctly"
> **else** 
> **   ** _echo_ "my.war was not built, some kind of problem?" 
>     _exit_ 1 
> **fi**  
> 
> # Stop the existing images, if they are running 
> LOCAL=_`docker ps -q --filter=ancestor=local`_
> PROD=_`docker ps -q --filter=ancestor=prod`_ 
> MYSQL=_`docker ps -q --filter=ancestor=mysql`_ 
> **if [**! **-z**"$LOCAL" **]**; **then** 
> **   ** _echo_ "Stopping running Local instance" 
>     _docker_ stop local 
> **fi** 
> **if [**! **-z**"$PROD" **]**; **then** 
> **   ** _echo_ "Stopping running Prod instance" 
>     _docker_ stop prod 
> **fi** 
> **if [**! **-z**"$MYSQL" **]**; **then** 
> **   ** _echo_ "Stopping MySQL instance" 
>     _docker_ stop mysql
> **fi**
> 
> # Let's just be cleanly and remove all those old dangly images 
> _docker_ system prune -f   
> 
> # Create a Docker image 
> **if [**" **$** {BUILD}" **==**'LOCAL' **]then** 
> **   ** _echo_ -e "Running a $BUILD build"
>     # Build all for local but only need Tomcat for Prod 
>     _docker_ build -t mysql -f deployment/docker/local/mysql/Dockerfile . 
>     _docker_ build -t local -f deployment/docker/local/tomcat/Dockerfile . 
> **else** 
> **   ** _echo_ -e "Running a $BUILD build" 
>     # Prod version should be prod name, don't need a database 
>     _docker_ build -t prod -f deployment/docker/prod/tomcat/Dockerfile . 
> **fi**
> 
> # Cleanup the files we only need for the war file 
> _rm_ www/index.html 
> _rm_ www/scripts/jquery.js

For Tomcat I have some configuration to do with the context and server files, that will vary for environment, but what I especially needed was a way in Docker to build up a self-signed certificate without manual intervention.  After some searching around and trial/error I was able to come up with the commands that I needed to build that.  
  
> **# Install useful toolsRUN apt-get update && \** 
> **    apt-get -y upgrade && \** 
> **    apt-get install -y wget vim curl procps tar libssl-dev --fix-missing --no-install-recommends**   
> 
> **# Configure Tomcat Container** 
> **COPY build/libs/my.war /usr/local/tomcat/webapps/** 
> **COPY deployment/docker/local/resources/local-server.xml /usr/local/tomcat/conf/server.xml** 
> **COPY deployment/docker/local/resources/local-context.xml /usr/local/tomcat/conf/context.xml** 
> **COPY deployment/docker/local/resources/local-environment.xml /usr/local/tomcat/conf/environment.xml** 
> **COPY deployment/docker/local/resources/local-testserver.xml /usr/local/tomcat/conf/testserver.xml**
>  
> **# MySQL Connector**
> 
> **WORKDIR /usr/local/tomcat/lib/** 
> **RUN curl -L -o mysql-connector-java-5.1.46.tar.gz https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.46.tar.gz** 
> **# Add in untar of the mysql-connector-java-5.1.46/mysql-connector-java-5.1.46.jar** 
> **RUN tar -xf mysql-connector-java-5.1.46.tar.gz mysql-connector-java-5.1.46/mysql-connector-java-5.1.46.jar** 
> **RUN cp mysql-connector-java-5.1.46/mysql-connector-java-5.1.46.jar .**   
> 
> **# Configure Tomcat User and GroupENV RUN\_USER    tomcat** 
> **ENV RUN\_GROUP   tomcat** 
> **RUN groupadd -r ${RUN\_GROUP} && useradd -g ${RUN\_GROUP} -d ${CATALINA\_HOME} -s /bin/bash ${RUN\_USER} && \** 
> **    chown -R ${RUN\_USER}:${RUN\_USER} $CATALINA\_HOMERUN chown -R ${RUN\_USER}.${RUN\_USER} /data/RUN chown -R ${RUN\_USER}.${RUN\_USER} /usr/local/resource-manager/**
> 
> **# Setup SSL for Tomcat** 
> **WORKDIR /usr/local/tomcat/conf/RUN $JAVA\_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA -storepass changeit -keypass changeit -noprompt -dname "CN=local.test.server, OU=Central, O=MYCOMP, L=BOSTON, S=Massachusetts, C=US" -keystore "/usr/local/tomcat/conf/.keystore"** 
> **RUN openssl req -newkey rsa:2048 -x509 -subj "/C=US/ST=Massachusetts/L=BOSTON/O=MYCOMP/CN=local.test.server" -keyout cakey.pem -out cacert.pem -passout pass:mypass** 
> **RUN openssl pkcs12 -export -in cacert.pem -inkey cakey.pem -out identity.p12 -name "local" -passin pass:mypass -password pass:mypass** 
> **RUN keytool -importkeystore -destkeystore identity.jks -deststorepass mypass -srckeystore identity.p12 -srcstoretype PKCS12 -srcstorepass mypass**
> **RUN keytool -import -file cacert.pem -keystore trust.jks -storepass mypass -noprompt**
>  
> **WORKDIR /usr/local/tomcat/**
> 
> **USER tomcat**


For the Production build I need an actual certificate, but since that environment is not fully ready I don't need to import that yet.

  
There is a shell script that gets the Local Development environment running, using Docker-Compose I am able to get the environment I need up and running.  Again the shell script has a path for Local and Production environments setting them up appropriately.  
 
> 
> **BUILD='LOCAL'** 
> **show\_help()** 
> **{** 
> **cat \<\< EOF** 
> **    usage:** 
> **        $0 -b LOCAL     for local Thunder and MySQL Docker  (DEFAULT)** 
> **        $0 -b PROD      for remote Thunder Docker image to connect to AWS RDS** 
> **EOF** 
> **}**   
> 
> **while getopts "hb:" opt** 
> **do** 
> **    case "$opt" in** 
> **        h)** 
> **            show\_help** 
> **            exit 0** 
> **        ;;** 
> **        b)** 
> **            BUILD=$OPTARG** 
> **        ;;** 
> **    esac** 
> **done**
> 
> **# Stop the existing images, if they are running** 
> **LOCAL=`docker ps -q --filter=ancestor=local`** 
> **PROD=`docker ps -q --filter=ancestor=prod`** 
> **MYSQL=`docker ps -q --filter=ancestor=mysql`**
> 
> **if [! -z "$LOCAL"]; then** 
> **    echo "Stopping running Local instance"** 
> **    docker stop local** 
> **fi** 
> **if [! -z "$PROD"]; then** 
> **    echo "Stopping running Prod instance"** 
> **    docker stop prod** 
> **fi**
> 
> **if [! -z "$MYSQL"]; then** 
> **    echo "Stopping MySQL instance"** 
> **    docker stop mysql** 
> **fi**
> 
> **# Check for Local or Prod build then use the right directories** 
> **if ["${BUILD}" == 'LOCAL']then** 
> **    echo -e "Starting a $BUILD environment"** 
> **    docker-compose -f deployment/docker/local/tomcat.yaml --verbose up -d** 
> **else** 
> **    echo -e "Starting a $BUILD environment"** 
> **    docker-compose -f deployment/docker/prod/tomcat.yaml --verbose up -d** 
> **fi**

The YAML file I set for the environment gets the Containers up and running with the settings I needed.  
 
> **services:** 
> **    tomcat:** 
> **        container\_name: tomcat** 
> **        image: local:latest** 
> **        environment:** 
> **              CATALINA\_OPTS: "-server -Xms1024m -Xmx2048m -XX:PermSize=1024m -XX:MaxPermSize=1536m -Dhost\_default\_domain=tlocal.test.server -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt\_socket,address=1043,server=y,suspend=n"** 
> **        ports:**
> **            - "127.0.0.1:80:8080"** 
> **            - "443:8443"** 
> **        restart: on-failure** 
> **        links:** 
> **            - mysql** 
> **    mysql:** 
> **        container\_name: mysql** 
> **        image: mysql:latest** 
> **        ports:** 
> **          - 3306:3306** 
> **        environment:** 
> **          MYSQL\_DATABASE: testdb** 
> **          MYSQL\_USER: testadmin** 
> **          MYSQL\_PASSWORD: testpass** 
> **          MYSQL\_ROOT\_PASSWORD: testpass** 
> **        mem\_limit: 1000000000**

With that I have my Tomcat and MySQL Containers up and running, I can point some local tests to the Test Server URL and have my tests run against and archive their runs locally.  This gives me flexibility in testing changes as I can do my code locally build my Docker environment and test without worrying about checking into the repository and checking out in another environment that may or may not be in use.