Docker + Java Microservices: Choosing the Base Image for Java 8/9 Microservices (on Linux and Windows)

Defining the base image is one of the most important decisions when creating docker containers. Here I will describe some of the reasoning when you do that for Java Spring Boot Microservices.

Introduction

Containers were designed to be self-contained and isolated. However, the environment in which it runs does affect the decision on what base image you should use.

You may run on a Windows machine that is already running Linux Docker Containers. Or the machine is running only windows containers (or no containers at all). Or maybe you are even thinking about running a Linux container on the same Windows Server. There are relevant questions on what Java base image you should choose.

In this article, I will share some of the things I learned along the way on how to make those decisions, and how to justify them to your Devops department.

Note: If you haven't yet learned how to create a Java Spring Boot Microservice, I recommend you this reading: Docker + Spring Boot Microservice (with Gradle).

Quick pick on Docker Layers

It would be wasteful to go too much into detail about how Docker Layers work due to the extensive documentation available in the Docker website.

However, it's important to review some of its practical concepts here for the purpose of this article:

  1. "Each Docker image has its own root filesystem, which needs to have some sort of OS installed" (Rohan Singh on his Stackoverflow answer)
  2. If you then create 3 microservice and Dockerize them using the same base layer, you will have to distribute your base layer just once, and then 3 smaller container images for each of your microservices.

Let's move on and talk about Java base images, which are the ones we are interested about.

Difference between Windows Containers and Linux Containers

Docker provides to its containers the same types of resources in both OS. It also has a compatible API for both distributions.

However, there are differences on the “flavours” with which these resources are delivered to the containers.

