Build and deploy Java Spring Boot microservices on Kubernetes

Spring Boot is one of the popular Java microservices framework. Spring Cloud has a rich set of well integrated Java libraries to address runtime concerns as part of the Java application stack, and Kubernetes provides a rich featureset to run polyglot microservices. Together these technologies complement each other and make a great platform for Spring Boot applications.

In this code we demonstrate how a simple Spring Boot application can be deployed on top of Kubernetes. This application, Office Space, mimicks the fictitious app idea from Michael Bolton in the movie Office Space. The app takes advantage of a financial program that computes interest for transactions by diverting fractions of a cent that are usually rounded off into a seperate bank account.

The application uses a Java 8/Spring Boot microservice that computes the interest then takes the fraction of the pennies to a database. Another Spring Boot microservice is the notification service. It sends email when the account balance reach more than $50,000. It is triggered by the Spring Boot webserver that computes the interest. The frontend uses a Node.js app that shows the current account balance accumulated by the Spring Boot app. The backend uses a MySQL database to store the account balance.

Flow

Architecture

  1. The Transaction Generator service written in Python simulates transactions and pushes them to the Compute Interest microservice.
  2. The Compute Interest microservice computes the interest and then moves the fraction of pennies to the MySQL database to be stored.
  3. The Compute Interest microservice then calls the notification service to notify the user if an amount has been deposited in the user's account.
  4. The user retrieves the account balance by visiting the Node.js web interface.

Prerequisite

  • Kubernetes cluster
  • Maven
sudo apt update
sudo apt install default-jre default-jdk maven -y

Steps

Clone the repo

git clone https://gitlab.com/we-can-do-now/spring-boot/office-space-k8s.git
cd office-space-k8s/

Create the Database service

kubectl create -f account-database.yaml
service/account-database created
deployment.extensions/account-database created

Default credentials are already encoded in base64 in secrets.yaml.

Encoding in base64 does not encrypt or hide your secrets. Do not put this in your Github.

cat secrets.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: demo-credentials
type: Opaque
data:
  username: bWljaGFlbGJvbHRvbg==
  password: cGFzc3dvcmQ=
  host: YWNjb3VudC1kYXRhYmFzZQ==
  port: MzMwNg==
kubectl apply -f secrets.yaml
secret/demo-credentials created

Create the Spring Boot Compute-Interest-API Microservice

Compute-Interest-API is a Spring Boot app configured to use a MySQL database. The configuration is located in compute-interest-api/src/main/resources/application.properties in spring.datasource.*

cat containers/compute-interest-api/src/main/resources/application.properties
# ===============================
# = DATA SOURCE
# ===============================

# Set here configurations for the database connection

# Connection url for the database "account-database"
spring.datasource.url = jdbc:mysql://${MYSQL_DB_HOST}:${MYSQL_DB_PORT}/dockercon2017

# Username and password
spring.datasource.username = ${MYSQL_DB_USER}
spring.datasource.password = ${MYSQL_DB_PASSWORD}

# Keep the connection alive if idle for a long time (needed in production)
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1

spring.datasource.tomcat.max-wait=60000

# ===============================
# = JPA / HIBERNATE
# ===============================

# Use spring.jpa.properties.* for Hibernate native properties (the prefix is
# stripped before adding them to the entity manager).

# Show or not log for each sql query
spring.jpa.show-sql = true

# Hibernate ddl auto (create, create-drop, update): with "update" the database
# schema will be automatically updated accordingly to java entities found in
# the project
spring.jpa.hibernate.ddl-auto = update

# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy

# Allows Hibernate to generate SQL optimized for a particular DBMS
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

# Secret to share between client and server when enabling remote live restarting or debugging. DO NOT USE IN PRODUCTION
spring.devtools.remote.secret=thisismysecret

The application.properties is configured to use MYSQL_DB_* environment variables. These are defined in the compute-interest-api.yaml file. It is already configured to get the values from the Kubernetes Secrets that was created earlier.

