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.
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.
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
.
Within the Settings
page, click on Kubernetes and select the Enable Kubernetes
checkbox.
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.
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.
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:
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.
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.
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.
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
.
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.
You can further verify the pushed image on Docker Hub.
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:
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.
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.
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.
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 💬