4

I've created a generic docker building file which includes utilities such as kubectl and Helm, but now I want to add adoptopenjdk to the image along with Maven, but when I test it Java crashes with a core dump.

Here is the Dockerfile:

# docker-build-test-image

FROM    docker:stable-git

ENV     KUBERNETES_VERSION 1.11.10
ENV     HELM_VERSION 2.14.0
ENV     JDK_FILE=OpenJDK11U-jdk_x64_linux_hotspot_11.0.4_11.tar.gz
ENV     JDK_DIR=/opt/jdk-11.0.4+11
ENV     LANG=C.UTF-8
ENV     MAVEN_FILE apache-maven-3.3.9-bin.tar.gz

RUN     apk update && \
        apk add --no-cache curl && \
        apk add --no-cache bash && \
        apk add --no-cache jq && \
        apk add --no-cache gettext && \
        apk add --no-cache wget

RUN     apk add -U openssl curl tar gzip bash ca-certificates git && \
        curl -L -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
        curl -L -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk && \
        apk add glibc-2.28-r0.apk && \
        rm glibc-2.28-r0.apk

RUN     curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx && \
        mv linux-amd64/helm /usr/bin/ && \
        mv linux-amd64/tiller /usr/bin/ && \
        helm version --client && \
        tiller -version

RUN     curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl" && \
        chmod +x /usr/bin/kubectl && \
        kubectl version --client

# https://github.com/frol/docker-alpine-glibc/blob/master/Dockerfile
# Here we install GNU libc (aka glibc) and set C.UTF-8 locale as default.

RUN ALPINE_GLIBC_BASE_URL="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" && \
    ALPINE_GLIBC_PACKAGE_VERSION="2.28-r0" && \
    ALPINE_GLIBC_BASE_PACKAGE_FILENAME="glibc-$ALPINE_GLIBC_PACKAGE_VERSION.apk" && \
    ALPINE_GLIBC_BIN_PACKAGE_FILENAME="glibc-bin-$ALPINE_GLIBC_PACKAGE_VERSION.apk" && \
    ALPINE_GLIBC_I18N_PACKAGE_FILENAME="glibc-i18n-$ALPINE_GLIBC_PACKAGE_VERSION.apk" && \
    apk add --no-cache --virtual=.build-dependencies wget ca-certificates && \
    echo \
        "-----BEGIN PUBLIC KEY-----\
        MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApZ2u1KJKUu/fW4A25y9m\
        y70AGEa/J3Wi5ibNVGNn1gT1r0VfgeWd0pUybS4UmcHdiNzxJPgoWQhV2SSW1JYu\
        tOqKZF5QSN6X937PTUpNBjUvLtTQ1ve1fp39uf/lEXPpFpOPL88LKnDBgbh7wkCp\
        m2KzLVGChf83MS0ShL6G9EQIAUxLm99VpgRjwqTQ/KfzGtpke1wqws4au0Ab4qPY\
        KXvMLSPLUp7cfulWvhmZSegr5AdhNw5KNizPqCJT8ZrGvgHypXyiFvvAH5YRtSsc\
        Zvo9GI2e2MaZyo9/lvb+LbLEJZKEQckqRj4P26gmASrZEPStwc+yqy1ShHLA0j6m\
        1QIDAQAB\
        -----END PUBLIC KEY-----" | sed 's/   */\n/g' > "/etc/apk/keys/sgerrand.rsa.pub" && \
    wget \
        "$ALPINE_GLIBC_BASE_URL/$ALPINE_GLIBC_PACKAGE_VERSION/$ALPINE_GLIBC_BASE_PACKAGE_FILENAME" \
        "$ALPINE_GLIBC_BASE_URL/$ALPINE_GLIBC_PACKAGE_VERSION/$ALPINE_GLIBC_BIN_PACKAGE_FILENAME" \
        "$ALPINE_GLIBC_BASE_URL/$ALPINE_GLIBC_PACKAGE_VERSION/$ALPINE_GLIBC_I18N_PACKAGE_FILENAME" && \
    apk add --no-cache \
        "$ALPINE_GLIBC_BASE_PACKAGE_FILENAME" \
        "$ALPINE_GLIBC_BIN_PACKAGE_FILENAME" \
        "$ALPINE_GLIBC_I18N_PACKAGE_FILENAME" && \
    \
    rm "/etc/apk/keys/sgerrand.rsa.pub" && \
    /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 "$LANG" || true && \
    echo "export LANG=$LANG" > /etc/profile.d/locale.sh && \
    \
    apk del glibc-i18n && \
    \
    rm "/root/.wget-hsts" && \
    apk del .build-dependencies && \
    rm \
        "$ALPINE_GLIBC_BASE_PACKAGE_FILENAME" \
        "$ALPINE_GLIBC_BIN_PACKAGE_FILENAME" \
        "$ALPINE_GLIBC_I18N_PACKAGE_FILENAME"

WORKDIR /tmp
RUN     chmod 755 /opt
COPY    $JDK_FILE /tmp/
RUN     tar -zvxf /tmp/$JDK_FILE -C /opt/ \
        && rm /tmp/$JDK_FILE
ENV     JAVA_HOME $JDK_DIR
ENV     PATH "$PATH:$JDK_DIR/bin"

RUN     wget --no-cookies --no-check-certificate "https://archive.apache.org/dist/maven/maven-3/3.3.9/binaries/$MAVEN_FILE"
RUN     pwd
RUN     ls -ltr
RUN     tar -zvxf $MAVEN_FILE  -C /opt/
RUN     rm $MAVEN_FILE
ENV     PATH "$PATH:/opt/apache-maven-3.3.9/bin"

COPY    ./settings.xml /root/.m2/

