CON4429 - in a World of Containers

[email protected] @PaulSandoz [email protected] Director, @MikaelVidstedt

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. 1 Agenda

Producing images and running containers with JDK 9 Size analysis of JDK Docker Images A quick look at startup me

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 2 Java 8 Docker image • Official Java 8 SE (Server JRE) available on the docker store • See also DockerFiles on GitHub

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 3 In a world of containers we expect… • Many distribuons of Java runmes • Forces that push towards – Smaller images – Faster execuon using less resources (and respecng resource constraints)

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 4 Java has plans for a world of containers • Official OpenJDK builds will make it easier to distribute Java runmes • Java 9 tooling can produce custom Java runmes that are smaller • Current and future Java tooling will produce applicaon-specific Java runmes that startup faster

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 5 Producing Docker images with a JDK • Very easy to create a DockerFile that copies (or adds) a tarball of a JDK – A resulng Docker image will be large (> 300MB) • Not necessarily good for development or execuon – More stuff than required to build or run an applicaon

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 6 JDK 9 is modular • JDK 9 is modular and introduces modules to the Java plaorm • A module is a set of packages designed for reuse • Modules improve the reliability and maintainability of your programs • JDK 9 is itself composed of 79 modules

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 7 JDK 9 and custom Java runmes • JDK 9 comes with jlink, a tool that can create custom Java runmes – Such as, a Java runme consisng of just the java.base module • Note: the Java applicaon need not be modular

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 8 Project Portola and Alpine Linux • The OpenJDK Portola Project aims to provide a port of the JDK to the Alpine Linux distribuon • Early access builds of the JDK port are available • jlink can be used to create custom Java runmes for Alpine Linux

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 9 Demo

Creang Docker images with Alpine Linux, Java, and jlink

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 10 Running JDK 9 in Docker containers • The JDK has not necessarily been a model cizen and respecng resource constraints when running in a container • JDK 9 has a few improvements to respect resource constraints – These improvements have been back ported to a JDK 9 release

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 11 Respecng memory limits • The JDK respects group memory limits set for the container (see docker run memory constraints)

-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 12 Respecng CPU constraints • The JDK respects some CPU constraints set for the container (see docker run cpuset constraint) • java.lang.Runtime.availableProcessors reports correct number of CPUs

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 13 Stable execuon • The JVM ensures stable execuon when resources change • G1 Garbage Collector operates on acve CPU count discovered at JVM startup

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 14 Ongoing improvements planned for future releases • JDK-8146115 “Improve Docker container detecon and resource configuraon usage” • More robust container detecon logic – Evaluang support for further docker run flags --cpus --cpu-quota --cpu-period --cpu-shares • New -XX:ActiveProcessorCount flag • Total and Avail memory extracted from cgroup /proc file system

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 15 Ongoing improvements planned for future releases • JDK-8186248 “Allow selecng Heap % of available RAM” • Dra JEP: Container aware Java hp://.java.net/jeps/8182070 – Provide Java API access to container metrics

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 16 Demo

Running Java (jshell), in a docker container, and respecng resource constraints

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 17 For a more comprehensive demonstraon… • See tutorial on running and monitoring a Java applicaon in a Kubernetes cluster • Docker images can be created and published using Wercker

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 18 Size Analysis Viewer discreon advised: Bar charts ahead!

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 19 Docker Image

• Docker image using DockerFile 800 568 – FROM oraclelinux:7 JDK 700 oraclelinux:7 – ADD jdk-9+181-linux-x64_bin.tar.gz 600

500

400

• Let’s opmize! Size(MB) 300

200 229

100

0 Full JDK

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 20 Streamlining the JRE using jlink • Full JDK

– Default JDK (not jlink:ed) 800 568 • JDK java.base 700 oraclelinux:7 – jlink —add-modules java.base 600

• “ney” 500 – A set of modules expected to be sufficient for many Java applicaons 400

• jlink --add-modules Size(MB) 300 java.base, 60 46 java.logging, java.management, 200 229 229 229 java.xml, jdk.management, 100 jdk.unsupported 0 – Note: Does not include the ney Full JDK “netty” java.base applicaon code!

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 21 Streamlining the base image

