K8s Java

Overview

It seems easy to tune k8s pods for your Java Spring Boot application, right? Well it is not. Lets dive into it.

Java application was always resource hungry. For instance, I have never seen a java application starting in 1 second amd consuming less than 64MB of RAM.

Even a small Java Microservice Spring-boot based application has a huge infrastructure under the hood, with a lot fo components asking for threads (and RAM). If your applications provide a REST interface and use a database connection pooling, you are already spinning some threads in the background, just to manage the http connections.

When you have to configure the pod of your java microservices, there are some things to watch for. Let take an example with an explanation below

Threads

Below a tipical k8s resource snippet:

1resources:
2    limits:        
3        cpu: 2000m      
4        memory: "1024Mi"

Normally you should not specify a cpu limit, but if you want too, you need to know what are you doing. The cpu limits is more a cpu-per-second limit: every 100ms k8s will check if your application has already consumed all its cpu credit. If so, the application will be paused for the rest of the second.

This can lead to application slow down. The problem become critical if you have too much thread in respect of your cpu limit.

Some observations:

  • Your application uses multiple threads and many of those threads spend a lot of time actually not using the CPU.
  • A Java application need at least 2 cpu to start with. You can decrease this number but only for true tiny services (more below)
  • If possible do not specify a cpu limit: the pod will use all shared cpu on the node, and on average, this will enable it to elastically increase its performance. Anyway this approach is not liked by every SysAdmin because your cluster can be more unstable: if you specify the limit for every pod, it is a lot easier to guarantee stability.

Memory

Memory is linked to thread count. Every thread consume a lot of memory (with the exception of Virtual threads, but lets focus on simpler scenarios).

Set your maximum heap size to 75-80% of the memory available in the container, with the -XX:MaxRAMPercentage directive.

A value of 80% can too big for small containers, and can be too small for very big containers

Launch lines

Okey, so what? Below some tiny suggestions

For standard containers (i.e. 2-4GB RAM, 2+ cpu)

1    java -XX:MaxRAMPercentage=80 -XX:+UseParallelGC -XX:ActiveProcessorCount=<2x yourCpuLimit> -verbose:gc macro-service.jar

Very small container

If you have very small container, with little RAM and less than 2 CPU you should tune the parameters like

1    java -XX:MaxRAMPercentage=70 -XX:+UseSerialGC -verbose:gc tiny-service.jar

The SerialGC does not use threads, and so require less cpu resources to work.

References