Spring Boot MySQL Kubernetes Minikube Example

Spring Boot MySQL Kubernetes Minikube Example

Introduction

        Kubernetes is one of the best and most popular ways to deploy any application on the cloud today. There are many tutorials based on hello world kind of applications online, but they cannot be used to understand the full end to end implementation/deployment steps on Kubernetes. Hence I decided to come up with this tutorial. 

         In this tutorial we will be deploying a Spring Boot MySQL Microservice on Kubernetes. We will be using a local minikube cluster to deploy this microservice.

        If you are a beginner in Docker, Containers, Docker Images and using Dockerfile, then I strongly recommend that you read the blog posts below to understand more about them:
  1. Dropwizard Docker Example: This post gives you an overview of Docker, simple Docker commands, Dockerfile, dockerizing a simple application and Docker Hub
  2. Spring Boot MySQL Docker Compose Example: This post helps you to understand about multi-container Docker applications and how to run a microservice on Docker.

Kubernetes (K8s)

        Kubernetes is an open-source container-orchestration system for automating deployment, scaling, and management of containerized applications. You must be wondering as to why it is called K8s. K and s are the starting and ending letters of Kubernetes and 8 stands for the eight letters between K and s. It was originally designed and built by Google but now it is open sourced. It works with a range of container tools, including Docker. 

Some of the significant features of Kubernetes are:
  1. Automated rollouts and rollbacks
  2. Service discovery and load balancing
  3. Horizontal scaling
  4. Self-healing
  5. Storage orchestration
  6. Secret and configuration management
  7. Batch execution
You can read more about it on: Kubernetes Website.

Minikube

        Minikube is a tool that helps to run Kubernetes locally and easily. Minikube runs a single-node Kubernetes cluster inside a Virtual Machine (VM) on the laptop for users looking to try out Kubernetes.

Minikube provides us the following Kubernetes features:
  1. NodePorts
  2. DNS
  3. ConfigMaps, Secrets, PersistentVolumes
  4. Dashboards
  5. Container runtime
  6. Ingress
  7. Enabling Container Network Interface (CNI)
You can read more about Minikube on: Kubernetes Website.

Requirements to Run the Spring Boot MySQL Microservice on Docker:
  1. Kubectl
  2. VirtualBox
  3. Minikube
If you would like to understand the microservice we are running in this post,  please check my blog post on Spring Boot MySQL Integration by going to the link: Spring Boot MySQL Integration Tutorial

Minikube should be setup and running in your machine. To setup and run minikube, please refer to my post on: Minikube Setup

Please Note:

If you already have a Spring Boot and MySQL App container that you have created, this example will not work for you unless you have a container created using Docker Compose and have environment variables defined in it. The app container needs to be created using this: docker-compose.yml.

Once Minikube setup is completed, the Spring Boot MySQL microservice can be run on it in a few simple steps.

Step 1: Create required Kubernetes Secrets

Kubernetes Secrets helps us to manage and store sensitive information like passwords, ssh keys and OAuth tokens. Secrets are stored in etcd. etcd is a consistent and highly-available key value store used as Kubernetes' backing store for all cluster data. Secrets can be stored in an encrypted form by enabling encryption. To know more, please read: Kubernetes Secrets Documentation
In this post I am using Kubernetes Secrets to store MySQL database credentials. 

Please Note:
All minikube and kubectl commands are to be run in the Minikube installation directory. For me this is: /d/Programs/Kubernetes/Minikube

Lets begin by stating Minikube. I have observed that the apiserver in Minikube keeps shutting down if the memory allocated or CPUs allocated is less. Hence I am allocating 4 GB memory and 2 CPUs for Minikube using the command below:
  MINGW64 /d/Programs/Kubernetes/Minikube
  $ minikube --memory 4096 --cpus 2 start
  
Check if minikube is running by using status command below:
  MINGW64 /d/Programs/Kubernetes/Minikube
  $ minikube status
  minikube
  type: Control Plane
  host: Running
  kubelet: Running
  apiserver: Running
  kubeconfig: Configured
Secrets are created using kubectl commands below:
    $ kubectl create secret generic mysql-root-pass --from-literal=password=r00t
    secret/mysql-root-pass created
    
    $ kubectl create secret generic mysql-user-pass --from-literal=username=ajtechdeveloper --from-literal=password=@jtechd3v3l0p3r
    secret/mysql-user-pass created
    
    $ kubectl create secret generic mysql-db-url --from-literal=database=softwaredevelopercentral --from-literal=url='jdbc:mysql://springbootmysql-app-mysql:3306/softwaredevelopercentral?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false'
    secret/mysql-db-url created 