800 oraclelinux:7 vs oraclelinux:7-slim 568 700 568 JDK 600 • oraclelinux:7 (229 MB) Base – Contains Everything™ …and then 500 some 400

Size(MB) 300 – Certainly more than Java needs 46 60 200 229 229 229 • 46 60 oraclelinux:7-slim (118 MB) 100 118 118 118 – Streamlined to bare necessies 0 – Saves 111 MB • Further opmizaon - Strip out oraclelinux:7 + “netty” oraclelinux:7 + Full JDK oraclelinux:7 + java.base oraclelinux:7-slim + “netty” individual files oraclelinux:7-slim + Full JDK oraclelinux:7-slim + java.base – Analyze shared libraries/dependency graph and strip out unneeded files Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 22 Small. Simple. Secure.

Alpine Linux is a security-oriented, lightweight Linux distribuon based on musl libc and busybox. – hps://www.alpinelinux.org

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 23 Docker Base Images

Docker base image sizes 300 275 250

225 229 200 175 150

Size(MB) 125 100 117.6 75 50 25 3.966 0 oraclelinux:7 oraclelinux:7-slim alpine:3.6

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 24 Java Images Based on alpine:3.6

400

350

300 JDK alpine:3.6 250

200

Size(MB) 150

100

50

0 Full JDK java.base “netty”

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 25 Opmizing java.base with jlink opons

50 • default: no special opons 46 JDK 45 alpine:3.6 • --compress=2 40

35 34 – ZIP compression of resources 31 30

• --strip-debug 25

– Remove all debug informaon Size(MB) 20 – Don’t try this at home! 15 10

5 4 4 4 0 default —compress=2 —compress=2 —strip-debug

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 26 …but wait, there’s more! • What’s the theorecal minimum? • What’s actually in a java.base JRE?

Files Size (bytes) lib/modules 23,529,047 lib/server/libjvm.so 21,197,904 1,545,818 Sum 46,272,769

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 27 JVM Size

Note: Numbers/sizes are approximate

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 28 JVM Size – JIT Compiler(s)

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 29 JVM Size – GC(s)

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 30 JVM Size – Let’s keep one GC: Serial

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 31 JVM Size - Other

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 32 The “minimal” VM • The “minimal” VM weighs in at just Size of JVM variants 25.0 under 5MB – Sll fully Java compliant 20.0 • But 15.0 – Lacks many/most of the addional features Size(MB) 10.0 • No JIT compiler • Only Serial GC 5.0 • Very few debugging/serviceability features

0.0 server minimal • Probably not a good match for producon use-cases, but an interesng data point

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 33 A “minimal” Docker image • HelloWorld in ~20MB – Including the Alpine base image “minimal” VM + java.base 40.0 • More extreme Java runmes JDK 25.3 libjvm.so available can bring this down even 30.0 alpine:3.6 further (with some limitaons)

– SubstrateVM from Oracle Labs 20.0 12.9 10.5 Size(MB)

10.0 4.8 4.8 4.8

4.0 4.0 4.0 0.0 default —compress=2 —strip-debug

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 34 Future: Stripping out unused classes • Not every class in a module is necessarily used by the applicaon • Finding out which classes to use is non-trivial #classes Size (bytes) – Indeterminism (halng problem) All java.base classes 5714 19,178,884 Classes used by HelloWorld 506 8,796,290 – Reflecon 9% 46% • Area of research, stay tuned

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 35 Sharing across instances

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Sharing Across Instances • Micro-services and Docker encourages running many processes on the same machine • Chances are many instances will be running the exact same applicaon • OS shared libraries allows for sharing nave data • libc, libjvm.so all get shared automacally by the OS & Docker – Assuming same layer/file/inode • What about Java class data?

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 37 Class Data Sharing (CDS) – Dump me • Dump me process / “training run” – Preload a set of classes – Classes are parsed into their Java VM private internal representaons (class metadata) • Metadata is split into read-only (RO) and read-write (RW) parts, and allocated in separate memory regions – All loaded class metadata is saved to a file (the shared archive) A.class • New in JDK 9: Applicaon Class Data Sharing (AppCDS) B.class – Applicaon classes are supported C.class

