Spring Boot web app with Kubernetes

abbabe
10 min readApr 1, 2023

Our tutorial will guide you on how to utilize Helm Chart and nginx-ingress with Kubernetes (K8s). To achieve this, we will be using a simple Spring Boot web application.

Requirements

  • Google Cloud Account — Kubernetes Engine
  • Basic knowledge about Maven or Gradle

Content

1- Create a cluster using Google Kubernetes Engine

2- Creating Dockerfile and Build Images

3- Helm Installing

4- Nginx-Ingress Controller

5- Deploy Spring Boot web app

****

1- Create a cluster using Google Kubernetes Engine

You can easily to create a Kubernetes Cluster using Google Cloud Kubernetes Engine. But you have to login Google Cloud Console. Watch this video and read this. You can try free tier account.(300$ free credit). Creating a cluster is possible if you have successfully logged in to the console.

To begin, click on the hamburger menu on the left-hand side of the screen. Next, navigate to the Kubernetes Engine and select Clusters.

Begin by clicking on the “Create” button, and then select Autopilot from the list of options.

Customizing the cluster Name and Region is an option. To follow along with this tutorial, we will name the cluster “springboot” and choose the us-east-1 region. However, the default settings can also be used.

After that click NEXT:NETWORKING. We’ll use default configuration. So click Next:Advanced Settings button without changes. Then click NEXT:Review and Create.

Finally, click the Create Cluster button to complete the process. In just a few minutes, our cluster will be fully operational and ready to use.

Then select your cluster and connect using Command-line access. Run in cloud shell.

And then we’ll use Google Cloud Editor to create Dockerfile and other K8s files. So click Open Editor button.

2- Creating Dockerfile and Build Images

Our initial step is to download the sample project associated with Spring Boot from the GitHub repository. Additionally, to understand Spring Boot better, we recommend reading an article that discusses its fundamentals. Moreover, we need to have the knowledge to produce .jar packages using either maven or gradle.

Maven && MavenWrapper // Gradle && GradleWrapper

Java build tools are designed to streamline the software development process by automating tasks such as compilation and deployment. Our project is created both to use maven and gradle.

Now copy to project url from github repo to clone.

After that delete .git file to create new git repo.

ls
cd gs-spring-boot
ls -alt
rm -rf .git

Go to your Github account and create new repository that is named springboot-webapp.

After creating repo we’ll push application files to our new repo via cluster’s terminal.


git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/abbabe/springboot-webapp.git # write your own repo url
git push -u origin main

In this project we’ll use branches. Go to gs-spring-boot folder on your terminal.

* Prepare base branches namely `main`, `dev` for DevOps cycle.

Create `dev` base branch.

    git checkout main
