Introduction
Kubernetes is the container orchestration tool which has gained lot of traction in the last couple of years. Kubernetes is highly scalable and can be used on bare metal, VMs and on almost all public cloud offerings
In this blog post I will demo how you can deploy a simple spring boot application packaged as a docker image to a kubernetes cluster running on your local machine using minikube. We will start by writing a small application and then will package it as a docker image and finally will deploy it on kubernetes cluster. We will expose this application using Ingress so that it is available outside the cluster.
Prerequisite to this demo
- You are well versed with Spring boot and have jdk and maven tooling available
- You have docker installed to create docker images for your application
- You are aware of the basic constructs and architecture of Kubernetes
- You have minikube installed for spinning up a kubernetes cluster
If you have all the above tooling we can proceed with the demo or else please go through the general introduction and configure your system correctly before moving forward. There are plenty of resource out there to help you in getting started with Spring Boot, Docker and Kubernetes as this is outside the scope of the tutorial.
Lets get started with the demo
Create a Spring Boot App
Go to start.spring.io and bootstrap an application with web and lombok as mandatory dependencies.
Add these two model classes. These are used for creating our response JSON message. We will be populating this response with Host details which will help us understanding from where we are getting the response.
Host Details:
@Value
@Builder
public class HostDetails {
String hostname;
}
Pong Message :
@Value
@Builder
public class PongMessage {
String message;
HostDetails host;
}
Add a ping controller as highlighted below. This controller just fetches the host details and populate our model classes with the same. We send this as a JSON response to our caller.
@CrossOrigin
@RestController
@RequestMapping(value="/ping")
public class pingController {
InetAddress ip;
@RequestMapping(value="", method=RequestMethod.GET)
public ResponseEntity<PongMessage> sendPongWithHostDetails() {
String hostname = null;
try {
ip = InetAddress.getLocalHost();
hostname = ip.getHostName();
} catch (UnknownHostException e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
HostDetails host = HostDetails.builder()
.hostname(hostname)
.build();
PongMessage message = PongMessage.builder()
.message("pong")
.host(host)
.build();
return new ResponseEntity<>(message, HttpStatus.OK);
}
}
Run this application locally using the below command and test if you can query localhost:8080/ping
mvn spring-boot:run
If the ping controller is working fine, package the application as a fat jar using the below command
mvn clean package
Creating a Docker image for your App
Create a docker directory within the root of your application. This directory is used to populate the docker context for our docker build command.
Switch to the above directory and create a docker file like below and copy your jar from the target directory into it.
FROM openjdk:8-jre-alpine
MAINTAINER pulgupta
COPY k8Demo-0.0.1-SNAPSHOT.jar /opt/app/lib/
ENTRYPOINT ["/usr/bin/java"]
CMD ["-jar", "/opt/app/lib/k8Demo-0.0.1-SNAPSHOT.jar"]
EXPOSE 8888
Issue this command to create a docker image. I am using my docker hub id for creating the image and tagging it to latest. Please use your docker id here. In case you do not have a docker id please create a new one at hub.docker.com
docker build . -t pulgupta/hostping:latest
Push this docker image to your docker hub account so that it is available for your Kubernetes cluster for download.
docker push pulgupta/hostping
Creating Kubernetes Artifacts
Creating service
This service is used to link our pods and route traffic to the pods available for this Service. For Ingress(created in the later section) we are required to create a service of type NodePort which will allocate a port for this service on all the nodes available in the kubernetes cluster. Also we are mapping port 80 to the port 8080, which is the port where our spring application will be running inside the pods.
apiVersion: v1
kind: Service
metadata:
name: hostping
labels:
run: hostping
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
run: hostping
type: NodePort
Creating deployments
We are creating replica set with 2 replicas which will create two pods for the image which we have specified in the containers section. We also mention the port which will be exposed outside the container.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostping
spec:
selector:
matchLabels:
run: hostping
replicas: 2
template:
metadata:
labels:
run: hostping
spec:
containers:
- name: hostping
image: pulgupta/hostping
ports:
- containerPort: 8080
Creating Ingress
Ingress will behave as a smart router and will redirect external traffic to our service based on the routing rules we are specifying here. As per the below rules when Ingress will see a request coming to /ping it will redirect the request to our service “hostping”.
NOTE: Enable ingress add-on for ingress to work in minikube
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: hostping
spec:
rules:
- http:
paths:
- path: /ping
backend:
serviceName: hostping
servicePort: 80
Finally create all these artefacts using the below command. k8Templates is a file with all the three kubernetes artefacts as mentioned above and separated by three hyphen —. You can also create three separate files and run this command once each for Service.yaml, deployment.yaml and Ingress.yaml
kubectl create -f k8Templates
Testing your deployment
Find out the minikube ip address using the below command
minikube ip
If you have followed all the steps correctly then you will be able to query the application using the minikube ip and the path /ping for example : http://192.168.99.100/ping
Note: If you will refresh the page again and again you will see two different hostnames. This is because in our deployment we have specified 2 replicas and thus two pods were created for this application. These pods are hit alternatively by our service and thus we see two different hostnames.
Further action
In the next blog we will be creating a frontend app and will setup a service to service communication with our host ping app and the frontend.