Spring Data REST Service on Kubernetes
We build a spring data backed REST service, imaged with cloud native build pack and deployed to k8s.

Simple data backed Hateous REST service deployed to K8s.
Create the code:
@SpringBootApplication
public class VadalDataRestApplication {
public static void main(String[] args) {
SpringApplication.run(VadalDataRestApplication.class, args);
}
@Autowired
UserRepo userRepo;
@Autowired
RoleRepo roleRepo;
@PostConstruct
public void init() {
Role boss = new Role("boss");
Role director = new Role("director");
roleRepo.saveAll(Arrays.asList(boss, director));
userRepo.saveAll(Arrays.asList(new User("fred", boss), new User("wilma", director)));
}
}
Turn on SQL logging.
application.yml
server.port: 7777
spring:
jpa:
show-sql: true
Create the image (https://buildpacks.io):
This will create a cloud native build pack.
mvn spring-boot:build-image
docker images
vadal-data-rest 0.0.1-SNAPSHOT 363629e2dd33 40 years ago 256MB
Let's run this on K8s (I'm using desktop for docker K8s locally so this k8s can see the docker images, for minikube you need to point to it's VM's docker).
NOTE: If you have CI setup then this could do a similar thing, after you push the code. See my previous blog on connecting GitLab to K8s. Here, building the java image would just require the above mvn command instead of the docker file.
K8s local on Docker Desktop (macos)
Point kubectl to docker-for-desktop (rather than minikube)
kubectl config use-context docker-for-desktop
kubectl get cs
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health": "true"}
helm init (first time only to install tiller)
Wait for it to install. Install the dashboard.
helm install --wait --name k8s-dash --set service.type=NodePort,service.nodePort=31111 stable/kubernetes-dashboard
chrome://flags/#allow-insecure-localhost
https://localhost:31111/#!/login
You'll need a token to access the UI
token=`kubectl -n kube-system describe secret default | grep 'token:' | awk '{print $2}'
`
kubectl config set-credentials docker-for-desktop --token="${token}"
File will be in ~/.kube/config
or echo $token and use that.

Deploy the Spring REST service image
kubectl create deployment vadal-data-rest --image=vadal-data-rest:0.0.1-SNAPSHOT
deployment.apps/vadal-data-rest created
Expose the Internal Port 7777
kubectl expose deployment vadal-data-rest --type NodePort --port 8888 --target-port 7777
kubectl get svc vadal-data-rest
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
vadal-data-rest NodePort 10.108.4.93 <none> 8888:31894/TCP 1m
{ "_links": { "roles": { "href": "http://localhost:31894/roles" }, "user": { "href": "http://localhost:31894/u" }, "profile": { "href": "http://localhost:31894/profile" } } }
Load balancing with Nginx
helm install stable/nginx-ingress
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
k8s-dash-kubernetes-dashboard NodePort 10.109.122.227 <none> 443:31111/TCP 59m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
ponderous-olm-nginx-ingress-controller LoadBalancer 10.104.194.23 localhost 80:32192/TCP,443:31817/TCP 1m
ponderous-olm-nginx-ingress-default-backend ClusterIP 10.96.101.109 <none> 80/TCP 1m
vadal-data-rest NodePort 10.108.4.93 <none> 8888:31894/TCP 39m
Apply (name this lb.yml)
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: vadal-ingress
spec:
rules:
- host: vadal-data.info
http:
paths:
- backend:
serviceName: vadal-data-rest
servicePort: 8888
kubectl apply -f lb.yml
ingress.extensions/vadal-ingress created
Add vadal-data.info to /etc/hosts
127.0.0.1 vadal-data.info
Browse to: http://vadal-data.info
Should respond with the rest data details.
Logs
K8s Nginx logs:
192.168.65.3 - - [17/Jun/2020:22:09:36 +0000] "GET /favicon.ico HTTP/1.1" 404 146 "http://vadal-data.info/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.98 Safari/537.36" 378 0.013 [default-vadal-data-rest-8888] [] 10.1.0.8:7777 124 0.010 404 7c9566da4f3c0f31e9eacbfd890ac431
1
Scale
Scale to two
kubectl scale deployment/vadal-data-rest --replicas=2
deployment.extensions/vadal-data-rest scaled
kubectl get po
NAME READY STATUS RESTARTS AGE
k8s-dash-kubernetes-dashboard-84cb4cc6f-6t8kc 1/1 Running 0 1h
ponderous-olm-nginx-ingress-controller-6b4d695d86-7gxmq 1/1 Running 0 15m
ponderous-olm-nginx-ingress-default-backend-6fb8ff9645-59mvr 1/1 Running 0 15m
vadal-data-rest-5744484f95-87788 1/1 Running 0 1m
vadal-data-rest-5744484f95-v9d4h
Now that is neat. A new pod.
kubectl logs -f vadal-data-rest-5744484f95-87788
kubectl logs -f vadal-data-rest-5744484f95-v9d4h
Load balancing
Hit the endpoint repeatedly:
Log should round robin between the two:
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.role_id as role_id3_1_ from user user0_
Update the pod
kubectl patch deploy vadal-data-rest -p '{"spec":{"template":{"spec":{"terminationGracePeriodSeconds":31}}}}'
Conclusion
Docker imaged based deployment and scale in kubernetes makes it very easy to scale up (or down), easy to add any load balancer without polluting the code, and gives you choices. The docker image can be deployed anywhere (12Factor compliant).
K8s has inbuilt service discovery and levels of access. Fairly easy to add any load balancer, we added Nginx via helm without needing any yml file. The one yml we needed was to connect it to load balance the service.
Like it?
What about metrics and monitoring? Next time we add prometheus and grafana to monitor our services and display metrics.
Source code can be found here
https://gitlab.com/lightphos/spring/vadal