Passing customizable options to a GraalVM image execution in a distroless environment - java

For context, I'm building a java application compiled to GraalVM native image running on a distroless docker image in Kubernetes.
I've been trying to do something rather simple and hit a wall: I'd like to set custom heap size limits per environment via -XmxNNN. To do that, the options with which I'd like to run the application would be held in an environment variable. The problem arises due to the usage of a distroless image - which doesn't have bash and so ENTRYPOINT /application $OPTIONS doesn't work.
Is there an environment variable GraalVM supports on its own, or any other way of setting this?
I dont want to:
hardcode the values in the Docker image
hardcode the values by predefining them during native-image build

You could use busybox to get a shell inside a distroless container:
FROM gcr.io/distroless/base
...
COPY --from=amd64/busybox:1.31.1 /bin/busybox /busybox/busybox
RUN ["/busybox/busybox", "--install", "/bin"]
CMD ["/bin/sh", "-c", "java -version"]
You can find an example to this kind of Dockerfile here.
But I don't think this busybox shell is necessary needed.
Altough ENTRYPOINT /application $OPTIONS does not work, this will work ENTRYPOINT ["myapp", "arguments"]
Note that distroless images by default do not contain a shell. That means the Dockerfile ENTRYPOINT command, when defined, must be specified in vector form, to avoid the container runtime prefixing with a shell.
source: github

I'm not sure if it will work, but you could try setting the environment variable JAVA_TOOL_OPTIONS to the desired value:
JAVA_TOOL_OPTIONS=-XmxNNN
From the documentation:
This environment variable allows you to specify the initialization of
tools, specifically the launching of native or Java programming language
agents using the -agentlib or -javaagent options. In the following example
the environment variable is set so that the HPROF profiler is launched when > the application is started:
$ export JAVA_TOOL_OPTIONS="-agentlib:hprof"
This variable can also be used to augment the command line with other
options for diagnostic purposes. For example, you can supply the
-XX:OnError option to specify a script or command to be executed when a
fatal error occurs.
The GraalVM documentation itself provides an example of use as well, although in a different context.

Related

Pass parameters to my application in a image done with JIB

I would like to optimize my docker image using jib.
I have been using a Dockerfile like this:
FROM openjdk:8-jdk
ARG NAME
ADD $NAME app.jar
VOLUME /tmp
VOLUME /certificates
ENTRYPOINT exec java $JAVA_OPTIONS -jar app.jar
Now I am creating the image with mvn compile jib:dockerBuild but would like to know how to add the JAVA_OPTIONS to my application as I did in the past in the ENTRYPOINT of my old Dockerfile.
Thanks in advance
I assume that you are dynamically supplying an environment variable for some Java flags at runtime and you want your JVM to pick up the flags at runtime. (If your intention is to statically set the flags and bake them into the container image at image build-time, it should be done in a different way.)
Just set the desired flags in JAVA_TOOL_OPTIONS or JDK_JAVA_OPTIONS if Java 9+. Your JVM will automatically pick them up (although not all Java flags may not be supported with JAVA_TOOL_OPTIONS).

Environment variables are not getting expanded in JAVA_TOOL_OPTIONS

I am trying to integrate a javaagent for application monitoring. I'm using docker and I've setup an OpenJDK base image which is inherited by other application images.
Since javaagent requires a path to the jar file, for maintenance purposes I've defined the path in the base image as another env variable(AGENT_PATH) and I want to reuse the same env variable across all my app images. For some reason the environment variable isn't picked and the application container exits with error.
Base Image's Dockerfile
AGENT_PATH=/agent/agent.jar
This is how I've configured JAVA_TOOL_OPTIONS in application's Dockerfile.
JAVA_TOOL_OPTIONS="-javaagent:$AGENT_PATH + other JVM options"
This is the error message
Picked up JAVA_TOOL_OPTIONS: -javaagent:$AGENT_PATH
Error opening zip file or JAR manifest missing : $AGENT_PATH
Error occurred during initialization of VM
agent library failed to init: instrument
Why is AGENT_PATH not getting substituted properly ?
I've grepped through Hotspot implementation for understanding. I've found this.
This will depend on the Docker step. If you use RUN step the variables processing is not supported:
Unlike the shell form, the exec form does not invoke a command shell. This means that normal shell processing does not happen. For example, RUN [ "echo", "$HOME" ] will not do variable substitution on $HOME. If you want shell processing then either use the shell form or execute a shell directly, for example: RUN [ "sh", "-c", "echo $HOME" ]. When using the exec form and executing a shell directly, as in the case for the shell form, it is the shell that is doing the environment variable expansion, not docker.
In dockerfiles, all directives require a certain codeword. Assigning an environment variable is no different from this and needs the ENV codeword (read here).
Using this, the excerpt of your dockerfile should read
ENV AGENT_PATH=/agent/agent.jar
I was facing the same issue where the environment variables set in the JAVA_TOOL_OPTIONS were not getting substituted. So I had to mention those variables as -D parameters along with java command.
java -Djavax.net.ssl.keyStorePassword=$KeyStore_Secret .....
You can check if that is the case by hardcoding the values in the JAVA_TOOL_OPTIONS and it should work. Meanwhile, I am still looking at the reason for this.

