Tom Donohue Tom Donohue

7 tips to make your Java applications ready for Kubernetes

Kubernetes is an immensely powerful platform for deploying and orchestrating your applications. It’s already got a huge following amongst architects and DevOps folk.

So you might be at the stage now, where you’re developing your first application to run on Kubernetes.

The problem with Kubernetes is that there are so many moving parts, which doesn’t make it easy to learn. Many of the concepts are quite new, and it’s not easy to know immediately which of them directly affect you as a Java developer.

It’s also not immediately obvious how to optimise your application to make the most of Kubernetes.

So, to answer this, I’ve put together a list of some of the top things I think you need to know when developing Java applications that will run on Kubernetes.

1. Log to the console

As a Java developer, you’ve probably used Log4J for years to write logs in your application. You’ve probably then configured Log4J to write your logs to a file on disk.

However, when running in Kubernetes, you should avoid writing your logs to a file inside the container. Instead, you should write logs to the standard output stream, or console.

Logging to STDOUT is one of the pillars of the 12-factor app - a set of guidelines for designing applications that will run in the cloud.

2. Create a healthcheck service for your app

This is so that Kubernetes can check if your application is healthy. A healthcheck service gives basic status information about your application. The healthcheck can be used to determine whether your application is ready to service requests.

If you’re using some of the latest Java frameworks designed for the cloud, then basic healthcheck APIs come out-of-the-box.

3. Use Kubernetes for service discovery

An application usually depends on other services to run - like a database, message broker or an external API.

Previously, you might have maintained a configuration file (e.g. a Java .properties file) containing the URLs of all of these dependencies for dev, test and production.

Or, you might have used something like a Service Catalog or Service Registry to look up the right endpoint for a service. If these endpoints changed, you would need to change your config and restart your application.

When you’re running an app inside Kubernetes, you can use Kubernetes DNS to talk to another service just by using a DNS name, like http://myservice. Behind the scenes, Kubernetes does the service lookup and load balancing magic for you, and routes your request to a pod.

If you want to talk to a service in a different Kubernetes namespace, you just use its fully qualified DNS name, e.g. http://myservice.mynamespace.svc.cluster.local.

4. Think about memory limits and the Java heap size

This tip is a more general one about running Java in containers.

Kubernetes has features to manage the compute resources available to a container. You can set resource limits, such as CPU and memory, and Kubernetes will pass these limits to the container runtime (e.g. Docker).

If a container exceeds its memory limit, it is killed to prevent it consuming more resources than allowed. This is known as the OOMKilled error.

But, there are special implications for Java.

It’s important to realise that Java doesn’t (yet) understand container memory limits, and will assume that it has access to all memory on the host that the container is running on.

In other words, setting memory limits using Kubernetes will have no effect on a Java application, unless you specifically set a maximimum heap size (using Java’s -Xmx setting).

In fact, if you set a memory limit in Kubernetes, and Java exceeds that, your container will be killed.

So make sure you set your Java heap size, and set your Kubernetes memory limits accordingly.

5. Externalise your application config.

Kubernetes is a platform for running applications at huge scale. So there needs to be a way of ensuring your application config is easy to manage and change, when running dozens - if not hundreds - of instances of the same application.

To do that, the best way is to keep application config outside your container image. In other words, decouple all of your config from your application, and use the platform (Kubernetes) to manage it.

Thinking in terms of Java, this means don’t store application config (e.g. Java .properties files) inside your image.

Instead, use Kubernetes to inject your application config into your container when it starts.

Kubernetes has three main features for this:

  • ConfigMaps - these store configuration in key/value pairs, and can be used to store even whole configuration files
  • Secrets - these are used for storing sensitive configuration, like private keys or passwords
  • Environment variables - you can also assign simple environment variables to a deployment, containing configuration values

Build your image once, and use one of these Kubernetes features to inject config at runtime.

6. Use Kubernetes tools designed for Java developers

Some people say that Kubernetes is hard. But that’s becoming less true, as more tools appear that make it easier to develop for Kubernetes and learn it.

The best way to start is by running your own local Kubernetes cluster. The quickest way of doing this is to run minikube, a Kubernetes cluster-in-a-box, on your development machine. It gets you up and running quickly, bypassing all the hassle of setting up a full-blown Kubernetes environment.

Then, when you’re developing Java applications to run in Kubernetes, use the fabric8-maven-plugin. It’s a plugin for Maven which provides features that make it easier to build and deploy Java containers as part of your build process. It can even spin up a Kubernetes cluster for you and deploy your app.

There is also some tooling emerging from the Spring Cloud project specifically designed for working with Kubernetes clusters, which gives you functionality like hot-reloading of configuration, enhanced health checks and so on.

Keep an eye out. More tools will emerge as Kubernetes becomes more mainstream.

7. Design for high availability

This last one is more of a general piece of advice.

Kubernetes is designed for scaling your application to dozens, hundreds, thousands of instances.

Therefore when developing Java applications for Kubernetes - or indeed any cloud platform - you should expect that many instances of your application may be deployed concurrently in containers.

If you eventually start moving to a microservices architecture, then each individual service can be scaled independently, depending on the resource load.

That means you need to think carefully about how you implement your application:

  • That bit of code that saves data to disk? Does the data need to be shared between all containers? Will it handle many concurrent requests? Would a data grid or database be better?
  • Will your application gracefully handle being terminated? Is it stateless?
  • How will you handle transactions?

These are just some of the things to think about. One answer to some of these questions is to just run a single instance of your application in Kubernetes (known as 1 replica). But this is seen as an antipattern.

Better to take time now and think about how scaling will impact your application. It might affect you in more ways than you think.


Summary

Kubernetes is a very clever platform that can be used for all sorts of workloads.

However there is some benefit in understanding some of the features of Kubernetes, so you can write applications that make better use of the platform.

When thinking in terms of Java, this means paying close attention to your application logs, configuration and scalability. It also means having a basic understanding of how Java memory works and being more involved in how your application will reserve and use that memory.

And finally it means using the right tools for the job, and using every opportunity to learn standard practices, so you don’t feel like you’re completely on your own.

How are you using Kubernetes with your Java apps? Do you have any tips you want to share with other developers? Let me know in the comments below! Cheers!

Comments

What do you think? You can use Markdown in your comment.

To write code, indent each line with 4 spaces. Or, to paste a lot of code, you can put it in pastebin.com and share the link in your comment.