Step 2: Create MySQL Deployment yaml and deploy

To deploy MySQL Database we will use Kubernetes Persistent Volume (PV) which is a piece of storage in the Kubernetes cluster. A PersistentVolumeClaim (PVC) is a request for storage by a user just like a pod. Pods consume node resources whereas PVCs consume PV resources. To know more, please read: Kubernetes Persistent Volume Documentation.

For MySQL Database deployment, a yaml files needs to be created. We will use both Kubernetes Secrets and  Kubernetes Persistent Volume in this yaml file. If you observe in this yaml, I am using the image: MySQL version 5.7 . You can understand the yaml file by reading the comments that I have added. Here is the file: mysql-deployment.yaml:
    apiVersion: v1
kind: PersistentVolume            # Create PersistentVolume which is required for MySQL Database
metadata:
  name: mysql-pv
  labels:
    type: local
spec:
  storageClassName: standard      # Storage class. A PersistentVolume Claim requesting the same storageClass can be bound to this volume. 
  capacity:
    storage: 250Mi
  accessModes:
    - ReadWriteOnce
  hostPath:                       # hostPath PersistentVolume is used for both development and testing. It uses a file (or)directory on the Node to emulate the network-attached storage
    path: "/mnt/data"
  persistentVolumeReclaimPolicy: Retain  # Retain the PersistentVolume even after PersistentVolumeClaim is deleted. The volume is considered “released”. But it is not yet available for another claim because the previous claimant’s data remains on the volume. 
---    
apiVersion: v1
kind: PersistentVolumeClaim        # Create PersistentVolumeClaim to request a PersistentVolume storage
metadata:                          # The claim name and labels
  name: mysql-pv-claim
  labels:
    app: springbootmysql-app
spec:                              # Access mode and the resource limits
  storageClassName: standard       # Request certain storage class
  accessModes:
    - ReadWriteOnce                # ReadWriteOnce means that the volume can be mounted as read-write by a single Node
  resources:
    requests:
      storage: 250Mi
---
apiVersion: v1                    # The API version
kind: Service                     # Type of kubernetes resource 
metadata:
  name: springbootmysql-app-mysql         # Name of the resource
  labels:                         # Labels that will be applied to the resource
    app: springbootmysql-app
spec:
  ports:
    - port: 3306
  selector:                       # Selects any Pod with labels `app=springbootmysql-app,tier=mysql`
    app: springbootmysql-app
    tier: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment                    # Type of kubernetes resource
metadata:
  name: springbootmysql-app-mysql           # Name of the deployment
  labels:                           # Labels applied to this deployment 
    app: springbootmysql-app
spec:
  selector:
    matchLabels:                    # This deployment applies to the Pods matching the specified labels
      app: springbootmysql-app
      tier: mysql
  strategy:
    type: Recreate
  template:                         # Template for Pods in this deployment
    metadata:
      labels:                       # Labels to be applied to the Pods in this deployment
        app: springbootmysql-app
        tier: mysql
    spec:                           # The spec for the containers that will be run inside the Pods in this deployment
      containers:
      - image: mysql:5.7            # The container image
        name: mysql
        env:                        # Environment variables passed to the container 
        - name: MYSQL_ROOT_PASSWORD 
          valueFrom:                # Read environment variables from kubernetes secrets
            secretKeyRef:
              name: mysql-root-pass
              key: password
        - name: MYSQL_DATABASE
          valueFrom:
            secretKeyRef:
              name: mysql-db-url
              key: database
        - name: MYSQL_USER
          valueFrom:
            secretKeyRef:
              name: mysql-user-pass
              key: username
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-user-pass
              key: password
        ports:
        - containerPort: 3306        # The port that the container exposes       
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage  # This name should match the name specified in `volumes.name`
          mountPath: /var/lib/mysql
      volumes:                       # A PersistentVolume is mounted as a volume to the Pod  
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim    
To deploy this in Minikube, we use the kubectl command below:
    $ kubectl apply -f /d/projects/gitprojects/SpringBootMySQL/deployments/mysql-deployment.yaml   
    
To log into the MySQL database pod, get its pod name using the command below:
    $ kubectl get pods
    NAME                                          READY   STATUS    RESTARTS   AGE
    springbootmysql-app-mysql-7fbc9f88cc-7kxgk    1/1     Running   3          7d17h
    
