Sometimes I need to be hit in the head with an axe to find a solution to a problem that has been bugging me forever. A minimal Java container has been on my wish list since I found out about Docker and I’ve been writing about running Java in Docker for sometime already.
Official Java images have historically been mastodontic – cue picture above – I just tried “docker pull java
” and I got an image of 816.4MB
. A colleague of mine few days ago mentioned Alpine Linux, a minimalistic Linux distribution based on musl libc and BusyBox that comes with a nice package manager. And the base image is … 5Mb
?! Where have I been hiding? Why didn’t I know about this?! Anyway here’s my chance to make things right. The objective: to have a minimal Java container for my (and your) applications. Let’s dream together.
If OpenJDK 7 is good enough
If you are still using JDK7 and you don’t have a strong requirement to have the Oracle version, the easiest and leaner image I found is very simple to setup with this Dockerfile
:
FROM alpine:3.2
RUN apk --update add openjdk7-jre
CMD ["/usr/bin/java", "-version"]
- Build it with:
docker build -t yourname/minimal-java .
- Or if you are lazy just:
docker pull durdn/minimal-java
- Now you can test that
java
is installed with:
docker run -t durdn/minimal-java
Which outputs:
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.5) (Alpine 7.79.2.5.5-r0)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)
The result is a Java 7 runtime environment, ready for your Java 7 applications in only 123MB
instead than 800+MB
. NICE!
What about Oracle JRE/JDK 8?
For many applications teams prefer or require the Oracle JDK. In this case we can’t use Alpine package manager (yet), we have to wrangle the installation ourselves from the official Oracle packages. Do you want to see how that’s done? This is the list of steps:
- Install
curl
,tar
, andca-certificates
on the basealpine
image. - Install
glibc-2.21
which is a hard dependency of Java 8. - Download the Oracle JRE/JDK using tricks in this SO article.
- Remove spurious folders not needed (like
jdk/jre/lib/desktop
and others…). - Set the proper environment variables.
The whole process is well laid out amongst others in a clean Dockerfile
by anapsix which I list here for completeness:
# AlpineLinux with a glibc-2.21 and Oracle Java 8
FROM alpine:3.2
MAINTAINER Anastas Dancha [...]
# Install cURL
RUN apk --update add curl ca-certificates tar &&
curl -Ls https://circle-artifacts.com/gh/andyshinn/alpine-pkg-glibc/6/artifacts/0/home/ubuntu/alpine-pkg-glibc/packages/x86_64/glibc-2.21-r2.apk > /tmp/glibc-2.21-r2.apk &&
apk add --allow-untrusted /tmp/glibc-2.21-r2.apk
# Java Version
ENV JAVA_VERSION_MAJOR 8
ENV JAVA_VERSION_MINOR 45
ENV JAVA_VERSION_BUILD 14
ENV JAVA_PACKAGE jdk
# Download and unarchive Java
RUN mkdir /opt && curl -jksSLH "Cookie: oraclelicense=accept-securebackup-cookie"
http://download.oracle.com/otn-pub/java/jdk/${JAVA_VERSION_MAJOR}u${JAVA_VERSION_MINOR}-b${JAVA_VERSION_BUILD}/${JAVA_PACKAGE}-${JAVA_VERSION_MAJOR}u${JAVA_VERSION_MINOR}-linux-x64.tar.gz
| tar -xzf - -C /opt &&
ln -s /opt/jdk1.${JAVA_VERSION_MAJOR}.0_${JAVA_VERSION_MINOR} /opt/jdk &&
rm -rf /opt/jdk/*src.zip
/opt/jdk/lib/missioncontrol
/opt/jdk/lib/visualvm
/opt/jdk/lib/*javafx*
/opt/jdk/jre/lib/plugin.jar
/opt/jdk/jre/lib/ext/jfxrt.jar
/opt/jdk/jre/bin/javaws
/opt/jdk/jre/lib/javaws.jar
/opt/jdk/jre/lib/desktop
/opt/jdk/jre/plugin
/opt/jdk/jre/lib/deploy*
/opt/jdk/jre/lib/*javafx*
/opt/jdk/jre/lib/*jfx*
/opt/jdk/jre/lib/amd64/libdecora_sse.so
/opt/jdk/jre/lib/amd64/libprism_*.so
/opt/jdk/jre/lib/amd64/libfxplugins.so
/opt/jdk/jre/lib/amd64/libglass.so
/opt/jdk/jre/lib/amd64/libgstreamer-lite.so
/opt/jdk/jre/lib/amd64/libjavafx*.so
/opt/jdk/jre/lib/amd64/libjfx*.so
# Set environment
ENV JAVA_HOME /opt/jdk
ENV PATH ${PATH}:${JAVA_HOME}/bin
The result of building this image or pulling from anapsix/alpine-java
is a fully functional Oracle Java 8 image weighing only 173Mb
. Impressive!
Running Atlassian Stash on it
The whole point of the exercise above was for me to run a leaner container with Stash – our enterprise Git server – trying to shave space off from our official image. The task was a success – if not a smashing one. The final Stash image I produced weighs 368MB
which adds up to a ~30%
reduction over the official image. Here how I had to tweak the Dockerfile
:
FROM durdn/minimal-java8:stripped
MAINTAINER Atlassian Stash Team
ENV DOWNLOAD_URL https://downloads.atlassian.com/software/stash/downloads/atlassian-stash-
ENV STASH_HOME /var/atlassian/application-data/stash
RUN apk --update add git tar bash
# Install Atlassian Stash to the following location
ENV STASH_INST_DIR /opt/atlassian/stash
ENV STASH_VERSION 3.11.1
RUN mkdir -p ${STASH_INST_DIR}
&& curl -LO --silent ${DOWNLOAD_URL}${STASH_VERSION}.tar.gz
&& tar -xf atlassian-stash-${STASH_VERSION}.tar.gz -C ${STASH_INST_DIR} --strip-components 1
&& rm atlassian-stash-${STASH_VERSION}.tar.gz
&& mkdir -p ${STASH_INST_DIR}/conf/Catalina
&& chmod -R 700 ${STASH_INST_DIR}/conf/Catalina
&& chmod -R 700 ${STASH_INST_DIR}/logs
&& chmod -R 700 ${STASH_INST_DIR}/temp
&& chmod -R 700 ${STASH_INST_DIR}/work
VOLUME ["${STASH_INST_DIR}"]
# HTTP Port
EXPOSE 7990
# SSH Port
EXPOSE 7999
WORKDIR $STASH_INST_DIR
# Run in foreground
CMD ["./bin/start-stash.sh", "-fg"]
Bonus trick: How to strip an image
I love the layering ability of Docker images but for base images upon which I’ll build my stacks often I’d like them to consist of a single layer. It’s a mental thing more than anything so excuse my weirdness if you can.
Many times the extra layers in your base images will not be re-used. For those situations it can be helpful to strip an image of all its layers and flatten it. The technique to accomplish that is the following:
- First run it so that you have a container to refer to:
docker run -t durdn/minimal-java /bin/true
- Then export it and re-import it:
docker export `docker ps -q -n=1` | docker import - durdn/minimal-java:stripped
- Verify that the new image only has one layer with:
docker history durdn/minimal-java:stripped
IMAGE CREATED CREATED BY SIZE COMMENT
8eb82b59dee6 31 seconds ago 172.9 MB Imported from -
docker run -ti durdn/minimal-java8:stripped /opt/jdk/bin/java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
Conclusions
That’s it for my discoveries today. Pings, likes, comments, love or hate gladly received here in the comments, at @durdn or at my awesome team @atlassiandev.
(Credit for the epic alot picture goes to Hyperbole and a half).
You might also enjoy our ebook, “Hello World! A new grad’s guide to coding as a team” – a collection of essays designed to help new programmers succeed in a team setting. Grab it for yourself, your team, or the new computer science graduate in your life. Even seasoned coders might learn a thing or two.