Deploying Spring Boot Application on Kubernetes using Kubernetes Maven Plugin

Learn how to build efficient Docker image using Spring Boot Maven plugin, generate Kubernetes manifest at compile-time, and deploy on Kubernetes using Kubernetes Maven plugin from Eclipse JKube.

Andy Lian
FAUN — Developer Community 🐾

--

Photo by AfroRomanzo from Pexels

For many years now, enterprise application has been deployed either on bare metal or in virtual machines. Naturally, applications on bare metal are difficult to move around. Although virtual machines are there to solve some of the constraints in using bare metal, nevertheless they got their share of problems. Virtual machine requires a large capacity of storage counting in gigabytes as each of them containing a full operating system. As such, only a limited virtual machine can be consolidated into a single physical machine. Provisioning a virtual machine takes quite a fair amount of time and it is not very portable. In the end, virtual machines are still not able to deliver the kind of agility and savings that fast-moving businesses are demanding. This led to the emerging of container-based virtualization and Docker is by far the most pervading container tool in the enterprise.

What is Docker

Docker is a standalone software that can be installed on any machine to run a containerized application. Docker works a little like virtual machines but in a far more specific and granular way. While each virtual machine contains the entire operating system, Docker only requires containers to be shipped with things not already running on the host machine. Docker containers share a single operating system but compartmentalized from one another. This gives a significant performance boost and reduces the size of the application.

While Docker does well with smaller applications, large enterprise applications can involve a huge number of containers, sometimes hundreds or even thousands which becomes an overwhelming task for managing them. That is where container orchestration tools come in.

What is Kubernetes?

Kubernetes is the market leader and the standardized means of orchestrating containers and deploying distributed applications. It is a comprehensive system for automating deployment, scheduling, and scaling of containerized applications that before were carried out manually and support many containerization tools such as Docker.

Key Kubernetes functions include the following:

  • Deployment: Schedules and automates the deployment of containers to specified hosts and keeps containers running in the desired state.
  • Service discovery and load balancing: Exposes a container on the internet and employs load balancing when traffic spikes occur to maintain stability.
  • Self-healing capabilities: Restarts, replaces or reschedules containers when they fail or when nodes die and kill containers that do not respond to user-defined health checks.
  • Automated rollout and rollbacks: Roll out application changes and monitors application health for any issues, rolling back changes if something goes wrong.
  • Storage orchestration: Automatically mounts a persistent local or cloud storage system of choice as needed.

In this article, we will be deploying a simple Spring Boot application on the local Kubernetes cluster to showcase some of the basic Kubernetes resources. We will be employing Spring Boot Maven plugin and Kubernetes Maven plugin from Eclipse JKube to build a container image, publish to Docker Hub, generates Kubernetes manifest used to create resources such as pods, deployments, services, or ingresses, and finally deploy to our local Kubernetes cluster.

Install Docker Desktop

First thing first, we need to get Docker and Kubernetes running on our local machine. Visit https://docs.docker.com/get-docker to download and install Docker Desktop. Docker Desktop does not start automatically after the installation. To start Docker Desktop, search for Docker and select Docker Desktop in the search results.

Windows — Search Results for Docker

When the Docker Desktop icon, the white whale icon in the status bar stays steady, Docker Desktop is up-and-running and is accessible from any terminal window.

Enable Kubernetes

Docker Desktop comes with an option to install a standalone Kubernetes server running as a Docker container and Kubernetes client command, kubectl configured to connect to the local Kubernetes server.

Right-click the Docker Desktop icon, the white whale icon in the taskbar and select Settings.

Docker Desktop — Context Menu

Within the Settings page, click on Kubernetes and select the Enable Kubernetes checkbox.

Docker Desktop — Kubernetes Settings

Docker Desktop will download all the Kubernetes images in the background and get everything started up. When it is ready, you should see two green lights on the bottom of the Settings page saying Docker running and Kubernetes running.

Docker Desktop —Bottom Bar with Kubernetes Running

Generate a Spring Boot Application

We need a simple Spring Boot application to exhibit deployment on local Kubernetes.

