Kubernetes Learning Part V: Workload Resource - StatefulSet

Saravanan Gnanaguru - Apr 25 '22 - - Dev Community

Kubernetes Learning Part V

Table of content


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

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,

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 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode
  • 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> 
Enter fullscreen mode Exit fullscreen mode

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
StatefulSet
Persistent Volumes
Configure Persistent Volumes
Basics of StatefulSet
https://kubernetes.io/docs/concepts/workloads/

Full Source of Tutorial

  • The full source code can be accessible in this GitHub repo

Follow me on,

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .