Deploy a simple webapp and database on Minikube

Deploy a simple webapp and database on Minikube

Overview

Minikube is a micro version of Kubernetes that can be used for testing or development purposes in your local environment(Windows, Linux, macOS). it is a one-node Kubernetes cluster that runs on a particular virtualization software that you specify, which could be VirtualBox, HyperV, KVM, etc.

Objective

For this exercise, we will be deploying a simple mongo express web app that connects to a MongoDB database

Prerequisites

In other to achieve the above, we will need the following;

  • Minikube
  • kubectl
  • virtual box
  • terminal (Git bash or Powershell)
  • 2gb ram
  • 20gb disk space

Setting up the environment

To install minikube on Windows you can run the following command on your Powershell terminal;

New-Item -Path 'c:\' -Name 'minikube' -ItemType Directory -Force
Invoke-WebRequest -OutFile 'c:\minikube\minikube.exe' -Uri 'https://github.com/kubernetes/minikube/releases/latest/download/minikube-windows-amd64.exe' -UseBasicParsing

then make sure you add the binary to your PATH with the following command;

$oldPath = [Environment]::GetEnvironmentVariable('Path', [EnvironmentVariableTarget]::Machine)
if ($oldPath.Split(';') -inotcontains 'C:\minikube'){ `
  [Environment]::SetEnvironmentVariable('Path', $('{0};C:\minikube' -f $oldPath), [EnvironmentVariableTarget]::Machine) `
}

you will need to restart your powershell prompt before proceeding. Alternatively, you can just download the minikube installer for Windows here.

For Linux machines, you can download minikube with the following commands;

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

For MacOS, minikube can be downloaded with;

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64
sudo install minikube-darwin-amd64 /usr/local/bin/minikube

Kubectl is a utility tool that helps you interact with and manage resources inside kubernetes clusters. Most times kubectl is installed alongside minikube during minikube installation, but if that is not the case and you need to install kubectl, you can follow the detailed guide here.

Detailed instructions on downloading virtualbox for your prefered OS can be found here.

Now that all the tools we need are installed and ready, you can confirm them by running;

kubectl version

minikube version

This should return the following outputs;

PS C:\Users\joxhb\Desktop\minikube> minikube version minikube version: v1.23.2 commit: 0a0ad764652082477c00d51d2475284b5d39ceed

PS C:\Users\joxhb\Desktop\minikube> kubectl version Client Version: version.Info{Major:"1", Minor:"20+", GitVersion:"v1.20.8-dispatcher", GitCommit:"283881f025da4f5b3cefb6cd4c35f2ee4c2a79b8", GitTreeState:"clean", BuildDate:"2021-09-14T05:16:08Z", GoVersion:"go1.15.13", Compiler:"gc", Platform:"windows/amd64"}

Create a directory called "minikube" and move into that folder ;

mkdir minikube
cd minikube

This is where all our files will reside. Now clone the Github repository containing the files;

git clone https://github.com/Joshua-Igoni/minikubeXmongo.git

After cloning you will have the following files as shown below

PS C:\Users\joxhb\Desktop\kubernetes\minikube> ls


    Directory: C:\Users\joxhb\Desktop\kubernetes\minikube


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        11/13/2021   5:45 PM            106 configmap.yaml
-a----        11/13/2021   5:45 PM            876 mongo.yaml
-a----        11/13/2021   5:45 PM           1127 mongoexpress.yaml
-a----        11/13/2021   5:45 PM            159 secret.yaml

Explaining some k8s concepts

Kubernetes follows an object model style of operation which means that each thing that Kubernetes manages is represented by an object, and you can view and change these object attributes and state, examples of objects are pods, deployments, stateful sets, jobs, and cron jobs, services, volumes, etc. But for this exercise, we will be using pods, deployments, services, config maps, and secrets specifically. Pods are the smallest deployable objects in Kubernetes, it is the environment where a container image will exist and share networking and storage space. Deployments ensure that a defined set of pods is running at any given time. Services ensure communication between pods in a cluster and also to external endpoints outside the cluster depending on if it's an internal or external service. Configmaps help store configuration data, while secrets help store sensitive data such as passwords and usernames, etc.

There are basically two ways by which you can modify or manage the state of a cluster;

  • Declarative management
  • Imperative management

Declarative management (which we will be using) is the form of management where the desired state of the cluster that you want is being properly documented in manifest files (.yaml) and once executed, Kubernetes will work to bring that state into being. In other words, you do not have to worry about the "how" the described state comes about. However, in the Imperative management style, you have to tell Kubernetes exactly how it is supposed to bring that desired state into being through a series of commands.

Understanding the structure of manifest files

From our initial steps we have already cloned our manifest files from the GitHub repo, now let's understand how that really works.

mongo.yaml;

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongodb-deployment
  labels:
    app: mongodb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mongodb
  template:
    metadata:
      labels:
        app: mongodb
    spec:
      containers:
      - name: mongodb
        image: mongo
        ports:
        - containerPort: 27017
        env:
          - name: MONGO_INITDB_ROOT_USERNAME
            valueFrom:
              secretKeyRef:
                 name: mondb-secret
                 key: mongo-root-username 
          - name: MONGO_INITDB_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                 name: mondb-secret
                 key: mongo-root-password 

---
apiVersion: v1
kind: Service
metadata:
  name: mongodb-service
spec:
  selector:
    app: mongodb
  ports:
  - protocol: TCP
    port: 27017
    targetPort: 27017

From the file above, "apiVersion" describes which Kubernetes apiVersion is used to create the object. "kind" Defines the object you want to be created. "metadata" helps identify the Object, using names, unique id, namespace and labels. "Spec" provides more configuration details depending on the kind of object being created, "Template"" holds information on the container image to be pulled, ports to listen on, environment variables drawn from secrets and configmaps etc.

For this particular manifest file (mongo.yaml), Kubernetes will create a deployment object that will maintain the state of a single pod, which will pull the MongoDB image and listen on port 27017 while making reference to the username and password that has been stored in another Kubernetes object called a secret. It will also create an internal service (ClusterIP) that will aid in the communication between this MongoDB pod and the mongo express pod.

Mongoexpress.yaml;

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo-express
  labels:
    app: mongo-express
spec:
  selector:
    matchLabels:
      app: mongo-express
  template:
    metadata:
      labels:
        app: mongo-express
    spec:
      containers:
      - name: mongo-express
        image: mongo-express
        ports:
        - containerPort: 8081
        env:
          - name: ME_CONFIG_MONGODB_ADMINUSERNAME
            valueFrom:
              secretKeyRef:
                name: mongodb-secret
                key: mongo-root-username
          - name: ME_CONFIG_MONGODB_ADMINPASSWORD
            valueFrom:
              secretKeyRef:
                name: mongodb-secret
                key: mongo-root-password
          - name: ME_CONFIG_MONGODB_SERVER
            valueFrom:
              configMapKeyRef:
                name: mongodb-configmap
                key: database_url

---
apiVersion: v1
Kind: Service
metadata:
  name: mongoexpress-service
spec:
  selector:
    app: mongo-express
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 8081
      targetPort: 8081
      nodePort: 30000

This also follows the same structure explained earlier for the mongo.yaml file except that it pulls the mongo-express image and listens on port 8081, makes reference to the username and password in the Kubernetes secret object for authentication into the database, and also makes reference to the config map object that helps it identify the MongoDB pod inside the cluster through the internal service. In this file, an external service(Loadbalancer) is created to enable or facilitate incoming external connections to the pod.

Secret.yaml;

apiVersion: v1
kind: Secret
metadata:
  name: mondb-secret
type: Opaque
data:
  mongo-root-username: <your username> 
  mongo-root-password: <your password>

In this file, Kubernetes creates an object of type "secret" which stores sensitive information such as username and password for the database.

configmap.yaml;

apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb-configmap
data:
  database_url: mongodb-service

The configmap file holds configuation data that helps the mongo express app connect to the mongdb pod.

Architectural representation

Blank diagram.png

The above diagram describes the workflow of the client connection to the database through the services. The client computer makes a connection to the load balancer on port 8081, then through a node port on port 30000 then to the mongo express app on listening on port 8081. The mongo express app, through the config map and secrets, makes a connection to the internal service (clusterIP) on port 27017 which forwards the connection to the database on port 27017.

Starting the cluster and applying templates

Now that we understand the workflow and the syntax involved in the entire process, lets move on to create the minikube cluster and apply our template files.

You can start the minikube cluster with the following command;

minikube start

You should be getting the following output;

minikube start.PNG

Now run the following commands to create our desired state;

kubectl apply -f secret.yaml
kubectl apply -f configmap.yaml
kubectl apply -f mongo.yaml
kubectl apply -f mongoexpress.yaml

To check if the pods, deployments, and services are active, run the following commands;

kubectl get pods
kubectl get deployments
kubectl get services

you should be having something similar to what i have below;

results.PNG

Now run the following command to get the external IP address of the loadbalancer service we created earlier;

minikube service mongoexpress-service

an IP would be automatically generated, you can use that to access your application.

expose service.PNG

here is a screenshot of the application below;

database view.PNG

congratulations!!!, you just successfully set up a web app and database using minikube.

Conclusion

Minikube will serve well for testing, learning, and development purposes. So if you are a beginner, this is a great place to start and when you are comfortable you can graduate to bigger k8s systems, deploying bigger applications as your skills mature. There are other easy ways of setting up a cluster and they include managed cloud services such as EKS, GKE, AKS etc. You can also leverage that for more powerfull nodes to host compute or data intensive applications.

Thanks for reading and practicing, feel free to like and share.