javakuberneteskubernetes-helm

Java options within Kubernetes container


I am working with Java application and I’m going to deploy it within container. I have prepared Dockerfile with

ENTRYPOINT ["java", "-jar", "java_j.jar"]

in my Java application. I have prepared some helm charts too.

Is it possible to use only one variable to specify all Java options interested by me in it to use it within container.args (Deployment.yaml)?

{root}/values.yaml:

TEST_JAVA_OPTS = "-XX:+UseSerialGC"
TEST_JAVA_MEMORY_OPTS = "-Xmx256m -XX:MetaspaceSize=64m"
{root}/templates/Deployment.yaml

{root}/templates/Deployment.yaml

...
spec:
   containers:
      - name: test-java-service
        command:
           - java
           - '{{ .Values.TEST_JAVA_MEMORY_OPTS }}'
           - '{{ .Values.TEST_JAVA_OPTS }}'
           - -jar
           - java_j.jar
...

For now it doesn’t work to me because each my application startup failes with Improperly specified VM option. I guess it tries to give java entire string as one java option. That is wrong of course. My purpose is to avoid a lot of variables for each java option and to let change it in Deployment directly (I know that there is a possibility to set environment variables in Dockerfile at ENTRYPOINT part but let assume this option is disabled for us)

Kubernetes version: 1.28.12


Solution

  • In your Helm chart, you need to split out the different low-level JVM settings into individual items in the command: list. The easiest way to do this is to make the Helm-level settings be a list of options, and then you can iterate over it.

    # values.yaml
    jvmOptions:
      - -XX:UseSerialGC
      - -Xmx256m
      - -XX:MetaspaceSize=64m
    
    # templates/deployments.yaml
             command:
               - java
    {{- range .Values.jvmOptions }}
               - {{ toJson . }}
    {{- end }}
               - -jar
               - java_j.jar
    

    Since .Values.jvmOptions is a list here, the template range construct loops through it, setting . to each item in turn. In the example here, I use the toJson extension function to ensure each item is properly quoted as a string that fits on a single line.

    Nothing would stop you from having multiple lists of option settings that you combined this way.

    If you really want the JVM options as a space-separated string, then you need to split that string into words. There is a splitList extension function (not mentioned in the Helm documentation but it's there) that can do this.

    # values.yaml
    jvmOptions: "-XX:UseSerialGC -Xmx256M -XX:MetaspaceSize=64m"
    
    # templates/deployments.yaml
             command:
               - java
    {{- range splitList " " .Values.jvmOptions }}
               - {{ toJson . }}
    {{- end }}
               - -jar
               - java_j.jar
    

    The template part looks almost identical except for adding splitList in. Note that this is a fairly naïve splitting; there's not going to be any support for quoting or embedding spaces inside a single option or any non-space whitespace.

    Finally: note that the standard JVMs do support passing options in environment variables; see for example What is the difference between JDK_JAVA_OPTIONS and JAVA_TOOL_OPTIONS when using Java 11? You could just set this environment variable without trying to reconstruct command:. (IME if you have a choice, managing Kubernetes manifests tends to be easier if you can set environment variables as opposed to using command-line options.)

    # values.yaml
    jvmOptions: "-XX:UseSerialGC -Xmx256M -XX:MetaspaceSize=64m"
    
    # templates/deployments.yaml
             env:
    {{- with .Values.jvmOptions }}
               - name: JDK_JAVA_OPTIONS
                 value: {{ toJson . }}
    {{- end }}