Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add a slim version of the Corretto JDK 15 Docker images. #38

Closed
wants to merge 18 commits into from

Conversation

luketn
Copy link
Contributor

@luketn luketn commented Oct 24, 2020

Issue #34

Description of changes:
Add a slim version of the Corretto JDK 15 Docker images.

JLink is used (retaining all modules) to create a slimmer version of the JDK excluding man-pages, header files and debugging symbols.

The size saved is significant:

Uncompressed:
Alpine Linux: 175 MB (183 MB smaller)
image

Amazon Linux 2: 357 MB (113 MB smaller)
image

The compressed sizes when pushed to DockerHub are pretty good too.
Alpine Linux: 58 MB (140 MB smaller)
Amazon Linux 2: 123 MB (83 MB smaller)

Ref:
https://hub.docker.com/r/luketn/amazoncorretto/tags
https://hub.docker.com/_/amazoncorretto?tab=tags&page=1&name=15-al&ordering=last_updated
image

Note that the objcopy linux tool is required for jlink to run, without which the error will occur:
"java.io.IOException: Cannot run program “objcopy”: error=2, No such file or directory"
Ref: https://command-not-found.com/objcopy

Also note I have not attempted to update any of the Travis build files - only the proposed new Dockerfiles are added with this PR, meaning further work would be required to integrate them into the CI/CD pipeline.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

JLink is used (retaining all modules) to create a slimmer version of the JDK excluding man-pages, header files and debugging symbols - saving ~113MB.

Note that the objcopy linux tool is required for jlink to run, without which the error will occur:
"java.io.IOException: Cannot run program “objcopy”: error=2, No such file or directory"
Ref: https://command-not-found.com/objcopy
Copy link

@joshenders joshenders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments on the Debian Dockerfile.

#
# Slim:
# JLink is used (retaining all modules) to create a slimmer version of the JDK excluding man-pages, header files and debugging symbols - saving ~113MB.
RUN set -eux \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe your boolean ANDs obviates -e usage. In my testing, the build fails with or without -e when any command in the chain fails.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I also tested a failing command in the chain and saw the build fail.

&& apt-get update \
&& apt-get install -y --no-install-recommends \
curl ca-certificates gnupg software-properties-common fontconfig java-common \
&& curl -fL https://apt.corretto.aws/corretto.key | apt-key add - \
Copy link

@joshenders joshenders Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am aware that this is the official method recommended by Amazon however, this adds the key in manner which grants it validity over any software repository on the system, including those not distributed by Amazon. I understand this is quite pedantic but for illustrative purposes, the most correct and idiomatic way to add repositories on Debian is to add the following two apt config files:

  1. /etc/apt/sources.list.d/apt.corretto.aws.list
  2. /etc/apt/preferences.d/apt.corretto.aws.pref

And a gpg de-armored key (gpg --dearmor) here: /usr/share/keyrings/apt.corretto.aws.gpg.

apt.corretto.aws.list can be created with:

echo 'deb [signed-by=/usr/share/keyrings/apt.corretto.aws.gpg] https://apt.corretto.aws stable main' | tee /etc/apt/sources.list.d/apt.corretto.aws.list

The contents of apt.corretto.aws.pref should look like this:

Package: *
Pin: origin apt.corretto.aws
Pin-Priority: 1

Package: java-1.8.0-amazon-corretto-jdk
Pin: origin apt.corretto.aws
Pin-Priority: 500

Package: java-11-amazon-corretto-jdk
Pin: origin apt.corretto.aws
Pin-Priority: 500

Package: java-15-amazon-corretto-jdk
Pin: origin apt.corretto.aws
Pin-Priority: 500

This method also avoids pulling in software-properties-common for apt-key.

See:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

java-common can probably be removed as well. This is pulled in automatically as a dependency to corretto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the unnecessary java-common apt-get install.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the apt-get install, I struggled with this one. Could I suggest that this update be made as a separate PR to both this slim Dockerfile and the regular JDK Dockerfile by someone with better expertise in the Debian OS?

