Database persistence in kubernetes. Deployments vs Statefulsets

Erwan Riou
3 min readMay 5, 2021

When you build a microservice architecture, whenever on premise or in the cloud, you are confronted at one point to the persistence of your data. Kubernetes is a great solution regarding microservices and container solution, handling both stateless and stateful application. In this article let’s see more in detail how we can handle with more fiability the persistence of our stateful applications in a kubernetes cluster.

If i say kubernetes, you will automatically think about deployments as the way of managing your pods and replicas. But when we have to handle persistence, this is not the only solution, and actually might not even be the most appropriate one. Statefulset are at the contrary of deployments created for the purpose of persistency in mind and are somehow a better fit when we want to implement persistent applications such as databases.

  1. persistent deployment manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: your-database-deploy
spec:
replicas: 1
selector:
matchLabels:
app: your-database
template:
metadata:
labels:
app: your-database
spec:
volumes:
- name: your-database-storage
persistentVolumeClaim:
claimName: your-database-pvc
containers:
- name: your-database
# THE DOCKER IMAGE OF YOUR DATABASE TYPE, HERE MONGO
image: mongo:4.4.1
volumeMounts:
- name: your-database-storage
# THE PATH OR THE STORED DATA IN YOUR CONTAINER
mountPath: /var/lib/mongo
---
apiVersion: v1
kind: Service
metadata:
name: your-database-srv
spec:
selector:
app: your-database
ports:
- name: your-database
protocol: TCP
port: 27017
targetPort: 27017
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: your-database-pvc
spec:
accessModes:
# CAREFUL, DONT USE ReadWriteOnce with Deployment
- ReadOnlyMany
resources:
requests:
storage: 1Gi

As you can see its straight forward. You make sure to bind a volume to your database container. This binded volume is claiming ressources through the persistent volume claim.
but there is a hick. You cannot manage to use ReadWriteOnce with deployment because it cannot scale. For example, if i would have to have 2 replicas of this database, the 2nd replica would trigger an error as the persistentVolumeClaim would already be in use by the first replica.

This is a common mistake we all do at the beginning and that we learn through the way.

2. Persistent statefulset manifest

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: your-database-deploy
spec:
serviceName: your-database
replicas: 1
selector:
matchLabels:
app: your-database
template:
metadata:
labels:
app: your-database
spec:
terminationGracePeriodSeconds: 10
containers:
- name: your-database
image: mongo:4.4.1
ports:
- containerPort: 27017
volumeMounts:
- name: your-database-pvc
mountPath: /data/db
volumeClaimTemplates:
- metadata:
name: your-database-pvc
annotations:
volume.beta.kubernetes.io/storage-class: "standard"
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: your-database-srv
spec:
selector:
app: your-database
ports:
- name: your-database
protocol: TCP
port: 27017
targetPort: 27017

As you can see the Service remain the same, but there you don’t need to define a specific PersistentVolumeClaim kind. You can use an what we call a volumeClaimTemplate that will generate a specific PersistentVolumeClaim on the fly depending of the need of your statefulset. If your replica scale up, it would generate a 2nd PersistentVolumeClaim based on the volumeClaimTemplate.

This is quite amazing and surprisingly easier to configure and scalable.

3. Summary

As we compared both methods we could see that statefulset are more easier and more configurable than deployment in case of persistency. Statefulsets are leftover from kubernete API beside of been a great use in persistent case such this one. After reading this article you want to consider give it a try!

--

--