Kubernetes and Spring Boot

Kubernetes and Spring Boot

Kumar Pallav's photo
Kumar Pallav
·Jan 29, 2022·

10 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

Kubernetes with Spring Tutorial

Introduction to Kubernetes

Kubernetes (k8s) is an open-source system for automatic management, scaling, and deployment of containerized applications.

With the rise of microservices, we required a tool to deploy these independent applications. Containers like docker were the perfect fit for it. They provided a working environment for delivering microservices.

As the Microservices increased, so increases the number of containers. It becomes difficult to manage these containers with scripts. This problem has led to the creation of the container orchestration tool. Kubernetes is the answer to such a need.

Component of Kubernetes

ComponentOfKubernates.png

Nodes

Kubernetes is a solution for containerized applications. One of the containers could be a docker but there are many other container solutions available. In order to manage these containers, we require an abstraction layer that talks with different vendors seamlessly.

The pod can be considered as a working unit for Kubernetes. A pod can have one or more containers.

Service

Each node on Kubernetes is assigned an IP. While pods in nodes can talk with each other using IP, nodes are ephemeral. They can die at any moment. Whenever a node dies, Kubernetes will create another one in its place to keep the desired state. **The new pod will have a new IP assigned. This will create a problem while the pods interact via IP.

To solve this Kubernetes creates a service assigned to pods and they will interact using service rather than IP. Service will remain the same even if the ip attached changes.

Control Plane

As we know Kubernetes is all about container management, to manage it we require some components. These components which help to manage the containers effectively, if grouped together will be known as the control plane.

Let’s look at the components that make the control plane.

API Gateway

As the name suggests it provides the gateway to interact with all nodes and its port to the outside world. Any communication to the pods from outside the cluster can happen via the API gateway.

Scheduler

To manage the desired state whenever a pod dies, we require a mechanism that can check the state on the cluster and can schedule the new pods if required. This is the work of the scheduler running from the control plane.

Control Manager

All of the components in the control plane is managed by controller processes. There are different controllers like Node, Jobs, Endpoints, Service, and Token controllers. All of them combined together are known as Control Manager.

etcd

To manage effectively we require some sort of memory. etcd acts as a database for all of the config maps and secrets.

Deployment

To deploy anything on Kubernetes, we create a deployment object which is a YAML file. It has all of the information related to container images, its location, and the metadata corresponding to pods

Service

Service is another YAML file that holds the configuration of protocols that is required for nodes to communicate and serve.

ConfigMap

Applications deployed via pods require many configurations, if we keep them inside pod we will have to build images for every change

Secrets

Many of the configurations are secrets, like database passwords, we can’t reveal them. We can save them as secrets rather than plain text. Kubernetes doesn’t control the encryption mechanism but these secrets are kept as encrypted text in etcd database.

Deploying Sample Spring Boot App to Kubernetes

Deploying Sample Spring Boot App to Kubernetes

Now with the understanding of Kubernetes basic building blocks, let us try our hand in creating an application and deploying it on Kubernetes. To start we will go to https://start.sprting.io and create a simple application which one endpoint. We will only select reactive web as a dependency.

We will create a with a get endpoint, The endpoint will take a String via query String and will welcome the user.

@RestController
public class SpringK8SDemoController {

    @GetMapping("/welcome")
    public Mono<String> welcome (@RequestParam("name") String param){
        return  Mono.just("Welcome "+ param);
    }
}

Before we create the containerize the docker file, let’s the final jar file at pom.xml k8s-demo

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        **<finalName>k8s-demo</finalName>**
    </build>

Now we need to containerize the application. We will use a docker container. Let us add a file named Dockerfile. We will add it to the root directory of the project.

FROM openjdk:17-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/k8s-demo.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

The file is self-explanatory, it adds OpenJDK 17 to the container and uses 8080 port for exposing. It just copies the file to the target directory and names it app.jar. In the end, it starts with command as an entry point that’s java -jar app.jar.

Let’s build the docker image and run it to verify.