Access environment variable in .conf file for spring boot app

I have set environment variable by executing the command
export test=abcd
I can see test=abcd when I run printenvcommand
I have deployed a springboot.jar application and I am passing the JAVA_OPTS from the springboot.conf file.
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv=$test'
I started the app by service springboot start . When I check the process, env variable doesn't have the value of $test environment variable.
/usr/bin/java -Dsun.misc.URLClassPath.disableJarChecking=true -Dspring.profiles.active=aaa -Denv=.
How can I access the environment variable in the conf file? I read somewhere the environment variables will be stripped off when run as service. Basically I want to run as service springboot start which internally executes the below command
java -Dspring.profiles.active=aws -Denv=${whatever is set for env environment variable} -jar springboot.jar
I have tried the below configurations but nothing worked
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv='$test
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv='${test}
JAVA_OPTS='-Dspring.profiles.active=aaa -Denv=${test}'
JAVA_OPTS="-Dspring.profiles.active=aaa -Denv=$test"
Be careful about your quotes. Assuming that you use a "normal" shell, variables won't be substituted in single quotes.
java -Dspring.profiles.active=aws -Denv="$myvariable" -jar springboot.jar should lead to env being available in the JVM, no matter if you run it as a service or not.
If you can't get it to work, try to specify a hard coded value like this java -Dspring.profiles.active=aws -Denv=foo -jar springboot.jar. If env is now available in the JVM, your problem is with your shell or run mechanism. Verify that the user who runs the command (i.e. do you use sudo?) has the variable set.
I had the same problem where my .conf was referencing an environment variable which was in the .bashrc.
What I found out is:
The problem is service strips all environment variables but TERM, PATH and LANG which is a good thing. If you are executing the script directly nothing removes the environment variables so everything works.
https://unix.stackexchange.com/questions/44370/how-to-make-unix-service-see-environment-variables
One solution would be to install your app as a systemd service:
https://docs.spring.io/spring-boot/docs/1.3.x-SNAPSHOT/reference/html/deployment-install.html
Or another way is to use docker and you can specify extra configuration in the docker file, like loading a file which contains your environment variables.
As those solutions where not available in my case I ended up with having the value in the .conf file, like: -Denv=prod

How to parameterize Dockerfile?

I'm creating a Dockerfile for our Spring Boot application. The application takes a couple of command line parameters. At the end of Dockfile:
CMD java -jar Application.jar --bucket=bucket.list --key=lost
But is it a best practice to hardcode the values for bucket and key in the Dockfile?
If it is ok, then I can live with that. Otherwise, I would like to know how to parameterize the Dockfile.
The application will be deployed on AWS, if that opens the door for more suggestions.
Docker design focused in being independent as far as possible of Host environment, including when building a Docker image. There were a request to let Docker build accessing to host environmental variables but it was rejected looking for independence of host machine. There it is also commented some workaround that could fit your problem.
Anyway, what is supposed to do Application.jar? If it's an application supposed to be running inside the container (and not in building time) the correct way to launch it is using a custom script run when you start the container. There you can set your credential or any other information you wish to be accessed from a environment variable, that can be set when launching the container: docker run -e "MYKEY=secret" -e "MYBUCKET=bucket" myuser/myapp /my/custom/script
You can use environment variables or build arguments.
Build arguments allow you to specify parameters that are applied at buildtime when you execute docker build using the --build-arg ARG_NAME=ARG_VALUE command line parameter.
Environment variables allow you to specify parameters that are applied at runtime when you execute docker run using the -e "ENV_NAME=ENV_VALUE" command line parameter.

Run java with properties from environment variable

In my app i need send http requests via proxy. In terminal i start it by this:
java -Dhttp.proxyPort=**** -Dhttp.proxyHost=***.***.***.*** -jar app.jar
What environment variable i should use for starting on my apps without -D options, like
java -jar app.jar
OS Linux. Java 7.
Thx!
PS already tried JAVA_OPTS, JAVA_OPTIONS, _JAVA_OPTIONS, JAVA_TOOL_OPTIONS...
Java has two separate ways to pass parameters to programs:
Properties, which are typically specified in the command line arguments (as in your first example), loaded from files or manually added by code.
Environment Variables, which are determined by settings in your operating system.
These two concepts are separate; the former doesn't affect the latter and vice versa. As such, you cannot set a property by means of an environment variable.
Other options include loading a .properties file during runtime (assuming your proxy hasn't already been initialized at that point) or putting the full command (-D arguments and all) in a shell script for easier launching.
Nothing is going to work with the standard Java executable. The Java executable does not recognize any environment variables for setting general JVM options.
All those things that you have tried are conventions used by 3rd-party tools, scripts and launchers.
As far as I am aware, the only Java-specific environment variable that the Java executable pays attention to is CLASSPATH, and that is ignored when you run java with the -jar option.
Having said that, there is nothing1 stopping you from:
creating a wrapper script called java and putting it on your search path ahead of the standard java executable, or
creating a shell alias called java.
Such a wrapper or alias could get JVM options from an environment variable, and you could call it anything you wanted to.
1 - ... apart from good sense :-)

Categories

Resources