CDS archive

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 38 Class Data Sharing (CDS) - Runme • Archive is memory-mapped • RO pages shared, RW pages are shared copy-on-write • Classes read from mapped memory without overhead of searching, reading & parsing from JAR files • Like shared libraries, archive can be shared across Docker containers

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 39 AppCDS Benefits - Startup me and Footprint Example: WebLogic Server Base Domain Footprint Startup Time 10000 12 11 11.4 4,634 4,073 No AppCDS 4,652 4,137 10 AppCDS 9 1000

8 7 7.7

6 100

Time (s) Time 5 66

4 20 3 10

2 Note:Logarithmic!Size- (MB)

1

0 1 No AppCDS AppCDS Unique Shared Total • Sharing & savings increases with every instance • With 10 instances there is ~10% saving in total memory footprint

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 40 Summary – Opmizing a Java Docker Image • The naïve Java Docker image is large - 229MB base + 568MB JDK = 797MB • Can be significantly reduced – Using an appropriate base image • 117MB for oraclelinux:7-slim • 4MB for alpine:3.6 – Creang a custom jlinked JRE • ~60MB for “ney” • ~46MB for “java.base” • HelloWorld can be packaged with a full JVM in ~30MB • AppCDS enables sharing class data across JVM instances and Docker containers

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 41 A quick look at startup me

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 42 Ahead-of-Time (AOT) compilaon • Does with JIT compiled Java code what AppCDS does with Java class data – Pre-compile to shared library: jaotc --output libHelloWorld.so HelloWorld.class – Use in subsequent runs: java -XX:AOTLibrary=libHelloWorld.so HelloWorld • Experimental funconality introduced in JDK 9 – Only on linux-x64, other plaorms to follow • Benefits – Startup performance – Reduces me to peak performance – Saves on footprint by sharing across instances • Not-so-secret plan: use for enabling Java-based JIT compiler – OpenJDK Project “Metropolis” aims to move JVM funconality to Java

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 43 Docker+Java startup me Example Applicaon • Host java – Create socket – Setup java arguments for ProcessBuilder – Start docker image with Java app • Docker java – Create socket and connect to host – Send message and exit

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 44 Execuon Analysis

Average Startup Time for 50 clients in ms 1400

1200

1000

800 Java no CDS or AOT

Java with APPCDS & AOT

GO 600 SVM

400

200

0 Host Docker Run Docker Exec

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 45 ======CDS AOT GC Average start-up time for 50 iterations ======NO NO Parallel 296.776495 task-clock (msec) # 1.644 CPUs utilized ( +- 0.78% ) NO NO ParallelOld 299.844332 task-clock (msec) # 1.653 CPUs utilized ( +- 0.82% ) NO NO G1 385.857827 task-clock (msec) # 1.948 CPUs utilized ( +- 1.22% ) NO NO Serial 268.299592 task-clock (msec) # 1.597 CPUs utilized ( +- 0.81% ) NO YES Parallel 184.101911 task-clock (msec) # 1.118 CPUs utilized ( +- 1.20% ) NO YES ParallelOld 192.454881 task-clock (msec) # 1.128 CPUs utilized ( +- 1.01% ) NO YES G1 289.153747 task-clock (msec) # 1.541 CPUs utilized ( +- 1.38% ) NO YES Serial 169.313077 task-clock (msec) # 1.047 CPUs utilized ( +- 0.80% ) YES NO Parallel 229.502306 task-clock (msec) # 2.069 CPUs utilized ( +- 0.76% ) YES NO ParallelOld 232.277575 task-clock (msec) # 2.144 CPUs utilized ( +- 0.90% ) YES NO G1 301.349409 task-clock (msec) # 2.221 CPUs utilized ( +- 1.16% ) YES NO Serial 206.965560 task-clock (msec) # 1.975 CPUs utilized ( +- 0.69% ) YES YES Parallel 128.244920 task-clock (msec) # 1.337 CPUs utilized ( +- 1.06% ) YES YES ParallelOld 126.970865 task-clock (msec) # 1.303 CPUs utilized ( +- 1.01% ) YES YES G1 200.728556 task-clock (msec) # 1.647 CPUs utilized ( +- 1.35% ) YES YES Serial 91.769762 task-clock (msec) # 1.104 CPUs utilized ( +- 1.64% )

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 46 Q&A

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 47 Stay connected