Port forward the pod's port 3306 to your computer localhost (127.0.0.1) port 3306. Then you can use an installed existing MySQL client in your computer to log into the database. Use the command below for port forwarding:
     $ kubectl port-forward springbootmysql-app-mysql-7fbc9f88cc-7kxgk 3306:3306
     Forwarding from 127.0.0.1:3306 -> 3306
     Forwarding from [::1]:3306 -> 3306
     Handling connection for 3306
The open a new windows command prompt window, navigate to the MySQL installation location bin folder and login to the database using the command below, use the password r00t when prompted:
    D:\Programs\mysql-5.7.19-winx64\bin>mysql -u root -p -h 127.0.0.1
Here are a few commands to execute after logging into the MySQL Database:
  > show databases;
  > use softwaredevelopercentral;  
In this post, I am using a simple Employee Table to perform CRUD operations. Here is the script to create this table in the MySQL Database:
create table employee (
id int not null auto_increment primary key,
name varchar(50),
department varchar(50),
salary int(20)
);  

Step 3: Create Spring Boot App Deployment yaml and deploy

If you observe in this yaml, I am using the image (ajtechdeveloper/springbootmysql-app:latest) that I created using Docker Compose and published to Docker Hub in the post: Spring Boot MySQL Docker Compose Example. You can understand the yaml file by reading the comments that I have added. Here is the file: springboot-app-server.yaml:
    ---
apiVersion: apps/v1           # The API version
kind: Deployment              # Type of Kubernetes resource
metadata:
  name: springbootmysql-app-server    # Name of the Kubernetes resource
  labels:                     # Labels that will be applied to this resource
    app: springbootmysql-app-server
spec:
  replicas: 1                 # No. of replicas (or) pods to run in this deployment
  selector:
    matchLabels:              # The deployment applies to any pods mayching the specified labels
      app: springbootmysql-app-server
  template:                   # Template for creating the pods in this deployment
    metadata:
      labels:                 # Labels that will be applied to each Pod in this deployment
        app: springbootmysql-app-server
    spec:                     # Specification for the containers that will be run in the Pods
      containers:
      - name: springbootmysql-app-server
        image: ajtechdeveloper/springbootmysql-app:latest
        imagePullPolicy: IfNotPresent
        ports:
          - name: http
            containerPort: 4000 # The port that the container exposes
        resources:
          limits:
            cpu: 0.8
            memory: "1Gi"     # This Spring Boot App needs around 700 MB Memory 
        env:                  # Environment variables supplied to the Pod
        - name: SPRING_DATASOURCE_USERNAME # Name of the environment variable
          valueFrom:          # Get the value of environment variable from kubernetes secrets
            secretKeyRef:
              name: mysql-user-pass
              key: username
        - name: SPRING_DATASOURCE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-user-pass
              key: password
        - name: SPRING_DATASOURCE_URL
          valueFrom:
            secretKeyRef:
              name: mysql-db-url
              key: url
---
apiVersion: v1                # The API version
kind: Service                 # Type of the kubernetes resource
metadata:                     
  name: springbootmysql-app-server    # Name of the kubernetes resource
  labels:                     # Labels that will be applied to this resource
    app: springbootmysql-app-server
spec:                         
  type: NodePort              # The service will be exposed by opening a Port on each node and proxying it. 
  selector:
    app: springbootmysql-app-server   # The service exposes Pods with label `app=springbootmysql-app-server`
  ports:                      # Forward incoming connections on port 4000 to the target port 4000
  - name: http
    port: 4000
    targetPort: 4000
    
To deploy this in Minikube, we use the kubectl command below:
$ kubectl apply -f /d/projects/gitprojects/SpringBootMySQL/deployments/springboot-app-server.yaml   
    
To check the details of all pods, services, deployments and replicasets in the Kubernetes cluster use the command below:
$ kubectl get all
Here is the output:
$ kubectl get all
NAME                                              READY   STATUS    RESTARTS   AGE
pod/springbootmysql-app-mysql-7fbc9f88cc-7kxgk    1/1     Running   3          7d14h
pod/springbootmysql-app-server-84488b465d-8dkk5   1/1     Running   1          6d17h

NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/kubernetes                   ClusterIP   10.96.0.1        <none>        443/TCP          7d14h
service/springbootmysql-app-mysql    ClusterIP   None             <none>        3306/TCP         7d14h
service/springbootmysql-app-server   NodePort    10.106.245.185   <none>        4000:30657/TCP   6d17h