Ajeet Singh Raina, docker captain, wrote a great article on "A Comparative Study of Docker Engine on Windows Server Vs Linux Platform" (see more at http://collabnix.com/archives/1965), from which we can learn details about the resource sharing differences for each platform.

Source: "A Comparative Study of Docker Engine on Windows Server Vs Linux Platform" (http://collabnix.com/archives/1965)
Source: “A Comparative Study of Docker Engine on Windows Server Vs Linux Platform” (http://collabnix.com/archives/1965)

For that reason, the underlying operating system running the Docker Engine will affect (a) the base image supported and therefore (b) the applications that can possibly run on top of that base image.

Running Java Spring Boot Microservices, how do I choose between Windows Containers or Linux Containers?

The following flowchart is what I use as decision tree when it comes to defining what type of image I will be deploying:

My personal take is: I prefer in most of the cases, prefer Linux Containers running Alpine base images.

Can I run Windows Containers and Linux Containers alongside each other in the same Docker for Windows?

Currently, Docker for Windows cannot run Linux Containers alongside Windows Containers. That limitation might disappear, but I rarely see any benefit of locking myself in, even in the short run, specially when developing to run on top of the the JVM.

Also, the Linux Containers can run beautifully on Windows as well. The main difference is that they will run inside a Hyper-V Linux VM. And remember: Hyper-V is a Type1 Hypervisor, therefore suffers less with the overhead.

Hyper-V on requires BIOS Virtualization ON, and breaks VirtualBox

Hyper-V, as well as other Type 1 hypervisors, requires the Virtualization option to be ON in your BIOS.

That can cause some Type 2 Hypervisors (like VirtualBox, Virtual PC, VMWare Workstation) to stop working. It's a known problem between Hyper-V and VirtualBox to fight each other.

I will keep ourselves in the Containers land, but it's not hard to find more information about the differences between the 2 types of hypervisors.

Base Java Images for Linux Containers

Having left clear that my preference is run on top of Linux Containers the most I can, I will now show some details on using them.

Official Dockerhub Java Repository

Let's start with the official java repository on dockerhub, you will quickly notice that it's been DEPRECATED and it will be referring you to the openjdk repository.

OpenJDK Dockerhub Repository

When you land to the openjdk repository on Dockerhub, you will notice a large number of images, including a few ones I would like to highlight here for now:

  • openjdk:9-jre
  • openjdk:8-jre, and openjdk:8-jre-alpine
  • openjdk:7-jre, and openjdk:7-jre-alpine

As you can see that the 9-jdk is alone, but the 8-jre and 7-jre have companions: their alpine editions.

What is the difference? Their base image.

openjdk:8-jre-alpine, a smaller image

Taking a look at the size of the images, you will find the sizes for each image. And there, you will see the advantage of using an alpine image.

openjdk:9-jre         221 MB
openjdk:9-jre-alpine N/A
openjdk:8-jre 124 MB
openjdk:8-jre-alpine 56 MB
openjdk:7-jre 148 MB
openjdk:7-jre-alpine 62 MB

Let's dig a little to find why these images are so different.

Why *-alpine base images are smaller?

Regular openjdk uses a debian:jessie base image. On the other hand, the openjdk-alpine images use a linux alpine base image. The later is fairly lighter than the first.

These base images will use 2 different linux distros, and that is their main difference. To know more about these distros:

For Java Spring Boot Microservices, which image?

If you are writing a Java Spring Boot Microservice, you are probably better with the alpine image.

Unless you really use debian specific stuff directly from your java application, it's very unlikely it will be an advantage for you.

So, as a general rule, you may want to write your microservice containers using "8-jre-alpine" as the base image.

Openjdk images Under the Hood

Visiting the Github of the OpenJDK Dockerfiles of the images, you would quickly spot the difference:

openjdk:8-jre, Dockerfile

Source: https://github.com/docker-library/openjdk/blob/master/8-jre/Dockerfile

FROM buildpack-deps:jessie-curl
(...)

openjdk:8-jre-alpine, Dockerfile

Source: https://github.com/docker-library/openjdk/blob/master/8-jre/alpine/Dockerfile

FROM alpine:3.5
(...)

Alternatives to Linux Java Images?

A few days ago, I was doing some research on base image alternatives and I found an article named "Smaller Java Images with Alpines" from the Atlassian Developers blog, talking exactly about it.

In a nutshell, by the time the article was writing, docker pull java would result in approximately 800MB downloaded. And they were suggesting the alpine:3.2 image base layer as solution.

Fortunately, openjdk has better images now (even the regular images are smaller), including the alpine alternatives.

Therefore, my conclusion is that the official openjdk base images are stable enough to be used, and you should probably stick to them.

If you go away from the official images, beware of licensing. I'm going to go a little bit more into licensing further down the road in this paper.

Base Java Images for Windows Containers

Elton Stoneman has written a comprehensive article on “Windows Containers and Docker: The 5 Things You Need to Know” that I recommend.

There are basically 2 types of Containers that run on Windows:

  • Windows Server Containers: natively delivering Windows Kernel features to the Container, hence forcing the container to run a Windows base image that is compatible to those features of the Kernel.
  • Hyper-V Containers: which run inside a Linux virtual machine inside a Hyper-V (type 1 hypervisor), delivering Linux Kernel features to the Container, hence forcing the container to run a Linux base image.

In this section we will talk exclusively about Windows Server Containers, because the Hyper-V Containers do not differ from the Linux Containers mentioned above in any aspect.

"Switching to Windows Containers…"

At the moment, it’s only possible to run one or the other (Windows containers OR Linux Containers), not the 2 at the same time. This limitation will probably be removed in future and Windows will become advantageous in that aspect.

When you install Docker on a Windows machine you will see, in the context menu of your notification area Docker icon, the following:

When you do this, you will have selected to only run Windows Containers, which are the ones we will be covering from now onwards.

OpenJDK Dockerhub Repository (for Windows Images)

The Dockerhub repository has a lot of images for running windows containers as well. I will narrow down the list into the following:

openjdk:nanoserver            475 MB
openjdk:8-nanoserver 475 MB
openjdk:windowsservercore 5 GB
8-windowsservercore 5 GB

What? 5GB on WindowsServerCore? What can I do on the Nanoserver?

Yes, you got it right! windowsservercore images are 5GB, 90% bigger than the nanoserver images. Why? It's a whole Windows Server (Core Edition) packaged up.

Listening to the Dockercast Podcast with Stefan Scherer, I was introduced to a few limitations that the NanoServer can have. The one that caught my attention was the lack of ability to install MSIs.

I insist on my belief that: Docker Containers should run a simple (self-hosted, in case of web apps or rest apis) process runs with a single command line statement. Even (and specially) for ASP.NET WebApi, be it .NETCore or not.

If you stick to single command line self-hosted applications, the nanoserver will probably be enough for you, and a much lighter option.

Github for Windows JDK Container Images

Looking into Github of the OpenJDK Dockerfiles:

  • I found that most of the Dockerfiles for Windows containers load the entire JDK, not just the JVM.
  • Curiously, I did not find a JRE 7 or JDK 7 nanoserver.

openjdk:8-nanoserver, Dockerfile

Source: https://github.com/docker-library/openjdk/blob/4815a7dc43f0fb212c40f357624fb4ec943992bf/8-jdk/windows/nanoserver/Dockerfile

FROM microsoft/nanoserver
(...)

openjdk:8-windowsservercore, Dockerfile

Source: https://github.com/docker-library/openjdk/blob/4815a7dc43f0fb212c40f357624fb4ec943992bf/8-jdk/windows/windowsservercore/Dockerfile

FROM microsoft/windowsservercore
(...)

A word or two on Licensing…

Special thanks to Dermot Bradley who pointed me out to the Licensing problem on Oracle Java Images during the Northern Ireland Developers Conference April/2017 and also led me to check for the official OpenJDK Dockerhub Repository.
Dermot is a Devops Champion in Belfast, Northern Ireland area who has spoken in meetups and has a GREAT! presentation on Microcontainers, Microservices and Microservers, extremely recommended, who has helped me a lot to understand better about the underlying structure of Docker.

Your base image must not distribute Oracle's JDK/JRE, make sure your base image uses OpenJDK

Until not long ago, the only JRE/JDK version available for Java 8 was Oracle's.

And after the fight between Oracle and Google over using Java as development language for Android, they got a lot more serious about Licensing.

We now have OpenJDK 8 and 9, which does not breach their Licensing terms of distribution, even when distributed with your Docker containers.

Therefore, double check if you are not by mistake using any base image that breaches any license terms before you start doing your things with it.

Licensing on Windows Containers

Researching on Windows Containers license, here is what popped up:

For production, licensing is at the host level, i.e. each machine or VM which is running Docker. Your Windows licence on the host allows you to run any number of Windows Docker containers on that host. With Windows Server 2016 you get the commercially supported version of Docker included in the licence costs, with support from Microsoft and Docker, Inc.
For development, Docker for Windows runs on Windows 10 and is free, open-source software. Docker for Windows can also run a Linux VM on your machine, so you can use both Linux and Windows containers in development. Like the server version, your Windows 10 licence allows you to run any number of Windows Docker containers.
Windows admins will want a unified platform for managing images and containers. That’s Docker Datacenter which is separately licensed, and will be available for Windows soon.

(From: https://blog.docker.com/2017/01/docker-windows-server-image2docker/)

In simple terms, as long as the underlying Windows Server is properly licensed, you can run Windows Docker Containers with it. And form Windows Server 2016, you have specific licensing terms applying to it.

Conclusion

Linux Containers and Alpine images would be my choice for Java Spring Boot Microservices whenever it is possible.

It's just a matter of checking if your production environment will already be locked into Windows Containers, which will very unlikely be the case, and your decision will be an easy one.

Thanks for the time, and I hope you enjoyed it.