• Join us: DevOps Corner (Developer Lounge – Moscone West) • Learn more: openjdk.java.net | wercker.com/java • Follow: @OpenJDK, @wercker #JavaOne #DevOps

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | 48

Example DockerFile (jdk-9-alpine.Dockerfile) for building an image with JDK 9

# A JDK 9 development image for building FROM alpine:3.6 # Add the musl-based JDK 9 distribution RUN mkdir /opt # Download from http://jdk.java.net/9/ ADD jdk-9-ea+181_linux-x64-musl_bin.tar.gz /opt # Set up env variables ENV JAVA_HOME=/opt/jdk-9 ENV PATH=$PATH:$JAVA_HOME/bin CMD ["java", "-version"]

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Confidenal – Oracle Internal/Restricted/Highly Restricted 50 Example script (link.sh) using jlink to produce a smaller JDK 9 distribuon

#!/bin/sh rm -fr jdk-9-base-ea+181_linux-x64-musl docker run --rm \ --volume $PWD:/out \ jdk-9-alpine \ jlink --module-path /opt/jdk-9/jmods \ --add-modules java.base \ --compress 2 \ --no-header-files \ --output /out/jdk-9-base-ea+181_linux-x64-musl

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Confidenal – Oracle Internal/Restricted/Highly Restricted 51 Example DockerFile (jdk-9-base-alpine.Dockerfile) for building an image with a custom JDK 9 distribuon

# Make a docker image with a jlink'ed JDK 9 FROM alpine:3.6 # Add jlink'ed JDK 9 ADD jdk-9-base-ea+181_linux-x64-musl /opt/jdk-9 # Set up env variables ENV JAVA_HOME=/opt/jdk-9 ENV PATH=$PATH:$JAVA_HOME/bin CMD ["java", "-version"]

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Confidenal – Oracle Internal/Restricted/Highly Restricted 52 Commands to build images

# Build an image with JDK 9 docker build -t jdk-9-alpine -f jdk-9-alpine.Dockerfile .

# Create a custom JDK 9 distribution with just the java.base module bash link.sh

# Build an image with the custom JDK 9 distribution docker build -t jdk-9-base-alpine -f jdk-9-base-alpine.Dockerfile .

# Run the image docker run --rm jdk-9-base-alpine java --list-modules

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Confidenal – Oracle Internal/Restricted/Highly Restricted 53 jshell snippets (snippets.txt) long javaMaxMem() { return Runtime.getRuntime().maxMemory(); } import java.nio.file.*; long sysMaxMem() throws IOException { return Files.lines(Paths.get("/sys/fs/cgroup/memory/memory.limit_in_bytes")) .mapToLong(Long::valueOf) .findFirst().getAsLong(); }

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Confidenal – Oracle Internal/Restricted/Highly Restricted 54 jshell in Docker execuon commands

# Run without resource restrictions docker run \ --rm -it --volume $PWD:/in jdk-9-alpine \ jshell /in/snippets.txt

# Run with resource restrictions but no Java configutation docker run -m=384M --cpuset-cpus=0 \ --rm -it --volume $PWD:/in jdk-9-alpine \ jshell /in/snippets.txt

# Run with resource restrictions and Java configutation docker run -m=384M --cpuset-cpus=0 \ --rm -it --volume $PWD:/in jdk-9-alpine \ jshell /in/snippets.txt \ -J-XX:+UnlockExperimentalVMOptions -J-XX:+UseCGroupMemoryLimitForHeap \ -R-XX:+UnlockExperimentalVMOptions -R-XX:+UseCGroupMemoryLimitForHeap

Copyright © 2017, Oracle and/or its affiliates. All rights reserved. | Confidenal – Oracle Internal/Restricted/Highly Restricted 55