mvn clean install
cd target
docker build -t codingsaint/k8s-demo
docker run -p 9090:8080 k8s-demo

The app should run and if you send HTTP request http://localhost:9090/welcome?name=Pallav , it should respond as welcome Pallav

Spring Boot Deployment using Deployment Object

In Kubernetes, we deploy using a configuration file in YAML format. The standard file of configuration is called deployment. Let’s create a deployment file as below and we will discuss its content.

Let’s create a deployment.yaml file at the root of the spring boot project.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-demo

spec:
  selector:
    matchLabels:
      app: k8s-demo
  replicas: 3
  template:
    metadata:
      labels:
        app: k8s-demo
    spec:
      containers:
        - name: k8s-demo
          image: k8s-demo:0.0.1-SNAPSHOT
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080

In the above file, we are creating an object of kind deployment, named k8s-demo using metadata.name. At the spec level, we are defining that the app k8s-demo should have 3 replica

At spec.selector.spec.container we are defining the container properties. With imagePullPolicy as IfNotPresent it will look at local docker images first then at the remote.

Service Object

As we discussed the communication of pods, We need services that are attached to the pods. Service Object in Kubernetes can be created using YAML file. Let’s review the content of the service file. We are creating this file at root directory , let’s name it k8s-demo-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: k8s-demo-svc
spec:
  selector:
    app: k8s-demo
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
  type: NodePort

If you see spec , it clearly says to select (selector) the app k8s-demo. It also tells that the protocol is TCP.

Deploying to Kubernetes Cluster

Start Minikube

minikube start

You should be getting an output like below

PS C:\Users\07pal\development\tutorial\k8s\k8s-demo> minikube start
😄  minikube v1.25.1 on Microsoft Windows 11 Home Single Language 10.0.22000 Build 22000
✨  Using the docker driver based on existing profile
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
🔄  Restarting existing docker container for "minikube" ...
🐳  Preparing Kubernetes v1.23.1 on Docker 20.10.12 ...
    ▪ kubelet.housekeeping-interval=5m
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: storage-provisioner, default-storageclass
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

Build docker image

Before we deploy it to cluster we need Kubernetes and Docker images’ to be in sync . If you are on windows use below command

minikube docker-env | Invoke-Expression

If you are on Mac , use following

eval $(minikube docker-env)

Now we will build the docker image from the Dockerfile we created

docker build -t k8s-demo:0.0.1-SNAPSHOT .

This will create a Docker Image and we will deploy using our deployment object. The output of above command should be like

[+] Building 3.4s (8/8) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 32B                                                                                0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/openjdk:17-jdk-alpine                                           3.3s
 => [auth] library/openjdk:pull token for registry-1.docker.io                                                     0.0s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 69B                                                                                   0.0s
 => [1/2] FROM docker.io/library/openjdk:17-jdk-alpine@sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9  0.0s
 => CACHED [2/2] COPY target/k8s-demo.jar app.jar                                                                  0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:714ae55f7105935be0267470795b5dc890d17c1773b194d5bfda9a45fa2d8b8c                       0.0s
 => => naming to docker.io/library/k8s-demo:0.0.1-SNAPSHOT                                                         0.0s

You can verify the images at local docker repo using command

docker images

It should give output like

REPOSITORY                    TAG              IMAGE ID       CREATED         SIZE
**k8s-demo                      0.0.1-SNAPSHOT   714ae55f7105   5 days ago      346MB**
gcr.io/k8s-minikube/kicbase   v0.0.29          64d09634c60d   5 weeks ago     1.14GB
mysql                         latest           ecac195d15af   3 months ago    516MB
mongo                         latest           fefd78e9381a   3 months ago    699MB
jaegertracing/all-in-one      latest           9844aed41d42   4 months ago    59MB
docker101tutorial             latest           c492a391575a   4 months ago    28.3MB
postgres                      12               91d892a67245   4 months ago    314MB
consul                        latest           b74a0a01afc4   5 months ago    116MB
alpine/git                    latest           b8f176fa3f0d   8 months ago    25.1MB
testcontainers/ryuk           0.3.1            ee7515743e6f   13 months ago   12MB
postgres                      11.5-alpine      da01ecfbabe1   2 years ago     71.8MB
openjdk                       8-jdk-alpine     a3562aa0b991   2 years ago     105MB

