Kubernetes Learning Part V: Workload Resource - StatefulSet
Tags: kubernetes, beginners, tutorial
Published: 2022-04-25 10:17:26

## Kubernetes Learning Part V
## Table of content
- [Introduction](#introduction)
- [StatefulSets](#statefulsets)
- [Usecase for choosing StatefulSet](#usecase-for-choosing-statefulset)
- [What makes a StatefulSet config](#what-makes-a-statefulset-config)
- [How to deploy a StatefulSet](#how-to-deploy-a-statefulset)
 - [Step 1 Define Database Storage as Persistent volume](#step-1-define-databse-storage-as-persistent-volume)
 - [Step 2 Define Database Creds as Secret](#step-2-define-database-creds-as-secret)
 - [Step 3 Define Database Service](#step-3-define-database-service)
 - [Step 4 Define StatefulSet for Database deployment](#step-4-define-statefulset-for-database-deployment)
- [Deploy Database using Kubectl command](#deploy-database-using-kubectl-command)
- [Verify Deployed Database](#verify-deployed-database)
- [Notable other details](#notable-other-details)
 - [Deployment and Scaling Guarantees](#deployment-and-scaling-guarantees)
 - [Update strategies](#update-strategies)
- [Conclusion](#conclusion)
- [Further Reference](#further-reference)

---

## Introduction
- This is article is In this blog, we will continue discuss  `Workload Resource` - `StatefulSets`
- Topics and definitions are hand picked from Kubernetes documentation for the purpose of learning.
- This blog is part of the Kubernetes [Learning series](https://dev.to/chefgs/series/17756)

## StatefulSets
- StatefulSet helps to create/manage one or more related Pods that requires to track the state of the Pods
- For example, if any workload needs to store data persistently, then we can run it as a StatefulSet that matches each Pod with a PersistentVolume. 
- Our code, running in the Pods for that StatefulSet, can replicate data to other Pods in the same StatefulSet to improve overall resilience.

## Usecase for choosing StatefulSet
- Major use case of StatefulSet is, If we want to use **storage volumes to provide persistence** for our ***workload***, a StatefulSet can be chosen. 
- Even though individual Pods in a StatefulSet is failed, the **persistent Pod identifiers** make it easier to match existing volumes to the new Pods, that replace any pods that have failed earlier.
- As mentioned in the [docs](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#using-statefulsets),

> StatefulSets are valuable for applications that require one or more of the following.

> - Stable, unique network identifiers.
- Stable, persistent storage.
- Ordered, graceful deployment and scaling.
- Ordered, automated rolling updates.

## What makes a StatefulSet config
- There are few important concepts we need to know, for creating StatefulSet
 - ***Persistent volume*** - Storage definition, A PersistentVolume (PV) is a piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned using Storage Classes.
 - ***Persistent volume claim*** - Storage definition identifier request by user. It is similar to a Pod. Pods consume node resources and PVCs consume PV resources. Pods can request specific levels of resources (CPU and Memory). Claims can request specific size and access modes. 
As we discussed earlier, StatefulSet is suitable for apps with persistent storage. So we need to have config defined for _Persistent volume_ and _Persistent volume claim_. Refer the [persistent-volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) docs for more details
- Also we require below configs as well,
 - ***Service*** config - Definition of application service
 - ***StatefulSet*** config - Definition of application deployment

## How to deploy a StatefulSet
- Let us now try to create a `MySql` database using the `StatefulSet` configuration

### Step 1 Define Database Storage as Persistent volume
- Create config for PV and PVC for DB storage
- We create a PVC requesting 20gig of storage and hence the PV creates a storage for 20gig of storage in the cluster
```
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
```

### Step 2 Define Database Creds as Secret
- Create config `mysql` database, `username` and `password` as secret
- Below example shows the creation of credentials using `secret` config, using the `base64` encoded string
- For the purpose of demo, it is not recommended to expose username and password in config yamls, it is recommended to integrate secret management tools for this purpose.
```
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-password
type: Opaque
# Run `echo -n 'admin' | base64` to get the base64 encoded value
data:
  password: YWRtaW4=
  user: YWRtaW4K
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-user
type: Opaque
# Run `echo -n 'admin' | base64` to get the base64 encoded value
data:
  user: YWRtaW4K
```

### Step 3 Define Database Service
- We are creating a service that configures port 3306 as `port` and `targetPort`
- What is the difference between port and targetPort
 - Applications running inside the 
```
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
    - name: mysql-ports
      port: 3306
      targetPort: 3306
  selector:
    app: mysql
  clusterIP: None
```

### Step 4 Define StatefulSet for Database deployment
In the below example:
- The StatefulSet, named `mysql`, has a Spec that indicates that 2 replicas of the `mysql` container will be launched in Pods.
- The `volumes` section of config will provide stable storage using `PersistentVolumes` provisioned by a PersistentVolume Provisioner.
- Pod Selector should match with the `.spec.selector` field of a StatefulSet to match the labels of its `.spec.template.metadata.labels`. Failing to specify a matching Pod Selector will result in a validation error during StatefulSet creation.

```
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  serviceName: mysql
  replicas: 2
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - image: mysql:5.7
          name: mysql
          env:
            - name: MYSQL_USER
              valueFrom:
                secretKeyRef:
                  name: mysql-user
                  key: user
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-password
                  key: password
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-password
                  key: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-persistent-storage
          persistentVolumeClaim:
            claimName: mysql-pv-claim

```

---


## Deploy Database using Kubectl command
- Let us now execute `kubectl` command and see what is created
- First execute the PV and PVC to create storage config
```
$ kubectl apply -f mysql_pv_pvc.yml 
persistentvolume/mysql-pv-volume created
persistentvolumeclaim/mysql-pv-claim created
```
- Then execute stateful set creation config for database
```
$ kubectl apply -f mysql_statefulset_app.yml 
secret/mysql-password created
secret/mysql-user created
service/mysql created
statefulset.apps/mysql created
```
- We can also check the deployed pods using the command below,
```
$ kubectl get pods
NAME                          READY   STATUS    RESTARTS        AGE
mysql-0                       1/1     Running   0               11s
mysql-1                       1/1     Running   0               10s
```
- Since we deployed the pods in default namespace, we can use the `get pods` command without any namespaces

**Pod Replica Behavior**
- It shows the 2 replicas of `mysql` pods. In case if any healthy replica is failed, replica config make sure that, it spins up new pods and terminating other 

---

## Verify Deployed Database
- We can test the deployed database server by running a MySQL client to connect to the server
- This command creates a new Pod in the cluster running a MySQL client and connects it to the server through the Service. 
``` 
$ kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -padmin
```
- If it connects, we can see the `mysql>` prompt. This shows our stateful MySQL database is up and running.
```
If you don't see a command prompt, try pressing enter.

mysql> 
```

---

## Notable other details
### Deployment and Scaling Guarantees
For a StatefulSet with `N` replicas, 
- When Pods are being deployed, they are created sequentially, in order from `{0..N-1}`.
- When Pods are being deleted, they are terminated in reverse order, from {N-1..0}.
- Before a scaling operation is applied to a Pod, all of its predecessors must be Running and Ready.
- Before a Pod is terminated, all of its successors must be completely shutdown.

### Update strategies
- A StatefulSet's `.spec.updateStrategy` field allows you to configure and disable automated rolling updates for containers, labels, resource request/limits, and annotations for the Pods in a `StatefulSet`. There are two possible values:
- OnDelete
When a StatefulSet's `.spec.updateStrategy.type` is set to OnDelete, the StatefulSet controller will not automatically update the Pods in a StatefulSet. Users must manually delete Pods to cause the controller to create new Pods
- RollingUpdate
The `RollingUpdate` update strategy implements automated, rolling update for the Pods in a StatefulSet. This is the **`default update strategy`**.

## Conclusion
Hope this article is helpful to people getting started with kubernetes concepts on `StatefulSets`

Thanks for reading!


## Further Reference
[Workload Resources](https://kubernetes.io/docs/concepts/workloads/controllers/)
[StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
[Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)
[Configure Persistent Volumes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/)
[Basics of StatefulSet](https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/)
https://kubernetes.io/docs/concepts/workloads/

## Full Source of Tutorial
- The full source code can be accessible in this [GitHub repo](https://github.com/chefgs/learning2021/blob/main/kubernetes/mysql_singlenode_stateful_app/)


### Follow me on,
- [GitHub](https://github.com/chefgs)
- [LinkedIn](https://www.linkedin.com/in/saravanan-gnanaguru-1941a919/)
- [Twitter](https://twitter.com/saransid)
- [gsaravanan.dev](https://gsaravanan.dev)