Creating a lightweight Java environment that runs on Docker

Based on Java in a World of Containers, which was given in Japanese at the Oracle Groundbreakers APAC Tour in Tokyo on 11/13. I am writing in.

Introduction

The figure below is a module graph of java9 or later. DD_IQdaVoAAc6Pu.jpg

When writing an application in Java, it is rare to use everything in this module graph. For example, java.xml, corba, javaws are often unnecessary.

If you create a Docker image with Linux and JDK without thinking about it with Docker, it will be quite large. Therefore, it seems that containers and java are often said to be incompatible. Large capacity, heavy, slow startup, etc. Still, Java's features are also ideal for containers. There are many benefits such as runtime, hardware and OS independence, JVM-guaranteed security compatibility, guaranteed stable execution when the environment changes, ecosystem, and the best choice of containers. .. You can improve the JDK that is too heavy by default by modularizing the JDK itself with Jigsaw and organizing the dependencies.

Dockerfile

The OS creates an image with ubuntu: latest. At first, let's create an environment and play Hello World with the default full size.

Dockerfile


FROM ubuntu:latest
ADD openjdk-11.0.1_linux-x64_bin.tar.gz /opt/jdk/
ADD HelloWorld.class HelloWorld.class
ENV PATH /opt/jdk/jdk-11.0.1/bin:$PATH
CMD [ "java", "-showversion", "HelloWorld" ]

Build and run the image.

$ docker build -t helloworld-java .
$ docker run --rm helloworld-java
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
Hello World!

Let's take a look at the created image.

$ docker images helloworld-java
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
helloworld-java     latest              682ae9922b5b        2 minutes ago       396MB

There is about 400MB.

Custom JRE

Create a custom JRE using jdeps and jlink. You can see the module dependencies by using jdeps. Let's take a look at the Hello World module.

$ jdeps --list-deps HelloWorld.class
   java.base

Since the content is Hello World, there are few dependencies. At the HelloWorld level, it seems that you are only using the java.base module.

Let's create a custom JRE with jlink.

 jlink --compress=2 --module-path $JAVA_HOME/jmods --add-modules java.base --output jre-min

Since we are only using java.base, add-modules only this is specified. After execution, jre-min is created. In this environment, Hello World is the minimum required to run. The capacity was also 27MB.

python


.
├── Dockerfile
├── HelloWorld.class
├── HelloWorld.java
├── hello.jar
├── jre-min
│   ├── bin
│   │   ├── java
│   │   └── keytool
│   ├── conf
│   │   ├── net.properties
│   │   └── security
│   │       ├── java.policy
│   │       ├── java.security
│   │       └── policy
│   │           ├── README.txt
│   │           ├── limited
│   │           │   ├── default_US_export.policy
│   │           │   ├── default_local.policy
│   │           │   └── exempt_local.policy
│   │           └── unlimited
│   │               ├── default_US_export.policy
│   │               └── default_local.policy
│   ├── include
│   │   ├── classfile_constants.h
│   │   ├── darwin
│   │   │   └── jni_md.h
│   │   ├── jni.h
│   │   ├── jvmti.h
│   │   └── jvmticmlr.h
│   ├── legal
│   │   └── java.base
│   │       ├── COPYRIGHT
│   │       ├── LICENSE
│   │       ├── aes.md
│   │       ├── asm.md
│   │       ├── c-libutl.md
│   │       ├── cldr.md
│   │       ├── icu.md
│   │       ├── public_suffix.md
│   │       └── unicode.md
│   ├── lib
│   │   ├── classlist
│   │   ├── jli
│   │   │   └── libjli.dylib
│   │   ├── jrt-fs.jar
│   │   ├── jspawnhelper
│   │   ├── jvm.cfg
│   │   ├── libjava.dylib
│   │   ├── libjimage.dylib
│   │   ├── libjsig.dylib
│   │   ├── libnet.dylib
│   │   ├── libnio.dylib
│   │   ├── libosxsecurity.dylib
│   │   ├── libverify.dylib
│   │   ├── libzip.dylib
│   │   ├── modules
│   │   ├── security
│   │   │   ├── blacklisted.certs
│   │   │   ├── cacerts
│   │   │   ├── default.policy
│   │   │   └── public_suffix_list.dat
│   │   ├── server
│   │   │   ├── Xusage.txt
│   │   │   ├── libjsig.dylib
│   │   │   └── libjvm.dylib
│   │   └── tzdb.dat
│   └── release
└── openjdk-11.0.1_linux-x64_bin.tar.gz

