Advanced Kubernetes Setup for Spring Boot App With PostgreSQL DB
Minkube setup with Spring Actuators for probes, resource limits, and use of JVM Container support.
Join the DZone community and get the full member experience.
Join For FreeFor this example, the MovieManager project is used. It is a Spring Boot application build with Java and JPA. The frontend is done with Angular and Bootstrap 5. The PostgreSQL DB is used to store the movies and actors.
Kubernetes Setup
The Kubernetes (Minikube) setup will use probes that are supported by Spring Actuator to check the startup and the health of the MovieManager container. The Jre 16 supports resource limits that are used for cpu and memory. The PostgreSQL image will use CPU limits and store its data persistent. To deploy the setup to Minikube, Helm will be used.
MovieManager Updates
To use Java 16, the 'spring-boot-starter-parent' version '2.5.0' or newer is needed. For the probes, the 'spring-boot-starter-actuator' has to be added. That has been done in the pom.xml of the project:
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
...
The rest endpoints of are the exposed by Spring Actuator. To expose only the health endpoints, the application.properties have been updated:
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
management.health.enabled=true
management.endpoint.health.show-details=always
management.endpoints.web.exposure.include=health
management.endpoints.jmx.exposure.include=health
These enable the readiness probe endpoint for the startup checks and the liveness probe endpoint for the health checks.
The details are enabled for more information.
The exposure properties export only the health endpoints. If other endpoints are exposed a security configuration might be useful.
Dockerfile Updates
The Dockerfile of the MovieManager project:
FROM adoptopenjdk/openjdk16:alpine-jre
VOLUME /tmp
ARG SPRING_PROFILE=prod
ENV MY_SPRING_PROFILE=$SPRING_PROFILE
ARG JAR_FILE
ADD target/$JAR_FILE /app.jar
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=150 -XX:+UseStringDeduplication"
ENTRYPOINT exec java $JAVA_OPTS -Dspring.profiles.active=$MY_SPRING_PROFILE -Djava.security.egd=file:/dev/./urandom -jar /app.jar
It uses the AdoptOpenJdk 16 Alpine Jre to save space with the image.
It has the default Spring Profile 'prod'. Another one can be provided at build time.
The Java_Opts switch on the G1GC with a PauseTimeTarget of 150ms. The Memory limits are provided by the container and have been removed here.
The commands to build the project / Docker image can be found in the buildDocker.sh file. The MovieManager image is available on Docker Hub.
Helm Chart
To deploy the setup to Minikube, the newest versions of Kubectl, Minikube, and Helm need to be installed on the computer.
The Helm Chart is in this directory and has this values.yaml:
webAppName: moviemanagerapp
dbName: postgresserver
webImageName: angular2guy/moviemanager
webImageVersion: latest
dbImageName: postgres
dbImageVersion: 13
volumeClaimName: postgres-pv-claim
persistentVolumeName: task-pv-volume
webServiceName: moviemanagerservice
dbServiceName: postgresservice
#for production use replace the jwtTokenSecrect value with a random alphanumeric string of the same length or longer
jwtTokenSecrect: secret-key1234567890abcdefghijklmnopqrstuvpxyz
secret:
nameApp: app-env-secret
nameDb: db-env-secret
envDb:
normal:
POSTGRES_URL: "jdbc:postgresql://postgresservice:5432/movies"
secret:
POSTGRES_USER: dbuser
POSTGRES_PASSWORD: passwordtoreplace
POSTGRES_DB: movies
envApp:
normal:
JPA_SHOW_SQL: true
H2_CONSOLE: false
secret:
JWTTOKEN_SECRET: secret-key1234567890abcdefghijklmnopqrstuvwxyz
POSTGRES_USER: dbuser
POSTGRES_PASSWORD: passwordtoreplace
POSTGRES_URL: "jdbc:postgresql://postgresservice:5432/movies"
In the top 12 lines, the variables for the kubTemplate.yaml are defined.
In lines 14-16, the secret names for the MovieManager and the Postgresql DB are defined.
In lines 18-24, the public and opaque variables of the Postgresql image are defined. The base name 'envDb' is important and is used in a script
In lines 26-34, the public and opaque variables of the MovieManager image are defined. The base name is important is used in a script.
In the templates directory is the _helpers.tpl file that contains scripts that are used to set values in the kubTemplate.yaml. The added scripts are based on this blog post.
Create envApp values
*/}}
{{- define "helpers.list-envApp-variables"}}
{{- $secretName := .Values.secret.nameApp -}}
{{- range $key, $val := .Values.envApp.secret }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ $secretName }}
key: {{ $key }}
{{- end}}
{{- range $key, $val := .Values.envApp.normal }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end}}
{{- end }}
Create envDb values
*/}}
{{- define "helpers.list-envDb-variables"}}
{{- $secretName := .Values.secret.nameDb -}}
{{- range $key, $val := .Values.envDb.secret }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ $secretName }}
key: {{ $key }}
{{- end}}
{{- range $key, $val := .Values.envDb.normal }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end}}
{{- end }}
In this script, the functions 'list-envApp-variables' and 'list-envDb-variables' are created. They iterate the values defined in the values.yaml and add them to the deployment parts of the kubTemplate.yaml. That enables to externalize a variable amount of parameters of the images to the values.yaml.
The Setup Yaml
The kubTemplate.yaml contains the setup of the Kubernetes cluster.
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.secret.nameApp }}
type: Opaque
data:
{{- range $key, $val := .Values.envApp.secret }}
{{ $key }}: {{ $val | b64enc }}
{{- end}}
---
--- ...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.webAppName }}
labels:
app: {{ .Values.webAppName }}
spec:
replicas: 1
selector:
matchLabels:
app: {{ .Values.webAppName }}
template:
metadata:
labels:
app: {{ .Values.webAppName }}
spec:
containers:
- name: {{ .Values.webAppName }}
image: "{{ .Values.webImageName }}:{{ .Values.webImageVersion }}"
imagePullPolicy: Always
resources:
limits:
memory: "1G"
cpu: "0.5"
requests:
memory: "768M"
cpu: "0.5"
env:
{{- include "helpers.list-envApp-variables" . | indent 10 }}
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: "/actuator/health/livenessState"
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
startupProbe:
httpGet:
path: "/actuator/health/readinessState"
port: 8080
failureThreshold: 60
periodSeconds: 5
This shows only the part of the setup of the MovieManager.
In lines 1-9, an opaque key/value map is created with the secrets of the MovieManager image.
In lines 30-32, the image to be deployed is defined based on the values of the values.yaml.
In lines 33-39, the initial requested and max amounts of memory and cpu are defined. The Java 16 Jvm supports the container limits.
In lines 40-41, the function 'list--envApp-variables' is used to set the environment variables the image needs.
In lines 44-49, the livenessProbe of Kubernetes is setup to check the Spring Actuator endpoint every 5 seconds after an initial delay of 5 seconds. If it fails the container is restarted.
In lines 50-55, the startupProbe of Kubernetes setup to check if the image has started. It checks every 5 seconds for at most 60 times. After that the container gets started again. The livenessProbe is activated after a successful start.
Deployment
To deploy the setup for the MovieManager in Minikube, start an empty Minikube cluster and execute this command in the helm directory of the project:
helm install moviemanager ./ --set serviceType=NodePort
To check if the pods have started, execute:
kubectl get pods
Wait until both are ready.
To get the Minikube IP, execute:
minikube ip
for example: '192.168.99.100'
To get the port for the MovieManager, execute:
kubectl get services
for example: 'moviemanagerservice NodePort 10.107.170.112 <none> 8080:30197/TCP'
To open the MovieManager, open this Url:
http://<minikube ip>:<kubectl Nodeport>/
for example: 'http://192.168.99.100:30197/'
To Login to the test data:
User: 'John'
Password: 'Doe'
To start the Kubernetes Dashboard:
minikube dashboard
Conclusion
Spring Boot with Actuator and Kubernetes work together very well and are easy to setup. The newer Jvms support resource limits. That simplifies the configuration in Kubernetes with resource limits. With Helm and the blog post, it was easy to create a flexible script to setup the MovieManager project in Kubernetes. A thank you for the Blog post, Helm, Spring Actuator, Kubernetes probes, and the container support of the Jvms.
Opinions expressed by DZone contributors are their own.
Comments