Visit Spring Initializr to generate and download a Maven project with Spring Boot 2.x, Spring Web and Spring Boot Actuator dependencies.

Spring Initializr

As an alternative and a faster way, you can run the following command to invoke Spring Initializr using cURL.

The command will invoke Spring Initializr to generate a Maven project with Spring Boot 2.3.7.RELEASE, Spring Web and Spring Boot Actuator dependencies in jkube-app directory, package as a tarball and finally, extract to your current directory.

The terminal output should look something similar to this:

Terminal Output — Invoke https://start.spring.io/starter.tgz

Now that we have a generated Spring Boot project in ajkube-app directory, let us verify our Spring Boot application is fully functional before we Kubernetes-ize it. We will be taking advantage of Spring Boot Maven plugin that includes a run goal to quickly compile and run our application.

Run the following command to invoke run goal using Maven Wrapper batch script, mvnw.

mvnw spring-boot:run

You should have your application up and running in no time.

Terminal Output — Invoke spring-boot:run

Visit http://localhost:8080/actuator to verify that everything is working properly. You should be getting something similar to the following, depending on the browser you are using.

Spring Boot Application — Available Actuator Endpoints

Build Docker Image

It has always been pretty easy to build a Docker image from the fat jar produced by Spring Boot with the following dockerfile command.

FROM openjdk:8-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/spring-boot-app.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

While this approach works fine and concise, there are a few sub-optimal things, namely the Spring Boot produced fat jar in the image is not unpacked. There is always a certain amount of overhead when running a fat jar and this is especially noticeable in a containerized environment. In this article, instead of using the traditional way of building Docker image, we will have Spring Boot Maven plugin building our image. Since Spring Boot 2.3, Spring Boot Maven plugin has introduced a build-image goal which is using Cloud Native Buildpacks to build a Docker image in OCI image format.

By default, Spring Boot Maven plugin will use our project artifactId as the image name and version as the image tag. We need to change the image name to {your.docker-hub.username}/jkube-app because, in the latter part of the article, we need to push our image to Docker Hub. Spring Boot Maven plugin allows us to easily change the build image name using a spring-boot.build-image.imageName property. Let us add a property to the properties element in pom.xml as following.

Your modified properties in pom.xml should look like this:

Run the following command to invoke build-image goal using mvnw.

mvnw spring-boot:build-image

You should be getting a build success in your terminal output.

Terminal Output — Invoke spring-boot:build-image

Next is to verify the image has been built and stored locally in docker. Run the following command to list docker images with the repository name andylke/jkube-app.

docker images andylke/jkube-app

And you will be expecting to get your image repository andylke/jkube-app with tag 0.0.1-SNAPSHOT created 41 years ago. Yes, it is 41 years ago depending on the date you build your image, and it is a Cloud Native Buildpacks feature to ensure builds are reproducible.

Terminal Output — Invoke docker images

Add Kubernetes Maven Plugin

We will be making use of Kubernetes Maven plugin from Eclipse JKube project to publish the built image to Docker Hub generates Kubernetes manifest, and deploy to our local Kubernetes cluster.

You will need to import the project to an IDE of your choice. Add the following plugin for Kubernetes Maven plugin to build plugins in Maven POM, pom.xml for Spring Boot project.

Your modified build plugins in pom.xml should be similar to this:

Push Image to Docker Hub

Docker Hub is a cloud-based container image repository service provided by Docker Inc. We can think of it as GitHub where we can create, test, store and share our source code in repositories, but in the case of Docker Hub, we store container image in the repositories. It allows us to share container images with our team, customers, or the Docker community.

To have Kubernetes Maven plugin push our built image to Docker Hub, we need to supply the image name to the Kubernetes Maven plugin, by adding jkube.generator.name property in pom.xml like this:

By doing so, the Kubernetes Maven plugin will get the image name from spring-boot.build-image.imageName property.

By default, the Kubernetes Maven plugin always pushes images tagged as the latest to the remote repository. Since our built image is tagged with version rather than latest, we will need to disable this feature. Add the following jkube.skip.tag property with true value to pom.xml to disable Kubernetes Maven plugin overriding our tag in the supplied image name.

