Idea logo

The IntelliJ IDEA Blog

The Capable & Ergonomic Java IDE by JetBrains

Community Features IntelliJ IDEA Java Videos Webinars

Webinar Summary: Java, Containers and IntelliJ IDEA

On December 17, 2020, we hosted the live stream ‘Java, Containers and IntelliJ IDEA’ by the amazing Elder Moraes, RedHat Developer Advocate, Java Champion, Board member at Sou Java, and author of multiple books on Enterprise Java.

The code and presentation used in this live stream by the speaker can be accessed using this link. Elder’s presentation also includes a great comparison and summary of the tips that he demonstrated in his live demonstration.

Session details

In this live stream, Elder addressed four main issues – long build times for Docker images, large image sizes, maintainability issues, and resource allocation. To demonstrate this, he started by building a >660 MB Docker image and that he had reduced to ~95 MB by the end of the session.

How to lower build times for Docker images

Elder started his demonstration by addressing the concern of long build times. He mentioned that it is usual for developers to build hundreds or even thousands of Docker images. Often these images would be built on the CI/CD pipeline. So, any time saved on the build time of an image would make the overall process faster.

He used IntelliJ IDEA to show a Docker file and a Terminal Window to execute commands.

The Docker images included in Elder’s sample code (https://github.com/eldermoraes/java_and_containers) have Docker files with the prefix ‘DO’ or ‘DONT’. For each of these combinations, Elder demonstrated why you should choose one over the other.

By using two docker files with different sequences of commands, Elder demonstrated that commands that correspond to binaries that might change should be placed as late as possible in a Dockerfile. A Docker image is built in layers. When you rebuild an image, it tries to use the cache. However, if any command results in a changed binary, the command that follows it won’t be able to use the cache, resulting in longer build durations. For the following Docker file:

FROM debian:stretch

COPY lib/* /deployment/lib/
COPY *-runner.jar /deployment/app.jar

RUN apt-get update
RUN apt-get -y install openjdk-8-jdk ssh vim

CMD ["java", "-jar", "/deployment/app.jar"]`

Elder modified the sequence of commands as follows (adding application libraries after JDK since the latter might change more frequently):

FROM debian:stretch

RUN apt-get update
RUN apt-get -y install openjdk-8-jdk ssh vim

COPY lib/* /deployment/lib/
COPY *-runner.jar /deployment/app.jar

CMD ["java", "-jar", "/deployment/app.jar"]

Elder said that developers would do well to use specific file names instead of using . When use use , any changes in the folder can lead to cache invalidations. The following line in the preceding dockerfile:

COPY *-runner.jar /deployment/app.jar

…was replaced with the following (so that any changes due to * in the path do not invalidate the cache):

COPY quarkus-1.0.0-SNAPSHOT-runner.jar /deployment/app.jar

Another tip to decrease the build time for Docker images Elder demonstrated was how to create a group unit. He replaced the following commands:

RUN apt-get update
RUN apt-get -y install openjdk-8-jdk ssh vim

… with these:

RUN apt-get update \
&& apt-get -y install \
   openjdk-8-jdk ssh vim

In response to one of the follow-up questions by Jeff Duska, "Isn’t multistage builds better than grouping commands?", Elder accepted they are. He couldn’t show that in this stream because he could only cover the section on Docker in this session.

How to avoid large image sizes

Elder mentioned that one of the ways to reduce Docker images is to deny installation of any automatic recommendations. He suggested that developers shouldn’t include the features they don’t need and are not sure they will need in the future, such as ssh vim, for example. So instead of the following command in their Docker file:

RUN apt-get update \
&& apt-get -y install \
   openjdk-8-jdk ssh vim

…they should prefer the following:

RUN apt-get update \
&& apt-get -y install --no-install-recommends\
   openjdk-8-jdk

Elder showed how this step could result in reducing a Docker image by over 150 MB.

To reduce the size of the Docker image further, Elder suggested that developers should also consider removing the cache of their package manager, by modifying the group unit as follows:

RUN apt-get update \
&& apt-get -y install --no-install-recommends\
   openjdk-8-jdk \
&& rm -rf /var/lib/apt/lists/*

Even though this step managed to reduce only 15 MB from the Docker image, this difference could mean a lot when developers are looking at not just one image or container, but hundreds or even thousands of them.

Elder also suggested using tools and frameworks like Quarkus and GraalVM.

Maintainability

Elder encouraged developers to think of their Docker files as entities that would need to be maintained as code. One of the ways to write maintainable Docker files is to use official images, rather than creating their own (which usually work in most cases, if not all). For example, the following snippet from the preceding Dockerfile:

FROM debian:stretch

RUN apt-get update \
&& apt-get -y install --no-install-recommends\
   openjdk-8-jdk \
&& rm -rf /var/lib/apt/lists/*

…сan be replaced with:

FROM openjdk

Using tags to specify the OpenJDK version can go a long way for developers and even help prevent their applications from breaking. What if you didn’t specify a Java version and in absence of the version number, the Dockerfile pulled the latest Java version, with which your application might not be compatible? Elder replaced the following command:

FROM openjdk

…with the following, using a tag to specify the Java version:

FROM openjdk:8

In the next step, Elder replaced the preceding command with the following to be more specific than just specifying the Java version:

FROM openjdk:8-jre-alpine

Managing Resource Allocation

Elder shared that Java 8u121 and its previous versions were neither aware of containers or cgroups. Elder shared a hack (a script) developed by fabric8 for developers who want to run Java 8u121 or its previous versions in a container (please refer to the speaker’s presentation for details). This script will sit inside a container and set limits for the JVM on the CPU, memory, disk space, and network it can use.

The later Java versions Java 8u131, Java 9, and beyond have improved their support for containers.

Webinar Summary

Elder summarized his live demonstration by stating that Java and containers get along. He also encouraged developers to be intentional when they are building their Dockerfiles, and recommended using Java 11 instead of Java 8.

Docker Support in IntelliJ IDEA

Please refer to IntelliJ IDEA’s Documentation to check out how IntelliJ IDEA can help you get started with Docker, manage images, run and interact with containers, and let you deploy your applications inside containers for testing code in an environment that is identical to production.

You can use this link to download IntelliJ IDEA today.

Our next live stream

We are in the process of planning our next set of webinars and live streams for 2021. Follow us on Twitter so you don’t miss the updates.

Happy developing!

Discover more