cat compute-interest-api.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: compute-interest-api
  labels:
    app: office-space
spec:
  ports:
    - port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: office-space
    tier: compute
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: compute-interest-api
  labels:
    app: office-space
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: office-space
        tier: compute
    spec:
      containers:
        - image: anthonyamanse/compute-interest-api:v1
          imagePullPolicy: Always
          name: compute-interest-api
          env:
            - name: MYSQL_DB_USER
              valueFrom:
                secretKeyRef:
                  name: demo-credentials
                  key: username
            - name: MYSQL_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: demo-credentials
                  key: password
            - name: MYSQL_DB_HOST
              valueFrom:
                secretKeyRef:
                  name: demo-credentials
                  key: host
            - name: MYSQL_DB_PORT
              valueFrom:
                secretKeyRef:
                  name: demo-credentials
                  key: port
          ports:
            - containerPort: 8080

Build the project using Maven

cd containers/compute-interest-api
mvn package

After Maven has successfully built the Java project, you will need to build the Docker image using the provided Dockerfile.

cat Dockerfile
FROM maven:3.3.9-jdk-8-alpine
COPY . /app
WORKDIR /app
RUN apk update && apk add mysql mysql-client
ENTRYPOINT ["/app/custom-entrypoint.sh"]
CMD java -jar target/*.jar
docker build -t adithya321/compute-interest-api .
docker push adithya321/compute-interest-api

Once you have successfully pushed your image, modify compute-interest-api.yaml to use this image

cd ~/office-space-k8s/
vim compute-interest-api.yaml
...
spec:
    containers:
        - image: adithya321/compute-interest-api:latest
...

Deploy the Microservices

  • Deploy the Spring Boot Microservice
kubectl apply -f compute-interest-api.yaml
service/compute-interest-api created
deployment.extensions/compute-interest-api created
  • Deploy the Frontend service

The UI is a Node.js app serving static files (HTML, CSS, JavaScript) that shows the total account balance.

kubectl apply -f account-summary.yaml
service/account-summary created
deployment.extensions/account-summary created
  • Deploy the Transaction Generator service

The transaction generator is a Python app that generates random transactions with accumulated interest.

kubectl apply -f transaction-generator.yaml
service/transaction-generator created
deployment.extensions/transaction-generator created

Access Your Application

You can access your app publicly through your Cluster IP and the NodePort.

To find your IP:

kubectl get nodes -o wide
NAME                                                STATUS   ROLES    AGE   VERSION         INTERNAL-IP   EXTERNAL-IP      OS-IMAGE                             KERNEL-VERSION   CONTAINER-RUNTIME
gke-standard-cluster-1-default-pool-a6b2de38-4nhh   Ready    <none>   24m   v1.11.8-gke.6   10.128.0.2    35.206.106.166   Container-Optimized OS from Google   4.14.91+         docker://17.3.2
gke-standard-cluster-1-default-pool-a6b2de38-4tv3   Ready    <none>   24m   v1.11.8-gke.6   10.128.0.4    35.209.61.153    Container-Optimized OS from Google   4.14.91+         docker://17.3.2
gke-standard-cluster-1-default-pool-a6b2de38-d5sd   Ready    <none>   24m   v1.11.8-gke.6   10.128.0.3    35.209.252.98    Container-Optimized OS from Google   4.14.91+         docker://17.3.2

To find the NodePort of the account-summary service:

kubectl get svc account-summary
NAME              TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
account-summary   NodePort   10.11.255.41   <none>        80:30080/TCP   2m

On your browser, go to http://your-cluster-IP:30080

Account Summary 0

Scale the frontend deployment

kubectl scale deployment/account-summary --replicas=5
deployment.extensions/account-summary scaled

Refresh the page multiple times to see a different pod's hostname

Account Summary 1

Account Summary 2

results matching ""

    No results matching ""