git checkout -b dev
git push --set-upstream origin dev
  • * Optional
  • If you want to see branch name, you can write this command into .bashrc
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\[\e[1;31m\]\u\[\e[33m\]@\h# \W:\[\e[32m\]\[\e[1;36m\]\$(parse_git_branch)$\[\033[00m\]"

And now we can start to create Dockerfile . Go to Cloud Shell Editor and click File-Open Workspace , select gs-spring-boot folder.

  • Prepare a Dockerfile for the `spring boot web app` and save it under `gs-spring-boot/complete`.
FROM openjdk:17-jdk-slim
ARG EXPOSED_PORT=8080
ADD ./target/*.jar /app.jar
EXPOSE ${EXPOSED_PORT}
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

Our project is created with java.version.17. So we need to “jdk.17” base image to run .jar file. If you want to see java version , you can find into pom.xml under “complete” folder.

Check java version

java --version

openjdk 17.0.6 2023-01-17
OpenJDK Runtime Environment (build 17.0.6+10-Debian-1deb11u1)
OpenJDK 64-Bit Server VM (build 17.0.6+10-Debian-1deb11u1, mixed mode, sharing)

The command “-Djava.security.egd=file:/dev/./urandom” is necessary because the JVM (Java Virtual Machine) running inside a container is different from the one running on the actual machine. It is used to ensure the stable functioning of the JVM.

Our Dockerfile file is ready. While we build docker image we’ll use this file. But we have a problem. Because we don’t have .jar file to build docker image. After running the “maven package” command, a jar file will be created under the “target” directory.

ADD ./target/*.jar /app.jar

To create a jar file, we’ll use maven wrapper commands

* Test the compiled source code.

``` bash
./mvnw clean test
```
> Note: If you get `permission denied` error, try to give execution permission to **mvnw**.

chmod +x mvnw

* Take the compiled code and package it in its distributable `JAR` format.

``` bash
./mvnw clean package

!!! NOTE -Troubleshooting-

If you get an error message about java version,you have to need some changes like below.

Check the java version path then change with jdk-17

echo $JAVA_HOME
# output
# /usr/lib/jvm/java-11-openjdk-amd64

export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
export PATH=$JAVA_HOME/bin:$PATH
echo $JAVA_HOME
# output
# /usr/lib/jvm/java-17-openjdk-amd64

After build processing , jar file will be created under target folder.

And now we can build docker image. For this we’ll prepare a script.

  • Prepare a script to build the dev docker image and save it as `build-docker-image.sh` and save it under `complete` folder.
cd complete
nano build-docker-image.sh

# insert lines below to build-docker-image.sh
./mvnw clean package
docker build --force-rm -t "abbabe/springboot" $HOME/gs-spring-boot/complete

* Give execution permission to build-dev-docker-image.sh.

```bash
chmod +x build-docker-image.sh
```

* Build the image.

```bash
./build-docker-image.sh

After that you can check docker image

docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
abbabe/springboot latest 5770097be373 56 seconds ago 429MB

Then you should login to Docker Hub account to push your image.

docker login

After that you can push your image to Docker Hub. Then check your docker hub repo.

docker image push abbabe/springboot

- Commit the change, then push the script to the remote repo.

```bash
git add .
git commit -m “added script for docker-image and created Dockerfile”
git push

3- Helm installing

What is Helm ?

Helm is an open-source tool that simplifies the process of creating, packaging, configuring, and deploying applications to Kubernetes clusters. It allows developers to consolidate their application’s configuration files into a single reusable package called a “chart,” which can be easily shared, versioned, and installed.

A Helm chart is a convenient package that has all the resources required to deploy your application to a Kubernetes cluster. It contains YAML configuration files for deployments, services, secrets, and config maps, which are essential for defining the desired state of your application.

We will use Helm to install the nginx-ingress controller in this project. For this, first, we need to install Helm onto the Kubernetes cluster.

Installing Helm

curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
helm version

### output
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 11345 100 11345 0 0 84664 0 --:--:-- --:--:-- --:--:-- 84664
Helm v3.11.2 is available. Changing from version v3.9.3.
Downloading https://get.helm.sh/helm-v3.11.2-linux-amd64.tar.gzVerifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm

version.BuildInfo{Version:"v3.11.2", GitCommit:"912ebc1cd10d38d340f048efaf0abda047c3468e", GitTreeState:"clean", GoVersion:"go1.18.10"}

4- Nginx-Ingress Controller

The Nginx-Ingress Controller is an open-source Kubernetes controller that manages the ingress resources and routes incoming traffic to backend services in a Kubernetes cluster. In other words, it acts as a reverse proxy to route external traffic to the appropriate Kubernetes services.

To deploy Nginx-Ingress in this project, we have decided to use Helm Chart, which simplifies the process of installation and configuration.

Go to Artifact Hub and search nginx-ingress controller.

We will be making a few modifications within the values.yaml file. It is also possible for you to customize many other aspects of the configuration by adjusting the values.yaml file. To access the default values, navigate to the right-hand side of the chart page and select “Default Values.” Once there, you may copy and paste the content into your own values.yaml file.

Create a file under complete folder that is named “values.yaml”

nano values.yaml
# copy contents from default values.yaml

And then change “ingressClass: nginx” to “my-nginx “. Maybe you can have multiple ingresses installed in your kubernetes cluster.

Get repo info

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm repo ls

# output
NAME URL
ingress-nginx https://kubernetes.github.io/ingress-nginx
  • Install the chart with your values.yaml
helm install my-ingress ingress-nginx/ingress-nginx -f values.yaml

After installation to show ingress you can use this command.

helm list -d 

# output
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
my-ingress default 1 2023-04-01 13:31:02.170012165 +0000 UTC deployed ingress-nginx-4.6.0 1.7.0

5- Deploy Spring Boot web app

Let’s now prepare the Kubernetes files required to run our application. This will require deployment, service, and ingress files.

apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-spring
spec:
selector:
matchLabels:
app: hello-spring
replicas: 1
template:
metadata:
labels:
app: hello-spring
spec:
containers:
- name: hello-spring
image: abbabe/springboot
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: hello-spring
spec:
selector:
app: hello-spring
ports:
- name: http
port: 80
targetPort: 8080
type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-spring
annotations:
kubernetes.io/ingress.class: 'my-nginx'
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: springboot.dagicloud.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-spring
port:
number: 80

Deploy the application with following command.

kubectl apply -f K8s.yaml

Then run the commands below to show services, pod and ingress.

kubectl get svc

# output
hello-spring ClusterIP 10.115.1.230 <none> 80/TCP 12s
kubernetes ClusterIP 10.115.0.1 <none> 443/TCP 7h42m
my-ingress-ingress-nginx-controller LoadBalancer 10.115.0.69 34.138.183.49 80:30372/TCP,443:30263/TCP 39m
my-ingress-ingress-nginx-controller-admission ClusterIP 10.115.1.52 <none> 443/TCP 39m
kubectl get ing

# output
NAME CLASS HOSTS ADDRESS PORTS AGE
hello-spring <none> springboot.dagicloud.com 80 57s

In order to display our website, it is necessary to establish a Cname record. The host name for which we require the record is springboot.dagicloud.com, and we will be utilizing AWS Route 53 for this purpose. The external IP address that we will be using is 34.138.183.49.

NAME                                            TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
my-ingress-ingress-nginx-controller LoadBalancer 10.115.0.69 34.138.183.49 80:30372/TCP,443:30263/TCP 39m

Let’s take a look at the situation of pod. If everything ok, you may proceed to access the domain name.

kubectl get po
# output
NAME READY STATUS RESTARTS AGE
hello-spring-746589c956-gzf7k 1/1 Running 0 7m55s
my-ingress-ingress-nginx-controller-7b97484f88-62gdw 1/1 Running 0 46m
  • Merge `dev` into `main` branch to build and deploy the app on `Production environment`
git checkout main
git merge dev
git push origin main

And now destroy all application and cluster.

kubectl delete -f K8s.yaml
helm uninstall my-ingress
helm list -d
rm -rf gs-spring-boot

Go to Google Cloud and select springboot cluster. Click Delete button.

You can download all files from here

--

--