NAME                                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/springbootmysql-app-mysql    1/1     1            1           7d14h
deployment.apps/springbootmysql-app-server   1/1     1            1           6d17h

NAME                                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/springbootmysql-app-mysql-7fbc9f88cc    1         1         1       7d14h
replicaset.apps/springbootmysql-app-server-84488b465d   1         1         1       6d17h
To describe a pod, get the pod name and use the command below:
  $ kubectl describe pod springbootmysql-app-server-84488b465d-8dkk5
To check pod logs, get the pod name and use the command below:
  $ kubectl logs --tail=2000 springbootmysql-app-server-84488b465d-8dkk5
To check the service URL get the service name and use the command below:
  $ minikube service springbootmysql-app-server
  |-----------|----------------------------|-------------|-----------------------------|
  | NAMESPACE |            NAME            | TARGET PORT |             URL             |
  |-----------|----------------------------|-------------|-----------------------------|
  | default   | springbootmysql-app-server | http/4000   | http://192.168.99.100:30004 |
  |-----------|----------------------------|-------------|-----------------------------|
  * Opening service default/springbootmysql-app-server in default browser...    
Now the Spring Boot service APIs can be accessed using the url: http://192.168.99.100:30004/

API calls when the service is running:

1. POST API to create an employee
    JSON Request Body:
  {
   "name": "Mary",
   "department": "Accounts",
   "salary": 10000
  }

2. GET API to get all employees:

3. GET API to get employee by NAME

4. PUT  API to update an employee
    http://192.168.99.100:30004/employee
    JSON Request Body:
    {
        "id":1,
        "name": "Mary",
        "department": "Accounts",
        "salary": 12000
    }
5. DELETE API to delete an employee by ID

Other than these APIs, this application has the following APIs:

1. GET API to Ping and test if the application is up and running:

2. POST API to Ping and test if the application is up and running:
    JSON Request Body:
    {
     "input": "ping"
    }

Delete Service and Deployment

To delete service, get the service name and use the command below:
  $ kubectl delete services springbootmysql-app-server
To delete deployment, get the deployment name and use the command below:
  $ kubectl delete deployment springbootmysql-app-server

Stop Minikube

To stop Minikube, use the command below:
  $ minikube stop

Conclusion, Docker Hub Link and GitHub link: 

    This tutorial gives a comprehensive overview of running a Spring Boot MySQL microservice on Minikube. The code for the Spring Boot MySQL application used in this post is available on GitHub. The docker image used in this post is available on Docker Hub.   
    Learn the most popular and trending technologies like Blockchain, Cryptocurrency, Machine Learning, Chatbots, Internet of Things (IoT), Big Data Processing, Elastic Stack, React, Highcharts, Progressive Web Application (PWA), Angular 5, GraphQL, Akka HTTP, Play Framework, Dropwizard,   Docker, Netflix Eureka, Netflix Zuul, Spring Cloud, Spring Boot, Flask and RESTful Web Service integration with MongoDB, Kafka, Redis, Aerospike, MySQL DB in simple steps by reading my most popular blog posts at Software Developer Central.   
    If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter or Instagram. Please leave a note below if you have any questions or comments.

Comments

  1. This was an excellent tutorial for a beginner to learn how to create spring boot app and mysql within kubernetes.

    While going through it, I discovered one minor typo and corrected this line:

    kubectl create secret generic mysql-db-url --from-literal=database=softwaredevelopercentral --from-literal=url='jdbc:mysql://springbootmysql-app-mysql:3306/softwaredevelopercentral?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false'

    Instead of springboot-app-mysql we want springbootmysql-app-mysql

    Also I learned how to create the table without having to use the port forwarding step. You can access mysql within the docker bash:

    kubectl exec --stdin --tty pod/YOUR-MYSQL-POD-NAME-HERE -c mysql -- /bin/bash

    mysql -u root -p

    the password is r00t which has 2 zeros

    Once connected to mysql, you can continue to follow your steps to create the employee table. I was then able to use Postman to add employees.

    Awesome tutorial - I learned a lot and enjoyed working through it!

    ReplyDelete
    Replies
    1. Hi @brandon
      Thanks for your comment. Yes, there is a typo error, thanks for pointing that out. I have just corrected it.

      Delete

Post a Comment

Popular Posts

Elasticsearch, Logstash, Kibana Tutorial: Load MySQL Data into Elasticsearch

Dropwizard MySQL Integration Tutorial

Send Email in Java