The JDK_FILE "OpenJDK11U-jdk_x64_linux_hotspot_11.0.4_11.tar.gz" was downloaded directly from https://adoptopenjdk.net/releases.html?variant=openjdk11&jvmVariant=hotspot.

A really simple Java test file:

class HelloWorld 
{ 
    public static void main(String args[]) 
    { 
        System.out.println("Hello, World"); 
    } 
}

And I'm building it via GitLab pipeline:

image: ${BUILD_URL}/tools/build/docker-build-test-image/add-jdk:20191129.33

stages:
  - build

build:
  stage: build
  script:
    - java -version
    - javac HelloWorld.java
    - java HelloWorld

And here is the output:

$ java -version
openjdk version "11.0.4" 2019-07-16
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.4+11)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.4+11, mixed mode)
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  [thread 20 also had an error]SIGSEGV
 (0xb) at pc=0x00007f0c0251bba1, pid=18, tid=28
#
# JRE version: OpenJDK Runtime Environment (11.0.4+11) (build 11.0.4+11)
# Java VM: OpenJDK 64-Bit Server VM (11.0.4+11, mixed mode, tiered, compressed oops, serial gc, linux-amd64)
# Problematic frame:
# C  [libc.so.6+0x117ba1]
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e %P %I %h" (or dumping to /builds/d4e/playground/revills/java_test/core.18)
#
# An error report file with more information is saved as:
# /builds/d4e/playground/revills/java_test/hs_err_pid18.log
[thread 19 also had an error]
#
# If you would like to submit a bug report, please visit:
#   https://github.com/AdoptOpenJDK/openjdk-build/issues
#
/bin/bash: line 111:    18 Aborted                 (core dumped) java -version

Could you please let me know what I have done wrong?

Cheers, Steve

Steve
  • 445
  • 1
  • 8
  • 18
  • Java >8 doesn't work with an Alpine base. With this much stuff in a single image, the savings for Alpine vs. Ubuntu will be pretty negligible. Also, it's pretty unusual to put this much stuff in a single image; the more common practice is to limit an image to only the components you need to run a specific application. – David Maze Nov 30 '19 at 12:04
  • @DavidMaze -- Your first statement is completely untrue. It is not only possible, but quite easy to get both oracle java and openjdk running in Alpine at versions greater than 8. There are plenty of examples, including from the openjdk team themselves, on docker hub. Not that its as simple as running under 'normal' linux. The problem is glibc and musl, and posix compliance for environment variable names, both of which are quite easy to repair before you install and run the jdk. https://blog.gilliard.lol/2018/11/05/alpine-jdk11-images.html – Software Engineer Nov 30 '19 at 13:13

1 Answers1

3

If you build on basic alpine, you can add in the open-jdk and maven. It might look something like this:

from alpine:3.10

run apk --no-cache add openjdk11 --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community
run apk add bash vim curl wget jq docker git tar unzip bash-completion ca-certificates
run cd /opt && curl -sSl http://mirror.vorboss.net/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz | tar -xz

env PATH "$PATH:/opt/apache-maven-3.6.3/bin"
entrypoint ["/bin/bash"]

This will set up an alpine image with java 11 and the latest maven (as of writing). Typically we'd do this with a single run command to minimise the number of layers in our image, but I'll leave that optimisation to your imagination.

I've added a line showing how and where to install other tools, like Docker, bash, vim, curl, etc. Some of these are used installing maven (tar unzip), and some are just for decoration. You only really need curl, tar, and unzip, to answer this question, but as you're building a devbox I thought I'd add some more for demonstration.

When you are deciding which base image to use for a project like this, it's often best to start with the base OS, like apline:3, rather than some random image that happens to have one thing you need installed in it (git in your example). Treat it like OO -- you are inheriting the git image, so you're also a git image. In your case, you're not really a git image, you're a swiss-army knife and you've no idea what the underlying os really is with another choice (and if you use :latest as the tag you've no idea what it will become in the future).

Software Engineer
  • 15,457
  • 7
  • 74
  • 102
  • Thanks Engineer Dollery, in the GitLab pipeline we are trying to reduce the stages so we need Docker, Git, AdoptOpenJDK and Maven. How can we change your dockerfile to get the AdoptOpenJDK, it needs to be this version of the OpenJDK? – Steve Nov 30 '19 at 23:14
  • Thanks to your link form Matthew Gillian, I've created a dockerfile as such: https://pastebin.com/9XJYg9Yf Anyway to make it smaller? – Steve Dec 01 '19 at 00:56
  • To make the image smaller merge all the RUN statements into a single statement. Docker creates a layer per RUN command and that's very wasteful. Unless you're creating an image tree -- where you have lots of images based on each previous layer. If that's the case, you can use the --squash argument during building. – Software Engineer Dec 01 '19 at 15:27
  • Thanks Software Engineer (did you change handle?), sorry I was talking about the number of commands in the Dockerfile. To get AdoptOpenJDK working with Alpine I had to add lines 8 to 38, 30 lines compared to your nice and small example. – Steve Dec 01 '19 at 23:55
  • Yeah, changed my handle for privacy/security reasons. It's easier to join things up now than it was 7 years ago when I joined and decided to use my real name. – Software Engineer Dec 02 '19 at 00:14
  • Yeah your code will be longer because you're doing something non-standard. Standards are always shorter, faster, more reliable, more secure, etc. And if they're not, let eveyone know and the standard will change because everyone will have the same problems you do. Non-standard approaches are typically 100x more expensive than standard ones, and often a lot more than that. And, they're the gift that keeps on giving -- the cost is cumulative over time. Not that AdoptOpenJdk is that weird, but not using the default alpine repo is. – Software Engineer Dec 02 '19 at 00:20