Finally, your properties in pom.xml should appear as follows.

Now that everything is ready, let us run the below command to invoke push goal in the Kubernetes Maven plugin using mvnw. Be aware that you need to supply the command with your Docker Hub username and password that is used to login and push to your account.

Assure that you have your image pushed to Docker Hub as following.

Terminal Output — Invoke k8s:push

You can further verify the pushed image on Docker Hub.

Docker Hub — andylke/jkube-app

Generate Kubernetes Manifest

Kubernetes manifest is used to describe the Kubernetes resources used by our application. It is commonly defined in the form of YAML. Kubernetes manifests are used to create, modify and delete the described Kubernetes resources such as Pods, Deployments or Services in Kubernetes cluster. These are the three important objects in Kubernetes that you should know. Pods are the smallest deployable unit in Kubernetes that holds one or more dependent containers. Deployments define how Kubernetes should automate the launching of pod instances and ensure they are running as defined across all the nodes in the cluster. Services are used to set up networking in a cluster.

Kubernetes Maven plugin can either generate based on opinionated defaults, based on the configuration provided in XML config or resource templates in src/main/jkube directory.

By default, Kubernetes Maven plugin generates a Deployment with a single replica and Service resource that is only reachable from within the cluster, known as ClusterIP. To create a network load balancer service with an externally accessible IP address, we need to change the default Service type to LoadBalancer. Add a jkube.enricher.jkube-service.type property with value LoadBalancer as follows.

Run the following command to invoke the Kubernetes Maven plugin to generate Kubernetes manifests.

mvnw k8s:resource

You should be getting a build success like this:

Terminal Output — invoke resource goal

These Kubernetes manifests are all generated in target/classes/META-INF/jkube/kubernetes directory. You should find your generated jkube-app-deployment.yml and jkube-app-service.yml files appear like the following two files.

META-INF/jkube/kubernetes/jkube-app-deployment.yml
META-INF/jkube/kubernetes/jkube-app-service.yml

Apply Kubernetes Manifest on Local Kubernetes Cluster

Kubernetes Maven plugin will combine all generated Kubernetes manifests from the target/classes/META-INF/jkube/kubernetes directory into a single Kubernetes manifest file, target/classes/META-INF/jkube/kubernetes.yml. This consolidated Kubernetes manifest will be used when we invoke the apply goal in Kubernetes Maven plugin.

Run the following command to apply our generated Kubernetes manifest to our locally connected Kubernetes cluster.

mvnw k8s:apply

You should be getting the same output as the following.

Terminal Output — Invoke apply goal

Your Kubernetes manifest being applied successfully to the Kubernetes cluster does not guarantee the defined Kubernetes resources will start successfully. Always verify the state of your applied resources.

Run the following command to list all resources in your local Kubernetes cluster with labels app=jkube-app.

kubectl get all -l app=jkube-app

If all goes well, you should have a running Pod, a service with an external IP address, a deployment, and ReplicaSet.

Terminal Output — All Resources in Kubernetes

You can visit http://localhost:8080/actuator to verify your Spring Boot application in the running Pod is accessible.

We have shown in this article how we can easily deploy the Spring Boot application to a local Kubernetes cluster. We used Spring Boot Maven plugin to build us a more efficient Docker image. We have also made use of the Kubernetes Maven plugin from Eclipse JKube to push the built image to the Docker Hub repository, generate Kubernetes manifests and applied to our locally connected Kubernetes cluster.

As usual, the source code is available on GitHub.

Thanks for reading, and I hope that you find this article helpful.

👋 Join FAUN today and receive similar stories each week in your inbox! Get your weekly dose of the must-read tech stories, news, and tutorials.

Follow us on Twitter 🐦 and Facebook 👥 and Instagram 📷 and join our Facebook and Linkedin Groups 💬

If this post was helpful, please click the clap 👏 button below a few times to show your support for the author! ⬇

--

--

Full-stack software developer with a wide variety of coding niches skills used in building various aspects of IT solutions