DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Auto-Scaling a Spring Boot Native App With Nomad
  • Reactive Kafka With Streaming in Spring Boot
  • Spring Boot With Kubernetes
  • Containerization and Helm Templatization Best Practices for Microservices in Kubernetes

Trending

  • IoT and Cybersecurity: Addressing Data Privacy and Security Challenges
  • How to Merge HTML Documents in Java
  • Building an AI/ML Data Lake With Apache Iceberg
  • Monolith: The Good, The Bad and The Ugly
  1. DZone
  2. Software Design and Architecture
  3. Microservices
  4. Advanced Kubernetes Setup for Spring Boot App With PostgreSQL DB

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.

By 
Sven Loesekann user avatar
Sven Loesekann
·
Aug. 05, 21 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
17.5K Views

Join the DZone community and get the full member experience.

Join For Free

For 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:

XML
 
...
<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:

Properties files
 
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:

Dockerfile
 
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:

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.

YAML
 
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.

YAML
 
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.

Spring Framework Kubernetes Spring Boot Docker (software) PostgreSQL app

Opinions expressed by DZone contributors are their own.

Related

  • Auto-Scaling a Spring Boot Native App With Nomad
  • Reactive Kafka With Streaming in Spring Boot
  • Spring Boot With Kubernetes
  • Containerization and Helm Templatization Best Practices for Microservices in Kubernetes

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!