Let's run Hello World in this minimal environment.

$ jre-min/bin/java -showversion HelloWorld
java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)
Hello World!

Docker build with minimal java

Let's create a custom JRE with jlink and build it.

Dockerfile


FROM ubuntu:latest AS build
ADD openjdk-11.0.1_linux-x64_bin.tar.gz /opt/jdk/
ENV PATH /opt/jdk/jdk-11.0.1/bin:$PATH
RUN ["jlink", "--compress=2", "--module-path", "/opt/jdk/jdk-11/jmods", "--add-modules", "java.base", "--output", "/linked"]

FROM ubuntu:latest
COPY --from=build /linked /opt/jdk/
ENV PATH=$PATH:/opt/jdk/bin
ADD HelloWorld.class /

CMD [ "java", "-showversion", "HelloWorld" ]
$ docker images
REPOSITORY        TAG        IMAGE ID            CREATED             SIZE
helloworld-java   latest     e71329542c40        2 minutes ago       123MB
$ docker run -it helloworld-java
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
Hello World!

The size is less than a quarter of the previous one.

Lighter environment construction Docker build

We have the minimum required Java, but we still have 123MB. If only the method was used earlier, only Java could be made lighter, so we will also modify the OS. If you just want to run ava, ubuntu is too luxurious. Therefore, use alpine linux to create the minimum required linux environment. alpine is a lightweight Linux based on musl libc and BusyBox (it really doesn't contain anything ...). The minimum configuration seems to be lighter than 5MB. Refer to alpine-based Java environment Dockerfile published by AdoptOpenJDK Create an image with minimal configuration.

Dockerfile


FROM adoptopenjdk/openjdk11:alpine-slim AS jlink
RUN ["jlink", "--compress=2", \
     "--module-path", "/opt/java/openjdk/jmods", \
     "--add-modules", "java.base", \
     "--output", "/jlinked"]


FROM alpine

RUN apk --update add --no-cache ca-certificates curl openssl binutils xz \
    && GLIBC_VER="2.28-r0" \
    && ALPINE_GLIBC_REPO="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" \
    && GCC_LIBS_URL="https://archive.archlinux.org/packages/g/gcc-libs/gcc-libs-8.2.1%2B20180831-1-x86_64.pkg.tar.xz" \
    && GCC_LIBS_SHA256=e4b39fb1f5957c5aab5c2ce0c46e03d30426f3b94b9992b009d417ff2d56af4d \
    && ZLIB_URL="https://archive.archlinux.org/packages/z/zlib/zlib-1%3A1.2.9-1-x86_64.pkg.tar.xz" \
    && ZLIB_SHA256=bb0959c08c1735de27abf01440a6f8a17c5c51e61c3b4c707e988c906d3b7f67 \
    && curl -Ls https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
    && curl -Ls ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-${GLIBC_VER}.apk > /tmp/${GLIBC_VER}.apk \
    && apk add /tmp/${GLIBC_VER}.apk \
    && curl -Ls ${GCC_LIBS_URL} -o /tmp/gcc-libs.tar.xz \
    && echo "${GCC_LIBS_SHA256}  /tmp/gcc-libs.tar.xz" | sha256sum -c - \
    && mkdir /tmp/gcc \
    && tar -xf /tmp/gcc-libs.tar.xz -C /tmp/gcc \
    && mv /tmp/gcc/usr/lib/libgcc* /tmp/gcc/usr/lib/libstdc++* /usr/glibc-compat/lib \
    && strip /usr/glibc-compat/lib/libgcc_s.so.* /usr/glibc-compat/lib/libstdc++.so* \
    && curl -Ls ${ZLIB_URL} -o /tmp/libz.tar.xz \
    && echo "${ZLIB_SHA256}  /tmp/libz.tar.xz" | sha256sum -c - \
    && mkdir /tmp/libz \
    && tar -xf /tmp/libz.tar.xz -C /tmp/libz \
    && mv /tmp/libz/usr/lib/libz.so* /usr/glibc-compat/lib \
    && apk del binutils \
    && rm -rf /tmp/${GLIBC_VER}.apk /tmp/gcc /tmp/gcc-libs.tar.xz /tmp/libz /tmp/libz.tar.xz /var/cache/apk/*

COPY --from=jlink /jlinked /opt/jdk/

ADD HelloWorld.class /
CMD ["/opt/jdk/bin/java", "-showversion", "HelloWorld"]
$ docker build -t helloworld-java .
$ docker images
REPOSITORY                TAG                 IMAGE ID            CREATED              SIZE
helloworld-java           latest              407614883e2b        38 seconds ago       52.1MB

It is less than half of the previous one. It is about 1/8 of the original one.

Of course it can be executed

$ docker run -it helloworld-java
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.1+13)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.1+13, mixed mode)
Hello World!

Summary

It seems that containers and java are often said to be incompatible with each other due to their large capacity, heavy weight, and slow startup. After JDK9, you can create a dedicated environment with jdeps and jlink. Since it is possible to create the minimum necessary environment, it is possible to create a light container. By modifying the OS, you can create an environment that is even lighter and you like.

Recommended Posts

Creating a lightweight Java environment that runs on Docker
Create a docker image that runs a simple Java app
Build a Java development environment on Mac
Creating a java web application development environment with docker for mac part1
Consolidate your JavaFX app into a jar that runs on both Java 8/11
Prepare a scraping environment with Docker and Java
Build a development environment for Docker, java, vscode
Build a Java runtime environment on Sakura VPS
Create a Java development environment using jenv on Mac
[Note] Create a java environment from scratch with docker
A command that definitely cleans the local docker environment
Try to build a Java development environment using Docker
Oracle Java 8 on Docker Ubuntu
Creating a docker host on AWS using Docker Machine (personal memorandum)
Try Hello World using plain Java on a Docker container
Create a Vue3 environment with Docker!
Build a Node.js environment with Docker
Create a lightweight STNS Docker image
Run React on a Docker container
Prepare a transcendentally simple PHP & Apache environment on Mac with Docker
(For myself) Try creating a C # environment with docker + code-server, cloud9
Build a XAMPP environment on Ubuntu
Rails on Docker environment construction procedure
Run PureScript on a Docker container
Building a haskell environment with Docker + VS Code on Windows 10 Home
Build Unity development environment on docker
Install Java development environment on Mac
Build a web application development environment that uses Java, MySQL, and Redis with Docker CE for Windows
Access MySQL on a Docker container from a local (host OS) Java program
How to build a Ruby on Rails environment using Docker (for Docker beginners)
How to build a Ruby on Rails development environment with Docker (Rails 6.x)
How to build a Ruby on Rails development environment with Docker (Rails 5.x)
Create a java web application development environment with docker for mac part2
Build a PureScript development environment with Docker
Java development environment construction memo on Mac
Use docker in proxy environment on ubuntu 20.04.1
[Creating] A memorandum about coding in Java
[Java] Stepping on a JDK compiler bug
Create a MySQL environment with Docker from 0-> 1
Build a WAS execution environment from Docker
Let's create a Java development environment (updating)
[Ruby] Building a Ruby development environment on Ubuntu
Build Java 8 development environment on AWS Cloud9
Spring Boot + Docker Java development environment construction
Deploy a Java web app on Heroku
Build Redmine code reading environment on Docker
Creating a matrix class in Java Part 1
Build an environment with Docker on AWS
Build a JMeter environment on your Mac
Try the Docker environment on AWS ECS
Lightweight PHP 7.4 development environment created with Docker
Install Docker and create Java runtime environment
Build a simple Docker + Django development environment
A memo that installed Ubuntu 20.4 on HP Z2 Mini G4 and created a deep learning environment with Python Docker
[First environment construction] I tried to create a Rails 6 + MySQL 8.0 + Docker environment on Windows 10.
I built a Java EE environment on AWS and tried running a web application
Build a development environment to create Ruby on Jets + React apps with Docker
[Part 1] Creating a Docker container that delivers Markdown in HTML with Apache / Pandoc
Build a development environment for Docker + Rails6 + Postgresql
Try to build Java8 environment on Amazon Linux2
[Memo] Create a CentOS 8 environment easily with Docker