Deploy on Kubernetes

Let’s deploy it on Kubernetes using the deployment object that we created .

kubectl apply -f .\deployment.yaml

The above command will put out image from docker to kubernetes cluster and deploy as it is stated in deployment.yaml. The output should be looking something like

PS C:\Users\07pal\development\tutorial\k8s\k8s-demo> kubectl apply -f .\deployment.yaml
**deployment.apps/k8s-demo created**

We can verify our pods are up and running using

kubectl get pods

You will see pods name as output

PS C:\Users\07pal\development\tutorial\k8s\k8s-demo> kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
k8s-demo-675766fff7-92kxk   1/1     Running   0          102s
k8s-demo-675766fff7-grsdm   1/1     Running   0          102s
k8s-demo-675766fff7-z6z4s   1/1     Running   0          102s

If you want to check logs of individual pod , you can use kubectl logs as

kubectl logs  k8s-demo-675766fff7-92kxk

We can see the spring boot app running at cluster. The below output can be taken as proof

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.3)

2022-01-29 08:23:38.136  INFO 1 --- [           main] c.c.k8sdemo.K8sDemoApplication           : Starting K8sDemoApplication v0.0.1-SNAPSHOT using Java 17-ea on k8s-demo-675766fff7-92kxk with PID 1 (/app.jar started by root in /)
2022-01-29 08:23:38.138  INFO 1 --- [           main] c.c.k8sdemo.K8sDemoApplication           : No active profile set, falling back to default profiles: default
2022-01-29 08:23:42.756  INFO 1 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8080
2022-01-29 08:23:42.824  INFO 1 --- [           main] c.c.k8sdemo.K8sDemoApplication           : Started K8sDemoApplication in 6.006 seconds (JVM running for 7.407)

Creating Service

It’s running at a cluster we will not be able to access it directly so let us create a service . We will use the service file we created above to create the service

kubectl apply -f .\k8s-demo-svc.yaml

You can verify your service via kubectl get svc or kubectl get service

kubectl get svc
NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
k8s-demo-svc   NodePort    10.99.108.29   <none>        8080:31019/TCP   2m15s
kubernetes     ClusterIP   10.96.0.1      <none>        443/TCP          22m

Since we are on windows and even Kubernetes is running inside docker ,we need proxy to access pod , otherwise on linux we could have used cluster IP and the port 31019 (as shown above, for your case it will be different) .

Let’s hit the pod using below command. It will open your browser with proxy

minikube service k8s-demo-svc

It will also give following output

PS C:\Users\07pal\development\tutorial\k8s\k8s-demo> minikube service k8s-demo-svc
|-----------|--------------|-------------|---------------------------|
| NAMESPACE |     NAME     | TARGET PORT |            URL            |
|-----------|--------------|-------------|---------------------------|
| default   | k8s-demo-svc |        8080 | http://192.168.49.2:31019 |
|-----------|--------------|-------------|---------------------------|
🏃  Starting tunnel for service k8s-demo-svc.
|-----------|--------------|-------------|------------------------|
| NAMESPACE |     NAME     | TARGET PORT |          URL           |
|-----------|--------------|-------------|------------------------|
| default   | k8s-demo-svc |             | **http://127.0.0.1:52260** |
|-----------|--------------|-------------|------------------------|
🎉  Opening service default/k8s-demo-svc in default browser...
❗  Because you are using a Docker driver on windows, the terminal needs to be open to run it.

Here http://127.0.0.1:52260 is the URL where you can access your deployed pods. so you can check it via http://127.0.0.1:52260/welcome?message=Pallav

It will be giving you proper output.

 
Share this