Dockerize Java Aplication

Dockerize Java Aplication

A gist of docker files to build docker images containing pre-built Java application. You may build the Java project in the same container too, but it will make the deployment less agile and flexible. Not to mention that the docker file will be cluttered with some extra configurations for the building and testing phases.

FROM openjdk:8-jre-alpine

ENV APP_PATH=/opt/app
ENV APP_PORT=8080
ENV APP_HEAP_MIN=1g
ENV APP_HEAP_MAX=4g
ENV JMX_PORT=9000
ENV JMX_RMI_PORT=9000

# Root dir
RUN mkdir -p $APP_PATH
RUN rm -rf $APP_PATH/*
WORKDIR $APP_PATH

# Contains 
# - compiled class files
# - dependencies (typically a lib folder containing some jar files)
ADD target ./

# App logging directory
RUN mkdir -p logs

# (optional) Install dependencies
RUN apk update
RUN apk add imagemagick

# Entrypoint
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh

# Expose ports
EXPOSE $APP_PORT
EXPOSE $JMX_PORT

# (optional) Set timezone
RUN apk add --no-cache tzdata
RUN export TZ=Asia/Singapore

# Install Tini
# https://solidx.dev/getting-threaddump-from-java-process-with-pid-1-in-docker-container/
RUN apk add --no-cache tini

ENTRYPOINT ["/sbin/tini", "--", "sh", "entrypoint.sh"]

You may be curious what is the use of Tini. Find out from this post.

Below is the entrypoint.sh file which runs the Java application with enabled JVM garbage collection (GC) log, GC settings, JMX config and JVM heap config.

#!/bin/bash
set -e

cd $APP_PATH && exec java -server \
  -Xms$APP_HEAP_MIN -Xmx$APP_HEAP_MAX \
  -Xloggc:./logs/gc-%t.log \
  -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M \
  -XX:+PrintTenuringDistribution \
  -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps \
  -XX:-PrintGC -XX:+PrintGCDetails \
  -XX:+PrintHeapAtGC \
  -XX:MaxTenuringThreshold=7 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=1 -XX:SurvivorRatio=2 \
  -Dcom.sun.management.jmxremote.port=$JMX_PORT \
  -Dcom.sun.management.jmxremote.rmi.port=$JMX_RMI_PORT \
  -Dcom.sun.management.jmxremote=true \
  -Dcom.sun.management.jmxremote.ssl=false \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -Dcom.sun.management.jmxremote.local.only=false \
  -Djava.rmi.server.hostname=0.0.0.0 \
  -cp "libs/*:." com.company.module

TIP: You may need to optimize the GC strategy based on the nature of your application. This setting works very well for high throughput API calls with short-lived objects.

TIP: You may also want to streamline your deployment process with a popular CI like Jenkins, where you can have a job to build the Java project, and another job to build and deploy the docker container. I will cover this later in another post.

Running the container

docker run -p 8080:8080 -p 9000:9000 -e APP_HEAP_MIN=2g -e APP_HEAP_MAX=4g -e JMX_PORT=9000 -e JMX_RMI_PORT=9000 -v /var/log/app-prod/:/opt/app/logs --detach --rm --name app-prod <image_ID>

Leave a Reply

Your email address will not be published. Required fields are marked *