Guide to Kubernetes StatefulSet – When to Use It and Examples
Kubernetes automates container management tasks so you can efficiently deploy and scale your workloads. It can distribute your containers across clusters of hundreds or thousands of Nodes.
Most developers begin using Kubernetes for stateless apps. A stateless system doesn’t modify its environment or write any persistent data. These components are easy to deploy to Kubernetes because their container instances are interchangeable.
Kubernetes can also be used for stateful systems, though. This could be a database, a backend that writes files to persistent volumes, or a service where one replica is elected the leader to gain control of its neighbors.
In this article, you’ll learn how to use StatefulSet objects to reliably manage state in your cluster.
What are Kubernetes StatefulSets?
StatefulSets are used to manage stateful applications that require persistent storage, stable unique network identifiers, and ordered deployment and scaling. They are very useful for databases and data stores that require persistent storage or for distributed systems and consensus-based applications such as etcd and ZooKeeper.
A StatefulSet’s YAML manifest defines a template for its Pods. Kubernetes automatically creates, replaces, and deletes Pods as you scale the StatefulSet, while preserving any previously assigned identities.?
StatefulSets provide several advantages over the ReplicaSet and Deployment controllers used for stateless Pods:
StatefulSet vs. DaemonSet vs. Deployment
While all three are pretty similar, and their main purpose is to create pods based on your configuration, they are used for the following:
Read more: Kubernetes StatefulSet vs Deployment
When to use StatefulSets?
StatefulSets should be used when you’re deploying an application that requires stable identities for its Pods. Reach for a StatefulSet instead of a ReplicaSet or Deployment if your system will be disrupted when a specific Pod replica is replaced.
Replicated databases are a good example of the scenarios that StatefulSets accommodate. One Pod acts as the primary database node, handling both read and write operations, while additional Pods are deployed as read-only replicas.
Although each Pod may run the same container image, each one needs special configuration to set whether it’s in primary or read-only mode. This means your Pods possess their own state:
Regular ReplicaSets and Deployments aren’t suitable for this situation. Scaling down a Deployment removes arbitrary Pods, which could include the primary node in your database system. When you use a StatefulSet, Kubernetes terminates Pods in the opposite order to their creation. This ensures it’ll be postgres-2 that’s destroyed first.
Several other StatefulSet features also apply to this example:
StatefulSet example: Running PostgreSQL in Kubernetes
Ready to put this example into practice? Here’s how to run three replicas of PostgreSQL in Kubernetes using a StatefulSet.
####first we want to create dynamic storage and manage the local disk resources.
###to do the same simply install the fillowing utilitly
kubectl get sc
###currenly there is no storage class
领英推荐
###storage class in k8s are being used to dynamically create storage in the cloud.
##below mentioned utility will create a local storage class
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.24/deploy/local-path-storage.yaml
##########
kubectl get sc
####this will show you the storage class whcih is being created
local-path rancher.io/local-path Delete WaitForFirstConsumer false 3h1m
Creating a StatefulSet
First, create a headless service for your deployment. A headless service is a service that defines a port binding but has its clusterIP set to None. StatefulSets require you to create a headless service to control their network identities.
Copy the following YAML and save it as postgres-service.yaml in your working directory:
apiVersion: v1
kind: Service
metadata:
name: postgres
labels:
app: postgres
spec:
ports:
- name: postgres
port: 5432
clusterIP: None
selector:
app: postgres
Use Kubectl to add the service to your cluster:
$ kubectl apply -f postgres-service.yaml
service/postgres created
Next, copy the following YAML to postgres-statefulset.yaml. It defines a StatefulSet that runs three replicas of the postgres:latest image.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
selector:
matchLabels:
app: postgres
serviceName: postgres
replicas: 3
template:
metadata:
labels:
app: postgres
spec:
initContainers:
- name: postgres-init
image: postgres:latest
command:
- bash
- "-c"
- |
set -ex
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
if [[ $ordinal -eq 0 ]]; then
printf "I am the primary"
else
printf "I am a read-only replica"
fi
containers:
- name: postgres
image: postgres:latest
env:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
ports:
- name: postgres
containerPort: 5432
livenessProbe:
exec:
command:
- "sh"
- "-c"
- "pg_isready --host $POD_IP"
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 5
readinessProbe:
exec:
command:
- "sh"
- "-c"
- "pg_isready --host $POD_IP"
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-path"
resources:
requests:
storage: 1Gi
Apply the manifest to your cluster to create your StatefulSet:
$ kubectl apply -f postgres-statefulset.yaml
statefulset.apps/postgres created
Now you can list the Pods running in your cluster. The names of the three Pods from your StatefulSet will be suffixed with the sequential index they’ve been assigned:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
postgres-0 1/1 Running 0 74s
postgres-1 1/1 Running 0 63s
postgres-2 1/1 Running 0 51s
The StatefulSet creates each Pod in order, once the previous one has entered the Running state. This ensures the replicas don’t start until the previous Pod is ready to synchronize data. If a ReplicaSet had been used, all three Pods would have been created at the same time.
The StatefulSet uses init containers to determine whether new Pods are the Postgres primary or a replica. Each init container inspects its numeric index assigned by the StatefulSet controller; if it’s 0, the Pod is the first in the StatefulSet, so it becomes the primary database node.
Otherwise, it’s a replica:
$ kubectl logs postgres-0 -c postgres-init
I am the primary
$ kubectl logs postgres-1 -c postgres-init
I am a read-only replica
This demonstrates how StatefulSets let you consistently designate Pods as having a specific role. In a real-life Postgres example, you’d use your init containers to set up database replication from the primary Pod to the replicas. When the Pod’s index is 0, it should be configured as the primary; when it’s a higher number, the Pod is a replica that needs to synchronize the existing data and run in read-only mode.
Each Pod in the StatefulSet gets its own Persistent Volume and Persistent Volume Claim. These are created using the manifest template defined in the StatefulSet’s volumeClaimTemplates field.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-6b48180c-0728-4666-aea9-12e0960f732e 1Gi RWO Delete Bound postgres-sts/data-postgres-0 standard 10m
pvc-83fc4a44-4927-454e-83e8-2c2f4c80af07 1Gi RWO Delete Bound postgres-sts/data-postgres-1 standard 10m
pvc-d7496cf0-97d2-405b-bf95-b28bf9bcedec 1Gi RWO Delete Bound postgres-sts/data-postgres-2 standard 10m
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-postgres-0 Bound pvc-6b48180c-0728-4666-aea9-12e0960f732e 1Gi RWO standard 10m
data-postgres-1 Bound pvc-83fc4a44-4927-454e-83e8-2c2f4c80af07 1Gi RWO standard 10m
data-postgres-2 Bound pvc-d7496cf0-97d2-405b-bf95-b28bf9bcedec 1Gi RWO standard 10m
This allows the Pods to manage their own state, independently of the others in the StatefulSet.