I attempted to follow the instructions in the given reference documentation however saw errors with GPG validation during apt-get that I do not feel sufficiently well versed in Debian to address. I'm concerned that if I push through and implement something that I will inadvertently introduce bugs that would not be present using the standard install process.

Copy link

@joshenders joshenders Dec 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited my comment for clarity and future reference but I think your comment makes perfect sense. This policy should really be reviewed for all of these Debian derived containers and updated in a separate PR along with a corresponding update to the official documentation.

&& mkdir -p /usr/share/man/man1 || true \
&& apt-get update \
&& apt-get install -y java-15-amazon-corretto-jdk=1:$version \
&& apt-get install -y binutils-i586-linux-gnu \
Copy link

@joshenders joshenders Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why binutils-i586-linux-gnu and not binutils-i686-linux-gnu or just binutils? Also why isn't this grouped into the same invocation with the line above it? It could also be moved into the earlier apt-get install for clarity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point - grouped the install into the previous apt-get install, and used the more generic 'binutils' package.
As a benefit I no longer need to symbolic link the objcopy in the line below as binutils does this.

Copy link

@joshenders joshenders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments on the Debian Dockerfile after playing with it a bit more locally

curl ca-certificates gnupg software-properties-common fontconfig java-common \
&& curl -fL https://apt.corretto.aws/corretto.key | apt-key add - \
&& add-apt-repository 'deb https://apt.corretto.aws stable main' \
&& mkdir -p /usr/share/man/man1 || true \
Copy link

@joshenders joshenders Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|| true is unnecessary here. -p returns a zero exits status on failure to create the directory or intermediate path if it exists. (You wouldn't really want to hide this error even if it were necessary though...)

$ mkdir -p /tmp/test/it && mkdir -p /tmp/test/it
$ echo $?
0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

&& apt-get update \
&& apt-get install -y java-15-amazon-corretto-jdk=1:$version \
&& apt-get install -y binutils-i586-linux-gnu \
&& ln -s /usr/bin/i686-linux-gnu-objcopy /usr/bin/objcopy \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my testing this is already symlink'd to the correct objcopy. This may be unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) saw that too after switching to binutils.

Comment on lines 26 to 27
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
curl gnupg software-properties-common \
Copy link

@joshenders joshenders Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to move this to the end and include binutils as well? Additionally, does it make sense to also: apt-get clean --assume-yes && rm -rf /var/lib/apt/lists/*

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. I moved to the end and added binutils to the removed packages.
Removing binutils reduced the final image size by 15MB (from 268MB to 253MB).

Comment on lines 28 to 29
&& for line in $(java --list-modules); do echo -n "${line%@15}," >> modules-comma-separated.txt; done \
&& jlink --add-modules "$(cat modules-comma-separated.txt)" --no-man-pages --no-header-files --strip-debug --output /opt/corretto-slim \
Copy link

@joshenders joshenders Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be rewritten as the following single line without any temporary files:

jlink --add-modules "$(java --list-modules | sed -e 's/@[0-9].*$/,/' | tr -d \\n)" --no-man-pages --no-header-files --strip-debug --output /opt/corretto-slim

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Much cleaner. Also handles the newer minor version suffixes :). Updated on the other Dockerfiles as well.

@joshenders
Copy link

joshenders commented Dec 24, 2020

Forgot to mention, thanks for your effort here @luketn 😃. Since it will be used by many as a base image, I just want to make sure it's the highest possible quality before it gets merged.

@luketn
Copy link
Contributor Author

luketn commented Dec 29, 2020

Thanks for the awesome review @joshenders!

I've updated based on all of the comments, addressing all the issues with the exception of the GPG apt-get change which is a bit beyond my Debian skills.

If you do merge this PR, would you also add to the automation code to build and publish these new Dockerfiles to DockerHub? I'm happy to help make changes there too if needed, but felt that might be outside of the scope of a new contributor.

@luketn
Copy link
Contributor Author

luketn commented Dec 29, 2020

@joshenders - the review also managed to reduce the size of the image further as well (due to uninstalling binutils at the end - good thinking!).
e.g. the Alpine Linux compressed image is now down by 2.31MB (reduced from 58.19 MB to 55.88 MB):
https://hub.docker.com/layers/luketn/amazoncorretto/15-alpine-jdk-slim/images/sha256-88621b6a1b08cb4b2d50a7127ec081abb927f076d5e2232ccfe8105c3128b44a?context=explore

I also pushed all three Linux flavours to my personal DockerHub in case you'd like to see what the finished images would look like:
https://hub.docker.com/r/luketn/amazoncorretto/tags?page=1&ordering=last_updated

@LS80
Copy link

LS80 commented Jan 4, 2021

@luketn and @joshenders Thanks for working on this! Do you intend to add a slim version of the JDK 11 images also?

@luketn
Copy link
Contributor Author

luketn commented Jan 5, 2021

@luketn and @joshenders Thanks for working on this! Do you intend to add a slim version of the JDK 11 images also?

There actually already was a JDK 11 'JRE' Dockerfile in this repo (only for Alpine Linux). It appears not to have been published to DockerHub though.

I think it would be great if we could agree on a standard approach for a slim/jre build of all Corretto 11+ distributions on all Linux flavours.

@cliveverghese
Copy link
Contributor

Thank you for your feedback @luketn and @joshenders, We appreciate the interest in Corretto Slim images for Corretto-11 and above. However, The approach of using JLink was something that was previously discouraged by Dockerhub team..

The recommendation by joshenders is also valuable and we would like to incorporate this into the debian based docker images. If you have a Pull Request for that Please feel free to submit the same.

@luketn
Copy link
Contributor Author

luketn commented Jan 14, 2021

Thank you for your feedback @luketn and @joshenders, We appreciate the interest in Corretto Slim images for Corretto-11 and above. However, The approach of using JLink was something that was previously discouraged by Dockerhub team..

@cliveverghese I read through the comments by Dockerhub team. I think you're referring to this comment?:

I would think that if you want to publish a JRE image, you would probably want to also publish an "official" JRE package, like the AdoptOpenJDK community does, right?

The comment from the Dockerhub team doesn't seem to be counselling against creating a slim/JRE build - only proposing to expand it beyond just being a docker image to being an official separate package. I can't comment whether that would be a possibility, however I think that the size of the Corretto JDK package and the size of the Corretto docker image have different implications.

Since Docker uses layered image caching, you couldn't use the full Corretto JDK as a base image to construct a slim/JRE image. You would essentially have to start over and build from a base Linux image. The Dockerhub team acknowledge this in the next comment they make:

(Although my preference is still pretty strongly in favor of creating proper separate JRE packages, especially given how poorly multi-stage builds like this will interact with the Docker build cache.)

I think a compact version of the Corretto docker image would have a major impact on the Corretto JDK community. It would save a huge volume of unnecessary bytes being copied from disk into memory and over the network millions of times over.

My personal use-case as an example:
I am building Java services based on the Corretto 15 Amazon Linux 2 base image to deploy as Fargate tasks/services. I like the support implications of using Amazon Linux 2 within the AWS environment, and similarly the support implications of using the Corretto JDK too. I would like the base image to be as small as possible so that my services startup fast and use the least resources whilst doing so. Most of the size would come from the OS and JDK layers. Only a small percentage of the size comes from my service code. e.g. If my service was a 10MB jar:

amazonlinux:2 - 163MB (34%)
amazoncorretto:15-al2-jdk - 307MB (64%)
someservice:1 - 10MB (2%)

With the slim docker image we could save 45% of the Corretto layer, reducing it from 307MB to 170MB.

I could imagine a huge number of use-cases like mine which could benefit from a smaller distribution size.

If there is a possibility of reducing the size, either through this PR's approach or through another approach, I'd urge you to consider it.

@cliveverghese
Copy link
Contributor

We understand the use case and are would like to merge the changes, However, would you consider the following changes to the pull request before proceeding

  1. Could you remove the Amazon Corretto using the package manager command instead of rm so that the package manager is aware that the package is no longer installed.
  2. Could you also update the test case at verify-images.yml to include the new images?

@luketn
Copy link
Contributor Author

luketn commented Jan 17, 2021

Thanks @cliveverghese - that's awesome!

Re: the two points:

  1. Could you remove the Amazon Corretto using the package manager command instead of rm so that the package manager is aware that the package is no longer installed.

This is a little complex. I tried doing this (just swapping rm for yum remove), but it breaks the image. At the moment, we install the JDK with yum and then swap out the JDK directory with the jlink'd version. All the other configuration done by yum stays in place. However, when I tried removing the package with yum, the system loses the configured java context so java is no longer on the path and configured with alternatives etc... I could go through the yum installer and replicate whatever it was doing by hand if needed - otherwise would you consider leaving it as-is? To be honest I'm not sure what else yum / apt does when the JDK installer runs - it might not be much - I'll look into it.

  1. Could you also update the test case at verify-images.yml to include the new images?

Done. I've run the GitHub actions workflows on my fork and they are all passing. See results here:
https://github.com/luketn/corretto-docker/actions/runs/490921998

@luketn
Copy link
Contributor Author

luketn commented Jan 18, 2021

@cliveverghese - further on point 1:

  1. Could you remove the Amazon Corretto using the package manager command instead of rm so that the package manager is aware that the package is no longer installed.

I took a look at the RPM and DEB package builds in the corretto-jdk repository. It looks as though the post install scripts use the alternatives command to register Java symbolic links in /usr/bin:
RPM
https://github.com/corretto/corretto-jdk/blob/develop-jdk15/installers/linux/universal/rpm/scripts/postin_java.sh.template
and
https://github.com/corretto/corretto-jdk/blob/develop-jdk15/installers/linux/universal/rpm/scripts/postin_javac.sh.template
DEB
https://github.com/corretto/corretto-jdk/blob/develop-jdk15/installers/linux/universal/deb/scripts/postin_jdk.sh.template

I think we could register the new jlink'd slim JDK directory with alternatives / update-alternatives again after removing the original installed Corretto JDK.

This would also have the advantage of not setting man pages which don't exist any more following the jlink too - these would otherwise be dangling symbolic links.

I'll have a go at replicating these alternatives scripts in the Dockerfile for each OS.

@@ -8,13 +8,20 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
version: [8, 11, 15]
version-package: [8-jdk, 11-jdk, 15-jdk, 15-slim]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I've included the JRE 8 verification using the same include syntax, which cleans up the build a bit more too.

echo "${SHA_SUM} /etc/apk/keys/amazoncorretto.rsa.pub" | sha256sum -c - && \
echo "https://apk.corretto.aws" >> /etc/apk/repositories && \
apk add --no-cache amazon-corretto-15=$version-r0 binutils && \
/usr/lib/jvm/default-jvm/bin/jlink --add-modules "$(java --list-modules | sed -e 's/@[0-9].*$/,/' | tr -d \\n)" --no-man-pages --no-header-files --strip-debug --output /opt/corretto-slim && \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Using incubator modules: jdk.incubator.foreign, jdk.incubator.jpackage

Should these be filtered out? Incubating features must be opted into with the compiler, so it seems like they should also be opted into for your runtime image.

Suggested change
/usr/lib/jvm/default-jvm/bin/jlink --add-modules "$(java --list-modules | sed -e 's/@[0-9].*$/,/' | tr -d \\n)" --no-man-pages --no-header-files --strip-debug --output /opt/corretto-slim && \
/usr/lib/jvm/default-jvm/bin/jlink --add-modules "$(java --list-modules | grep -v incubator | sed -e 's/@[0-9].*$/,/' | tr -d \\n)" --no-man-pages --no-header-files --strip-debug --output /opt/corretto-slim && \

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend keeping these incubator modules in the slim JDK image. They are in the full JDK image too and I don't think they do any harm to the runtime of applications which do not use them. I could imagine some use-cases for them, particularly the foreign memory mapping one. I think one of the benefits of keeping all modules is that we're not imposing any opinion on what should or shouldn't be included in the JDK - only reducing the size by omitting files required only at development time.

@cliveverghese
Copy link
Contributor

Hi,
I have merged this PR as part of #50.
You can view the commit at bdbc4c3

Thank you for your effort in adding Slim images for Corretto-15.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants