How to Create a YAML Manifest Template in Monokle for Deploying Cassandra with StatefulSet
Tags: devops, kubernetes, cassandra, beginners
Published: 2022-02-20 10:18:40


##➡️ What is Monokle?
Managing and debugging K8s manifests and YAML file with 100 lines of code and soo many K8s objects and resources is quiet difficult sometimes. And here is where Monokle comes into play...

Welcome to Monokle - your friendly desktop UI for managing k8s manifests!

Monokle is an open-source tool developed by Kubeshop that makes it very easy to manage and inspect your Kubernetes Manifests, showing you how your manifests and different objects are connected to each other. You can edit and refractor resources, preview and debug resources, deploy them to our local or cloud K8s cluster and much more!  

Apart from all this Monokel also helps you to create manifests both from scratch and from templates. With templates you don't need to write a yaml file from scratch, you can simply select the template and add few necessary info like namespace, image name, no. of replicas and a manifest file is ready for you to deploy.

If you are new to Monokel, check out this [Intro to Monokel](https://youtu.be/Rdc31plYOLM) video by Kunal Kushwaha to get started with the tool.

##➡️ What will this Template do?
Cassandra is a database designed to handle big data workloads across multiple nodes without a single point of failure. We will be creating a StatefulSet Template that will help us deploying Cassandra Database to a K8s cluster.

In this blog, I will take you through the steps to create and use your own custom Template for Monokel from Scratch.

🚀Lets get started!! 

---

##➡️ Prerequisites
Prior knowledge of working with YAML files, Kubernetes resources and objects like Pods, StatefulSets, Services would be beneficial. You can checkout video tutorials on all these stuff by Kunal Kushwaha in his [DevOps Bootcamp](https://youtube.com/playlist?list=PL9gnSGHSqcnoqBXdMwUTRod4Gi3eac2Ak) playlist. 

##➡️ Setting up the project 
The templates in monokle are installed via a plugin, and that plugin is basically our github repo that contains `package.json` file in order to be a monokle plugin

####✅Step 1: Creating a Github Repository
Create a Github Repository by the name `monokle-templates-plugin`. After that clone that empty repo into your local machine using the following command.

```
git clone https://github.com/YOUR_GITHUB_USERNAME/monokle-templates-plugin.git
```
Now enter into that folder using `cd monokle-templates-plugin`

####✅Step 2: Creating `package.json` file
Create a file named `package.json` inside your root folder for our template plugin.

This file should have the following content:

```json
{
  "name": "Template Plugins",
  "description": "Custom templates plugin",
  "version": "1.0.0",
  "author": "YOUR_NAME",
  "repository": "https://github.com/YOUR_GITHUB_USERNAME/monokle-templates-plugin",
  "monoklePlugin": {
    "id": "com.github.YOUR_GITHUB_USERNAME.plugin.templates.cassandra-statefulset-template",
    "helpUrl": "https://github.com/YOUR_GITHUB_USERNAME/monokle-templates-plugin",
    "modules": [
      {
        "type": "template",
        "path": "cassandra-statefulset-template"
      }
    ]
  }
}
```

> ⚠️ **REMEMBER:** Replace your name and Github username wherever mentioned.

---

##➡️ Creating the Template 
In previous step you created a `package.json` file and inside modules you set some path. So now create a folder named `cassandra-statefulset-template` inside your root folder that has `package.json`.

This folder will actually contain all the files required to create your custom template. It will have following four parts/files:

1. Monokle template configuration
2. Defining Form Schema
3. Defining Form UI
4. Kubernetes YAML manifest with placeholders

###📄File 1: Monokle template configuration
This file is Monokle-specific and defines what type of template you are creating. There are two types of templates in Monokle for now: **vanilla** and **helm-charts**; we will be creating a simple vanilla type template.

Create the file `monokle-template.json` with following content:

```json
{
    "name": "Deploy Cassandra with StatefulSet",
    "id": "com.github.YOUR_GITHUB_USERNAME.plugin.templates.cassandra-statefulset-template",
    "author": "YOUR_NAME",
    "version": "1.0.0",
    "description": "Creates a Deployemnt, Service and Storage Class for Cassandra database using Statefulsets",
    "repository" : "",
    "type": "vanilla",
    "forms": [
      {
        "name": "Deployment Settings",
        "description": "Specify the no. of replicas, namespace, cluster name and related settings of database",
        "schema": "form-schema.json",
        "uiSchema": "form-ui-schema.json"
      }
    ],
    "manifests": [
      {
        "filePath": "template.yaml"  
      }
    ],
    "resultMessage": "Cassandra database deployed successfully",
    "helpUrl": "https://github.com/YOUR_GITHUB_USERNAME/monokle-templates-plugin"
}
```

> ⚠️ **REMEMBER:** Replace your name and Github username wherever mentioned.

###📄File 2: Defining Form Schema
In the code above, we have defined the fields `forms.schema`, this will contain the form fields that we will request the user to input and are necessary to create the manifest file.

Create the file `form-schema.json` with the following content:

```json
{
    "type": "object",
    "required": [
      "name",
      "replicas"
    ],
    "properties": {
      "name": {
        "type": "string",
        "default": "cassandra" 
      },
      "namespace": {
        "type": "string"
      },
      "replicas": {
        "type": "number"
      },
      "cluster_name": {
        "type": "string",
        "default": "K8Demo"
      },
      "datacenter_name": {
        "type": "string",
        "default": "datacenter1-K8Demo"
      },
      "rack_name": {
        "type": "string",
        "default": "rack1-K8Demo"
      }
    }
  }
```

> 📌 **NOTE:** `required` refers to the compulsary input fields, `type` describes the data type of input field, `default` is the default value of the input field.

###📄File 3: Defining Form UI
Monokle uses the react-json-schema-form frontend component to render the forms for these templates. It takes the form fields definition which we just created, and a UI form definition, which we will build next.

Create a file named `form-ui-schema.json`, this will basically defines how the form is going to appear (titles for the fields, descriptions, etc).

```json
{
    "name": {
      "ui:title": "Name",
      "ui:help": "The name of the generated service and deployment"
    },
    "namespace": {
      "ui:title": "Namespace",
      "ui:help": "The target namespace for the generated service and deployment",
      "ui:widget": "namespaceSelection"
    },
    "replicas": {
      "ui:title": "Replicas",
      "ui:help": "Number of pod replicas"
    },
    "cluster_name": {
      "ui:title": "Cassandra Cluster Name",
      "ui:help": "Name of the cluster"
    },
    "datacenter_name": {
      "ui:title": "Cassandra DataCenter Name",
      "ui:help": "Name of the Datacenter where data will be stored. It can be anything, for example datacenter1"
    },
    "rack_name": {
      "ui:title": "Cassandra Rack Name",
      "ui:help": "Name of the Rack where datacenter will be created. It can be anything, for example rack1"
    }
  }
```

> 📌 **NOTE:** `title` refers to name of the input field, `help` describes the field, `widget` creates the dropdown menu.

###📄File 4: Kubernetes YAML manifest template
Finally, this is where we will actually define our Cassandra StatefulSet manifest template with the placeholders that we will update with the form data. This manifest YAML file will contain the following K8s objects and resources to deploy cassandra pods:
1. **StatefulSet** - to create cassandra pods
2. **StorageClass** -  for dynamic provisioning of Persistent Volumes
3. **Service** - for connecting pods to each other and client 

Create the `template.yaml` file with the following config:

```yml
# This is your StatefulSet to create pods
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: [[forms[0].name]]
  labels:
    app: [[forms[0].name]]
[[ forms[0].namespace ? "  namespace: " + forms[0].namespace + "\n" : ""]]
spec:
  serviceName: [[forms[0].name]]
  replicas: [[forms[0].replicas]]
  selector:
    matchLabels:
      app: [[forms[0].name]]
  template:
    metadata:
      labels:
        app: [[forms[0].name]]
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: [[forms[0].name]]
        image: gcr.io/google-samples/cassandra:v13
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
            cpu: "500m"
            memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: 
              - /bin/sh
              - -c
              - nodetool drain
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: [[forms[0].cluster_name]]
          - name: CASSANDRA_DC
            value: [[forms[0].datacenter_name]]
          - name: CASSANDRA_RACK
            value: [[forms[0].rack_name]]
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        # These volume mounts are persistent. They are like inline claims,
        # but not exactly because the names need to match exactly one of
        # the stateful pod volumes.
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  # These are converted to volume claims by the controller
  # and mounted at the paths mentioned above.
  # do not use these in production until ssd GCEPersistentDisk or other ssd pd
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: fast
      resources:
        requests:
          storage: 1Gi
---
# StorageClass for Persistent Volume Claim
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd
---
# This is your Service for the pods
apiVersion: v1
kind: Service
metadata:
  labels:
    app: [[forms[0].name]]
  name: [[forms[0].name]]
[[ forms[0].namespace ? "  namespace: " + forms[0].namespace + "\n" : ""]]
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: [[forms[0].name]]
```

> 📌 **NOTE:** As you can see, for placeholders we are using format **[[forms[0].name]]** where `form[0]` is used to access the form defined in `monokle-template.json`, and `.name` refers to one of our input field defined in the form schema.

👏🏻Awesome, with this step we have finally created our Monokle Template. 

---

##➡️ Uploading the Template to Github Repo 
Now that we have defined our template configuration, the form definition and our manifest with its placeholders, it's time to push them to our Github Repo we made and cloned in the first step, so that we can import our template in Monokle in the later step.

Before proceeding, verify if you are in your root folder i.e `monokle-templates-plugin`
```
git add .
git commit -m "cassandra monokle template"
git push origin main
```

##➡️ Importing your Template in Monokle
We need to import the template form our Github repo to Monokle in our configured project.

####✅Step 1:
Download Monokle from here, Open the Monokle Interface and you can find **Open Pugins Manager** button on the top-right corner.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hti23ufsrx4ypeuipn16.jpg)

####✅Step 2:
Click on **install** pulgin. Copy the url of your template Github repo and paste it here. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axm9h3cqniz4llbxh3b6.jpg)

Click on **Download and install pulgin**.

####✅Step 3:
To verify if your template is successfully imported, it should appear in Plugins Manager with the name **Template Pulgins** 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5u1tbtdxfny6ucoe9hw1.jpg)

👏🏻Awesome, we have successfully imported our template. It's time to test out our custom cassandra template.

##➡️ Trying out your Template
Lets use our template to create the resources required to deploy the Cassandra pods by filling up the template form.

####✅Step 1:
Start a new project in monokle by selecting **Start from a template**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j5lmxggd663ctxakooi5.jpg) 

####✅Step 2:
Assign the project name, I named it **Awesome Monokle** because it is actually amazing.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bl2urxts1sllirc6hxks.jpg)

Now proceed further to **Select a Template**

####✅Step 3:
You can find your custom template that you just imported with the name **Deploy Cassandra with StatefulSet**. Use this template and proceed futher to fill up the template form.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ex1gzp4mtky0fxicqvc.jpg)

####✅Step 4:
Fill up all the fields in the deployment settings or keep them to default options as required.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6tnhjhd6asgw3r5v3aba.jpg)

Click on **Submit** button and you can see that following resources have been created successfully.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1u1b0ipi88u6inwcg4ja.jpg)

####✅Step 5:
Go back to your project. Have a look at your navigator and you can find StatefulSets, Services, and StorageClass created with the name **my-cassandra**, by your cassandra template.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcc3j1fck3s8aovmniwj.jpg)

To deploy cassandra pods to your K8s cluster, click on the **Deploy** button on the top-right corner in Editor.

🥳Congratulations!!🥳 Now sit back, relax and give yourself some time to appreciate that you have created your own custom template and used it on Monokel.

🚀 Feel free to contact me on my socials if you get stuck anywhere [LinkedIn](https://www.linkedin.com/in/yash-gangwar-6bab161b5/), [Github](https://github.com/yashgangwar7558), [Twitter](https://twitter.com/yashgangwar7558) 

🧑‍💻Happy Learning !!👩‍💻

---

##📚 Some more resources...
1) All the files and code mentioned above can be found on my [Github Repo](https://github.com/yashgangwar7558/monokle-templates-plugin).
2) Want to create a basic pod template? Checkout [tutorial](https://youtu.be/Yn6Ly1eaZ4g) by Kunal Kushwaha and [blog](https://kubeshop.io/blog/how-to-create-a-yaml-manifest-template-in-monokle) by Kubeshop.
3) Learn more about Monokel? Checkout their official [documentation](https://kubeshop.github.io/monokle/).