Skip to main content

Chapter 7.1 - In-Depth StatefulSets

Learning Objectives​

By the end of this chapter, you will be able to:

  • Understand StatefulSets in depth
  • Deploy databases with StatefulSets
  • Manage scaling and updates
  • Understand init containers in StatefulSets
  • Implement master-slave replication
  • Manage quorums and leader election

Introduction​

StatefulSets are essential for stateful applications. This chapter dives deeper into the concepts and presents advanced use cases.


StatefulSet Architecture​

Components​


Example: PostgreSQL Database​

Step 1: Headless Service​

apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
clusterIP: None
selector:
app: postgres
ports:
- port: 5432
name: postgres

Step 2: ConfigMap for Configuration​

apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
data:
postgresql.conf: |
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB

Step 3: Secret for Credentials​

apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
type: Opaque
data:
postgres-password: cG9zdGdyZXM= # base64
replication-password: cmVwbGljYXRpb24= # base64

Step 4: StatefulSet​

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
initContainers:
- name: init-postgres
image: postgres:14
command:
- /bin/bash
- -c
- |
set -e
if [[ $HOSTNAME == "postgres-0" ]]; then
echo "Initializing primary database..."
else
echo "Waiting for primary to be ready..."
until pg_isready -h postgres-0.postgres; do
sleep 1
done
fi
env:
- name: PGHOST
value: postgres-0.postgres
containers:
- name: postgres
image: postgres:14
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: postgres-password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
- name: postgres-config
mountPath: /etc/postgresql
volumes:
- name: postgres-config
configMap:
name: postgres-config
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 50Gi

Init Containers​

Init containers run before the main containers and can:

  • Initialize the database
  • Wait for other services to be ready
  • Configure the environment
  • Check dependencies

Example:

spec:
template:
spec:
initContainers:
- name: wait-for-db
image: busybox:1.35
command:
- sh
- -c
- |
until nc -z postgres-0.postgres 5432; do
echo "Waiting for database..."
sleep 2
done
containers:
- name: app
image: my-app:1.0

Master-Slave Replication​

Architecture​

Configuration:

spec:
template:
spec:
containers:
- name: postgres
env:
- name: POSTGRES_REPLICATION_MODE
value: "master" # For postgres-0
- name: POSTGRES_MASTER_HOST
value: "postgres-0.postgres" # For postgres-1, postgres-2

Quorum and Leader Election​

For applications requiring a quorum (e.g., etcd, Consul):

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
spec:
serviceName: etcd
replicas: 3 # Quorum: (3/2) + 1 = 2
template:
spec:
containers:
- name: etcd
image: quay.io/coreos/etcd:v3.5.0
env:
- name: ETCD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: ETCD_INITIAL_CLUSTER
value: "etcd-0=http://etcd-0.etcd:2380,etcd-1=http://etcd-1.etcd:2380,etcd-2=http://etcd-2.etcd:2380"

Quorum: (N/2) + 1, where N = number of replicas


Scaling​

Scaling Up​

kubectl scale statefulset postgres --replicas=5

Process:

  1. Creates postgres-3
  2. Waits for postgres-3 to be Ready
  3. Creates postgres-4
  4. Waits for postgres-4 to be Ready

Scaling Down​

kubectl scale statefulset postgres --replicas=2

Process:

  1. Deletes postgres-4 (last)
  2. Waits for postgres-4 to terminate
  3. Deletes postgres-3
  4. Waits for postgres-3 to terminate

Important: For databases, ensure data is backed up before scaling down.


Updates​

Rolling Update​

spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0 # Update all Pods

Process:

  1. Updates postgres-2 (last)
  2. Updates postgres-1
  3. Updates postgres-0 (first)

Partial Update (Canary)​

spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Keeps postgres-0 and postgres-1 on the old version

Result: Only postgres-2 is updated.


Useful Commands​

Management​

# View StatefulSets
kubectl get statefulset
kubectl get sts

# Details
kubectl describe statefulset postgres

# View Pods
kubectl get pods -l app=postgres

# Logs of a specific Pod
kubectl logs postgres-0

Scaling​

# Scaling up
kubectl scale statefulset postgres --replicas=5

# Scaling down
kubectl scale statefulset postgres --replicas=2

Updates​

# Update the image
kubectl set image statefulset/postgres postgres=postgres:15

# View update status
kubectl rollout status statefulset/postgres

# Rollback
kubectl rollout undo statefulset/postgres

Best Practices​

1. Regular Backups​

Set up automatic volume backups.

2. Monitoring​

Monitor the status of each Pod and performance metrics.

3. Appropriate Quorum​

Use an odd number of replicas to avoid ties.

4. Init Containers​

Use init containers for initialization and checks.

5. Headless Service​

Always create a headless service for DNS discovery.


Summary​

In this chapter, you learned:

In-depth StatefulSets: Stateful applications with stable identities
Databases: Deploying PostgreSQL with replication
Init containers: Initialization before main containers
Replication: Master-slave configuration
Quorum: Calculation and configuration for distributed applications
Scaling: Guaranteed order (ascending/descending)
Updates: RollingUpdate and partial updates
Best practices: Backups, monitoring, quorum


Next Steps​

Chapter 7.2: DaemonSets
Chapter 7.3: Jobs
Chapter 7.4: CronJobs


Chapter created: December 2024