dockerdockerfiledocker-javadistroless

Use CMD parameters in distroless image


I am using azul/zulu-openjdk-distroless:17.0.6 image and want to execute the java command with parameters but it fails at the runtime

Here is my docker image:

FROM alpine:3.17.3 as build-env

ARG DATADOG_VERSION=1.8.3

RUN wget https://github.com/DataDog/dd-trace-java/releases/download/v${DATADOG_VERSION}/dd-java-agent-${DATADOG_VERSION}.jar

FROM azul/zulu-openjdk-distroless:17.0.6

COPY --from=build-env dd-java-agent-*.jar javaagent.jar
COPY *-app/target/*.jar app.jar
ARG JAVA_AGENT="-javaagent:javaagent.jar"

CMD ["${JAVA_AGENT}", "-jar", "app.jar"]

I get the following error when I run it:

Error: Could not find or load main class ${JAVA_AGENT}
Caused by: java.lang.ClassNotFoundException: ${JAVA_AGENT}

I tried with ENV instead of ARG and ENTRYPOINT instead of CMD, none of them worked. I tried without the [] brackets but then I get another error:

docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/bin/sh": stat /bin/sh: no such file or directory: unknown.

If I put the value directly like CMD ["-javaagent:javaagent.jar", "-jar", "app.jar"] then it works but the container runs in a pipeline where the variable's value is coming from so I want to use variable. I would also use another one for JAVA_OPTS which is much harder to hardcode.

Is there any possibility to solve it somehow or the only solution is to hardcode the parameters?


Solution

  • Java 17 supports a JDK_JAVA_OPTIONS environment variable to supply additional command-line options to the JVM. If you set this as an environment variable, you can override it when you run the image, and Dockerfile ENV to set it does do basic environment replacement.

    ARG JAVA_AGENT="-javaagent:javaagent.jar"
    ENV JDK_JAVA_OPTIONS="$JAVA_AGENT"
    CMD ["-jar", "app.jar"]
    

    Note that docker run -e JDK_JAVA_OPTIONS=... and other similar settings complely replace the preƫxisting value of this variable, so you'd have to repeat the -javaagent:... option.


    There are two forms of CMD (and ENTRYPOINT and RUN). Exec form uses the JSON-array syntax, but never does any sort of variable expansion or other processing; the list of strings in the JSON array is used directly as the list of words making up the command and its arguments. Shell form takes any string but is run through /bin/sh -c, and that shell does the expansion. If you have both an ENTRYPOINT and a CMD then both parts are separately wrapped in sh -c if required, then combined together into a single command line.

    This combination of things puts several non-obvious restrictions on variable expansion. In your case, variable expansion requires a shell; Docker on its own does not replace environment variables in CMD at all. Since your distroless image doesn't contain a shell, this won't work.

    The other implication of this rule is that, if you are using a shell-form CMD to do variable expansion, it must be a complete shell command. In your case it looks like the base image declares a ENTRYPOINT ["java"]; if you try to use a shell-form CMD then the container will run java sh -c '...' which will produce an unusual error (admittedly, not the one you're actually seeing).

    If you're passing this parameter as a command-line option in a distroless image, you have to hard-code it in the CMD. You might be able to COPY a non-fixed file into javaagent.jar, so the filename will be fixed and the command-line option can be constant, but the contents of the file may not be.