Kubernetes StatefulSet — Examples & Best Practices

Picture of a beach with some rocks stacked up in a vertical pile

What Are Stateful Applications?

What Are StatefulSets?

When to Use StatefulSets

  1. Assume you deployed a MySQL database in the Kubernetes cluster and scaled this to three replicas, and a frontend application wants to access the MySQL cluster to read and write data. The read request will be forwarded to three Pods. However, the write request will only be forwarded to the first (primary) Pod, and the data will be synced with the other Pods. You can achieve this by using StatefulSets.
  2. Deleting or scaling down a StatefulSet will not delete the volumes associated with the stateful application. This gives you your data safety. If you delete the MySQL Pod or if the MySQL Pod restarts, you can have access to the data in the same volume.

Deployment vs. StatefulSets

my-app-123ab
my-app-098bd
my-app-890yt
my-app-jk879
my-app-kl097
my-app-76hf7
  1. Ordered numbers for each Pod
  2. The first Pod can be a primary, which makes it a good choice when creating a replicated database setup, which handles both reading and writing
  3. Other Pods act as replicas
  4. New Pods will only be created if the previous Pod is in running state and will clone the previous Pod’s data
  5. Deletion of Pods occurs in reverse order

How to Create a StatefulSet in Kubernetes

Create a Secret

apiVersion: v1
kind: Secret
metadata:
name: mysql-password
type: opaque
stringData:
MYSQL_ROOT_PASSWORD: password
kubectl apply -f mysql-secret.yaml
kubectl get secrets

Create a MySQL StatefulSet Application

kubectl get pvNAME                   CAPACITY   ACCESS MODES   RECLAIM     STATUS
pvc-e0567 10Gi RWO Retain Bound
kubectl get pvc

NAME STATUS VOLUME CAPACITY ACCESS
mysql-store-mysql-set-0 Bound pvc-e0567d43ffc6405b 10Gi RWO
kubectl get storageclass

NAME PROVISIONER RECLAIMPOLICY
linode-block-storage linodebs.csi.linode.com Delete
linode-block-storage-retain (default) linodebs.csi.linode.com Retain
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-set
spec:
selector:
matchLabels:
app: mysql
serviceName: "mysql"
replicas: 3
template:
metadata:
labels:
app: mysql
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-store
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-password
key: MYSQL_ROOT_PASSWORD
volumeClaimTemplates:
- metadata:
name: mysql-store
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "linode-block-storage-retain"
resources:
requests:
storage: 5Gi
  1. The kind is a StatefulSet. kind tells Kubernetes to create a MySQL application with the stateful feature.
  2. The password is taken from the Secret object using the secretKeyRef.
  3. The Linode block storage was used in the volumeClaimTemplates. If you are not mentioning any storage class name here, then it will take the default storage class in your cluster.
  4. The replication count here is 3 (using the replica parameter), so it will create three Pods named mysql-set-0, mysql-set-1, and mysql-set-2.
kubectl apply -f mysql.yaml
kubectl get pods

NAME READY STATUS RESTARTS AGE
mysql-set-0 1/1 Running 0 142s
mysql-set-1 1/1 Running 0 132s
mysql-set-2 1/1 Running 0 120s

Create a Service for the StatefulSet Application

apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
clusterIP: None
selector:
app: mysql

Create a Client for MySQL

apiVersion: v1
kind: Pod
metadata:
name: mysql-client
spec:
containers:
- name: mysql-container
image: alpine
command: ['sh','-c', "sleep 1800m"]
imagePullPolicy: IfNotPresent
kubectl apply -f mysql-client.yaml
kubectl exec --stdin --tty mysql-client -- sh
apk add mysql-client

Access the MySQL Application Using the MySQL Client

kubectl exec -it mysql-client /bin/sh
mysql -u root -p -h host-server-name
stateful_name-ordinal_number.mysql.default.svc.cluster.local

#Example
mysql-set-0.mysql.default.svc.cluster.local
mysql -u root -p -h mysql-set-0.mysql.default.svc.cluster.local
create database erp; exit;
mysql -u root -p -h mysql-set-1.mysql.default.svc.cluster.local mysql -u root -p -h mysql-set-2.mysql.default.svc.cluster.local

Best Practices

  1. Create a separate namespace for databases.
  2. Place all the needed components for stateful applications, such as ConfigMaps, Secrets, and Services, in the particular namespace.
  3. Put your custom scripts in the ConfigMaps.
  4. Use headless service instead of load balancer service while creating Service objects.
  5. Use the HashiCorp Vault for storing your Secrets.
  6. Use the persistent volume storage for storing the data. Then your data won’t be deleted even if the Pod dies or crashes.

Conclusion

--

--

--

>> www.loft.sh << Build Your Internal Kubernetes Platform With Virtual Clusters, Namespace Self-Service & Secure Multi-Tenancy

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Create TestNG Project In Eclipse & Run Selenium Test Script

Cleaning your Code

Advantages Of Web Development With Ruby On Rails

A Spotlight on CSS Viewport Units

Viewport Within a Web Browser

Build a Python CI/CD System; Code quality with pylint

Tips for Developing User-Facing Tools in Jupyter Notebooks

Problems You Didn’t Know Existed In Control Valves.

File system backup for Ceph in Kubernetes

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Loft Labs

Loft Labs

>> www.loft.sh << Build Your Internal Kubernetes Platform With Virtual Clusters, Namespace Self-Service & Secure Multi-Tenancy

More from Medium

Using Kubernetes Ephemeral Containers for Troubleshooting

A time lapse picture of blue and red streaks of light

YAKDT: Yet Another Kubernetes Development Toolkit

Kubernetes Security

How to Create Deployments and Services in Kubernetes? | ARMO