Containerize your Java applications for Kubernetes
This article describes how to containerize your Java applications for deployment on Kubernetes.
For guidance on container memory, JVM heap memory, garbage collectors (GCs), and vCPU cores, see Containerize your Java applications.
Determine the appropriate VM SKU for the Kubernetes node pool
Determine whether the Kubernetes node pool or pools that are available for your cluster can fit the container memory and vCPU cores that you intend to use. If the node pool can host the application, then continue on. Otherwise, provision a node pool that's appropriate for the amount of container memory and number of vCPU cores you're targeting.
Keep in mind that the cost of a VM SKU is proportional to the number of cores and amount of memory. After you determine your starting point in terms of vCPUs and memory for one container instance, determine whether you can meet your application's needs by horizontal scaling only. For reliable, always-on systems, a minimum of two replicas must be available. Scale up and out as needed.
Set CPU requests and limits
If you must limit the CPU, ensure that you apply the same value for both limits
and requests
in the deployment file. The JVM doesn't dynamically adjust its runtime, such as the GC and other thread pools. The JVM reads the number of processors available only during startup time.
Tip
Set same value for CPU requests and CPU limits.
containers:
- image: myimage
name: myapp
resources:
limits:
cpu: "2"
requests:
cpu: "2"
Understand JVM available processors
When the HotSpot JVM in OpenJDK identifies that it's running inside a container, it uses values such as cpu_quota
and cpu_period
to determine how many processors are available to it. In general, any value up to 1000m
millicores are identified as a single processor machine. Any value between 1001m
and 2000m
is identified as a dual processor machine, and so forth. This information is available through the API Runtime.getRuntime().availableProcessors(). This value may also be used by some of the concurrent GCs to configure their threads. Other APIs, libraries, and frameworks may also use this information to configure thread pools.
Kubernetes CPU quotas are related to the amount of time a process spends in the CPU, and not the number of CPUs available to the process. Multi-threaded runtimes such as the JVM may still use multiple processors concurrently, with multiple threads. Even if a container has a limit of one vCPU, the JVM may be instructed to see two or more available processors.
To inform the JVM of the exact number of processors it should be seeing in a Kubernetes environment, use the following JVM flag:
-XX:ActiveProcessorCount=N
Set memory request and limits
Set the memory limits to the amount that you previously determined. Be sure the memory limits number is the container memory and NOT the JVM heap memory value.
Tip
Set the memory requests equal to the memory limits.
containers:
- name: myimage
image: myapp
resources:
limits:
memory: "4Gi"
requests:
memory: "4Gi"
Set the JVM arguments in the deployment file
Remember to set the JVM heap memory to the amount you've previously determined. We recommend that you pass this value as an environment variable so you can easily change it without needing to rebuild the container image.
containers:
- name: myimage
image: myapp
env:
- name: JAVA_OPTS
value: "-XX:+UseParallelGC -XX:MaxRAMPercentage=75"
Next steps
- Java containerization strategies
- Jakarta EE on Azure container runtimes
- Oracle WebLogic Server
- IBM WebSphere Liberty, Open Liberty, and traditional WebSphere